@gajae-code/coding-agent 0.18.10 → 0.18.12

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 +295 -0
  2. package/dist/types/async/job-manager.d.ts +7 -3
  3. package/dist/types/async/result-formatting.d.ts +2 -0
  4. package/dist/types/autoresearch/git.d.ts +1 -1
  5. package/dist/types/capability/fs.d.ts +2 -2
  6. package/dist/types/cli/accounts-cli.d.ts +2 -2
  7. package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
  8. package/dist/types/cli/install-activation.d.ts +10 -4
  9. package/dist/types/cli/public-command-errors.d.ts +1 -0
  10. package/dist/types/cli/update-cli.d.ts +14 -1
  11. package/dist/types/cli-ordinary.d.ts +2 -2
  12. package/dist/types/commands/accounts.d.ts +2 -1
  13. package/dist/types/commands/migrate.d.ts +1 -1
  14. package/dist/types/commands/sdk.d.ts +5 -0
  15. package/dist/types/commit/changelog/index.d.ts +1 -0
  16. package/dist/types/config/autorouting-tier-map.d.ts +30 -0
  17. package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
  18. package/dist/types/config/config-hot-reload.d.ts +39 -0
  19. package/dist/types/config/file-lock.d.ts +8 -1
  20. package/dist/types/config/model-discovery-manager.d.ts +2 -0
  21. package/dist/types/config/model-profile-activation.d.ts +62 -6
  22. package/dist/types/config/model-registry.d.ts +56 -5
  23. package/dist/types/config/model-resolver.d.ts +2 -0
  24. package/dist/types/config/models-config-schema.d.ts +10 -0
  25. package/dist/types/config/settings-schema.d.ts +46 -3
  26. package/dist/types/config/settings.d.ts +33 -1
  27. package/dist/types/dap/config.d.ts +8 -4
  28. package/dist/types/debug/system-info.d.ts +6 -1
  29. package/dist/types/discovery/mcp-compat.d.ts +3 -1
  30. package/dist/types/edit/modes/patch.d.ts +5 -0
  31. package/dist/types/edit/modes/replace.d.ts +3 -0
  32. package/dist/types/exec/bash-executor.d.ts +12 -0
  33. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
  34. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  35. package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
  36. package/dist/types/migrate/adapters/codex.d.ts +2 -1
  37. package/dist/types/migrate/adapters/index.d.ts +10 -1
  38. package/dist/types/migrate/executor.d.ts +3 -1
  39. package/dist/types/migrate/types.d.ts +15 -3
  40. package/dist/types/modes/components/settings-selector.d.ts +6 -0
  41. package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
  42. package/dist/types/modes/theme/theme.d.ts +1 -0
  43. package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
  44. package/dist/types/runtime-mcp/manager.d.ts +7 -0
  45. package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
  46. package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
  47. package/dist/types/sdk/broker/broker-exit.d.ts +2 -2
  48. package/dist/types/sdk/broker/broker.d.ts +7 -1
  49. package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
  50. package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
  51. package/dist/types/sdk/broker/ensure.d.ts +136 -7
  52. package/dist/types/sdk/broker/hop.d.ts +36 -0
  53. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
  54. package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
  55. package/dist/types/sdk/broker/session-index.d.ts +4 -1
  56. package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
  57. package/dist/types/sdk/bus/index.d.ts +23 -0
  58. package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
  59. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  60. package/dist/types/sdk/cli/session-cli.d.ts +0 -1
  61. package/dist/types/sdk/host/session-runtime.d.ts +32 -0
  62. package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
  63. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
  64. package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
  65. package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
  66. package/dist/types/sdk/session-list.d.ts +1 -1
  67. package/dist/types/session/agent-session.d.ts +58 -2
  68. package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
  69. package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
  70. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
  71. package/dist/types/session/messages.d.ts +5 -1
  72. package/dist/types/session/session-manager.d.ts +13 -3
  73. package/dist/types/session/startup-auth-config.d.ts +24 -2
  74. package/dist/types/session/streaming-output.d.ts +11 -0
  75. package/dist/types/session/terminal-abort.d.ts +3 -1
  76. package/dist/types/session-import/service.d.ts +1 -1
  77. package/dist/types/skill-state/workflow-mutation-guard.d.ts +7 -3
  78. package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
  79. package/dist/types/task/executor.d.ts +19 -2
  80. package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
  81. package/dist/types/tools/atomic-file-write.d.ts +9 -7
  82. package/dist/types/tools/bash-allowed-prefixes.d.ts +11 -0
  83. package/dist/types/tools/descriptors.d.ts +2 -0
  84. package/dist/types/tools/fetch.d.ts +1 -1
  85. package/dist/types/tools/index.d.ts +2 -0
  86. package/dist/types/tools/python.d.ts +1 -1
  87. package/dist/types/tools/report-issue.d.ts +41 -0
  88. package/dist/types/tools/subagent.d.ts +23 -0
  89. package/dist/types/web/scrapers/types.d.ts +8 -1
  90. package/dist/types/web/search/providers/insane.d.ts +7 -1
  91. package/dist/types/web/search/providers/searxng.d.ts +5 -0
  92. package/package.json +8 -8
  93. package/scripts/build-binary.ts +2 -1
  94. package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
  95. package/scripts/generate-docs-index.ts +8 -1
  96. package/scripts/generate-sdk-operation-inventory.ts +22 -4
  97. package/src/async/job-manager.ts +356 -56
  98. package/src/async/result-formatting.ts +40 -0
  99. package/src/autoresearch/git.ts +9 -2
  100. package/src/autoresearch/prompts/prompt-setup.md +1 -1
  101. package/src/capability/fs.ts +27 -19
  102. package/src/capability/index.ts +2 -2
  103. package/src/cli/accounts-cli.ts +176 -8
  104. package/src/cli/auth-broker-cli.ts +21 -11
  105. package/src/cli/doctor/install-repairs.ts +35 -5
  106. package/src/cli/fast-help.ts +1 -1
  107. package/src/cli/install-activation.ts +47 -4
  108. package/src/cli/migrate-cli.ts +7 -5
  109. package/src/cli/public-command-errors.ts +4 -0
  110. package/src/cli/root-flags.ts +2 -2
  111. package/src/cli/stats-cli.ts +9 -5
  112. package/src/cli/update-cli.ts +224 -7
  113. package/src/cli-ordinary.ts +7 -2
  114. package/src/commands/accounts.ts +10 -5
  115. package/src/commands/migrate.ts +3 -3
  116. package/src/commands/sdk.ts +199 -42
  117. package/src/commands/ultragoal.ts +16 -8
  118. package/src/commit/changelog/index.ts +39 -1
  119. package/src/commit/message.ts +10 -3
  120. package/src/config/autorouting-tier-map.ts +30 -0
  121. package/src/config/config-hot-reload-trust.ts +148 -0
  122. package/src/config/config-hot-reload.ts +734 -0
  123. package/src/config/file-lock.ts +216 -56
  124. package/src/config/model-bindings-applier.ts +85 -47
  125. package/src/config/model-discovery-manager.ts +10 -0
  126. package/src/config/model-profile-activation.ts +716 -113
  127. package/src/config/model-registry.ts +1334 -158
  128. package/src/config/model-resolver.ts +3 -1
  129. package/src/config/models-config-schema.ts +6 -0
  130. package/src/config/settings-schema.ts +60 -3
  131. package/src/config/settings.ts +458 -24
  132. package/src/coordinator-mcp/policy.ts +26 -1
  133. package/src/crash/sanitize.ts +4 -0
  134. package/src/customization/import.ts +3 -1
  135. package/src/dap/config.ts +31 -11
  136. package/src/dap/defaults.json +1 -1
  137. package/src/debug/crash-diagnostics.ts +2 -1
  138. package/src/debug/report-bundle.ts +19 -0
  139. package/src/debug/system-info.ts +8 -3
  140. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
  141. package/src/discovery/agents-md.ts +1 -1
  142. package/src/discovery/helpers.ts +1 -2
  143. package/src/discovery/mcp-compat.ts +120 -17
  144. package/src/edit/apply-patch/index.ts +12 -1
  145. package/src/edit/index.ts +13 -17
  146. package/src/edit/modes/patch.ts +187 -8
  147. package/src/edit/modes/replace.ts +136 -0
  148. package/src/eval/js/context-manager.ts +29 -3
  149. package/src/eval/js/worker-entry.ts +51 -18
  150. package/src/eval/py/executor.ts +14 -4
  151. package/src/eval/py/runner.py +144 -5
  152. package/src/exec/bash-executor.ts +81 -4
  153. package/src/exec/bash-shell-guardian.ts +4 -1
  154. package/src/exec/bash-shell-supervisor.ts +8 -4
  155. package/src/exec/isolated-shell.ts +5 -1
  156. package/src/export/html/index.ts +46 -6
  157. package/src/extensibility/custom-tools/wrapper.ts +3 -1
  158. package/src/extensibility/extensions/loader.ts +8 -1
  159. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
  160. package/src/gjc-runtime/gc-runtime.ts +33 -6
  161. package/src/gjc-runtime/launch-tmux.ts +7 -2
  162. package/src/gjc-runtime/launch-worktree.test.ts +148 -0
  163. package/src/gjc-runtime/launch-worktree.ts +101 -14
  164. package/src/gjc-runtime/managed-owner-admission.ts +46 -2
  165. package/src/gjc-runtime/session-resolution.ts +38 -6
  166. package/src/gjc-runtime/session-state-lock.ts +59 -14
  167. package/src/gjc-runtime/state-runtime.ts +23 -19
  168. package/src/hooks/native-skill-hook.ts +14 -11
  169. package/src/internal-urls/docs-index.generated.ts +14 -13
  170. package/src/internal-urls/local-protocol.ts +142 -8
  171. package/src/lsp/index.ts +29 -0
  172. package/src/lsp/workspace-path.ts +23 -0
  173. package/src/migrate/action-planner.ts +57 -0
  174. package/src/migrate/adapters/claude-code.ts +23 -4
  175. package/src/migrate/adapters/codex.ts +22 -4
  176. package/src/migrate/adapters/index.ts +22 -4
  177. package/src/migrate/adapters/opencode.ts +6 -1
  178. package/src/migrate/executor.ts +84 -7
  179. package/src/migrate/report.ts +19 -2
  180. package/src/migrate/types.ts +14 -4
  181. package/src/modes/DESIGN.md +51 -0
  182. package/src/modes/acp/permission-mode.ts +18 -1
  183. package/src/modes/components/assistant-message.ts +55 -1
  184. package/src/modes/components/settings-selector.ts +103 -37
  185. package/src/modes/controllers/command-controller.ts +38 -2
  186. package/src/modes/controllers/plan-mode-controller.ts +17 -7
  187. package/src/modes/controllers/selector-controller.ts +62 -6
  188. package/src/modes/interactive-mode.ts +68 -0
  189. package/src/modes/theme/theme.ts +15 -0
  190. package/src/prompts/tools/debug.md +1 -1
  191. package/src/prompts/tools/read.md +1 -1
  192. package/src/prompts/tools/report-issue.md +13 -0
  193. package/src/prompts/tools/task.md +1 -1
  194. package/src/runtime-credential-selector.ts +2 -2
  195. package/src/runtime-mcp/content-limits.ts +7 -0
  196. package/src/runtime-mcp/manager-pool.test.ts +5 -4
  197. package/src/runtime-mcp/manager.ts +32 -0
  198. package/src/runtime-mcp/oauth-flow.ts +32 -6
  199. package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
  200. package/src/runtime-mcp/render.ts +16 -1
  201. package/src/runtime-mcp/tool-bridge.ts +126 -17
  202. package/src/sdk/broker/broker-exit.ts +8 -5
  203. package/src/sdk/broker/broker.ts +342 -82
  204. package/src/sdk/broker/daemon-entry.ts +338 -116
  205. package/src/sdk/broker/doctor-restart.ts +2 -1
  206. package/src/sdk/broker/ensure.ts +1143 -265
  207. package/src/sdk/broker/hop.ts +274 -0
  208. package/src/sdk/broker/lifecycle-ledger.ts +35 -11
  209. package/src/sdk/broker/lock-host-identity.ts +29 -0
  210. package/src/sdk/broker/session-index.ts +62 -12
  211. package/src/sdk/bus/chat-command-policy.ts +16 -3
  212. package/src/sdk/bus/index.ts +52 -21
  213. package/src/sdk/bus/reconciliation-store.ts +201 -8
  214. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  215. package/src/sdk/cli/session-cli.ts +21 -6
  216. package/src/sdk/host/session-runtime.test.ts +118 -23
  217. package/src/sdk/host/session-runtime.ts +352 -47
  218. package/src/sdk/lifecycle/service.ts +1 -0
  219. package/src/sdk/prompt-deadline-flush.ts +78 -5
  220. package/src/sdk/prompt-deadline-manager.ts +68 -14
  221. package/src/sdk/protocol/adapter-validation.ts +53 -11
  222. package/src/sdk/protocol/operation-inventory.generated.json +101 -2
  223. package/src/sdk/reconciliation-extensions.ts +1 -0
  224. package/src/sdk/session-list.ts +1 -0
  225. package/src/sdk/session.ts +114 -61
  226. package/src/session/agent-session.ts +1043 -81
  227. package/src/session/internal/managed-session-scope.ts +126 -39
  228. package/src/session/internal/managed-session-storage.ts +14 -11
  229. package/src/session/internal/native-publish-outcome.ts +5 -1
  230. package/src/session/messages.ts +47 -6
  231. package/src/session/session-manager.ts +236 -14
  232. package/src/session/startup-auth-config.ts +116 -8
  233. package/src/session/streaming-output.ts +262 -4
  234. package/src/session/terminal-abort.ts +14 -2
  235. package/src/session-import/service.ts +1 -0
  236. package/src/skill-state/workflow-mutation-guard.ts +491 -32
  237. package/src/slash-commands/builtin-registry.ts +15 -3
  238. package/src/slash-commands/helpers/credential-switch.ts +10 -5
  239. package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
  240. package/src/task/executor.ts +103 -12
  241. package/src/task/index.ts +389 -175
  242. package/src/tools/ast-edit-write-path.ts +64 -0
  243. package/src/tools/ast-edit.ts +18 -1
  244. package/src/tools/atomic-file-write.ts +46 -15
  245. package/src/tools/auto-generated-guard.ts +3 -0
  246. package/src/tools/bash-allowed-prefixes.ts +153 -5
  247. package/src/tools/bash-interactive.ts +14 -2
  248. package/src/tools/bash.ts +14 -0
  249. package/src/tools/debug.ts +7 -6
  250. package/src/tools/descriptors.test.ts +44 -0
  251. package/src/tools/descriptors.ts +34 -2
  252. package/src/tools/fetch.ts +3 -2
  253. package/src/tools/index.ts +2 -0
  254. package/src/tools/output-meta.ts +23 -2
  255. package/src/tools/python.ts +1 -1
  256. package/src/tools/read.ts +17 -35
  257. package/src/tools/report-issue.ts +184 -0
  258. package/src/tools/subagent-render.ts +114 -12
  259. package/src/tools/subagent.ts +108 -1
  260. package/src/tools/todo-write.ts +41 -3
  261. package/src/tools/tool-catalog.generated.ts +31 -3
  262. package/src/tools/write.ts +35 -3
  263. package/src/utils/commit-message-generator.ts +9 -2
  264. package/src/utils/file-mentions.ts +29 -1
  265. package/src/utils/title-generator.ts +7 -2
  266. package/src/web/scrapers/index.ts +3 -1
  267. package/src/web/scrapers/types.ts +8 -0
  268. package/src/web/scrapers/youtube.ts +3 -2
  269. package/src/web/search/providers/insane.ts +91 -28
  270. package/src/web/search/providers/searxng.ts +62 -10
