

























11import { resolveAcpAgentPolicyError, resolveAcpDispatchPolicyError } from "../../acp/policy.js";
22import { formatAcpRuntimeErrorText } from "../../acp/runtime/error-text.js";
3-import { toAcpRuntimeError } from "../../acp/runtime/errors.js";
3+import { type AcpRuntimeError, toAcpRuntimeError } from "../../acp/runtime/errors.js";
44import { resolveAcpThreadSessionDetailLines } from "../../acp/runtime/session-identifiers.js";
55import {
66isSessionIdentityPending,
@@ -34,6 +34,7 @@ import {
3434resolveAgentTurnAttachments,
3535resolveInlineAgentImageAttachments,
3636} from "./agent-turn-attachments.js";
37+import { resolveFirstContextText } from "./context-text.js";
3738import {
3839createAcpDispatchDeliveryCoordinator,
3940type AcpDispatchDeliveryCoordinator,
@@ -79,19 +80,6 @@ type DispatchProcessedRecorder = (
7980},
8081) => void;
818282-function resolveFirstContextText(
83-ctx: FinalizedMsgContext,
84-keys: Array<"BodyForAgent" | "BodyForCommands" | "CommandBody" | "RawBody" | "Body">,
85-): string {
86-for (const key of keys) {
87-const value = ctx[key];
88-if (typeof value === "string") {
89-return value;
90-}
91-}
92-return "";
93-}
94-9583function resolveAcpPromptText(ctx: FinalizedMsgContext): string {
9684return resolveFirstContextText(ctx, [
9785"BodyForAgent",
@@ -168,6 +156,59 @@ export type AcpDispatchAttemptResult = {
168156counts: Record<ReplyDispatchKind, number>;
169157};
170158159+type AcpDispatchStatsSnapshot = {
160+turns: { queueDepth: number };
161+runtimeCache: { activeSessions: number };
162+};
163+type AcpDispatchOutcome = { kind: "ok" } | { kind: "error"; error: AcpRuntimeError };
164+165+function finishAcpDispatchAttempt(params: {
166+queuedFinal: boolean;
167+dispatcher: ReplyDispatcher;
168+delivery: AcpDispatchDeliveryCoordinator;
169+getStats: () => AcpDispatchStatsSnapshot;
170+sessionKey: string;
171+runId?: string;
172+startedAt: number;
173+outcome: AcpDispatchOutcome;
174+lifecyclePhase?: "end" | "error";
175+recordProcessed: DispatchProcessedRecorder;
176+markIdle: (reason: string) => void;
177+}): AcpDispatchAttemptResult {
178+const counts = params.dispatcher.getQueuedCounts();
179+params.delivery.applyRoutedCounts(counts);
180+const acpStats = params.getStats();
181+const runId = normalizeOptionalString(params.runId);
182+if (runId && params.lifecyclePhase) {
183+emitAgentEvent({
184+ runId,
185+sessionKey: params.sessionKey,
186+stream: "lifecycle",
187+data: {
188+phase: params.lifecyclePhase,
189+startedAt: params.startedAt,
190+endedAt: Date.now(),
191+ ...(params.outcome.kind === "error" ? { error: params.outcome.error.message } : {}),
192+},
193+});
194+}
195+if (params.outcome.kind === "ok") {
196+logVerbose(
197+`acp-dispatch: session=${params.sessionKey} outcome=ok latencyMs=${Date.now() - params.startedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`,
198+);
199+params.recordProcessed("completed", { reason: "acp_dispatch" });
200+} else {
201+logVerbose(
202+`acp-dispatch: session=${params.sessionKey} outcome=error code=${params.outcome.error.code} latencyMs=${Date.now() - params.startedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`,
203+);
204+params.recordProcessed("completed", {
205+reason: `acp_error:${normalizeLowercaseStringOrEmpty(params.outcome.error.code)}`,
206+});
207+}
208+params.markIdle("message_completed");
209+return { queuedFinal: params.queuedFinal, counts };
210+}
211+171212const ACP_STALE_BINDING_UNBIND_REASON = "acp-session-init-failed";
172213173214function isStaleSessionInitError(params: { code: string; message: string }): boolean {
@@ -437,6 +478,22 @@ export async function tryDispatchAcpReply(params: {
437478});
438479439480const acpDispatchStartedAt = Date.now();
481+const finishAttempt = (options: {
482+queuedFinal: boolean;
483+outcome: AcpDispatchOutcome;
484+lifecyclePhase?: "end" | "error";
485+}) =>
486+finishAcpDispatchAttempt({
487+ ...options,
488+dispatcher: params.dispatcher,
489+ delivery,
490+getStats: () => acpManager.getObservabilitySnapshot(params.cfg),
491+ sessionKey,
492+runId: params.runId,
493+startedAt: acpDispatchStartedAt,
494+recordProcessed: params.recordProcessed,
495+markIdle: params.markIdle,
496+});
440497try {
441498const dispatchPolicyError = resolveAcpDispatchPolicyError(params.cfg);
442499if (dispatchPolicyError) {
@@ -451,17 +508,10 @@ export async function tryDispatchAcpReply(params: {
451508text: formatAcpRuntimeErrorText(acpResolution.error),
452509isError: true,
453510});
454-const counts = params.dispatcher.getQueuedCounts();
455-delivery.applyRoutedCounts(counts);
456-const acpStats = acpManager.getObservabilitySnapshot(params.cfg);
457-logVerbose(
458-`acp-dispatch: session=${sessionKey} outcome=error code=${acpResolution.error.code} latencyMs=${Date.now() - acpDispatchStartedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`,
459-);
460-params.recordProcessed("completed", {
461-reason: `acp_error:${normalizeLowercaseStringOrEmpty(acpResolution.error.code)}`,
511+return finishAttempt({
512+queuedFinal: delivered,
513+outcome: { kind: "error", error: acpResolution.error },
462514});
463-params.markIdle("message_completed");
464-return { queuedFinal: delivered, counts };
465515}
466516const agentPolicyError = resolveAcpAgentPolicyError(params.cfg, resolvedAcpAgent);
467517if (agentPolicyError) {
@@ -572,28 +622,11 @@ export async function tryDispatchAcpReply(params: {
572622 shouldEmitResolvedIdentityNotice,
573623})) || queuedFinal;
574624575-const counts = params.dispatcher.getQueuedCounts();
576-delivery.applyRoutedCounts(counts);
577-const acpStats = acpManager.getObservabilitySnapshot(params.cfg);
578-const runId = normalizeOptionalString(params.runId);
579-if (runId) {
580-emitAgentEvent({
581- runId,
582- sessionKey,
583-stream: "lifecycle",
584-data: {
585-phase: "end",
586-startedAt: acpDispatchStartedAt,
587-endedAt: Date.now(),
588-},
589-});
590-}
591-logVerbose(
592-`acp-dispatch: session=${sessionKey} outcome=ok latencyMs=${Date.now() - acpDispatchStartedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`,
593-);
594-params.recordProcessed("completed", { reason: "acp_dispatch" });
595-params.markIdle("message_completed");
596-return { queuedFinal, counts };
625+return finishAttempt({
626+ queuedFinal,
627+outcome: { kind: "ok" },
628+lifecyclePhase: "end",
629+});
597630} catch (err) {
598631await projector.flush(true);
599632const acpError = toAcpRuntimeError({
@@ -610,30 +643,10 @@ export async function tryDispatchAcpReply(params: {
610643isError: true,
611644});
612645queuedFinal = queuedFinal || delivered;
613-const counts = params.dispatcher.getQueuedCounts();
614-delivery.applyRoutedCounts(counts);
615-const acpStats = acpManager.getObservabilitySnapshot(params.cfg);
616-const runId = normalizeOptionalString(params.runId);
617-if (runId) {
618-emitAgentEvent({
619- runId,
620- sessionKey,
621-stream: "lifecycle",
622-data: {
623-phase: "error",
624-startedAt: acpDispatchStartedAt,
625-endedAt: Date.now(),
626-error: acpError.message,
627-},
628-});
629-}
630-logVerbose(
631-`acp-dispatch: session=${sessionKey} outcome=error code=${acpError.code} latencyMs=${Date.now() - acpDispatchStartedAt} queueDepth=${acpStats.turns.queueDepth} activeRuntimes=${acpStats.runtimeCache.activeSessions}`,
632-);
633-params.recordProcessed("completed", {
634-reason: `acp_error:${normalizeLowercaseStringOrEmpty(acpError.code)}`,
646+return finishAttempt({
647+ queuedFinal,
648+outcome: { kind: "error", error: acpError },
649+lifecyclePhase: "error",
635650});
636-params.markIdle("message_completed");
637-return { queuedFinal, counts };
638651}
639652}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。