@gajae-code/coding-agent 0.15.6 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +99 -0
- package/dist/types/async/job-manager.d.ts +32 -1
- package/dist/types/browser-backend.d.ts +12 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +14 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/sdk.d.ts +2 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/billing-path.d.ts +25 -0
- package/dist/types/config/config-schema-version.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +5 -0
- package/dist/types/config/model-registry.d.ts +22 -0
- package/dist/types/config/settings-schema.d.ts +27 -8
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -3
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/acp/acp-agent.d.ts +21 -21
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -2
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +79 -2
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
- package/dist/types/sdk/broker/state-version.d.ts +21 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/index.d.ts +9 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +12 -5
- package/dist/types/sdk/cli/session-cli.d.ts +20 -3
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/query/cursor.d.ts +4 -2
- package/dist/types/sdk/host/query/handlers.d.ts +1 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +70 -17
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +3 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
- package/dist/types/sdk/receipt-state.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +6 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/sdk/turn-result.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +52 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +29 -2
- package/dist/types/session/session-manager.d.ts +79 -2
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +16 -0
- package/dist/types/tools/browser/launch.d.ts +10 -0
- package/dist/types/tools/browser/registry.d.ts +8 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -10
- package/dist/types/tools/job.d.ts +3 -1
- package/dist/types/utils/external-editor.d.ts +7 -0
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +7 -4
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +13 -1
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +107 -9
- package/src/browser-backend.ts +29 -0
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/plugin-cli.ts +29 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +140 -23
- package/src/commands/launch.ts +86 -18
- package/src/commands/sdk.ts +135 -28
- package/src/config/autorouting-tier-map.ts +12 -0
- package/src/config/billing-path.ts +42 -0
- package/src/config/config-schema-version.ts +18 -1
- package/src/config/file-lock.ts +158 -83
- package/src/config/model-preset-registry.ts +178 -23
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +291 -42
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +27 -8
- package/src/config/settings.ts +51 -15
- package/src/coordinator-mcp/projection-scan.ts +27 -6
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +115 -21
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +27 -0
- package/src/extensibility/extensions/types.ts +8 -3
- package/src/extensibility/gjc-plugins/installer.ts +51 -11
- package/src/extensibility/runtime-skill-discovery.ts +110 -3
- package/src/extensibility/skills.ts +22 -12
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/launch-worktree.ts +285 -4
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +213 -14
- package/src/gjc-runtime/session-state-sidecar.ts +938 -39
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/main.ts +61 -3
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +610 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/model-selector.ts +76 -8
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/session-selector.ts +23 -15
- package/src/modes/components/sessions-dashboard.ts +8 -5
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +23 -9
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +30 -4
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +30 -2
- package/src/modes/controllers/selector-controller.ts +200 -98
- package/src/modes/interactive-mode.ts +36 -12
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/prompt-action-autocomplete.ts +30 -4
- package/src/modes/runtime-init.ts +12 -9
- package/src/modes/theme/theme.ts +5 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/ui-helpers.ts +22 -3
- package/src/prompts/agent-fragments/browser-aside.md +22 -0
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/ask.md +1 -1
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +5 -0
- package/src/sdk/broker/broker.ts +1646 -27
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +81 -3
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle-ledger.ts +6 -0
- package/src/sdk/broker/lifecycle.ts +440 -75
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +591 -0
- package/src/sdk/broker/startup-budget.ts +144 -2
- package/src/sdk/broker/state-version.ts +43 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
- package/src/sdk/bus/discord-daemon.ts +50 -10
- package/src/sdk/bus/index.ts +84 -7
- package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
- package/src/sdk/bus/lifecycle-commands.ts +4 -0
- package/src/sdk/bus/notification-service.ts +22 -5
- package/src/sdk/bus/prompt-reconciliation.ts +7 -2
- package/src/sdk/bus/reconciliation-store.ts +55 -31
- package/src/sdk/bus/slack-conversation.ts +1 -1
- package/src/sdk/bus/slack-daemon.ts +72 -38
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +41 -15
- package/src/sdk/cli/session-cli.ts +297 -26
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/host/control/dispatch.ts +101 -40
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/events.ts +1 -0
- package/src/sdk/host/host.ts +29 -2
- package/src/sdk/host/query/cursor.ts +52 -7
- package/src/sdk/host/query/handlers.ts +33 -12
- package/src/sdk/host/query/revision-store.ts +12 -0
- package/src/sdk/host/session-runtime.test.ts +673 -14
- package/src/sdk/host/session-runtime.ts +674 -103
- package/src/sdk/host/surface-policy.ts +3 -3
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +17 -4
- package/src/sdk/prompt-deadline-manager.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +105 -3
- package/src/sdk/protocol/operation-registry.ts +14 -4
- package/src/sdk/receipt-state.ts +10 -0
- package/src/sdk/router/session-router.ts +219 -118
- package/src/sdk/session.ts +416 -195
- package/src/sdk/startup-capability.ts +1 -0
- package/src/sdk/turn-result.ts +22 -0
- package/src/session/agent-session.ts +1628 -318
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +40 -3
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/session-manager.ts +850 -97
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/slash-commands/builtin-registry.ts +41 -0
- package/src/task/executor.ts +9 -4
- package/src/telemetry/events.ts +1138 -12
- package/src/tools/ask-contract.test.ts +64 -0
- package/src/tools/ask-contract.ts +36 -1
- package/src/tools/ask.ts +10 -8
- package/src/tools/bash-interactive.ts +11 -2
- package/src/tools/bash.ts +412 -75
- package/src/tools/browser/launch.ts +37 -0
- package/src/tools/browser/registry.ts +99 -9
- package/src/tools/cron.ts +6 -4
- package/src/tools/descriptors.test.ts +15 -0
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +10 -6
- package/src/tools/job.ts +4 -1
- package/src/tools/monitor.ts +12 -9
- package/src/tools/resolve.ts +30 -10
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/utils/external-editor.ts +72 -1
|
@@ -6,9 +6,21 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { sanitizePromptFailure } from "../prompt-failure";
|
|
9
|
-
import type {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import type {
|
|
10
|
+
PromptReconciliationStatus,
|
|
11
|
+
SdkPromptFailureCode,
|
|
12
|
+
SdkPromptStopReason,
|
|
13
|
+
SdkPromptTerminalOutcome,
|
|
14
|
+
TurnPromptReconciliation,
|
|
15
|
+
} from "../prompt-status";
|
|
16
|
+
import { type ReceiptState, reduceReceiptState } from "../receipt-state";
|
|
17
|
+
import {
|
|
18
|
+
reduceTurnResultContent,
|
|
19
|
+
reportableTurnResultContent,
|
|
20
|
+
sanitizeTurnResultContent,
|
|
21
|
+
type TurnResultContent,
|
|
22
|
+
type TurnResultPage,
|
|
23
|
+
} from "../turn-result";
|
|
12
24
|
import {
|
|
13
25
|
PROMPT_RECONCILIATION_ACTIVE_CAPACITY,
|
|
14
26
|
PROMPT_RECONCILIATION_TERMINAL_CAPACITY,
|
|
@@ -22,6 +34,68 @@ import type {
|
|
|
22
34
|
} from "./reconciliation-store";
|
|
23
35
|
|
|
24
36
|
const EMPTY_PROMPT_FAILURE = { code: "prompt_failed", message: "Prompt submission failed." } as const;
|
|
37
|
+
const PRESERVED_PROVIDER_FAILURE_CODES = new Set([
|
|
38
|
+
"provider_down",
|
|
39
|
+
"provider_unavailable",
|
|
40
|
+
"provider_rejected",
|
|
41
|
+
"upstream_stream_interrupted",
|
|
42
|
+
"argument_validation",
|
|
43
|
+
"execution",
|
|
44
|
+
"upstream_error",
|
|
45
|
+
"transport_reset",
|
|
46
|
+
]);
|
|
47
|
+
const isPreservedProviderFailure = (code: string | undefined): boolean =>
|
|
48
|
+
code !== undefined && (PRESERVED_PROVIDER_FAILURE_CODES.has(code) || code.startsWith("provider_http_"));
|
|
49
|
+
const STOP_REASONS: readonly SdkPromptStopReason[] = [
|
|
50
|
+
"end_turn",
|
|
51
|
+
"max_tokens",
|
|
52
|
+
"max_turn_requests",
|
|
53
|
+
"refusal",
|
|
54
|
+
"cancelled",
|
|
55
|
+
];
|
|
56
|
+
const FAILURE_CODES: readonly SdkPromptFailureCode[] = ["prompt_failed", "prompt_deadline_exceeded"];
|
|
57
|
+
|
|
58
|
+
const normalizeTerminalOutcome = (outcome: unknown): SdkPromptTerminalOutcome | undefined => {
|
|
59
|
+
if (outcome === null || typeof outcome !== "object") return undefined;
|
|
60
|
+
const candidate = outcome as Record<string, unknown>;
|
|
61
|
+
if (
|
|
62
|
+
candidate.kind === "stopped" &&
|
|
63
|
+
STOP_REASONS.includes(candidate.reason as SdkPromptStopReason) &&
|
|
64
|
+
["agent", "client_cancel"].includes(candidate.provenance as string)
|
|
65
|
+
)
|
|
66
|
+
return {
|
|
67
|
+
kind: "stopped",
|
|
68
|
+
reason: candidate.reason as SdkPromptStopReason,
|
|
69
|
+
provenance: candidate.provenance as "agent" | "client_cancel",
|
|
70
|
+
};
|
|
71
|
+
if (
|
|
72
|
+
candidate.kind === "failed" &&
|
|
73
|
+
FAILURE_CODES.includes(candidate.code as SdkPromptFailureCode) &&
|
|
74
|
+
typeof candidate.message === "string" &&
|
|
75
|
+
["agent_failed", "deadline"].includes(candidate.provenance as string)
|
|
76
|
+
)
|
|
77
|
+
return {
|
|
78
|
+
kind: "failed",
|
|
79
|
+
code: candidate.code as SdkPromptFailureCode,
|
|
80
|
+
message:
|
|
81
|
+
candidate.code === "prompt_deadline_exceeded" ? "Prompt deadline exceeded." : "Prompt submission failed.",
|
|
82
|
+
provenance: candidate.provenance as "agent_failed" | "deadline",
|
|
83
|
+
};
|
|
84
|
+
return undefined;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const failedOutcomeForError = (error: { code: string; message: string }): SdkPromptTerminalOutcome => ({
|
|
88
|
+
kind: "failed",
|
|
89
|
+
code: "prompt_failed",
|
|
90
|
+
message: error.message,
|
|
91
|
+
provenance: "agent_failed",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const isDeadlineOutcome = (outcome: SdkPromptTerminalOutcome | undefined): boolean =>
|
|
95
|
+
outcome?.kind === "failed" && outcome.provenance === "deadline";
|
|
96
|
+
|
|
97
|
+
const isProviderFailureOutcome = (outcome: SdkPromptTerminalOutcome | undefined): boolean =>
|
|
98
|
+
outcome?.kind === "failed" && outcome.provenance === "agent_failed";
|
|
25
99
|
|
|
26
100
|
export type { ReconciliationKind };
|
|
27
101
|
|
|
@@ -360,6 +434,84 @@ export function createKindAwareReconciliation(
|
|
|
360
434
|
const record = candidate.get(keyOf(kind, correlation));
|
|
361
435
|
if (!record || record.kind === "steer") return { value: undefined, changed: false };
|
|
362
436
|
if (record.terminalAt !== undefined) {
|
|
437
|
+
if (frame.type === "agent_end") {
|
|
438
|
+
const content = reduceTurnResultContent(record.content, frame.content);
|
|
439
|
+
const receiptState = reduceReceiptState(
|
|
440
|
+
record.receiptState,
|
|
441
|
+
reportableTurnResultContent(content) ? "present" : undefined,
|
|
442
|
+
);
|
|
443
|
+
const incomingOutcome = normalizeTerminalOutcome(frame.outcome);
|
|
444
|
+
const providerErrorRecord = record.error;
|
|
445
|
+
const providerError =
|
|
446
|
+
providerErrorRecord !== undefined && providerErrorRecord.code !== "prompt_deadline_exceeded";
|
|
447
|
+
const providerOutcome =
|
|
448
|
+
incomingOutcome?.kind === "failed" && incomingOutcome.provenance === "agent_failed"
|
|
449
|
+
? incomingOutcome
|
|
450
|
+
: undefined;
|
|
451
|
+
let outcome = record.outcome;
|
|
452
|
+
let error = record.error;
|
|
453
|
+
if (isDeadlineOutcome(record.outcome)) {
|
|
454
|
+
if (providerOutcome !== undefined) {
|
|
455
|
+
// A real provider terminal failure is stronger evidence than the
|
|
456
|
+
// synthetic deadline claim. Retain the provider's outcome and
|
|
457
|
+
// diagnostic rather than surfacing a contradictory deadline.
|
|
458
|
+
outcome = providerOutcome;
|
|
459
|
+
error = providerError
|
|
460
|
+
? providerErrorRecord
|
|
461
|
+
: { code: providerOutcome.code, message: providerOutcome.message };
|
|
462
|
+
} else if (providerError) {
|
|
463
|
+
outcome = failedOutcomeForError(providerErrorRecord);
|
|
464
|
+
error = providerErrorRecord;
|
|
465
|
+
} else if (
|
|
466
|
+
incomingOutcome?.kind === "stopped" ||
|
|
467
|
+
kind !== "prompt" ||
|
|
468
|
+
frame.hasActivity === true ||
|
|
469
|
+
reportableTurnResultContent(content)
|
|
470
|
+
) {
|
|
471
|
+
// A completed lifecycle event also supersedes a synthetic deadline.
|
|
472
|
+
outcome = incomingOutcome?.kind === "stopped" ? incomingOutcome : undefined;
|
|
473
|
+
error = undefined;
|
|
474
|
+
}
|
|
475
|
+
} else if (outcome === undefined) {
|
|
476
|
+
if (providerOutcome !== undefined) {
|
|
477
|
+
outcome = providerOutcome;
|
|
478
|
+
error = providerError
|
|
479
|
+
? providerErrorRecord
|
|
480
|
+
: { code: providerOutcome.code, message: providerOutcome.message };
|
|
481
|
+
} else if (record.status === "failed" && providerError) {
|
|
482
|
+
outcome = failedOutcomeForError(providerErrorRecord);
|
|
483
|
+
error = providerErrorRecord;
|
|
484
|
+
} else if (
|
|
485
|
+
record.status === "terminal_ok" &&
|
|
486
|
+
incomingOutcome?.kind === "stopped" &&
|
|
487
|
+
error === undefined
|
|
488
|
+
) {
|
|
489
|
+
outcome = incomingOutcome;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const status =
|
|
493
|
+
outcome?.kind === "failed"
|
|
494
|
+
? "failed"
|
|
495
|
+
: outcome?.kind === "stopped" || (isDeadlineOutcome(record.outcome) && error === undefined)
|
|
496
|
+
? "terminal_ok"
|
|
497
|
+
: record.status;
|
|
498
|
+
if (
|
|
499
|
+
content === record.content &&
|
|
500
|
+
receiptState === record.receiptState &&
|
|
501
|
+
outcome === record.outcome &&
|
|
502
|
+
error === record.error &&
|
|
503
|
+
status === record.status
|
|
504
|
+
)
|
|
505
|
+
return { value: undefined, changed: false };
|
|
506
|
+
record.content = content;
|
|
507
|
+
record.receiptState = receiptState;
|
|
508
|
+
record.status = status;
|
|
509
|
+
if (outcome === undefined) delete record.outcome;
|
|
510
|
+
else record.outcome = outcome;
|
|
511
|
+
if (error === undefined) delete record.error;
|
|
512
|
+
else record.error = error;
|
|
513
|
+
return { value: undefined, changed: true };
|
|
514
|
+
}
|
|
363
515
|
// The terminal is claimed by one delivery path while the reason arrives on
|
|
364
516
|
// another, so ordering is not the caller's to control. A late agent_failed
|
|
365
517
|
// enriches the settled record instead of being dropped; it must not resurrect
|
|
@@ -369,6 +521,12 @@ export function createKindAwareReconciliation(
|
|
|
369
521
|
// survives reconnect/restart reconciliation.
|
|
370
522
|
if (frame.type === "agent_failed") {
|
|
371
523
|
const failure = sanitizePromptFailure(frame.error);
|
|
524
|
+
if (record.error?.code === "prompt_deadline_exceeded") {
|
|
525
|
+
record.error = failure;
|
|
526
|
+
record.outcome = failedOutcomeForError(failure);
|
|
527
|
+
record.status = "failed";
|
|
528
|
+
return { value: undefined, changed: true };
|
|
529
|
+
}
|
|
372
530
|
if (
|
|
373
531
|
record.error !== undefined &&
|
|
374
532
|
!(record.error.code === "agent_failed" && failure.code !== "agent_failed")
|
|
@@ -385,7 +543,7 @@ export function createKindAwareReconciliation(
|
|
|
385
543
|
delete record.deadlineMaxAt;
|
|
386
544
|
record.status = "in_flight";
|
|
387
545
|
record.startedAt = now();
|
|
388
|
-
|
|
546
|
+
record.content = reduceTurnResultContent(record.content, frame.content);
|
|
389
547
|
return { value: undefined, changed: true };
|
|
390
548
|
}
|
|
391
549
|
if (frame.type === "agent_failed") {
|
|
@@ -399,43 +557,76 @@ export function createKindAwareReconciliation(
|
|
|
399
557
|
return { value: undefined, changed: true };
|
|
400
558
|
}
|
|
401
559
|
const pendingOutcome = record.pendingOutcome;
|
|
560
|
+
const frameOutcome = normalizeTerminalOutcome(frame.type === "agent_end" ? frame.outcome : undefined);
|
|
402
561
|
delete record.deadlineRecoveryPending;
|
|
403
562
|
delete record.deadlineMaxAt;
|
|
404
563
|
record.terminalAt = now();
|
|
405
|
-
|
|
564
|
+
record.content = reduceTurnResultContent(record.content, frame.content);
|
|
406
565
|
if (pendingOutcome !== undefined) {
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
566
|
+
const providerErrorRecord = record.error;
|
|
567
|
+
const providerError =
|
|
568
|
+
providerErrorRecord !== undefined && providerErrorRecord.code !== "prompt_deadline_exceeded";
|
|
569
|
+
const preserveProviderError =
|
|
570
|
+
providerError || isDeadlineOutcome(pendingOutcome) || isProviderFailureOutcome(frameOutcome);
|
|
571
|
+
let terminalOutcome: SdkPromptTerminalOutcome = pendingOutcome;
|
|
572
|
+
if (frameOutcome?.kind === "failed" && frameOutcome.provenance === "agent_failed")
|
|
573
|
+
terminalOutcome = frameOutcome;
|
|
574
|
+
else if (providerError && (pendingOutcome.kind !== "failed" || isDeadlineOutcome(pendingOutcome)))
|
|
575
|
+
terminalOutcome = failedOutcomeForError(providerErrorRecord);
|
|
576
|
+
else if (isDeadlineOutcome(pendingOutcome) && frameOutcome?.kind === "stopped")
|
|
577
|
+
terminalOutcome = frameOutcome;
|
|
416
578
|
record.outcome = terminalOutcome;
|
|
417
|
-
record.pendingOutcome
|
|
579
|
+
delete record.pendingOutcome;
|
|
418
580
|
if (terminalOutcome.kind === "failed") {
|
|
419
581
|
record.status = "failed";
|
|
420
|
-
|
|
582
|
+
if (!providerError || !preserveProviderError)
|
|
583
|
+
record.error = { code: terminalOutcome.code, message: terminalOutcome.message };
|
|
421
584
|
} else {
|
|
422
585
|
record.status = "terminal_ok";
|
|
586
|
+
delete record.error;
|
|
423
587
|
}
|
|
424
|
-
record.receiptState =
|
|
425
|
-
|
|
588
|
+
record.receiptState = reduceReceiptState(
|
|
589
|
+
record.receiptState,
|
|
590
|
+
reportableTurnResultContent(record.content) ? "present" : (record.pendingReceiptState ?? "missing"),
|
|
591
|
+
);
|
|
592
|
+
delete record.pendingReceiptState;
|
|
426
593
|
} else {
|
|
427
|
-
|
|
594
|
+
const providerErrorRecord = record.error;
|
|
595
|
+
const providerError =
|
|
596
|
+
providerErrorRecord !== undefined && providerErrorRecord.code !== "prompt_deadline_exceeded";
|
|
597
|
+
const terminalOutcome = isProviderFailureOutcome(frameOutcome)
|
|
598
|
+
? frameOutcome
|
|
599
|
+
: providerError &&
|
|
600
|
+
(frameOutcome === undefined || isDeadlineOutcome(frameOutcome) || frameOutcome.kind === "stopped")
|
|
601
|
+
? failedOutcomeForError(providerErrorRecord)
|
|
602
|
+
: frameOutcome;
|
|
603
|
+
if (terminalOutcome !== undefined) {
|
|
604
|
+
record.outcome = terminalOutcome;
|
|
605
|
+
if (terminalOutcome.kind === "failed") {
|
|
606
|
+
record.status = "failed";
|
|
607
|
+
if (!providerError) record.error = { code: terminalOutcome.code, message: terminalOutcome.message };
|
|
608
|
+
} else {
|
|
609
|
+
record.status = "terminal_ok";
|
|
610
|
+
delete record.error;
|
|
611
|
+
}
|
|
612
|
+
} else if (
|
|
428
613
|
kind === "prompt" &&
|
|
429
614
|
record.error === undefined &&
|
|
430
615
|
frame.type === "agent_end" &&
|
|
431
|
-
frame.outcome?.kind !== "stopped" &&
|
|
432
616
|
!frame.content?.text.trim() &&
|
|
433
617
|
!frame.hasActivity
|
|
434
618
|
) {
|
|
435
619
|
record.status = "failed";
|
|
436
620
|
record.error = EMPTY_PROMPT_FAILURE;
|
|
437
|
-
|
|
438
|
-
record.
|
|
621
|
+
record.outcome = failedOutcomeForError(EMPTY_PROMPT_FAILURE);
|
|
622
|
+
} else if (record.error !== undefined) {
|
|
623
|
+
record.status = "failed";
|
|
624
|
+
record.outcome = failedOutcomeForError(record.error);
|
|
625
|
+
} else record.status = "terminal_ok";
|
|
626
|
+
record.receiptState = reduceReceiptState(
|
|
627
|
+
record.receiptState,
|
|
628
|
+
reportableTurnResultContent(record.content) ? "present" : "missing",
|
|
629
|
+
);
|
|
439
630
|
}
|
|
440
631
|
cleanupRecords(candidate);
|
|
441
632
|
return { value: undefined, changed: true };
|
|
@@ -449,13 +640,14 @@ export function createKindAwareReconciliation(
|
|
|
449
640
|
receiptState?: Extract<ReceiptState, "present" | "missing">,
|
|
450
641
|
): Promise<SdkPromptTerminalOutcome> =>
|
|
451
642
|
await queueMutation(candidate => {
|
|
643
|
+
const normalized = normalizeTerminalOutcome(outcome) ?? outcome;
|
|
452
644
|
const record = candidate.get(keyOf(kind, correlation));
|
|
453
645
|
if (!record || record.terminalAt !== undefined || record.kind !== kind)
|
|
454
|
-
return { value:
|
|
646
|
+
return { value: normalized, changed: false };
|
|
455
647
|
if (record.pendingOutcome !== undefined) return { value: record.pendingOutcome, changed: false };
|
|
456
|
-
record.pendingOutcome =
|
|
648
|
+
record.pendingOutcome = normalized;
|
|
457
649
|
record.pendingReceiptState = receiptState;
|
|
458
|
-
return { value:
|
|
650
|
+
return { value: normalized, changed: true };
|
|
459
651
|
});
|
|
460
652
|
|
|
461
653
|
const finalizeOutcome = async (
|
|
@@ -481,11 +673,25 @@ export function createKindAwareReconciliation(
|
|
|
481
673
|
const record = candidate.get(keyOf(kind, correlation));
|
|
482
674
|
if (!record || record.terminalAt !== undefined || record.kind !== kind)
|
|
483
675
|
return { value: undefined, changed: false };
|
|
484
|
-
const
|
|
676
|
+
const requestedOutcome = normalizeTerminalOutcome(outcome) ?? record.pendingOutcome;
|
|
677
|
+
const providerErrorRecord = record.error;
|
|
678
|
+
const providerError =
|
|
679
|
+
providerErrorRecord !== undefined && providerErrorRecord.code !== "prompt_deadline_exceeded";
|
|
680
|
+
const finalOutcome = providerError
|
|
681
|
+
? failedOutcomeForError(providerErrorRecord)
|
|
682
|
+
: requestedOutcome?.kind === "stopped"
|
|
683
|
+
? requestedOutcome
|
|
684
|
+
: providerError &&
|
|
685
|
+
(requestedOutcome === undefined ||
|
|
686
|
+
(requestedOutcome.kind === "failed" && isDeadlineOutcome(requestedOutcome)))
|
|
687
|
+
? failedOutcomeForError(providerErrorRecord)
|
|
688
|
+
: requestedOutcome;
|
|
485
689
|
record.terminalAt = now();
|
|
486
690
|
if (finalOutcome?.kind === "failed") {
|
|
487
691
|
record.status = "failed";
|
|
488
|
-
|
|
692
|
+
if (recordError !== undefined && !(providerError && recordError.code === "prompt_deadline_exceeded"))
|
|
693
|
+
record.error = recordError;
|
|
694
|
+
else if (!providerError) record.error = { code: finalOutcome.code, message: finalOutcome.message };
|
|
489
695
|
} else if (
|
|
490
696
|
kind === "prompt" &&
|
|
491
697
|
((finalOutcome === undefined && !sanitizedContent?.text.trim()) ||
|
|
@@ -493,12 +699,21 @@ export function createKindAwareReconciliation(
|
|
|
493
699
|
) {
|
|
494
700
|
record.status = "failed";
|
|
495
701
|
record.error = EMPTY_PROMPT_FAILURE;
|
|
702
|
+
record.outcome = failedOutcomeForError(EMPTY_PROMPT_FAILURE);
|
|
703
|
+
} else if (finalOutcome?.kind === "stopped") {
|
|
704
|
+
// A stopped terminal is authoritative only when no provider failure
|
|
705
|
+
// was recorded before finalization.
|
|
706
|
+
record.status = "terminal_ok";
|
|
707
|
+
delete record.error;
|
|
496
708
|
} else record.status = "terminal_ok";
|
|
497
|
-
record.content = sanitizedContent;
|
|
498
|
-
record.outcome = finalOutcome;
|
|
499
|
-
record.receiptState =
|
|
500
|
-
|
|
501
|
-
|
|
709
|
+
record.content = reduceTurnResultContent(record.content, sanitizedContent);
|
|
710
|
+
if (record.status !== "failed" || record.outcome === undefined) record.outcome = finalOutcome;
|
|
711
|
+
record.receiptState = reduceReceiptState(
|
|
712
|
+
record.receiptState,
|
|
713
|
+
reportableTurnResultContent(record.content) ? "present" : (record.pendingReceiptState ?? "unknown"),
|
|
714
|
+
);
|
|
715
|
+
delete record.pendingOutcome;
|
|
716
|
+
delete record.pendingReceiptState;
|
|
502
717
|
cleanupRecords(candidate);
|
|
503
718
|
return { value: undefined, changed: true };
|
|
504
719
|
});
|
|
@@ -522,7 +737,8 @@ export function createKindAwareReconciliation(
|
|
|
522
737
|
delete record.pendingOutcome;
|
|
523
738
|
delete record.pendingReceiptState;
|
|
524
739
|
delete record.receiptState;
|
|
525
|
-
|
|
740
|
+
if (record.error?.code === "prompt_deadline_exceeded" || !isPreservedProviderFailure(record.error?.code))
|
|
741
|
+
delete record.error;
|
|
526
742
|
record.deadlineRecoveryPending = true;
|
|
527
743
|
if (deadlineMaxAt !== undefined) record.deadlineMaxAt = deadlineMaxAt;
|
|
528
744
|
return { value: undefined, changed: true };
|
|
@@ -583,7 +799,7 @@ export function createKindAwareReconciliation(
|
|
|
583
799
|
? keyOf(kind, { commandId: selector.commandId, turnId: selector.turnId })
|
|
584
800
|
: undefined;
|
|
585
801
|
const record = key === undefined ? undefined : records.get(key);
|
|
586
|
-
if (!record || record.kind === "steer") return { status: "unknown" };
|
|
802
|
+
if (!record || record.kind === "steer") return { status: "unknown", receiptState: "unknown" };
|
|
587
803
|
const identity = {
|
|
588
804
|
// Durable terminal markers (kind "terminal") are not prompt/skill
|
|
589
805
|
// invocation pages: never surface them under a prompt/skill kind.
|
|
@@ -593,13 +809,16 @@ export function createKindAwareReconciliation(
|
|
|
593
809
|
...(record.clientRef !== undefined ? { clientRef: record.clientRef } : {}),
|
|
594
810
|
acceptedAt: record.acceptedAt,
|
|
595
811
|
};
|
|
596
|
-
if (record.status === "accepted") return { status: "accepted", ...identity };
|
|
597
|
-
if (record.status === "in_flight")
|
|
812
|
+
if (record.status === "accepted") return { status: "accepted", receiptState: "absent", ...identity };
|
|
813
|
+
if (record.status === "in_flight")
|
|
814
|
+
return { status: "in_flight", receiptState: "absent", ...identity, startedAt: record.startedAt };
|
|
598
815
|
const terminal = {
|
|
599
816
|
...identity,
|
|
600
817
|
...(record.startedAt !== undefined ? { startedAt: record.startedAt } : {}),
|
|
601
818
|
terminalAt: record.terminalAt as number,
|
|
602
|
-
|
|
819
|
+
receiptState: record.receiptState ?? "unknown",
|
|
820
|
+
...(record.outcome !== undefined ? { outcome: record.outcome } : {}),
|
|
821
|
+
...(reportableTurnResultContent(record.content) ? { content: record.content } : {}),
|
|
603
822
|
};
|
|
604
823
|
if (record.status === "terminal_ok")
|
|
605
824
|
return {
|
|
@@ -613,20 +832,49 @@ export function createKindAwareReconciliation(
|
|
|
613
832
|
if (!store) return;
|
|
614
833
|
const run = async () => {
|
|
615
834
|
const loaded = (await store.load()).filter(record => ownedKinds.has(record.kind));
|
|
835
|
+
let sanitizedDurableRecord = false;
|
|
616
836
|
const candidate = new Map(
|
|
617
837
|
loaded.map(record => {
|
|
618
|
-
const
|
|
619
|
-
|
|
838
|
+
const safeOutcome = (
|
|
839
|
+
outcome: SdkPromptTerminalOutcome | undefined,
|
|
840
|
+
): SdkPromptTerminalOutcome | undefined =>
|
|
841
|
+
outcome?.kind === "failed"
|
|
620
842
|
? {
|
|
621
|
-
...
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
message: "Steer delivery cannot be proven after process restart.",
|
|
627
|
-
},
|
|
843
|
+
...outcome,
|
|
844
|
+
message:
|
|
845
|
+
outcome.code === "prompt_deadline_exceeded"
|
|
846
|
+
? "Prompt deadline exceeded."
|
|
847
|
+
: "Prompt submission failed.",
|
|
628
848
|
}
|
|
629
|
-
:
|
|
849
|
+
: outcome;
|
|
850
|
+
let hydrated: DurableReconciliationRecord;
|
|
851
|
+
if (record.kind === "steer") {
|
|
852
|
+
const safeError = record.error === undefined ? undefined : sanitizePromptFailure(record.error);
|
|
853
|
+
if (
|
|
854
|
+
record.error !== undefined &&
|
|
855
|
+
(safeError?.code !== record.error.code || safeError.message !== record.error.message)
|
|
856
|
+
)
|
|
857
|
+
sanitizedDurableRecord = true;
|
|
858
|
+
hydrated =
|
|
859
|
+
record.status === "accepted"
|
|
860
|
+
? {
|
|
861
|
+
...record,
|
|
862
|
+
status: "uncertain",
|
|
863
|
+
settledAt: now(),
|
|
864
|
+
error: {
|
|
865
|
+
code: "process_restart_uncertain",
|
|
866
|
+
message: "Steer delivery cannot be proven after process restart.",
|
|
867
|
+
},
|
|
868
|
+
}
|
|
869
|
+
: { ...record, ...(safeError === undefined ? {} : { error: safeError }) };
|
|
870
|
+
} else {
|
|
871
|
+
hydrated = {
|
|
872
|
+
...record,
|
|
873
|
+
...(record.error === undefined ? {} : { error: sanitizePromptFailure(record.error) }),
|
|
874
|
+
outcome: safeOutcome(record.outcome),
|
|
875
|
+
pendingOutcome: safeOutcome(record.pendingOutcome),
|
|
876
|
+
};
|
|
877
|
+
}
|
|
630
878
|
return [
|
|
631
879
|
record.kind === "steer" ? steerKey(record.clientRef) : keyOf(record.kind, record),
|
|
632
880
|
hydrated,
|
|
@@ -640,7 +888,7 @@ export function createKindAwareReconciliation(
|
|
|
640
888
|
// prompt/skill admission — stalling reconciliationReady so a concurrent
|
|
641
889
|
// abort misses the not-yet-registered pending preflight entry (#4522).
|
|
642
890
|
const settledSteers = loaded.some(record => record.kind === "steer" && record.status === "accepted");
|
|
643
|
-
if (settledSteers)
|
|
891
|
+
if (settledSteers || sanitizedDurableRecord)
|
|
644
892
|
await store.transact(current => [
|
|
645
893
|
...current.filter(record => !ownedKinds.has(record.kind)),
|
|
646
894
|
...[...candidate.values()].filter(record => ownedKinds.has(record.kind)).map(record => ({ ...record })),
|
|
@@ -323,6 +323,10 @@ export function formatLifecycleOutcome(r: SessionLifecycleResponse, verb?: Lifec
|
|
|
323
323
|
case "discovery_timeout":
|
|
324
324
|
case "readiness_timeout":
|
|
325
325
|
return "\u23f3 The session did not become ready in time. It may still be starting \u2014 check /session_recent.";
|
|
326
|
+
case "worktree_preparation_timeout":
|
|
327
|
+
return "\u23f3 Worktree preparation exceeded its deadline before the session host was spawned.";
|
|
328
|
+
case "dependency_preparation_timeout":
|
|
329
|
+
return "\u23f3 Dependency preparation exceeded its deadline before the session host was spawned.";
|
|
326
330
|
case "close_refused":
|
|
327
331
|
return "\u26a0\ufe0f Close refused: that session is not GJC-managed or did not match.";
|
|
328
332
|
case "not_found":
|
|
@@ -939,11 +939,28 @@ export async function checkNotificationHealth(opts: HealthOptions): Promise<Noti
|
|
|
939
939
|
} else if (!state) {
|
|
940
940
|
checks.push({ name: "daemon", level: "ok", detail: "no daemon ownership record (none running)" });
|
|
941
941
|
} else if (!daemon.alive) {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
942
|
+
let lockPresent = false;
|
|
943
|
+
try {
|
|
944
|
+
const daemonDir = daemonPaths(opts.settings.getAgentDir()).dir;
|
|
945
|
+
const files = await fs.readdir(daemonDir);
|
|
946
|
+
const lockName = path.basename(daemonPaths(opts.settings.getAgentDir()).lock);
|
|
947
|
+
lockPresent = files.includes(lockName);
|
|
948
|
+
} catch {
|
|
949
|
+
lockPresent = false;
|
|
950
|
+
}
|
|
951
|
+
if (lockPresent) {
|
|
952
|
+
checks.push({
|
|
953
|
+
name: "daemon",
|
|
954
|
+
level: "warn",
|
|
955
|
+
detail: `daemon owner pid ${daemon.pid} is not alive; run recovery to clear the stale lock`,
|
|
956
|
+
});
|
|
957
|
+
} else {
|
|
958
|
+
checks.push({
|
|
959
|
+
name: "daemon",
|
|
960
|
+
level: "warn",
|
|
961
|
+
detail: `daemon owner pid ${daemon.pid} is not alive; no lock present (daemon exited cleanly)`,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
947
964
|
} else if (!daemon.heartbeatFresh) {
|
|
948
965
|
checks.push({ name: "daemon", level: "warn", detail: `daemon pid ${daemon.pid} heartbeat is stale` });
|
|
949
966
|
} else if (telegramConfigured && !daemon.identityMatches) {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { PROMPT_FAILURE_CODE_MAX, sanitizePromptFailure } from "../prompt-failure";
|
|
26
26
|
import type { SdkPromptTerminalOutcome } from "../prompt-status";
|
|
27
|
-
import type
|
|
27
|
+
import { type ReceiptState, reduceReceiptState } from "../receipt-state";
|
|
28
28
|
|
|
29
29
|
export { PROMPT_FAILURE_CODE_MAX, sanitizePromptFailure };
|
|
30
30
|
export const PROMPT_RECONCILIATION_ACTIVE_CAPACITY = 128;
|
|
@@ -209,6 +209,11 @@ export function createPromptReconciliation(options: { now?: () => number } = {})
|
|
|
209
209
|
const record = records.get(keyOf(correlation));
|
|
210
210
|
if (!record) return;
|
|
211
211
|
if (record.terminalAt !== undefined) {
|
|
212
|
+
if (frame.type === "agent_end")
|
|
213
|
+
record.receiptState = reduceReceiptState(
|
|
214
|
+
record.receiptState,
|
|
215
|
+
frame.finalText?.trim() ? "present" : undefined,
|
|
216
|
+
);
|
|
212
217
|
// The terminal is claimed by one path while the reason arrives on another,
|
|
213
218
|
// so ordering is not the caller's to control. A late agent_failed enriches
|
|
214
219
|
// the settled record instead of being dropped; it must not resurrect it,
|
|
@@ -229,7 +234,7 @@ export function createPromptReconciliation(options: { now?: () => number } = {})
|
|
|
229
234
|
return;
|
|
230
235
|
}
|
|
231
236
|
record.terminalAt = now();
|
|
232
|
-
record.receiptState = frame.finalText?.trim() ? "present" : "missing";
|
|
237
|
+
record.receiptState = reduceReceiptState(record.receiptState, frame.finalText?.trim() ? "present" : "missing");
|
|
233
238
|
if (frame.type === "agent_failed") {
|
|
234
239
|
record.status = "failed";
|
|
235
240
|
record.error = sanitizePromptFailure(frame.error);
|