@@ -73,7 +73,7 @@ import {
73
73
  syntheticNamespaceCollision,
74
74
  } from "../model-profile-model";
75
75
  import { projectQ10Models } from "../models.js";
76
- import { flushWorktreeOnPromptDeadline } from "../prompt-deadline-flush";
76
+ import { autosaveWorktreeOnPromptDeadline } from "../prompt-deadline-flush";
77
77
  import {
78
78
  PromptDeadlineManager,
79
79
  type PromptDeadlinePublicationResult,
@@ -103,6 +103,7 @@ import {
103
103
  createKindAwareReconciliation,
104
104
  createReconciliationStore,
105
105
  type KindAwareReconciliation,
106
+ type ReconciliationStoreInstrumentationEvent,
106
107
  resolveReconciliationSessionFile,
107
108
  } from "../reconciliation-extensions";
108
109
  import {
@@ -445,6 +446,8 @@ export interface SdkOnlyTerminalAbortSeams {
445
446
  getReconciliationStore?: () => SdkOnlyReconciliationStore | undefined;
446
447
  getTerminalTurnEpoch: () => number | undefined;
447
448
  getActivePromptHandle: () => string | undefined;
449
+ onDeadlineRecoveryCheckpointForTests?: (checkpoint: SdkOnlyDeadlineRecoveryCheckpoint) => void;
450
+ onPersistenceInstrumentationForTests?: (event: PersistenceInstrumentationEvent) => void;
448
451
  /** Re-read the active prompt's owning SDK connection for the owner-mismatch
449
452
  * recheck; falls back to the runtime-tracked owner when absent (review
450
453
  * thread P1). */
@@ -481,6 +484,35 @@ export interface SdkOnlyTerminalAbortSeams {
481
484
  maxDurableTerminalReservationsForTests?: number;
482
485
  }
483
486
 
487
+ export interface SdkOnlyDeadlineRecoveryCheckpoint {
488
+ at: number;
489
+ stage:
490
+ | "terminalization-start"
491
+ | "terminalization-result"
492
+ | "publication-start"
493
+ | "publication-result"
494
+ | "note-transition-start"
495
+ | "note-transition-awaiting-finalization"
496
+ | "note-transition-finalization-ready"
497
+ | "note-transition-before-persist"
498
+ | "note-transition-persisted"
499
+ | "note-transition-persist-failed"
500
+ | "deadline-recovery-retired";
501
+ correlation: InvocationCorrelation;
502
+ result?: "settled" | "uncertain" | "published" | "not-published" | "rejected";
503
+ reason?: string;
504
+ recordStatus?: string;
505
+ recordRevision?: number;
506
+ recordTerminalAt?: number;
507
+ pendingFinalization?: boolean;
508
+ eventCaptured?: boolean;
509
+ eventPrepared?: boolean;
510
+ terminalCommitted?: boolean;
511
+ leaseCurrent?: boolean;
512
+ pendingToolCount?: number;
513
+ errorCode?: string;
514
+ }
515
+
484
516
  /**
485
517
  * The transport-neutral SDK session runtime.
486
518
  *
@@ -1024,8 +1056,26 @@ function canonicalizeHydratedDiagnostics(record: InvocationRecord): InvocationRe
1024
1056
  return canonical;
1025
1057
  }
1026
1058
 
1059
+ export interface PersistenceInstrumentationEvent {
1060
+ at: number;
1061
+ phase: "enqueued" | "started" | "completed" | "failed";
1062
+ operation?: string;
1063
+ substep?: string;
1064
+ error?: string;
1065
+ /** Detailed information about lock acquisition, promise identity, db state, etc. */
1066
+ detail?: string;
1067
+ /** Time spent waiting (ms) before the operation started. */
1068
+ waitMs?: number;
1069
+ }
1070
+
1027
1071
  export function createInvocationReconciliation(
1028
- options: { stateRoot?: string; sessionId?: string; store?: SdkOnlyReconciliationStore } = {},
1072
+ options: {
1073
+ stateRoot?: string;
1074
+ sessionId?: string;
1075
+ store?: SdkOnlyReconciliationStore;
1076
+ onDeadlineRecoveryCheckpointForTests?: (checkpoint: SdkOnlyDeadlineRecoveryCheckpoint) => void;
1077
+ onPersistenceInstrumentationForTests?: (event: PersistenceInstrumentationEvent) => void;
1078
+ } = {},
1029
1079
  ): InvocationReconciliation {
1030
1080
  const ACTIVE_CAPACITY = 256;
1031
1081
  const TERMINAL_CAPACITY = 512;
@@ -1063,6 +1113,17 @@ export function createInvocationReconciliation(
1063
1113
  }
1064
1114
  >();
1065
1115
  const pendingTerminalVisibility = new Map<string, { visibleRecord: InvocationRecord; writes: number }>();
1116
+ const recordDeadlineRecoveryCheckpoint = (
1117
+ stage: SdkOnlyDeadlineRecoveryCheckpoint["stage"],
1118
+ correlation: InvocationCorrelation,
1119
+ details: Omit<SdkOnlyDeadlineRecoveryCheckpoint, "at" | "correlation" | "stage"> = {},
1120
+ ): void => {
1121
+ try {
1122
+ options.onDeadlineRecoveryCheckpointForTests?.({ at: Date.now(), stage, correlation, ...details });
1123
+ } catch {
1124
+ // A test observer must never participate in durable reconciliation.
1125
+ }
1126
+ };
1066
1127
  const retainPendingTerminalVisibility = (recordKey: string, visibleRecord: InvocationRecord): (() => void) => {
1067
1128
  const existing = pendingTerminalVisibility.get(recordKey);
1068
1129
  if (existing) existing.writes += 1;
@@ -1079,36 +1140,120 @@ export function createInvocationReconciliation(
1079
1140
  };
1080
1141
  const persist = async (): Promise<void> => {
1081
1142
  const run = async (): Promise<void> => {
1143
+ try {
1144
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "enqueued" });
1145
+ } catch {
1146
+ // A test observer must never participate in durable reconciliation.
1147
+ }
1148
+ try {
1149
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started" });
1150
+ } catch {
1151
+ // Ignore observer errors.
1152
+ }
1082
1153
  // Construct the candidate only when this serialized write starts. A
1083
1154
  // pre-await full snapshot lets a later agent_start/agent_end transition
1084
1155
  // be overwritten on disk by an older queued write even though the live
1085
1156
  // map has already converged.
1086
1157
  const snapshot = [...records.values()].map(record => ({ ...record }));
1087
1158
  if (store) {
1088
- await store.transact(current => [
1089
- ...current.filter(record => record.kind !== "prompt" && record.kind !== "skill"),
1090
- ...snapshot.map(record => ({ ...record })),
1091
- ]);
1159
+ const promiseId = `transact-${crypto.randomUUID().slice(0, 8)}`;
1160
+ const lockWaitStart = Date.now();
1161
+ try {
1162
+ options.onPersistenceInstrumentationForTests?.({
1163
+ at: lockWaitStart,
1164
+ phase: "started",
1165
+ operation: "store.transact",
1166
+ detail: `promise=${promiseId} awaiting-lock`,
1167
+ });
1168
+ } catch {}
1169
+ const lockAcquiredTime = Date.now();
1170
+ try {
1171
+ await store.transact(current => [
1172
+ ...current.filter(record => record.kind !== "prompt" && record.kind !== "skill"),
1173
+ ...snapshot.map(record => ({ ...record })),
1174
+ ]);
1175
+ } catch (error) {
1176
+ try {
1177
+ options.onPersistenceInstrumentationForTests?.({
1178
+ at: Date.now(),
1179
+ phase: "failed",
1180
+ operation: "store.transact",
1181
+ detail: `promise=${promiseId} lock-wait=${Date.now() - lockWaitStart}ms`,
1182
+ error: error instanceof Error ? error.message : String(error),
1183
+ });
1184
+ } catch {}
1185
+ throw error;
1186
+ }
1187
+ try {
1188
+ options.onPersistenceInstrumentationForTests?.({
1189
+ at: Date.now(),
1190
+ phase: "completed",
1191
+ operation: "store.transact",
1192
+ detail: `promise=${promiseId} lock-acquire=${lockAcquiredTime - lockWaitStart}ms`,
1193
+ });
1194
+ } catch {}
1092
1195
  return;
1093
1196
  }
1094
1197
  if (!reconciliationFile) return;
1095
1198
  const directory = path.dirname(reconciliationFile);
1096
1199
  const temporary = `${reconciliationFile}.${process.pid}.${crypto.randomUUID()}.tmp`;
1200
+ try {
1201
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "mkdir" });
1202
+ } catch {}
1097
1203
  await fs.mkdir(directory, { recursive: true, mode: 0o700 });
1204
+ try {
1205
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed", substep: "mkdir" });
1206
+ } catch {}
1207
+ try {
1208
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "writeFile" });
1209
+ } catch {}
1098
1210
  await fs.writeFile(
1099
1211
  temporary,
1100
1212
  JSON.stringify({ version: 1, sessionId: options.sessionId, records: [...snapshot] }),
1101
1213
  { encoding: "utf8", mode: 0o600 },
1102
1214
  );
