@gajae-code/coding-agent 0.11.11 → 0.12.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 (344) hide show
  1. package/CHANGELOG.md +131 -4
  2. package/dist/types/async/job-manager.d.ts +17 -3
  3. package/dist/types/cli/read-cli.d.ts +1 -0
  4. package/dist/types/commands/deep-interview.d.ts +9 -0
  5. package/dist/types/commands/harness.d.ts +22 -0
  6. package/dist/types/commands/read.d.ts +6 -0
  7. package/dist/types/config/model-registry.d.ts +4 -0
  8. package/dist/types/config/provider-auth-health.d.ts +14 -0
  9. package/dist/types/config/provider-ranking.d.ts +59 -0
  10. package/dist/types/config/settings-schema.d.ts +83 -1
  11. package/dist/types/config/settings.d.ts +2 -0
  12. package/dist/types/exa/mcp-client.d.ts +10 -1
  13. package/dist/types/exec/bash-executor.d.ts +45 -12
  14. package/dist/types/extensibility/extensions/types.d.ts +38 -4
  15. package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
  16. package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
  17. package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
  18. package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
  19. package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
  20. package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
  21. package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
  22. package/dist/types/extensibility/shared-events.d.ts +6 -0
  23. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
  24. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
  25. package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
  26. package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
  27. package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
  28. package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
  29. package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
  30. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +70 -1
  31. package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
  32. package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
  33. package/dist/types/gjc-runtime/team-runtime.d.ts +27 -1
  34. package/dist/types/gjc-runtime/team-store.d.ts +12 -0
  35. package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
  36. package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
  37. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +17 -0
  38. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
  39. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +31 -80
  40. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
  41. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  42. package/dist/types/main.d.ts +1 -1
  43. package/dist/types/memories/index.d.ts +2 -0
  44. package/dist/types/modes/acp/acp-agent.d.ts +10 -19
  45. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  46. package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
  47. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  48. package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
  49. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  50. package/dist/types/modes/components/session-observer-overlay.d.ts +22 -0
  51. package/dist/types/modes/components/settings-selector.d.ts +11 -0
  52. package/dist/types/modes/components/status-line/gh.d.ts +5 -0
  53. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  54. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
  55. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  56. package/dist/types/modes/interactive-mode.d.ts +2 -0
  57. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  58. package/dist/types/modes/types.d.ts +2 -0
  59. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  60. package/dist/types/reminders/star-reminder.d.ts +3 -13
  61. package/dist/types/runtime-mcp/manager.d.ts +1 -0
  62. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  63. package/dist/types/sdk/acp/adapter.d.ts +3 -1
  64. package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
  65. package/dist/types/sdk/broker/transport.d.ts +2 -0
  66. package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
  67. package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -1
  68. package/dist/types/sdk/bus/config.d.ts +6 -2
  69. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  70. package/dist/types/sdk/bus/index.d.ts +44 -1
  71. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +44 -0
  72. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  73. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
  74. package/dist/types/sdk/bus/reconciliation-store.d.ts +69 -0
  75. package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
  76. package/dist/types/sdk/bus/rich-render.d.ts +2 -0
  77. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
  78. package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
  79. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -4
  80. package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
  81. package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
  82. package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
  83. package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
  84. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  85. package/dist/types/sdk/host/host.d.ts +2 -0
  86. package/dist/types/sdk/host/query/handlers.d.ts +5 -0
  87. package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
  88. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  89. package/dist/types/sdk/prompt-status.d.ts +23 -3
  90. package/dist/types/sdk/session.d.ts +6 -0
  91. package/dist/types/session/agent-session.d.ts +82 -12
  92. package/dist/types/session/artifacts.d.ts +8 -0
  93. package/dist/types/session/blob-store.d.ts +57 -4
  94. package/dist/types/session/internal/managed-session-storage.d.ts +8 -1
  95. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  96. package/dist/types/session/session-manager.d.ts +48 -19
  97. package/dist/types/session/session-storage.d.ts +4 -0
  98. package/dist/types/session/streaming-output.d.ts +96 -12
  99. package/dist/types/setup/credential-import.d.ts +12 -0
  100. package/dist/types/skill-state/active-state.d.ts +12 -4
  101. package/dist/types/skill-state/workflow-hud.d.ts +9 -0
  102. package/dist/types/task/executor.d.ts +3 -2
  103. package/dist/types/task/index.d.ts +3 -1
  104. package/dist/types/task/provider-retry-status.d.ts +8 -0
  105. package/dist/types/task/receipt.d.ts +7 -1
  106. package/dist/types/task/types.d.ts +23 -0
  107. package/dist/types/tools/ask-answer-registry.d.ts +2 -2
  108. package/dist/types/tools/bash-interactive.d.ts +4 -1
  109. package/dist/types/tools/bash.d.ts +3 -1
  110. package/dist/types/tools/browser/launch.d.ts +7 -0
  111. package/dist/types/tools/fetch.d.ts +17 -1
  112. package/dist/types/tools/image-gen.d.ts +4 -0
  113. package/dist/types/tools/index.d.ts +4 -0
  114. package/dist/types/tools/output-meta.d.ts +44 -3
  115. package/dist/types/tools/path-utils.d.ts +1 -0
  116. package/dist/types/tools/read-internals.d.ts +12 -0
  117. package/dist/types/tools/read.d.ts +45 -7
  118. package/dist/types/tools/resource-gc.d.ts +4 -0
  119. package/dist/types/tools/review.d.ts +5 -7
  120. package/dist/types/tools/subagent.d.ts +2 -0
  121. package/dist/types/tools/tool-result.d.ts +7 -1
  122. package/dist/types/utils/gh.d.ts +13 -0
  123. package/dist/types/utils/image-resize.d.ts +1 -0
  124. package/dist/types/web/search/providers/kimi.d.ts +2 -0
  125. package/dist/types/web/search/providers/searxng.d.ts +7 -0
  126. package/dist/types/web/search/providers/xai.d.ts +2 -0
  127. package/package.json +11 -13
  128. package/scripts/acp-conformance-agent.ts +218 -0
  129. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  130. package/scripts/capture-gjc-bundle-settings.ts +306 -0
  131. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  132. package/scripts/generate-sdk-operation-inventory.ts +5 -0
  133. package/scripts/resident-memory-bench.ts +1341 -0
  134. package/scripts/run-acp-conformance.ts +285 -0
  135. package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
  136. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  137. package/src/async/job-manager.ts +78 -20
  138. package/src/capability/index.ts +8 -0
  139. package/src/cli/fast-help.ts +1 -1
  140. package/src/cli/plugin-cli.ts +176 -18
  141. package/src/cli/read-cli.ts +2 -1
  142. package/src/cli.ts +1 -0
  143. package/src/commands/deep-interview.ts +29 -9
  144. package/src/commands/harness.ts +35 -12
  145. package/src/commands/read.ts +11 -2
  146. package/src/commands/team.ts +20 -2
  147. package/src/config/file-lock.ts +11 -2
  148. package/src/config/model-registry.ts +21 -2
  149. package/src/config/provider-auth-health.ts +42 -0
  150. package/src/config/provider-ranking.ts +119 -0
  151. package/src/config/settings-schema.ts +74 -1
  152. package/src/config/settings.ts +246 -108
  153. package/src/coordinator-mcp/question-gate-codec.ts +1 -1
  154. package/src/coordinator-mcp/server.ts +5 -12
  155. package/src/defaults/gjc/skills/deep-interview/SKILL.md +230 -82
  156. package/src/defaults/gjc/skills/ralplan/SKILL.md +77 -41
  157. package/src/defaults/gjc/skills/team/SKILL.md +3 -1
  158. package/src/defaults/gjc/skills/ultragoal/SKILL.md +62 -39
  159. package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
  160. package/src/defaults/gjc-defaults.ts +4 -4
  161. package/src/eval/py/executor.ts +7 -2
  162. package/src/exa/mcp-client.ts +12 -3
  163. package/src/exec/bash-executor.ts +144 -27
  164. package/src/extensibility/extensions/runner.ts +15 -0
  165. package/src/extensibility/extensions/types.ts +46 -5
  166. package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
  167. package/src/extensibility/gjc-plugins/index.ts +19 -2
  168. package/src/extensibility/gjc-plugins/installer.ts +189 -50
  169. package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
  170. package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
  171. package/src/extensibility/gjc-plugins/observability.ts +3 -3
  172. package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
  173. package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
  174. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
  175. package/src/extensibility/gjc-plugins/schema.ts +84 -12
  176. package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
  177. package/src/extensibility/gjc-plugins/types.ts +141 -0
  178. package/src/extensibility/gjc-plugins/validation.ts +9 -0
  179. package/src/extensibility/shared-events.ts +5 -0
  180. package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
  181. package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
  182. package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
  183. package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
  184. package/src/gjc-runtime/deep-interview-state.ts +12 -943
  185. package/src/gjc-runtime/gc-render.ts +1 -0
  186. package/src/gjc-runtime/gc-runtime.ts +11 -1
  187. package/src/gjc-runtime/launch-tmux.ts +25 -6
  188. package/src/gjc-runtime/managed-owner-admission.ts +1 -1
  189. package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
  190. package/src/gjc-runtime/ralplan-runtime.ts +1149 -98
  191. package/src/gjc-runtime/state-runtime.ts +2 -1
  192. package/src/gjc-runtime/state-writer.ts +70 -222
  193. package/src/gjc-runtime/team-launch.ts +42 -5
  194. package/src/gjc-runtime/team-runtime.ts +1085 -40
  195. package/src/gjc-runtime/team-store.ts +32 -0
  196. package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
  197. package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
  198. package/src/gjc-runtime/team-workers.ts +1 -0
  199. package/src/gjc-runtime/tmux-owner-isolation.ts +28 -0
  200. package/src/gjc-runtime/tmux-sessions.ts +58 -13
  201. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
  202. package/src/gjc-runtime/ultragoal-runtime.ts +414 -618
  203. package/src/gjc-runtime/workflow-manifest.generated.json +105 -449
  204. package/src/gjc-runtime/workflow-manifest.ts +38 -247
  205. package/src/hooks/native-skill-hook.ts +17 -4
  206. package/src/hooks/skill-state.ts +1 -23
  207. package/src/internal-urls/docs-index.generated.ts +21 -22
  208. package/src/internal-urls/local-root-gc.ts +127 -0
  209. package/src/main.ts +9 -5
  210. package/src/memories/index.ts +10 -0
  211. package/src/modes/DESIGN.md +98 -0
  212. package/src/modes/acp/acp-agent.ts +339 -191
  213. package/src/modes/acp/acp-event-mapper.ts +14 -7
  214. package/src/modes/acp/acp-mode.ts +39 -3
  215. package/src/modes/components/assistant-message.ts +123 -22
  216. package/src/modes/components/gjc-bundle-settings.ts +508 -0
  217. package/src/modes/components/irc-sidebar.ts +176 -62
  218. package/src/modes/components/model-selector.ts +61 -4
  219. package/src/modes/components/notifications-settings-editor.ts +12 -1
  220. package/src/modes/components/oauth-selector.ts +96 -28
  221. package/src/modes/components/read-tool-group.ts +82 -14
  222. package/src/modes/components/session-observer-overlay.ts +457 -83
  223. package/src/modes/components/settings-selector.ts +116 -23
  224. package/src/modes/components/status-line/gh.ts +16 -0
  225. package/src/modes/components/tool-execution.ts +138 -27
  226. package/src/modes/components/tool-status-header.ts +3 -14
  227. package/src/modes/components/transcript-viewer-overlay.ts +99 -24
  228. package/src/modes/controllers/command-controller.ts +4 -0
  229. package/src/modes/controllers/event-controller.ts +109 -27
  230. package/src/modes/controllers/extension-ui-controller.ts +26 -2
  231. package/src/modes/controllers/input-controller.ts +18 -3
  232. package/src/modes/controllers/selector-controller.ts +127 -36
  233. package/src/modes/interactive-mode.ts +62 -3
  234. package/src/modes/irc-observation-ledger.ts +71 -15
  235. package/src/modes/runtime-init.ts +22 -2
  236. package/src/modes/types.ts +2 -0
  237. package/src/modes/utils/ui-helpers.ts +94 -1
  238. package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
  239. package/src/prompts/agents/architect.md +9 -1
  240. package/src/prompts/agents/critic.md +9 -0
  241. package/src/prompts/system/eager-todo.md +8 -4
  242. package/src/prompts/system/system-prompt.md +11 -5
  243. package/src/prompts/tools/bash.md +5 -5
  244. package/src/prompts/tools/read.md +4 -2
  245. package/src/prompts/tools/search-tool-bm25.md +5 -0
  246. package/src/prompts/tools/task.md +1 -1
  247. package/src/reminders/star-reminder.ts +4 -42
  248. package/src/runtime-mcp/manager.ts +19 -4
  249. package/src/runtime-mcp/smithery-auth.ts +17 -2
  250. package/src/runtime-mcp/smithery-connect.ts +11 -1
  251. package/src/sdk/acp/adapter.ts +22 -6
  252. package/src/sdk/broker/lifecycle.ts +25 -6
  253. package/src/sdk/broker/transport.ts +35 -3
  254. package/src/sdk/bus/attachment-registry.ts +5 -1
  255. package/src/sdk/bus/chat-daemon-cli.ts +4 -39
  256. package/src/sdk/bus/chat-daemon-control.ts +6 -3
  257. package/src/sdk/bus/config.ts +18 -2
  258. package/src/sdk/bus/control-drain-lease.ts +104 -0
  259. package/src/sdk/bus/index.ts +984 -146
  260. package/src/sdk/bus/kind-aware-reconciliation.ts +317 -0
  261. package/src/sdk/bus/lifecycle-commands.ts +4 -1
  262. package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
  263. package/src/sdk/bus/prompt-reconciliation.ts +2 -1
  264. package/src/sdk/bus/reconciliation-store.ts +303 -0
  265. package/src/sdk/bus/rich-draft.ts +1 -1
  266. package/src/sdk/bus/rich-render.ts +2 -2
  267. package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
  268. package/src/sdk/bus/telegram-cli.ts +33 -8
  269. package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
  270. package/src/sdk/bus/telegram-daemon-contract.ts +11 -4
  271. package/src/sdk/bus/telegram-daemon.ts +916 -23
  272. package/src/sdk/bus/telegram-reference.ts +40 -3
  273. package/src/sdk/bus/threaded-render.ts +48 -3
  274. package/src/sdk/bus/topic-registry.ts +20 -0
  275. package/src/sdk/host/control/dispatch.ts +5 -1
  276. package/src/sdk/host/control/operations.ts +1 -1
  277. package/src/sdk/host/host.ts +23 -1
  278. package/src/sdk/host/query/handlers.ts +55 -0
  279. package/src/sdk/host/query/revision-store.ts +36 -2
  280. package/src/sdk/host/reverse-leases.ts +44 -7
  281. package/src/sdk/mcp/server.ts +46 -18
  282. package/src/sdk/prompt-status.ts +16 -3
  283. package/src/sdk/protocol/operation-inventory.generated.json +51 -0
  284. package/src/sdk/protocol/operation-registry.ts +7 -2
  285. package/src/sdk/session.ts +101 -10
  286. package/src/session/agent-session.ts +1286 -220
  287. package/src/session/artifacts.ts +22 -0
  288. package/src/session/auth-broker-config.ts +10 -3
  289. package/src/session/blob-store.ts +915 -24
  290. package/src/session/internal/managed-session-scope.ts +44 -6
  291. package/src/session/internal/managed-session-storage.ts +48 -6
  292. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  293. package/src/session/session-manager.ts +2364 -438
  294. package/src/session/session-storage.ts +9 -1
  295. package/src/session/streaming-output.ts +500 -111
  296. package/src/setup/credential-import.ts +75 -13
  297. package/src/setup/provider-onboarding.ts +19 -4
  298. package/src/skill-state/active-state.ts +32 -10
  299. package/src/skill-state/workflow-hud.ts +42 -2
  300. package/src/skill-state/workflow-mutation-guard.ts +560 -8
  301. package/src/task/executor.ts +122 -39
  302. package/src/task/index.ts +23 -14
  303. package/src/task/provider-retry-status.ts +16 -0
  304. package/src/task/receipt.ts +23 -7
  305. package/src/task/render.ts +6 -2
  306. package/src/task/types.ts +108 -0
  307. package/src/tools/ask-answer-registry.ts +12 -6
  308. package/src/tools/ask.ts +169 -118
  309. package/src/tools/ast-edit.ts +1 -0
  310. package/src/tools/ast-grep.ts +1 -0
  311. package/src/tools/bash-interactive.ts +8 -3
  312. package/src/tools/bash.ts +503 -60
  313. package/src/tools/browser/launch.ts +41 -6
  314. package/src/tools/fetch.ts +114 -23
  315. package/src/tools/find.ts +1 -0
  316. package/src/tools/image-gen.ts +29 -5
  317. package/src/tools/index.ts +4 -0
  318. package/src/tools/output-meta.ts +281 -20
  319. package/src/tools/path-utils.ts +8 -3
  320. package/src/tools/read-internals.ts +44 -0
  321. package/src/tools/read.ts +1560 -310
  322. package/src/tools/resource-gc.ts +182 -18
  323. package/src/tools/review.ts +5 -7
  324. package/src/tools/search.ts +1 -0
  325. package/src/tools/subagent-render.ts +6 -2
  326. package/src/tools/subagent.ts +6 -0
  327. package/src/tools/telegram-send.ts +6 -3
  328. package/src/tools/tool-result.ts +14 -1
  329. package/src/utils/gh.ts +39 -0
  330. package/src/utils/image-resize.ts +3 -0
  331. package/src/web/search/providers/anthropic.ts +4 -4
  332. package/src/web/search/providers/codex.ts +1 -1
  333. package/src/web/search/providers/kimi.ts +15 -5
  334. package/src/web/search/providers/searxng.ts +47 -4
  335. package/src/web/search/providers/xai.ts +7 -2
  336. package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
  337. package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
  338. package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
  339. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
  340. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
  341. package/src/gjc-runtime/deep-interview-draft.ts +0 -843
  342. package/src/gjc-runtime/deep-interview-payload.ts +0 -262
  343. package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
  344. package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
