@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.
Files changed (270) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/dist/types/async/job-manager.d.ts +32 -1
  3. package/dist/types/browser-backend.d.ts +12 -0
  4. package/dist/types/cli/args.d.ts +2 -0
  5. package/dist/types/cli/root-flags.d.ts +14 -0
  6. package/dist/types/cli/update-cli.d.ts +14 -0
  7. package/dist/types/cli.d.ts +7 -0
  8. package/dist/types/commands/launch.d.ts +10 -0
  9. package/dist/types/commands/sdk.d.ts +2 -0
  10. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  11. package/dist/types/config/billing-path.d.ts +25 -0
  12. package/dist/types/config/config-schema-version.d.ts +13 -1
  13. package/dist/types/config/file-lock.d.ts +5 -0
  14. package/dist/types/config/model-registry.d.ts +22 -0
  15. package/dist/types/config/settings-schema.d.ts +27 -8
  16. package/dist/types/config/settings.d.ts +2 -0
  17. package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
  18. package/dist/types/crash/record-loader.d.ts +15 -0
  19. package/dist/types/extensibility/extensions/types.d.ts +8 -3
  20. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  21. package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
  22. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  23. package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
  24. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
  25. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  26. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  27. package/dist/types/main.d.ts +1 -0
  28. package/dist/types/master-mode/context.d.ts +21 -0
  29. package/dist/types/master-mode/first-request.d.ts +34 -0
  30. package/dist/types/master-mode/snapshot.d.ts +32 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +21 -21
  32. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  33. package/dist/types/modes/components/model-selector.d.ts +4 -0
  34. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  35. package/dist/types/modes/components/session-selector.d.ts +1 -1
  36. package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
  37. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  38. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  39. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  40. package/dist/types/modes/interactive-mode.d.ts +16 -2
  41. package/dist/types/modes/print-mode.d.ts +17 -0
  42. package/dist/types/modes/theme/theme.d.ts +2 -1
  43. package/dist/types/modes/types.d.ts +2 -0
  44. package/dist/types/modes/ui-language.d.ts +7 -1
  45. package/dist/types/sdk/broker/broker.d.ts +79 -2
  46. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  47. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  48. package/dist/types/sdk/broker/index.d.ts +3 -0
  49. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
  50. package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
  51. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  52. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  53. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  54. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  55. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  56. package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
  57. package/dist/types/sdk/broker/state-version.d.ts +21 -1
  58. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  59. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  60. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
  61. package/dist/types/sdk/bus/index.d.ts +9 -1
  62. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
  63. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
  64. package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
  65. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  66. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  67. package/dist/types/sdk/cli/index.d.ts +1 -0
  68. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  69. package/dist/types/sdk/cli/rows.d.ts +12 -5
  70. package/dist/types/sdk/cli/session-cli.d.ts +20 -3
  71. package/dist/types/sdk/client/client.d.ts +3 -2
  72. package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
  73. package/dist/types/sdk/host/control/index.d.ts +1 -1
  74. package/dist/types/sdk/host/control/operations.d.ts +2 -0
  75. package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
  76. package/dist/types/sdk/host/events.d.ts +1 -0
  77. package/dist/types/sdk/host/host.d.ts +10 -0
  78. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  79. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  80. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  81. package/dist/types/sdk/host/session-runtime.d.ts +70 -17
  82. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  83. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  84. package/dist/types/sdk/lifecycle-session.d.ts +3 -1
  85. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  86. package/dist/types/sdk/receipt-state.d.ts +2 -0
  87. package/dist/types/sdk/router/session-router.d.ts +6 -0
  88. package/dist/types/sdk/session.d.ts +16 -0
  89. package/dist/types/sdk/startup-capability.d.ts +2 -1
  90. package/dist/types/sdk/turn-result.d.ts +6 -0
  91. package/dist/types/session/agent-session.d.ts +52 -7
  92. package/dist/types/session/credential-store-errors.d.ts +1 -0
  93. package/dist/types/session/fold-coordinator.d.ts +29 -2
  94. package/dist/types/session/session-manager.d.ts +79 -2
  95. package/dist/types/session/yield-queue.d.ts +5 -4
  96. package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
  97. package/dist/types/tools/ask.d.ts +1 -0
  98. package/dist/types/tools/bash.d.ts +16 -0
  99. package/dist/types/tools/browser/launch.d.ts +10 -0
  100. package/dist/types/tools/browser/registry.d.ts +8 -0
  101. package/dist/types/tools/gh.d.ts +8 -0
  102. package/dist/types/tools/index.d.ts +10 -10
  103. package/dist/types/tools/job.d.ts +3 -1
  104. package/dist/types/utils/external-editor.d.ts +7 -0
  105. package/examples/extensions/README.md +1 -1
  106. package/examples/extensions/pirate.ts +7 -4
  107. package/package.json +7 -7
  108. package/scripts/generate-sdk-operation-inventory.ts +13 -1
  109. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  110. package/src/async/job-manager.ts +107 -9
  111. package/src/browser-backend.ts +29 -0
  112. package/src/cli/accounts-cli.ts +9 -5
  113. package/src/cli/args.ts +17 -0
  114. package/src/cli/fast-help.ts +1 -0
  115. package/src/cli/plugin-cli.ts +29 -0
  116. package/src/cli/root-flags.ts +5 -0
  117. package/src/cli/update-cli.ts +140 -23
  118. package/src/commands/launch.ts +86 -18
  119. package/src/commands/sdk.ts +135 -28
  120. package/src/config/autorouting-tier-map.ts +12 -0
  121. package/src/config/billing-path.ts +42 -0
  122. package/src/config/config-schema-version.ts +18 -1
  123. package/src/config/file-lock.ts +158 -83
  124. package/src/config/model-preset-registry.ts +178 -23
  125. package/src/config/model-profiles.ts +5 -5
  126. package/src/config/model-registry.ts +291 -42
  127. package/src/config/model-resolver.ts +5 -0
  128. package/src/config/settings-schema.ts +27 -8
  129. package/src/config/settings.ts +51 -15
  130. package/src/coordinator-mcp/projection-scan.ts +27 -6
  131. package/src/coordinator-mcp/question-state.ts +9 -11
  132. package/src/coordinator-mcp/server.ts +115 -21
  133. package/src/crash/index-store.ts +2 -0
  134. package/src/crash/record-loader.ts +10 -0
  135. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  136. package/src/extensibility/extensions/runner.ts +27 -0
  137. package/src/extensibility/extensions/types.ts +8 -3
  138. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  139. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  140. package/src/extensibility/skills.ts +22 -12
  141. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  142. package/src/gjc-runtime/launch-worktree.ts +285 -4
  143. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  144. package/src/gjc-runtime/session-state-lock.ts +213 -14
  145. package/src/gjc-runtime/session-state-sidecar.ts +938 -39
  146. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  147. package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
  148. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  149. package/src/hooks/skill-state.ts +6 -0
  150. package/src/internal-urls/docs-index.generated.ts +15 -15
  151. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  152. package/src/main.ts +61 -3
  153. package/src/master-mode/context.ts +50 -0
  154. package/src/master-mode/first-request.ts +65 -0
  155. package/src/master-mode/snapshot.ts +205 -0
  156. package/src/modes/acp/acp-agent.ts +610 -161
  157. package/src/modes/components/footer.ts +4 -2
  158. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  159. package/src/modes/components/model-selector.ts +76 -8
  160. package/src/modes/components/oauth-selector.ts +6 -1
  161. package/src/modes/components/session-selector.ts +23 -15
  162. package/src/modes/components/sessions-dashboard.ts +8 -5
  163. package/src/modes/components/settings-selector.ts +1 -0
  164. package/src/modes/components/status-line/model-name.ts +53 -0
  165. package/src/modes/components/status-line/priority-row.ts +107 -0
  166. package/src/modes/components/status-line/segments.ts +23 -9
  167. package/src/modes/components/tool-status-header.ts +151 -15
  168. package/src/modes/controllers/command-controller.ts +1 -1
  169. package/src/modes/controllers/event-controller.ts +30 -4
  170. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  171. package/src/modes/controllers/input-controller.ts +30 -2
  172. package/src/modes/controllers/selector-controller.ts +200 -98
  173. package/src/modes/interactive-mode.ts +36 -12
  174. package/src/modes/print-mode.ts +64 -0
  175. package/src/modes/prompt-action-autocomplete.ts +30 -4
  176. package/src/modes/runtime-init.ts +12 -9
  177. package/src/modes/theme/theme.ts +5 -0
  178. package/src/modes/types.ts +2 -0
  179. package/src/modes/ui-language.ts +49 -2
  180. package/src/modes/utils/ui-helpers.ts +22 -3
  181. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  182. package/src/prompts/system/master-mode.md +28 -0
  183. package/src/prompts/tools/ask.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -0
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/sdk/acp/adapter.ts +5 -0
  187. package/src/sdk/broker/broker.ts +1646 -27
  188. package/src/sdk/broker/endpoint-authority.ts +86 -0
  189. package/src/sdk/broker/ensure.ts +81 -3
  190. package/src/sdk/broker/index.ts +3 -0
  191. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  192. package/src/sdk/broker/lifecycle.ts +440 -75
  193. package/src/sdk/broker/master-capability.ts +232 -0
  194. package/src/sdk/broker/session-index.ts +121 -17
  195. package/src/sdk/broker/session-scope.ts +272 -0
  196. package/src/sdk/broker/spawn-authority.ts +1031 -0
  197. package/src/sdk/broker/spawn-substrate.ts +591 -0
  198. package/src/sdk/broker/startup-budget.ts +144 -2
  199. package/src/sdk/broker/state-version.ts +43 -14
  200. package/src/sdk/broker/transport.ts +1 -0
  201. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  202. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  203. package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
  204. package/src/sdk/bus/discord-daemon.ts +50 -10
  205. package/src/sdk/bus/index.ts +84 -7
  206. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  207. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  208. package/src/sdk/bus/notification-service.ts +22 -5
  209. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  210. package/src/sdk/bus/reconciliation-store.ts +55 -31
  211. package/src/sdk/bus/slack-conversation.ts +1 -1
  212. package/src/sdk/bus/slack-daemon.ts +72 -38
  213. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  214. package/src/sdk/cli/index.ts +1 -0
  215. package/src/sdk/cli/master-cli.ts +259 -0
  216. package/src/sdk/cli/rows.ts +41 -15
  217. package/src/sdk/cli/session-cli.ts +297 -26
  218. package/src/sdk/client/client.ts +14 -8
  219. package/src/sdk/host/control/dispatch.ts +101 -40
  220. package/src/sdk/host/control/index.ts +2 -0
  221. package/src/sdk/host/control/operations.ts +2 -0
  222. package/src/sdk/host/control/runtime-gate.ts +203 -11
  223. package/src/sdk/host/events.ts +1 -0
  224. package/src/sdk/host/host.ts +29 -2
  225. package/src/sdk/host/query/cursor.ts +52 -7
  226. package/src/sdk/host/query/handlers.ts +33 -12
  227. package/src/sdk/host/query/revision-store.ts +12 -0
  228. package/src/sdk/host/session-runtime.test.ts +673 -14
  229. package/src/sdk/host/session-runtime.ts +674 -103
  230. package/src/sdk/host/surface-policy.ts +3 -3
  231. package/src/sdk/lifecycle/broker-client.ts +28 -0
  232. package/src/sdk/lifecycle/service.ts +279 -20
  233. package/src/sdk/lifecycle-session.ts +17 -4
  234. package/src/sdk/prompt-deadline-manager.ts +2 -1
  235. package/src/sdk/protocol/operation-inventory.generated.json +105 -3
  236. package/src/sdk/protocol/operation-registry.ts +14 -4
  237. package/src/sdk/receipt-state.ts +10 -0
  238. package/src/sdk/router/session-router.ts +219 -118
  239. package/src/sdk/session.ts +416 -195
  240. package/src/sdk/startup-capability.ts +1 -0
  241. package/src/sdk/turn-result.ts +22 -0
  242. package/src/session/agent-session.ts +1628 -318
  243. package/src/session/blob-store.ts +15 -5
  244. package/src/session/credential-store-errors.ts +2 -0
  245. package/src/session/fold-coordinator.ts +40 -3
  246. package/src/session/internal/managed-session-storage.ts +153 -78
  247. package/src/session/session-manager.ts +850 -97
  248. package/src/session/yield-queue.ts +24 -10
  249. package/src/setup/frictionless-onboarding.ts +2 -0
  250. package/src/slash-commands/builtin-registry.ts +41 -0
  251. package/src/task/executor.ts +9 -4
  252. package/src/telemetry/events.ts +1138 -12
  253. package/src/tools/ask-contract.test.ts +64 -0
  254. package/src/tools/ask-contract.ts +36 -1
  255. package/src/tools/ask.ts +10 -8
  256. package/src/tools/bash-interactive.ts +11 -2
  257. package/src/tools/bash.ts +412 -75
  258. package/src/tools/browser/launch.ts +37 -0
  259. package/src/tools/browser/registry.ts +99 -9
  260. package/src/tools/cron.ts +6 -4
  261. package/src/tools/descriptors.test.ts +15 -0
  262. package/src/tools/descriptors.ts +5 -1
  263. package/src/tools/gh.ts +104 -48
  264. package/src/tools/github-cache.ts +2 -2
  265. package/src/tools/index.ts +10 -6
  266. package/src/tools/job.ts +4 -1
  267. package/src/tools/monitor.ts +12 -9
  268. package/src/tools/resolve.ts +30 -10
  269. package/src/tools/tool-catalog.generated.ts +2 -2
  270. 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 { PromptReconciliationStatus, SdkPromptTerminalOutcome, TurnPromptReconciliation } from "../prompt-status";