1215
+ try {
1216
+ options.onPersistenceInstrumentationForTests?.({
1217
+ at: Date.now(),
1218
+ phase: "completed",
1219
+ substep: "writeFile",
1220
+ });
1221
+ } catch {}
1222
+ try {
1223
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "chmod" });
1224
+ } catch {}
1103
1225
  await fs.chmod(temporary, 0o600);
1226
+ try {
1227
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed", substep: "chmod" });
1228
+ } catch {}
1229
+ try {
1230
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "started", substep: "rename" });
1231
+ } catch {}
1104
1232
  await fs.rename(temporary, reconciliationFile);
1233
+ try {
1234
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed", substep: "rename" });
1235
+ } catch {}
1105
1236
  };
1106
1237
  const pending = persistenceChain.then(run, run);
1107
1238
  persistenceChain = pending.then(
1108
1239
  () => undefined,
1109
1240
  () => undefined,
1110
1241
  );
1111
- await pending;
1242
+ try {
1243
+ await pending;
1244
+ try {
1245
+ options.onPersistenceInstrumentationForTests?.({ at: Date.now(), phase: "completed" });
1246
+ } catch {}
1247
+ } catch (error) {
1248
+ try {
1249
+ options.onPersistenceInstrumentationForTests?.({
1250
+ at: Date.now(),
1251
+ phase: "failed",
1252
+ error: error instanceof Error ? error.message : String(error),
1253
+ });
1254
+ } catch {}
1255
+ throw error;
1256
+ }
1112
1257
  };