@@ -126,6 +126,38 @@ export interface GjcTeamApiClaimResult {
126
126
  claim_token?: string;
127
127
  reason?: string;
128
128
  }
129
+ export type GjcTeamWorkerClaimSelection =
130
+ | { kind: "none" }
131
+ | { kind: "exact"; task: GjcTeamTask; claim: GjcTeamTaskClaim }
132
+ | { kind: "ambiguous"; tasks: GjcTeamTask[] };
133
+
134
+ export function selectCurrentClaimedTaskForWorker(
135
+ tasks: readonly GjcTeamTask[],
136
+ workerId: string,
137
+ ): GjcTeamWorkerClaimSelection {
138
+ const claimed = tasks.filter(
139
+ task =>
140
+ task.status === "in_progress" &&
141
+ task.assignee === workerId &&
142
+ task.claim?.owner === workerId &&
143
+ task.owner === workerId,
144
+ );
145
+ if (claimed.length === 0) return { kind: "none" };
146
+ if (claimed.length === 1) {
147
+ const task = claimed[0]!;
148
+ return { kind: "exact", task, claim: task.claim! };
149
+ }
150
+ return { kind: "ambiguous", tasks: claimed };
151
+ }
152
+ export function findGjcTeamClaimedTaskForWorker(
153
+ tasks: readonly GjcTeamTask[],
154
+ workerId: string,
155
+ ): GjcTeamTask | undefined {
156
+ const active = selectCurrentClaimedTaskForWorker(tasks, workerId);
157
+ if (active.kind === "exact") return active.task;
158
+ if (active.kind === "ambiguous") return undefined;
159
+ return tasks.find(task => task.status === "blocked" && task.assignee === workerId && task.claim?.owner === workerId);
160
+ }
129
161
 