10
- import type { ReceiptState } from "../receipt-state";
11
- import { sanitizeTurnResultContent, type TurnResultContent, type TurnResultPage } from "../turn-result";
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
- if (frame.content) record.content = sanitizeTurnResultContent(frame.content.text);
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
- if (frame.content) record.content = sanitizeTurnResultContent(frame.content.text);
564
+ record.content = reduceTurnResultContent(record.content, frame.content);
406
565
  if (pendingOutcome !== undefined) {
407
- const terminalOutcome =
408
- record.error !== undefined && pendingOutcome.kind !== "failed"
409
- ? {
410
- kind: "failed" as const,
411
- code: "prompt_failed" as const,
412
- message: record.error.message,
413
- provenance: "agent_failed" as const,
414
- }
415
- : pendingOutcome;
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 = undefined;
579
+ delete record.pendingOutcome;
418
580
  if (terminalOutcome.kind === "failed") {
419
581
  record.status = "failed";
420
- record.error = { code: terminalOutcome.code, message: terminalOutcome.message };
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 = record.pendingReceiptState ?? "missing";
425
- record.pendingReceiptState = undefined;
588
+ record.receiptState = reduceReceiptState(
589
+ record.receiptState,
590
+ reportableTurnResultContent(record.content) ? "present" : (record.pendingReceiptState ?? "missing"),
591
+ );
592
+ delete record.pendingReceiptState;
426
593
  } else {
427
- if (
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
- } else record.status = record.error === undefined ? "terminal_ok" : "failed";
438
- record.receiptState = frame.content?.text?.trim() ? "present" : "missing";
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: outcome, changed: false };
646
+ return { value: normalized, changed: false };
455
647
  if (record.pendingOutcome !== undefined) return { value: record.pendingOutcome, changed: false };
456
- record.pendingOutcome = outcome;
648
+ record.pendingOutcome = normalized;
457
649
  record.pendingReceiptState = receiptState;
458
- return { value: outcome, changed: true };
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 finalOutcome = outcome ?? record.pendingOutcome;
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
- record.error = recordError ?? { code: finalOutcome.code, message: finalOutcome.message };
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 = record.pendingReceiptState ?? "unknown";
500
- record.pendingOutcome = undefined;
501
- record.pendingReceiptState = undefined;
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
- delete record.error;
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") return { status: "in_flight", ...identity, startedAt: record.startedAt };
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
- ...(record.content !== undefined ? { content: record.content } : {}),
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 hydrated =
619
- record.kind === "steer" && record.status === "accepted"
838
+ const safeOutcome = (
839
+ outcome: SdkPromptTerminalOutcome | undefined,
840
+ ): SdkPromptTerminalOutcome | undefined =>
841
+ outcome?.kind === "failed"
620
842
  ? {
621
- ...record,
622
- status: "uncertain" as const,
623
- settledAt: now(),
624
- error: {
625
- code: "process_restart_uncertain",
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
- : { ...record };
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
- checks.push({
943
- name: "daemon",
944
- level: "warn",
945
- detail: `daemon owner pid ${daemon.pid} is not alive; run recovery to clear the stale lock`,
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 { ReceiptState } from "../receipt-state";
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);