1113
1258
  // Retention contract (#4547): terminal records are never age-evicted; only
1114
1259
  // the per-kind oldest-terminal-first capacity trim removes them, so a
@@ -1257,7 +1402,22 @@ export function createInvocationReconciliation(
1257
1402
  let record = records.get(recordKey);
1258
1403
  if (!record) return;
1259
1404
  const pending = pendingFinalizations.get(recordKey);
1405
+ const isPromptEnd = kind === "prompt" && frame.type === "agent_end";
1406
+ if (isPromptEnd)
1407
+ recordDeadlineRecoveryCheckpoint("note-transition-start", correlation, {
1408
+ recordStatus: record.status,
1409
+ recordRevision: record.revision,
1410
+ recordTerminalAt: record.terminalAt,
1411
+ pendingFinalization: pending?.finalizedRecord === record,
1412
+ });
1260
1413
  if (pending?.finalizedRecord === record) {
1414
+ if (isPromptEnd)
1415
+ recordDeadlineRecoveryCheckpoint("note-transition-awaiting-finalization", correlation, {
1416
+ recordStatus: record.status,
1417
+ recordRevision: record.revision,
1418
+ recordTerminalAt: record.terminalAt,
1419
+ pendingFinalization: true,
1420
+ });
1261
1421
  const upgrade = Promise.withResolvers<void>();
1262
1422
  pending.upgrades.add(upgrade);
1263
1423
  let upgradeError: unknown;
@@ -1269,6 +1429,13 @@ export function createInvocationReconciliation(
1269
1429
  // so a real lifecycle event is never swallowed.
1270
1430
  }
1271
1431
  const current = records.get(recordKey);
1432
+ if (isPromptEnd)
1433
+ recordDeadlineRecoveryCheckpoint("note-transition-finalization-ready", correlation, {
1434
+ recordStatus: current?.status,
1435
+ recordRevision: current?.revision,
1436
+ recordTerminalAt: current?.terminalAt,
1437
+ pendingFinalization: pendingFinalizations.get(recordKey) === pending,
1438
+ });
1272
1439
  const incomingOutcome = frame.type === "agent_end" ? canonicalTerminalOutcome(frame.outcome) : undefined;
1273
1440
  const providerFailure =
1274
1441
  current?.error !== undefined && current.error.code !== "prompt_deadline_exceeded";
@@ -1650,10 +1817,31 @@ export function createInvocationReconciliation(
1650
1817
  record.terminalAt === undefined && next.terminalAt !== undefined
1651
1818
  ? retainPendingTerminalVisibility(recordKey, record)
1652
1819
  : undefined;
1820
+ if (isPromptEnd)
1821
+ recordDeadlineRecoveryCheckpoint("note-transition-before-persist", correlation, {
1822
+ recordStatus: next.status,
1823
+ recordRevision: next.revision,
1824
+ recordTerminalAt: next.terminalAt,
1825
+ pendingFinalization: pendingFinalizations.has(recordKey),
1826
+ });
1653
1827
  records.set(recordKey, next);
1654
1828
  try {
1655
1829
  await persist();
1830
+ if (isPromptEnd)
1831
+ recordDeadlineRecoveryCheckpoint("note-transition-persisted", correlation, {
1832
+ recordStatus: next.status,
1833
+ recordRevision: next.revision,
1834
+ recordTerminalAt: next.terminalAt,
1835
+ pendingFinalization: pendingFinalizations.has(recordKey),
1836
+ });
1656
1837
  } catch (error) {
1838
+ if (isPromptEnd)
1839
+ recordDeadlineRecoveryCheckpoint("note-transition-persist-failed", correlation, {
1840
+ recordStatus: next.status,
1841
+ recordRevision: next.revision,
1842
+ recordTerminalAt: next.terminalAt,
1843
+ pendingFinalization: pendingFinalizations.has(recordKey),
1844
+ });
1657
1845
  if (records.get(recordKey) === next) records.set(recordKey, record);
1658
1846
  throw error;
1659
1847
  } finally {
@@ -6419,16 +6607,38 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6419
6607
  (typeof ctx.sessionManager.getSessionFile === "function" ? ctx.sessionManager.getSessionFile() : undefined) ??
6420
6608
  resolveReconciliationSessionFile(undefined, stateRoot, sessionId);
6421
6609
  const sessionIdentity = `${sessionId}\u0000${sessionFile}`;
6610
+ const reconciliationInstrumentation = options.terminalAbortSeams?.onPersistenceInstrumentationForTests
6611
+ ? (event: ReconciliationStoreInstrumentationEvent) => {
6612
+ try {
6613
+ options.terminalAbortSeams?.onPersistenceInstrumentationForTests?.(event as never);
6614
+ } catch {}
6615
+ }
6616
+ : undefined;
6422
6617
  const reconciliationStore =
6423
6618
  options.terminalAbortSeams?.getReconciliationStore?.() ??
6424
- createReconciliationStore({ sessionFile, sessionId });
6425
- const reconciliation = createInvocationReconciliation({ store: reconciliationStore });
6619
+ createReconciliationStore({
6620
+ sessionFile,
6621
+ sessionId,
6622
+ onInstrumentationForTests: reconciliationInstrumentation,
6623
+ });
6624
+ const reconciliation = createInvocationReconciliation({
6625
+ store: reconciliationStore,
6626
+ onDeadlineRecoveryCheckpointForTests: options.terminalAbortSeams?.onDeadlineRecoveryCheckpointForTests,
6627
+ onPersistenceInstrumentationForTests: options.terminalAbortSeams?.onPersistenceInstrumentationForTests,
6628
+ });
6426
6629
  await reconciliation.hydrate();
6427
6630
  const steerReconciliation = createKindAwareReconciliation({
6428
6631
  store: reconciliationStore as never,
6429
6632
  ownedKinds: ["steer"],
6430
6633
  });
6431
6634
  await steerReconciliation.hydrateFromStore();
6635
+ const recordDeadlineRecoveryCheckpoint = (checkpoint: Omit<SdkOnlyDeadlineRecoveryCheckpoint, "at">): void => {
6636
+ try {
6637
+ options.terminalAbortSeams?.onDeadlineRecoveryCheckpointForTests?.({ ...checkpoint, at: Date.now() });
6638
+ } catch {
6639
+ // A test observer must never participate in deadline correctness.
6640
+ }
6641
+ };
6432
6642
  const deadlineManager = new PromptDeadlineManager({
6433
6643
  reconciliation,
6434
6644
  getLeaseMs: () => resolveSdkPromptDeadlineMs(options.settings?.get("sdk.promptDeadlineMs" as never)),
@@ -6448,6 +6658,9 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6448
6658
  const seams = options.terminalAbortSeams;
6449
6659
  const handle = seams?.getActivePromptHandle();
6450
6660
  const epoch = seams?.getTerminalTurnEpoch();
6661
+ // Capture lifecycle ownership independently of settlement proof. Without
6662
+ // a tool-set observer the later terminalization hook still fails closed,
6663
+ // but a matching real end must remain private and recoverable meanwhile.
6451
6664
  if (!seams || !handle || epoch === undefined) return;
6452
6665
  const existing = deadlineTerminalizationObservations.get(key);
6453
6666
  if (existing) return () => cleanupDeadlineTerminalizationObservation(key, existing);
@@ -6495,8 +6708,36 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6495
6708
  const observation = deadlineTerminalizationObservations.get(target);
6496
6709
  const seams = options.terminalAbortSeams;
6497
6710
  const pendingToolExecutions = seams?.pendingToolExecutions;
6498
- if (!observation || !seams || !pendingToolExecutions) return "uncertain";
6499
- const pendingTools = () => pendingToolExecutions(observation.handle);
6711
+ recordDeadlineRecoveryCheckpoint({
6712
+ stage: "terminalization-start",
6713
+ correlation,
6714
+ eventCaptured: observation?.eventCaptured,
6715
+ eventPrepared: observation?.eventPrepared,
6716
+ terminalCommitted: observation?.terminalCommitted,
6717
+ });
6718
+ let pendingToolCount: number | undefined;
6719
+ const finish = (result: PromptDeadlineTerminalization, reason: string): PromptDeadlineTerminalization => {
6720
+ recordDeadlineRecoveryCheckpoint({
6721
+ stage: "terminalization-result",
6722
+ correlation,
6723
+ result,
6724
+ reason,
6725
+ eventCaptured: observation?.eventCaptured,
6726
+ eventPrepared: observation?.eventPrepared,
6727
+ terminalCommitted: observation?.terminalCommitted,
6728
+ pendingToolCount,
6729
+ });
6730
+ // Notify deadline manager about termination result to adjust retry strategy
6731
+ deadlineManager.notifyTerminationResult(correlation, result, reason);
6732
+ return result;
6733
+ };
6734
+ if (!observation || !seams || !pendingToolExecutions)
6735
+ return finish("uncertain", "missing-observation-or-tool-observer");
6736
+ const pendingTools = () => {
6737
+ const pending = pendingToolExecutions(observation.handle);
6738
+ pendingToolCount = pending.length;
6739
+ return pending;
6740
+ };
6500
6741
  const awaitEventPreparation = async (): Promise<boolean> => {
6501
6742
  if (observation.eventPrepared) return true;
6502
6743
  const captured = await Promise.race([
@@ -6507,7 +6748,15 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6507
6748
  };
6508
6749
  if (observation.eventCaptured) {
6509
6750
  const prepared = await awaitEventPreparation();
6510
- return prepared && pendingTools().length === 0 ? "settled" : "uncertain";
6751
+ pendingToolCount = pendingTools().length;
6752
+ return finish(
6753
+ prepared && pendingToolCount === 0 ? "settled" : "uncertain",
6754
+ !prepared
6755
+ ? "captured-event-not-prepared"
6756
+ : pendingToolCount > 0
6757
+ ? "tools-still-pending"
6758
+ : "captured-event-and-tools-settled",
6759
+ );
6511
6760
  }
6512
6761
 
6513
6762
  if (!observation.eventCaptured) {
@@ -6521,13 +6770,13 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6521
6770
  entry => lifecycleCorrelationKey(entry.correlation) === target,
6522
6771
  )
6523
6772
  )
6524
- return "uncertain";
6773
+ return finish("uncertain", "lifecycle-batch-no-longer-owns-correlation");
6525
6774
  if (
6526
6775
  !isCurrent() ||
6527
6776
  seams.getActivePromptHandle() !== observation.handle ||
6528
6777
  seams.getTerminalTurnEpoch() !== observation.epoch
6529
6778
  )
6530
- return "uncertain";
6779
+ return finish("uncertain", "lease-or-active-run-mismatch");
6531
6780
  }
6532
6781
  await waitForToolCallBoundary({
6533
6782
  pending: pendingTools,
@@ -6536,14 +6785,22 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6536
6785
  });
6537
6786
  if (observation.eventCaptured) {
6538
6787
  const prepared = await awaitEventPreparation();
6539
- return prepared && pendingTools().length === 0 ? "settled" : "uncertain";
6788
+ pendingToolCount = pendingTools().length;
6789
+ return finish(
6790
+ prepared && pendingToolCount === 0 ? "settled" : "uncertain",
6791
+ !prepared
6792
+ ? "captured-event-not-prepared"
6793
+ : pendingToolCount > 0
6794
+ ? "tools-still-pending"
6795
+ : "captured-event-and-tools-settled",
6796
+ );
6540
6797
  }
6541
6798
  if (
6542
6799
  !isCurrent() ||
6543
6800
  seams.getActivePromptHandle() !== observation.handle ||
6544
6801
  seams.getTerminalTurnEpoch() !== observation.epoch
6545
6802
  )
6546
- return "uncertain";
6803
+ return finish("uncertain", "lease-or-active-run-mismatch-after-tool-boundary");
6547
6804
 
6548
6805
  let steeringSnapshotToken: number | undefined;
6549
6806
  let proof: { status: string; terminalScope?: unknown } | undefined;
@@ -6560,7 +6817,15 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6560
6817
  });
6561
6818
  if (proof.status !== "settled") {
6562
6819
  const prepared = await awaitEventPreparation();
6563
- return prepared && pendingTools().length === 0 ? "settled" : "uncertain";
6820
+ pendingToolCount = pendingTools().length;
6821
+ return finish(
6822
+ prepared && pendingToolCount === 0 ? "settled" : "uncertain",
6823
+ !prepared
6824
+ ? "abort-proof-not-settled-and-event-not-prepared"
6825
+ : pendingToolCount > 0
6826
+ ? "tools-still-pending"
6827
+ : "captured-event-and-tools-settled",
6828
+ );
6564
6829
  }
6565
6830
  const terminalScope = proof.terminalScope as
6566
6831
  | { abortedAttemptEpoch?: unknown; lineageIdHash?: unknown }
@@ -6570,17 +6835,26 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6570
6835
  typeof terminalScope.lineageIdHash !== "string" ||
6571
6836
  isOwnedAttemptRegistrationIncomplete(terminalScope.lineageIdHash, observation.epoch)
6572
6837
  )
6573
- return "uncertain";
6838
+ return finish("uncertain", "terminal-scope-mismatch");
6574
6839
  const exactJobs = findOwnedRegistrationsForTurn(terminalScope.lineageIdHash, observation.epoch);
6575
6840
  if (exactJobs.length > 0) {
6576
6841
  const endpointId = exactJobs[0]?.endpointId;
6577
6842
  const manager = AsyncJobManager.forEndpoint(endpointId) ?? AsyncJobManager.instance();
6578
- if (!manager || (await settleOwnedWork(manager, exactJobs, 500)) !== "stopped") return "uncertain";
6843
+ if (!manager || (await settleOwnedWork(manager, exactJobs, 500)) !== "stopped")
6844
+ return finish("uncertain", "owned-work-not-stopped");
6579
6845
  }
6580
6846
  const prepared = await awaitEventPreparation();
6581
- return prepared && pendingTools().length === 0 ? "settled" : "uncertain";
6847
+ pendingToolCount = pendingTools().length;
6848
+ return finish(
6849
+ prepared && pendingToolCount === 0 ? "settled" : "uncertain",
6850
+ !prepared
6851
+ ? "captured-event-not-prepared"
6852
+ : pendingToolCount > 0
6853
+ ? "tools-still-pending"
6854
+ : "captured-event-and-tools-settled",
6855
+ );
6582
6856
  } catch {
6583
- return "uncertain";
6857
+ return finish("uncertain", "terminal-abort-error");
6584
6858
  } finally {
6585
6859
  if (steeringSnapshotToken !== undefined && proof?.status !== "settled")
6586
6860
  seams.discardTerminalAbortSteeringSnapshot?.(steeringSnapshotToken);
@@ -6592,21 +6866,57 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6592
6866
  const terminalOutcome = observation
6593
6867
  ? deadlineOutcomeAfterAbort(correlation, observation.terminalOutcome, observation.terminalHasActivity)
6594
6868
  : undefined;
6595
- if (!observation?.eventCaptured || !observation.eventPrepared || terminalOutcome === undefined)
6869
+ let terminalIsCurrent: boolean | undefined;
6870
+ let pendingToolCount = -1;
6871
+ const recordPublicationResult = (
6872
+ result: SdkOnlyDeadlineRecoveryCheckpoint["result"],
6873
+ reason: string,
6874
+ errorCodeValue?: string,
6875
+ ): void => {
6876
+ recordDeadlineRecoveryCheckpoint({
6877
+ stage: "publication-result",
6878
+ correlation,
6879
+ result,
6880
+ reason,
6881
+ eventCaptured: observation?.eventCaptured,
6882
+ eventPrepared: observation?.eventPrepared,
6883
+ terminalCommitted: observation?.terminalCommitted,
6884
+ leaseCurrent: terminalIsCurrent,
6885
+ pendingToolCount,
6886
+ errorCode: errorCodeValue,
6887
+ });
6888
+ };
6889
+ recordDeadlineRecoveryCheckpoint({
6890
+ stage: "publication-start",
6891
+ correlation,
6892
+ eventCaptured: observation?.eventCaptured,
6893
+ eventPrepared: observation?.eventPrepared,
6894
+ terminalCommitted: observation?.terminalCommitted,
6895
+ });
6896
+ if (!observation?.eventCaptured || !observation.eventPrepared || terminalOutcome === undefined) {
6897
+ recordPublicationResult("rejected", "missing-captured-terminal-outcome");
6596
6898
  return false;
6899
+ }
6597
6900
  if (!observation.terminalCommitted) {
6598
6901
  const pendingTools = options.terminalAbortSeams?.pendingToolExecutions;
6599
- let terminalIsCurrent = false;
6600
- let pendingToolCount = -1;
6601
6902
  try {
6602
6903
  terminalIsCurrent = isCurrent();
6603
6904
  if (pendingTools) pendingToolCount = pendingTools(observation.handle).length;
6604
- } catch {
6905
+ } catch (error) {
6906
+ recordPublicationResult("rejected", "publication-precheck-threw", errorCode(error));
6907
+ return false;
6908
+ }
6909
+ if (!terminalIsCurrent || !pendingTools || pendingToolCount > 0) {
6910
+ recordPublicationResult("rejected", "publication-precheck-rejected");
6605
6911
  return false;
6606
6912
  }
6607
- if (!terminalIsCurrent || !pendingTools || pendingToolCount > 0) return false;
6608
6913
  try {
6609
- if (!isCurrent() || pendingTools(observation.handle).length > 0) return false;
6914
+ terminalIsCurrent = isCurrent();
6915
+ pendingToolCount = pendingTools(observation.handle).length;
6916
+ if (!terminalIsCurrent || pendingToolCount > 0) {
6917
+ recordPublicationResult("rejected", "publication-recheck-rejected");
6918
+ return false;
6919
+ }
6610
6920
  // Commit the authoritative Q26 result before exposing its terminal
6611
6921
  // boundary; a crash between publication and this write would strand
6612
6922
  // restart recovery with only a hidden pending claim.
@@ -6619,40 +6929,35 @@ export function createSdkSessionRuntimeExtension(api: ExtensionAPI, options: Cre
6619
6929
  observation.terminalOutcome = terminalOutcome;
6620
6930
  observation.terminalCommitted = true;
6621
6931
  observation.clearUnrecordedFailure?.();
6622
- } catch {
6932
+ } catch (error) {
6933
+ recordPublicationResult("rejected", "terminal-transition-persist-failed", errorCode(error));
6623
6934
  return false;
6624
6935
  }
6625
6936
  }
6626
6937
  const published = publishDeadlineTerminalBoundary(observation, correlation, terminalOutcome);
6938
+ recordPublicationResult(published ? "published" : "not-published", "terminal-boundary-result");
6627
6939
  return { outcome: terminalOutcome, published } satisfies PromptDeadlinePublicationResult;
6628
6940
  },
6629
6941
  // Persist the agent's uncommitted work before the retirement below tears
6630
6942
  // the session down (#5583). Best effort by contract: failures are logged
6631
6943
  // inside the flush and the deadline outcome is unaffected.
6632
6944
  onDeadlineExceeded: async (_correlation, signal, isCurrent) => {
6633
- if (options.settings?.get("sdk.flushWorktreeOnDeadline" as never) === false) return;
6634
- // `has` is true only for a value the user actually wrote, so this
6635
- // separates an explicit opt-in from the schema default. The flush only
6636
- // honours the default inside a linked worktree the session owns.
6637
- const configured = options.settings?.get("sdk.flushWorktreeOnDeadline" as never);
6638
- const hasExplicitSetting =
6639
- typeof options.settings?.has === "function"
6640
- ? options.settings.has("sdk.flushWorktreeOnDeadline" as never)
6641
- : configured === true;
6642
- const explicitOptIn = hasExplicitSetting === true && configured === true;
6643
- await flushWorktreeOnPromptDeadline(ctx.cwd, {
6644
- agentDir: options.settings?.getAgentDir(),
6645
- explicitOptIn,
6646
- isCurrent,
6647
- signal,
6648
- });
6945
+ // Project `.gjc` files are not an opt-in. The shared helper reads the
6946
+ // user/global layer and runtime overrides only (#5583).
6947
+ await autosaveWorktreeOnPromptDeadline(ctx.cwd, options.settings, { isCurrent, signal });
6649
6948
  },
6650
6949
  onExpired: correlation => {
6651
6950
  retireAcceptedQueueCancellation(acceptedQueueCancellations, correlation, true);
6652
6951
  const owner = lifecycleOwnerHolder.state;
6653
- if (!owner) return;
6654
- removeLifecycleReferences(owner, correlation);
6655
- maybeRetireLifecycleOwner(owner);
6952
+ if (owner) {
6953
+ removeLifecycleReferences(owner, correlation);
6954
+ maybeRetireLifecycleOwner(owner);
6955
+ }
6956
+ recordDeadlineRecoveryCheckpoint({
6957
+ stage: "deadline-recovery-retired",
6958
+ correlation,
6959
+ terminalCommitted: true,
6960
+ });
6656
6961
  },
6657
6962
  });
6658
6963
  const pending: Array<{
@@ -686,6 +686,7 @@ const savedSessionScopeDetailCodes = new Set([
686
686
  "migration_busy",
687
687
  "atomic_unavailable",
688
688
  "invalid_request",
689
+ "permission_denied",
689
690
  "durability_failed",
690
691
  "durability_not_provable",
691
692
  "capacity_exceeded",