130
162
  type EventAppender = (event: {
131
163
  type: string;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Runtime-owned `gjc team` worker heartbeat.
3
+ *
4
+ * Worker liveness used to be published only when the model remembered to call
5
+ * `gjc team api update-worker-heartbeat` between turns. A worker inside a
6
+ * single tool call longer than `GJC_TEAM_HEARTBEAT_STALE_MS` (default 120s) was
7
+ * therefore reported as stale and had its task claim requeued mid-flight — even
8
+ * though the claim lease it invalidated is 30 minutes long. This module makes
9
+ * the worker process itself publish liveness while a turn is in flight, so the
10
+ * heartbeat records what the process is doing instead of what the model
11
+ * remembered to report.
12
+ *
13
+ * The published record carries the writer's real pid, so leader-side diagnostics
14
+ * name the process that is actually working rather than a placeholder.
15
+ */
16
+ import { logger } from "@gajae-code/utils";
17
+ import { parseHeartbeatStaleMs, refreshGjcWorkerHeartbeat, type WorkerHeartbeatFile } from "./team-runtime";
18
+
19
+ /** Publish several times per stale window so one missed tick cannot cross it. */
20
+ const HEARTBEAT_INTERVAL_DIVISOR = 3;
21
+ const MIN_HEARTBEAT_INTERVAL_MS = 1;
22
+ const MAX_HEARTBEAT_INTERVAL_MS = 30_000;
23
+
24
+ export interface GjcTeamWorkerIdentity {
25
+ teamName: string;
26
+ workerId: string;
27
+ }
28
+
29
+ /** Resolves the team identity injected into a worker pane by `gjc team` startup. */
30
+ export function resolveGjcTeamWorkerIdentity(env: NodeJS.ProcessEnv = process.env): GjcTeamWorkerIdentity | undefined {
31
+ const teamName = env.GJC_TEAM_NAME?.trim();
32
+ const workerId = env.GJC_TEAM_WORKER_ID?.trim() || env.GJC_TEAM_INTERNAL_WORKER?.split("/").pop()?.trim();
33
+ return teamName && workerId ? { teamName, workerId } : undefined;
34
+ }
35
+
36
+ /** Refresh cadence derived from the leader's stale window; `0` disables publishing. */
37
+ export function resolveGjcTeamWorkerHeartbeatIntervalMs(env: NodeJS.ProcessEnv = process.env): number {
38
+ const staleMs = parseHeartbeatStaleMs(env);
39
+ if (staleMs <= 0) return 0;
40
+ return Math.min(
41
+ MAX_HEARTBEAT_INTERVAL_MS,
42
+ Math.max(MIN_HEARTBEAT_INTERVAL_MS, Math.floor(staleMs / HEARTBEAT_INTERVAL_DIVISOR)),
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Publish one runtime-owned heartbeat for the worker this process is running as.
48
+ * Returns `undefined` when the process is not a team worker.
49
+ *
50
+ * `turn_count` and process-incarnation metadata are merged under the same
51
+ * mutation fence used by CLI heartbeat updates.
52
+ */
53
+ export async function writeGjcTeamWorkerRuntimeHeartbeat(
54
+ cwd: string = process.cwd(),
55
+ env: NodeJS.ProcessEnv = process.env,
56
+ ): Promise<WorkerHeartbeatFile | undefined> {
57
+ const identity = resolveGjcTeamWorkerIdentity(env);
58
+ if (!identity) return undefined;
59
+ return refreshGjcWorkerHeartbeat(identity.teamName, identity.workerId, process.pid, cwd, env);
60
+ }
61
+
62
+ export interface GjcTeamWorkerHeartbeatReporterOptions {
63
+ write: () => Promise<void>;
64
+ intervalMs: number;
65
+ }
66
+
67
+ /**
68
+ * Publishes a worker heartbeat while a turn is in flight.
69
+ *
70
+ * Writes never overlap; a slow or failing write is logged and skipped rather
71
+ * than queued, because only the most recent record matters. The timer is
72
+ * unreferenced so it can never hold the process open.
73
+ */
74
+ export class GjcTeamWorkerHeartbeatReporter {
75
+ readonly #write: () => Promise<void>;
76
+ readonly #intervalMs: number;
77
+ #timer: NodeJS.Timeout | undefined;
78
+ #inFlight: Promise<void> | undefined;
79
+ #disposed = false;
80
+
81
+ constructor(options: GjcTeamWorkerHeartbeatReporterOptions) {
82
+ this.#write = options.write;
83
+ this.#intervalMs = options.intervalMs;
84
+ }
85
+
86
+ /** Reporter for the current process, or `undefined` when it is not a team worker. */
87
+ static forProcess(
88
+ cwd: () => string,
89
+ env: NodeJS.ProcessEnv = process.env,
90
+ ): GjcTeamWorkerHeartbeatReporter | undefined {
91
+ if (!resolveGjcTeamWorkerIdentity(env)) return undefined;
92
+ const intervalMs = resolveGjcTeamWorkerHeartbeatIntervalMs(env);
93
+ if (intervalMs <= 0) return undefined;
94
+ return new GjcTeamWorkerHeartbeatReporter({
95
+ intervalMs,
96
+ write: async () => {
97
+ await writeGjcTeamWorkerRuntimeHeartbeat(cwd(), env);
98
+ },
99
+ });
100
+ }
101
+
102
+ get isRunning(): boolean {
103
+ return this.#timer !== undefined;
104
+ }
105
+
106
+ /** Publish immediately, then keep publishing until {@link stop} or {@link dispose}. */
107
+ start(): void {
108
+ if (this.#disposed || this.#timer) return;
109
+ this.#publish();
110
+ this.#timer = setInterval(() => this.#publish(), this.#intervalMs);
111
+ this.#timer.unref?.();
112
+ }
113
+
114
+ /** Stop periodic publishing after one final record, so the idle stamp is fresh. */
115
+ stop(): void {
116
+ if (!this.#timer) return;
117
+ clearInterval(this.#timer);
118
+ this.#timer = undefined;
119
+ this.#publish();
120
+ }
121
+
122
+ /** Await the write in flight, if any. */
123
+ async flush(): Promise<void> {
124
+ while (this.#inFlight) {
125
+ await this.#inFlight;
126
+ }
127
+ }
128
+
129
+ /** Permanently stop publishing. Idempotent. */
130
+ dispose(): void {
131
+ this.#disposed = true;
132
+ if (this.#timer) {
133
+ clearInterval(this.#timer);
134
+ this.#timer = undefined;
135
+ }
136
+ }
137
+
138
+ #publish(): void {
139
+ if (this.#disposed || this.#inFlight) return;
140
+ const run = this.#write()
141
+ .catch((error: unknown) => {
142
+ logger.warn("GJC team worker heartbeat publish failed", { error: String(error) });
143
+ })
144
+ .finally(() => {
145
+ if (this.#inFlight === run) this.#inFlight = undefined;
146
+ });
147
+ this.#inFlight = run;
148
+ }
149
+ }
@@ -0,0 +1,363 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { appendJsonl, type StateWriterOptions } from "./state-writer";
4
+
5
+ export type GjcTeamWorkerMemoryGuardState = "idle" | "advisory" | "retrying" | "checkpointed" | "replaced" | "blocked";
6
+
7
+ export type GjcTeamWorkerMemoryGuardCheckpointKind =
8
+ | "clean"
9
+ | "eligible"
10
+ | "protected_only"
11
+ | "conflicted"
12
+ | "git_error";
13
+
14
+ export type GjcTeamWorkerMemoryGuardPidProbe =
15
+ | { kind: "live"; start_time: string }
16
+ | { kind: "absent" }
17
+ | { kind: "unverifiable"; reason: string };
18
+
19
+ export interface GjcTeamWorkerMemoryGuardCheckpoint {
20
+ kind: GjcTeamWorkerMemoryGuardCheckpointKind;
21
+ files: string[];
22
+ head?: string | null;
23
+ commit?: string | null;
24
+ recorded_at: string;
25
+ }
26
+
27
+ export interface GjcTeamWorkerMemoryGuardReplacement {
28
+ old_pane_id?: string;
29
+ new_pane_id?: string;
30
+ recorded_at: string;
31
+ }
32
+
33
+ export interface GjcTeamWorkerMemoryGuardLedger {
34
+ schema_version: 1;
35
+ worker_id: string;
36
+ platform: string;
37
+ state: GjcTeamWorkerMemoryGuardState;
38
+ automatic_action_allowed: boolean;
39
+ retry_count: number;
40
+ retry_limit: number;
41
+ current_task_id?: string;
42
+ last_incident_id?: string;
43
+ last_reason?: string;
44
+ last_pid_probe?: GjcTeamWorkerMemoryGuardPidProbe;
45
+ last_checkpoint?: GjcTeamWorkerMemoryGuardCheckpoint;
46
+ last_replacement?: GjcTeamWorkerMemoryGuardReplacement;
47
+ updated_at: string;
48
+ }
49
+
50
+ export interface GjcTeamWorkerMemoryGuardSelectionCandidate {
51
+ worker_id: string;
52
+ platform: string;
53
+ excess_bytes: number;
54
+ retry_count: number;
55
+ retry_limit: number;
56
+ blocked?: boolean;
57
+ current_task_id?: string;
58
+ }
59
+
60
+ export interface GjcTeamWorkerMemoryGuardSelection {
61
+ worker_id: string;
62
+ excess_bytes: number;
63
+ retry_count: number;
64
+ current_task_id?: string;
65
+ }
66
+
67
+ const ledgerKeys = new Set([
68
+ "schema_version",
69
+ "worker_id",
70
+ "platform",
71
+ "state",
72
+ "automatic_action_allowed",
73
+ "retry_count",
74
+ "retry_limit",
75
+ "current_task_id",
76
+ "last_incident_id",
77
+ "last_reason",
78
+ "last_pid_probe",
79
+ "last_checkpoint",
80
+ "last_replacement",
81
+ "updated_at",
82
+ ]);
83
+ const checkpointKeys = new Set(["kind", "files", "head", "commit", "recorded_at"]);
84
+ const replacementKeys = new Set(["old_pane_id", "new_pane_id", "recorded_at"]);
85
+ const absentPidProbeKeys = new Set(["kind"]);
86
+ const livePidProbeKeys = new Set(["kind", "start_time"]);
87
+ const unverifiablePidProbeKeys = new Set(["kind", "reason"]);
88
+
89
+ function isRecord(value: unknown): value is Record<string, unknown> {
90
+ return typeof value === "object" && value !== null && !Array.isArray(value);
91
+ }
92
+
93
+ function hasExactKeys(value: Record<string, unknown>, keys: Set<string>): boolean {
94
+ return Object.keys(value).every(key => keys.has(key));
95
+ }
96
+
97
+ function isNonEmptyString(value: unknown): value is string {
98
+ return typeof value === "string" && value.trim().length > 0;
99
+ }
100
+
101
+ function isTimestamp(value: unknown): value is string {
102
+ return typeof value === "string" && Number.isFinite(Date.parse(value));
103
+ }
104
+
105
+ function isRetryCount(value: unknown): value is number {
106
+ return Number.isInteger(value) && (value as number) >= 0;
107
+ }
108
+
109
+ function isWorkerMemoryGuardState(value: unknown): value is GjcTeamWorkerMemoryGuardState {
110
+ return ["idle", "advisory", "retrying", "checkpointed", "replaced", "blocked"].includes(String(value));
111
+ }
112
+
113
+ function isCheckpointKind(value: unknown): value is GjcTeamWorkerMemoryGuardCheckpointKind {
114
+ return ["clean", "eligible", "protected_only", "conflicted", "git_error"].includes(String(value));
115
+ }
116
+
117
+ export function workerMemoryGuardLedgerPath(dir: string, workerId: string): string {
118
+ return path.join(dir, "workers", workerId, "memory-guard.json");
119
+ }
120
+
121
+ export function normalizeGjcTeamWorkerMemoryGuardPidProbe(
122
+ value: unknown,
123
+ ): GjcTeamWorkerMemoryGuardPidProbe | undefined {
124
+ if (value === undefined) return undefined;
125
+ if (!isRecord(value)) throw new Error("invalid_worker_memory_guard_pid_probe");
126
+ const kind = typeof value.kind === "string" ? value.kind : "";
127
+ if (kind === "absent") {
128
+ if (!hasExactKeys(value, absentPidProbeKeys)) throw new Error("invalid_worker_memory_guard_pid_probe:absent");
129
+ return { kind: "absent" };
130
+ }
131
+ if (kind === "live") {
132
+ if (!hasExactKeys(value, livePidProbeKeys) || !isNonEmptyString(value.start_time))
133
+ throw new Error("invalid_worker_memory_guard_pid_probe:live");
134
+ return { kind: "live", start_time: value.start_time.trim() };
135
+ }
136
+ if (kind === "unverifiable") {
137
+ if (!hasExactKeys(value, unverifiablePidProbeKeys) || !isNonEmptyString(value.reason))
138
+ throw new Error("invalid_worker_memory_guard_pid_probe:unverifiable");
139
+ return { kind: "unverifiable", reason: value.reason.trim() };
140
+ }
141
+ throw new Error("invalid_worker_memory_guard_pid_probe");
142
+ }
143
+
144
+ export function isCanonicalGjcTeamWorkerMemoryGuardCheckpoint(
145
+ value: unknown,
146
+ ): value is GjcTeamWorkerMemoryGuardCheckpoint {
147
+ return (
148
+ isRecord(value) &&
149
+ hasExactKeys(value, checkpointKeys) &&
150
+ isCheckpointKind(value.kind) &&
151
+ Array.isArray(value.files) &&
152
+ value.files.every(file => isNonEmptyString(file)) &&
153
+ (value.head === undefined || value.head === null || isNonEmptyString(value.head)) &&
154
+ (value.commit === undefined || value.commit === null || isNonEmptyString(value.commit)) &&
155
+ isTimestamp(value.recorded_at)
156
+ );
157
+ }
158
+
159
+ export function isCanonicalGjcTeamWorkerMemoryGuardReplacement(
160
+ value: unknown,
161
+ ): value is GjcTeamWorkerMemoryGuardReplacement {
162
+ return (
163
+ isRecord(value) &&
164
+ hasExactKeys(value, replacementKeys) &&
165
+ (value.old_pane_id === undefined || isNonEmptyString(value.old_pane_id)) &&
166
+ (value.new_pane_id === undefined || isNonEmptyString(value.new_pane_id)) &&
167
+ isTimestamp(value.recorded_at)
168
+ );
169
+ }
170
+
171
+ export function isCanonicalGjcTeamWorkerMemoryGuardPidProbe(value: unknown): value is GjcTeamWorkerMemoryGuardPidProbe {
172
+ try {
173
+ return normalizeGjcTeamWorkerMemoryGuardPidProbe(value) !== undefined;
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+
179
+ export function isCanonicalGjcTeamWorkerMemoryGuardLedger(value: unknown): value is GjcTeamWorkerMemoryGuardLedger {
180
+ return (
181
+ isRecord(value) &&
182
+ hasExactKeys(value, ledgerKeys) &&
183
+ value.schema_version === 1 &&
184
+ isNonEmptyString(value.worker_id) &&
185
+ isNonEmptyString(value.platform) &&
186
+ isWorkerMemoryGuardState(value.state) &&
187
+ typeof value.automatic_action_allowed === "boolean" &&
188
+ isRetryCount(value.retry_count) &&
189
+ isRetryCount(value.retry_limit) &&
190
+ (value.current_task_id === undefined || isNonEmptyString(value.current_task_id)) &&
191
+ (value.last_incident_id === undefined || isNonEmptyString(value.last_incident_id)) &&
192
+ (value.last_reason === undefined || isNonEmptyString(value.last_reason)) &&
193
+ (value.last_pid_probe === undefined || isCanonicalGjcTeamWorkerMemoryGuardPidProbe(value.last_pid_probe)) &&
194
+ (value.last_checkpoint === undefined || isCanonicalGjcTeamWorkerMemoryGuardCheckpoint(value.last_checkpoint)) &&
195
+ (value.last_replacement === undefined ||
196
+ isCanonicalGjcTeamWorkerMemoryGuardReplacement(value.last_replacement)) &&
197
+ isTimestamp(value.updated_at)
198
+ );
199
+ }
200
+
201
+ export function createInitialGjcTeamWorkerMemoryGuardLedger(input: {
202
+ workerId: string;
203
+ platform: string;
204
+ now: string;
205
+ retryLimit?: number;
206
+ }): GjcTeamWorkerMemoryGuardLedger {
207
+ const retryLimit = Number.isInteger(input.retryLimit) && (input.retryLimit ?? 0) > 0 ? input.retryLimit! : 2;
208
+ return {
209
+ schema_version: 1,
210
+ worker_id: input.workerId,
211
+ platform: input.platform.trim() || "unknown",
212
+ state: "idle",
213
+ automatic_action_allowed: false,
214
+ retry_count: 0,
215
+ retry_limit: retryLimit,
216
+ updated_at: input.now,
217
+ };
218
+ }
219
+
220
+ function workerIndex(workerId: string): number {
221
+ const match = /(?:^|[^0-9])(\d+)$/.exec(workerId);
222
+ return match ? Number.parseInt(match[1]!, 10) : Number.MAX_SAFE_INTEGER;
223
+ }
224
+
225
+ export function selectGjcTeamWorkerMemoryGuardCandidate(
226
+ candidates: readonly GjcTeamWorkerMemoryGuardSelectionCandidate[],
227
+ ): GjcTeamWorkerMemoryGuardSelection | undefined {
228
+ const eligible = candidates
229
+ .filter(candidate => candidate.platform === "linux")
230
+ .filter(candidate => Number.isFinite(candidate.excess_bytes) && candidate.excess_bytes > 0)
231
+ .filter(candidate => !candidate.blocked)
232
+ .filter(candidate => candidate.retry_count < candidate.retry_limit)
233
+ .sort((left, right) => {
234
+ if (right.excess_bytes !== left.excess_bytes) return right.excess_bytes - left.excess_bytes;
235
+ if (left.retry_count !== right.retry_count) return left.retry_count - right.retry_count;
236
+ const leftIndex = workerIndex(left.worker_id);
237
+ const rightIndex = workerIndex(right.worker_id);
238
+ if (leftIndex !== rightIndex) return leftIndex - rightIndex;
239
+ return left.worker_id.localeCompare(right.worker_id);
240
+ });
241
+ const first = eligible[0];
242
+ return first
243
+ ? {
244
+ worker_id: first.worker_id,
245
+ excess_bytes: first.excess_bytes,
246
+ retry_count: first.retry_count,
247
+ current_task_id: first.current_task_id,
248
+ }
249
+ : undefined;
250
+ }
251
+
252
+ export type TeamWorkerMemoryGuardAction = "advisory" | "replace" | "blocked";
253
+ export type TeamWorkerMemoryGuardResult = "noop" | "scheduled" | "succeeded" | "failed" | "blocked";
254
+
255
+ export interface TeamWorkerMemoryGuardLedgerEntry {
256
+ schema_version: 1;
257
+ recorded_at: string;
258
+ incident_id: string;
259
+ team_name: string;
260
+ worker_id: string;
261
+ task_id: string;
262
+ claim_token: string;
263
+ attempt: number;
264
+ platform: NodeJS.Platform;
265
+ action: TeamWorkerMemoryGuardAction;
266
+ result: TeamWorkerMemoryGuardResult;
267
+ reason: string;
268
+ }
269
+
270
+ const teamWorkerMemoryGuardActions = new Set<TeamWorkerMemoryGuardAction>(["advisory", "replace", "blocked"]);
271
+ const teamWorkerMemoryGuardResults = new Set<TeamWorkerMemoryGuardResult>([
272
+ "noop",
273
+ "scheduled",
274
+ "succeeded",
275
+ "failed",
276
+ "blocked",
277
+ ]);
278
+
279
+ export function isCanonicalTeamWorkerMemoryGuardLedgerEntry(value: unknown): value is TeamWorkerMemoryGuardLedgerEntry {
280
+ if (!isRecord(value)) return false;
281
+ const requiredKeys = [
282
+ "schema_version",
283
+ "recorded_at",
284
+ "incident_id",
285
+ "team_name",
286
+ "worker_id",
287
+ "task_id",
288
+ "claim_token",
289
+ "attempt",
290
+ "platform",
291
+ "action",
292
+ "result",
293
+ "reason",
294
+ ];
295
+ return (
296
+ Object.keys(value).length === requiredKeys.length &&
297
+ requiredKeys.every(key => Object.hasOwn(value, key)) &&
298
+ value.schema_version === 1 &&
299
+ isTimestamp(value.recorded_at) &&
300
+ isNonEmptyString(value.incident_id) &&
301
+ isNonEmptyString(value.team_name) &&
302
+ isNonEmptyString(value.worker_id) &&
303
+ isNonEmptyString(value.task_id) &&
304
+ isNonEmptyString(value.claim_token) &&
305
+ Number.isInteger(value.attempt) &&
306
+ (value.attempt as number) > 0 &&
307
+ isNonEmptyString(value.platform) &&
308
+ teamWorkerMemoryGuardActions.has(value.action as TeamWorkerMemoryGuardAction) &&
309
+ teamWorkerMemoryGuardResults.has(value.result as TeamWorkerMemoryGuardResult) &&
310
+ isNonEmptyString(value.reason)
311
+ );
312
+ }
313
+
314
+ export function teamWorkerMemoryGuardDir(workerDirPath: string): string {
315
+ return path.join(workerDirPath, "memory-guard");
316
+ }
317
+
318
+ export function teamWorkerMemoryGuardLedgerPath(workerDirPath: string): string {
319
+ return path.join(teamWorkerMemoryGuardDir(workerDirPath), "ledger.jsonl");
320
+ }
321
+
322
+ export function canMutateTeamWorkerMemoryGuard(platform: NodeJS.Platform): boolean {
323
+ return platform === "linux";
324
+ }
325
+
326
+ export function advisoryReasonForTeamWorkerMemoryGuard(platform: NodeJS.Platform): string | undefined {
327
+ return canMutateTeamWorkerMemoryGuard(platform) ? undefined : `unsupported_platform:${platform}`;
328
+ }
329
+
330
+ export async function appendTeamWorkerMemoryGuardLedgerEntry(
331
+ workerDirPath: string,
332
+ entry: TeamWorkerMemoryGuardLedgerEntry,
333
+ options?: StateWriterOptions,
334
+ ): Promise<string> {
335
+ await fs.mkdir(teamWorkerMemoryGuardDir(workerDirPath), { recursive: true });
336
+ return appendJsonl(teamWorkerMemoryGuardLedgerPath(workerDirPath), entry, options);
337
+ }
338
+
339
+ export async function readTeamWorkerMemoryGuardLedger(
340
+ workerDirPath: string,
341
+ ): Promise<TeamWorkerMemoryGuardLedgerEntry[]> {
342
+ const ledgerPath = teamWorkerMemoryGuardLedgerPath(workerDirPath);
343
+ if (!(await Bun.file(ledgerPath).exists())) return [];
344
+ const entries: TeamWorkerMemoryGuardLedgerEntry[] = [];
345
+ for (const [index, line] of (await Bun.file(ledgerPath).text()).split(/\r?\n/).filter(Boolean).entries()) {
346
+ const parsed = JSON.parse(line) as unknown;
347
+ if (!isCanonicalTeamWorkerMemoryGuardLedgerEntry(parsed))
348
+ throw new Error(`invalid_team_worker_memory_guard_ledger:${ledgerPath}:${index + 1}`);
349
+ entries.push(parsed);
350
+ }
351
+ return entries;
352
+ }
353
+
354
+ export function nextTeamWorkerMemoryGuardAttempt(
355
+ entries: readonly TeamWorkerMemoryGuardLedgerEntry[],
356
+ incidentId: string,
357
+ ): number {
358
+ let attempt = 0;
359
+ for (const entry of entries) {
360
+ if (entry.incident_id === incidentId && entry.attempt > attempt) attempt = entry.attempt;
361
+ }
362
+ return attempt + 1;
363
+ }
@@ -352,6 +352,7 @@ export async function writeGjcWorkerStartupAck(
352
352
  pid: typeof input.pid === "number" ? input.pid : undefined,
353
353
  session: typeof input.session === "string" ? input.session : undefined,
354
354
  protocol_version: String(input.protocol_version ?? "1"),
355
+ replacement_token: typeof input.replacement_token === "string" ? input.replacement_token : undefined,
355
356
  ack_at: runtime.now(),
356
357
  };
357
358
  await runtime.writeJson(path.join(runtime.workerDir(dir, worker), "startup-ack.json"), ack);
@@ -110,6 +110,16 @@ export interface TmuxServerProof {
110
110
  startTime?: string;
111
111
  cgroup?: CgroupInfo;
112
112
  sessionNames?: string[];
113
+ /**
114
+ * Whether `pid` is kernel-proved evidence about the live tmux server.
115
+ *
116
+ * Only Linux can prove a server PID (`/proc` + cgroup classification), so
117
+ * probes on other platforms may report a placeholder PID with this flag set
118
+ * to `false`. Guards must not build a `#{pid}` predicate from an unproven
119
+ * PID: no live tmux server can ever satisfy it. Omitted means proven, which
120
+ * keeps every probe that reads a real `#{pid}` unchanged.
121
+ */
122
+ pidProven?: boolean;
113
123
  }
114
124
 
115
125
  export interface PlanRequest {
@@ -960,6 +970,24 @@ export function lifecyclePaths(stateDir: string, sessionId: string, generation:
960
970
  };
961
971
  }
962
972
 
973
+ export interface MemoryGuardClaimPaths {
974
+ root: string;
975
+ databaseFile: string;
976
+ walFile: string;
977
+ shmFile: string;
978
+ }
979
+
980
+ export function memoryGuardClaimPaths(stateDir: string, sessionId: string): MemoryGuardClaimPaths {
981
+ const root = path.join(stateDir, "memory-guard-claims", sessionId);
982
+ const databaseFile = path.join(root, "claims.sqlite");
983
+ return {
984
+ root,
985
+ databaseFile,
986
+ walFile: `${databaseFile}-wal`,
987
+ shmFile: `${databaseFile}-shm`,
988
+ };
989
+ }
990
+
963
991
  export async function replaceOwnerGeneration(
964
992
  stateDir: string,
965
993
  sessionId: string,