@gajae-code/coding-agent 0.14.2 → 0.15.0

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 (345) hide show
  1. package/CHANGELOG.md +120 -1
  2. package/dist/types/async/job-manager.d.ts +4 -1
  3. package/dist/types/autoresearch/dashboard.d.ts +29 -4
  4. package/dist/types/autoresearch/data-context.d.ts +25 -0
  5. package/dist/types/autoresearch/git.d.ts +65 -7
  6. package/dist/types/autoresearch/harness.d.ts +58 -0
  7. package/dist/types/autoresearch/prompts.d.ts +51 -0
  8. package/dist/types/autoresearch/runs.d.ts +173 -0
  9. package/dist/types/autoresearch/types.d.ts +9 -139
  10. package/dist/types/capability/types.d.ts +20 -0
  11. package/dist/types/cli/accounts-cli.d.ts +1 -0
  12. package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
  13. package/dist/types/config/autorouting-contract.d.ts +146 -0
  14. package/dist/types/config/autorouting-generator.d.ts +40 -0
  15. package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
  16. package/dist/types/config/autorouting.d.ts +35 -0
  17. package/dist/types/config/model-discovery-manager.d.ts +8 -1
  18. package/dist/types/config/model-registry.d.ts +30 -1
  19. package/dist/types/config/provider-selection-policy.d.ts +23 -0
  20. package/dist/types/config/settings-schema.d.ts +61 -3
  21. package/dist/types/config/settings.d.ts +3 -0
  22. package/dist/types/coordinator/contract.d.ts +1 -1
  23. package/dist/types/coordinator-mcp/durability.d.ts +29 -0
  24. package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
  25. package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
  26. package/dist/types/coordinator-mcp/server.d.ts +14 -0
  27. package/dist/types/crash/index-store.d.ts +3 -0
  28. package/dist/types/crash/record-loader.d.ts +2 -0
  29. package/dist/types/crash/upstream/relay.d.ts +5 -3
  30. package/dist/types/cursor.d.ts +12 -0
  31. package/dist/types/defaults/gjc-defaults.d.ts +35 -1
  32. package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
  33. package/dist/types/discovery/helpers.d.ts +1 -1
  34. package/dist/types/edit/index.d.ts +2 -1
  35. package/dist/types/eval/py/executor.d.ts +2 -0
  36. package/dist/types/extensibility/extensions/loader.d.ts +32 -0
  37. package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
  40. package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
  41. package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
  42. package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
  44. package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
  45. package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
  46. package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
  47. package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
  48. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
  49. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
  50. package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
  51. package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
  52. package/dist/types/hooks/skill-keywords.d.ts +1 -1
  53. package/dist/types/main.d.ts +0 -10
  54. package/dist/types/modes/acp/acp-agent.d.ts +13 -6
  55. package/dist/types/modes/components/model-selector.d.ts +14 -0
  56. package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
  57. package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
  58. package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
  59. package/dist/types/modes/interactive-mode.d.ts +3 -0
  60. package/dist/types/modes/types.d.ts +9 -0
  61. package/dist/types/rlm/artifacts.d.ts +4 -4
  62. package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
  63. package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
  64. package/dist/types/runtime-mcp/config.d.ts +6 -0
  65. package/dist/types/runtime-mcp/manager.d.ts +10 -0
  66. package/dist/types/sdk/acp/adapter.d.ts +0 -4
  67. package/dist/types/sdk/broker/broker.d.ts +1 -1
  68. package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
  69. package/dist/types/sdk/broker/session-index.d.ts +15 -0
  70. package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
  71. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
  72. package/dist/types/sdk/bus/index.d.ts +8 -0
  73. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
  74. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
  75. package/dist/types/sdk/client/client.d.ts +39 -4
  76. package/dist/types/sdk/host/host.d.ts +1 -0
  77. package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
  78. package/dist/types/sdk/host/model-pin.d.ts +29 -0
  79. package/dist/types/sdk/host/session-runtime.d.ts +2 -1
  80. package/dist/types/sdk/lifecycle-session.d.ts +13 -0
  81. package/dist/types/sdk/router/session-router.d.ts +27 -1
  82. package/dist/types/sdk/session.d.ts +11 -1
  83. package/dist/types/session/agent-session.d.ts +36 -6
  84. package/dist/types/session/artifacts.d.ts +16 -1
  85. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  86. package/dist/types/session/session-manager.d.ts +84 -2
  87. package/dist/types/session/session-staging-paths.d.ts +3 -0
  88. package/dist/types/skill-state/canonical-skills.d.ts +1 -1
  89. package/dist/types/skill-state/workflow-hud.d.ts +12 -21
  90. package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
  91. package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
  92. package/dist/types/system-prompt.d.ts +23 -0
  93. package/dist/types/task/executor.d.ts +35 -4
  94. package/dist/types/task/index.d.ts +17 -3
  95. package/dist/types/task/receipt.d.ts +3 -1
  96. package/dist/types/task/types.d.ts +144 -0
  97. package/dist/types/tools/atomic-file-write.d.ts +72 -0
  98. package/dist/types/tools/descriptors.d.ts +1 -0
  99. package/dist/types/tools/index.d.ts +36 -1
  100. package/dist/types/tools/move-session.d.ts +40 -0
  101. package/dist/types/tools/python.d.ts +12 -0
  102. package/dist/types/tools/resource-gc.d.ts +0 -3
  103. package/dist/types/tools/search.d.ts +8 -1
  104. package/dist/types/tools/skill.d.ts +9 -4
  105. package/dist/types/tools/subagent.d.ts +33 -3
  106. package/dist/types/utils/xml-escape.d.ts +2 -0
  107. package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
  108. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
  109. package/package.json +11 -12
  110. package/scripts/build-sdk-package-smoke.ts +7 -5
  111. package/scripts/check-autorouting-tier-map.ts +183 -0
  112. package/scripts/generate-sdk-operation-inventory.ts +12 -0
  113. package/scripts/generate-tool-catalog.ts +40 -5
  114. package/src/async/job-manager.ts +5 -2
  115. package/src/autoresearch/dashboard.ts +134 -314
  116. package/src/autoresearch/data-context.ts +35 -0
  117. package/src/autoresearch/git.ts +186 -26
  118. package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
  119. package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
  120. package/src/autoresearch/prompts/prompt.md +103 -0
  121. package/src/autoresearch/prompts.ts +126 -0
  122. package/src/autoresearch/runs.ts +708 -0
  123. package/src/autoresearch/types.ts +9 -150
  124. package/src/capability/index.ts +4 -4
  125. package/src/capability/types.ts +20 -0
  126. package/src/cli/accounts-cli.ts +21 -5
  127. package/src/cli/config-cli.ts +25 -1
  128. package/src/cli/fast-help.ts +1 -2
  129. package/src/cli/list-models.ts +5 -8
  130. package/src/cli/setup-cli.ts +8 -0
  131. package/src/cli.ts +2 -21
  132. package/src/commands/autoresearch.ts +33 -0
  133. package/src/commands/coordinator.ts +15 -0
  134. package/src/commands/launch.ts +2 -4
  135. package/src/commands/quick-lane.ts +1 -1
  136. package/src/commands/sdk.ts +24 -0
  137. package/src/commands/state.ts +1 -1
  138. package/src/config/autorouting-contract.ts +474 -0
  139. package/src/config/autorouting-generator.ts +238 -0
  140. package/src/config/autorouting-tier-map.ts +6108 -0
  141. package/src/config/autorouting.ts +120 -0
  142. package/src/config/model-discovery-manager.ts +42 -11
  143. package/src/config/model-registry.ts +348 -68
  144. package/src/config/provider-selection-policy.ts +60 -9
  145. package/src/config/settings-schema.ts +195 -6
  146. package/src/config/settings.ts +62 -1
  147. package/src/config.ts +2 -3
  148. package/src/coordinator/contract.ts +0 -1
  149. package/src/coordinator-mcp/codex-handoff.ts +68 -42
  150. package/src/coordinator-mcp/durability.ts +200 -0
  151. package/src/coordinator-mcp/event-webhook.ts +278 -0
  152. package/src/coordinator-mcp/question-state.ts +109 -46
  153. package/src/coordinator-mcp/server.ts +932 -504
  154. package/src/crash/index-store.ts +126 -22
  155. package/src/crash/record-loader.ts +7 -0
  156. package/src/crash/upstream/relay.ts +82 -34
  157. package/src/cursor.ts +84 -1
  158. package/src/debug/index.ts +26 -0
  159. package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
  160. package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
  161. package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
  162. package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
  163. package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
  164. package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
  165. package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
  166. package/src/defaults/gjc-defaults.ts +89 -2
  167. package/src/defaults/gjc-skills.generated.ts +24 -17
  168. package/src/discovery/builtin.ts +18 -5
  169. package/src/discovery/helpers.ts +6 -6
  170. package/src/edit/index.ts +5 -2
  171. package/src/eval/py/executor.ts +8 -0
  172. package/src/extensibility/extensions/loader.ts +135 -13
  173. package/src/extensibility/extensions/wrapper.ts +1 -2
  174. package/src/extensibility/gjc-plugins/schema.ts +1 -1
  175. package/src/extensibility/runtime-skill-discovery.ts +2 -2
  176. package/src/extensibility/skill-management.ts +2 -3
  177. package/src/extensibility/skills.ts +16 -8
  178. package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
  179. package/src/gjc-runtime/gc-render.ts +0 -1
  180. package/src/gjc-runtime/gc-runtime.ts +1 -11
  181. package/src/gjc-runtime/launch-tmux.ts +3 -3
  182. package/src/gjc-runtime/psmux-detect.ts +2 -2
  183. package/src/gjc-runtime/python-transcript.ts +58 -0
  184. package/src/gjc-runtime/ralplan-runtime.ts +6 -18
  185. package/src/gjc-runtime/session-layout.ts +18 -7
  186. package/src/gjc-runtime/session-state-sidecar.ts +58 -9
  187. package/src/gjc-runtime/state-renderer.ts +0 -38
  188. package/src/gjc-runtime/state-runtime.ts +43 -22
  189. package/src/gjc-runtime/state-schema.ts +1 -1
  190. package/src/gjc-runtime/tmux-common.ts +7 -7
  191. package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
  192. package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
  193. package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
  194. package/src/gjc-runtime/workflow-command-ref.ts +15 -11
  195. package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
  196. package/src/gjc-runtime/workflow-manifest.ts +43 -83
  197. package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
  198. package/src/hooks/skill-keywords.ts +3 -3
  199. package/src/hooks/skill-state.ts +2 -2
  200. package/src/internal-urls/docs-index.generated.ts +26 -24
  201. package/src/lsp/index.ts +25 -5
  202. package/src/main.ts +2 -27
  203. package/src/modes/acp/acp-agent.ts +200 -75
  204. package/src/modes/components/model-selector.ts +216 -13
  205. package/src/modes/components/oauth-selector.ts +5 -5
  206. package/src/modes/components/skill-hud/render.ts +173 -35
  207. package/src/modes/components/smart-routing-panel.ts +531 -0
  208. package/src/modes/components/tool-status-header.ts +6 -3
  209. package/src/modes/controllers/command-controller.ts +17 -7
  210. package/src/modes/controllers/input-controller.ts +44 -9
  211. package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
  212. package/src/modes/controllers/selector-controller.ts +298 -65
  213. package/src/modes/interactive-mode.ts +8 -1
  214. package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
  215. package/src/modes/types.ts +9 -1
  216. package/src/prompts/agents/planner.md +1 -1
  217. package/src/prompts/system/system-prompt.md +1 -1
  218. package/src/prompts/system/volatile-project-context.md +2 -1
  219. package/src/prompts/tools/move-session.md +15 -0
  220. package/src/prompts/tools/python.md +12 -0
  221. package/src/prompts/tools/skill-discovery.md +1 -1
  222. package/src/prompts/tools/skill.md +2 -2
  223. package/src/prompts/tools/task-summary.md +1 -0
  224. package/src/prompts/tools/task.md +6 -0
  225. package/src/rlm/artifacts.ts +12 -10
  226. package/src/runtime/optional-runtime-services.ts +3 -2
  227. package/src/runtime/workspace-tree-service.ts +11 -3
  228. package/src/runtime-mcp/config.ts +8 -1
  229. package/src/runtime-mcp/manager.ts +33 -0
  230. package/src/sdk/acp/adapter.ts +48 -1
  231. package/src/sdk/broker/broker.ts +30 -2
  232. package/src/sdk/broker/discovery.ts +217 -2
  233. package/src/sdk/broker/lifecycle.ts +385 -59
  234. package/src/sdk/broker/session-index.ts +243 -48
  235. package/src/sdk/broker/transport.ts +1 -0
  236. package/src/sdk/bus/chat-daemon-control.ts +8 -2
  237. package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
  238. package/src/sdk/bus/index.ts +208 -11
  239. package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
  240. package/src/sdk/bus/reconciliation-store.ts +27 -5
  241. package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
  242. package/src/sdk/client/client.ts +204 -22
  243. package/src/sdk/host/host.ts +16 -0
  244. package/src/sdk/host/internal-autorouting-state.ts +13 -0
  245. package/src/sdk/host/model-pin.ts +101 -0
  246. package/src/sdk/host/reverse-leases.ts +2 -1
  247. package/src/sdk/host/session-runtime.test.ts +224 -1
  248. package/src/sdk/host/session-runtime.ts +125 -37
  249. package/src/sdk/lifecycle/service.ts +3 -1
  250. package/src/sdk/lifecycle-session.ts +42 -1
  251. package/src/sdk/protocol/operation-inventory.generated.json +66 -0
  252. package/src/sdk/router/session-router.ts +375 -36
  253. package/src/sdk/session.ts +547 -47
  254. package/src/session/account-inventory.ts +1 -1
  255. package/src/session/agent-session.ts +765 -189
  256. package/src/session/artifacts.ts +234 -4
  257. package/src/session/internal/managed-session-storage.ts +91 -4
  258. package/src/session/provider-safety-stop-hint.ts +3 -2
  259. package/src/session/session-manager.ts +775 -16
  260. package/src/session/session-staging-paths.ts +8 -0
  261. package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
  262. package/src/setup/host-plugin-setup.ts +2 -2
  263. package/src/skill-state/active-state.ts +2 -2
  264. package/src/skill-state/canonical-skills.ts +1 -1
  265. package/src/skill-state/initial-phase.ts +1 -1
  266. package/src/skill-state/workflow-hud.ts +41 -41
  267. package/src/skill-state/workflow-mutation-guard.ts +26 -7
  268. package/src/slash-commands/builtin-registry.ts +77 -0
  269. package/src/slash-commands/helpers/autorouting-status.ts +55 -0
  270. package/src/system-prompt.ts +102 -5
  271. package/src/task/discovery.ts +2 -3
  272. package/src/task/executor.ts +617 -43
  273. package/src/task/index.ts +262 -15
  274. package/src/task/receipt.ts +30 -0
  275. package/src/task/render.ts +11 -1
  276. package/src/task/types.ts +316 -0
  277. package/src/tools/atomic-file-write.ts +594 -0
  278. package/src/tools/bisect.ts +3 -2
  279. package/src/tools/descriptors.test.ts +3 -0
  280. package/src/tools/descriptors.ts +24 -0
  281. package/src/tools/image-gen.ts +5 -1
  282. package/src/tools/index.ts +62 -5
  283. package/src/tools/move-session.ts +107 -0
  284. package/src/tools/python.ts +205 -0
  285. package/src/tools/read.ts +88 -15
  286. package/src/tools/renderers.ts +2 -0
  287. package/src/tools/resource-gc.ts +8 -177
  288. package/src/tools/search.ts +30 -6
  289. package/src/tools/skill-discovery.ts +1 -0
  290. package/src/tools/skill.ts +37 -7
  291. package/src/tools/subagent-render.ts +119 -13
  292. package/src/tools/subagent.ts +91 -74
  293. package/src/tools/tool-catalog.generated.ts +64 -2
  294. package/src/tools/write.ts +16 -12
  295. package/src/utils/xml-escape.ts +14 -0
  296. package/src/web/search/providers/codex.ts +2 -2
  297. package/src/workflow/workflow-intent-diff.ts +13 -12
  298. package/src/workflow/workflow-intent-report-consumer.ts +6 -6
  299. package/dist/types/autoresearch/helpers.d.ts +0 -24
  300. package/dist/types/autoresearch/index.d.ts +0 -2
  301. package/dist/types/autoresearch/state.d.ts +0 -17
  302. package/dist/types/autoresearch/storage.d.ts +0 -142
  303. package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
  304. package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
  305. package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
  306. package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
  307. package/dist/types/commands/team.d.ts +0 -24
  308. package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
  309. package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
  310. package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
  311. package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
  312. package/dist/types/gjc-runtime/team-store.d.ts +0 -168
  313. package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
  314. package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
  315. package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
  316. package/dist/types/rlm/index.d.ts +0 -47
  317. package/dist/types/rlm/preset.d.ts +0 -23
  318. package/dist/types/rlm/python-tool.d.ts +0 -16
  319. package/src/autoresearch/command-resume.md +0 -14
  320. package/src/autoresearch/index.ts +0 -536
  321. package/src/autoresearch/prompt.md +0 -103
  322. package/src/autoresearch/resume-message.md +0 -10
  323. package/src/autoresearch/state.ts +0 -273
  324. package/src/autoresearch/storage.ts +0 -699
  325. package/src/autoresearch/tools/init-experiment.ts +0 -272
  326. package/src/autoresearch/tools/log-experiment.ts +0 -524
  327. package/src/autoresearch/tools/run-experiment.ts +0 -475
  328. package/src/autoresearch/tools/update-notes.ts +0 -109
  329. package/src/commands/rlm.ts +0 -19
  330. package/src/commands/team.ts +0 -238
  331. package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
  332. package/src/defaults/gjc/skills/team/SKILL.md +0 -467
  333. package/src/gjc-runtime/team-gc.ts +0 -49
  334. package/src/gjc-runtime/team-launch.ts +0 -404
  335. package/src/gjc-runtime/team-notify.ts +0 -326
  336. package/src/gjc-runtime/team-runtime.ts +0 -5923
  337. package/src/gjc-runtime/team-store.ts +0 -878
  338. package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
  339. package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
  340. package/src/gjc-runtime/team-workers.ts +0 -1123
  341. package/src/prompts/system/rlm-report-command.md +0 -1
  342. package/src/prompts/system/rlm-research.md +0 -23
  343. package/src/rlm/index.ts +0 -365
  344. package/src/rlm/preset.ts +0 -76
  345. package/src/rlm/python-tool.ts +0 -68
@@ -0,0 +1,8 @@
1
+ import * as path from "node:path";
2
+
3
+ export const SESSION_STAGING_DIRNAME = ".staging" as const;
4
+
5
+ /** True when a path is an immediate child of the reserved session staging directory. */
6
+ export function isStagedSessionPath(filePath: string): boolean {
7
+ return path.basename(path.dirname(path.resolve(filePath))) === SESSION_STAGING_DIRNAME;
8
+ }
@@ -21,7 +21,6 @@ When the goal is to hand GJC a whole workflow rather than micro-manage one promp
21
21
 
22
22
  - `gjc_delegate_plan` — run consensus planning (`/skill:ralplan`) to a pending-approval plan.
23
23
  - `gjc_delegate_execute` — run execution (`/skill:ultragoal`) to completion with verification.
24
- - `gjc_delegate_team` — run parallel team execution (`/skill:team`) with internal tmux workers.
25
24
 
26
25
  Each delegate starts (or reuses) a session, sends one workflow-tagged turn, and returns a durable `turn_id`. Pass `cwd` and `task`; set `allow_mutation: true` only when the bridge startup mutation class is enabled and the user has approved changes. Poll the returned `turn_id` with `{{TOOL_PREFIX}}_await_turn` or watch for the `delegation.started` event, exactly as with `send_prompt`. Drop to the manual start/send tools only for fine-grained control the delegate tools do not cover.
27
26
 
@@ -78,7 +78,7 @@ export function buildHostPluginSetup(host: HostPluginKind, flags: HostPluginSetu
78
78
  installGuidance: [
79
79
  `Add the local marketplace: /plugin marketplace add ${marketplaceRoot}`,
80
80
  "Install the plugin: /plugin install gajae-code",
81
- "Then call gjc_delegate_plan / gjc_delegate_execute / gjc_delegate_team from Claude Code.",
81
+ "Then call gjc_delegate_plan / gjc_delegate_execute from Claude Code.",
82
82
  ],
83
83
  coordinatorConfigPreview: { command: "gjc", args: ["mcp-serve", "coordinator"], env },
84
84
  mutationPolicy:
@@ -104,7 +104,7 @@ export function buildHostPluginSetup(host: HostPluginKind, flags: HostPluginSetu
104
104
  installGuidance: [
105
105
  `Add the local marketplace: codex plugin marketplace add ${marketplaceRoot}`,
106
106
  "Install the plugin: codex plugin add gajae-code@gajae-code-local",
107
- "Then call gjc_delegate_plan / gjc_delegate_execute / gjc_delegate_team from Codex.",
107
+ "Then call gjc_delegate_plan / gjc_delegate_execute from Codex.",
108
108
  ],
109
109
  coordinatorConfigPreview: { command: "gjc", args: ["mcp-serve", "coordinator"], env },
110
110
  mutationPolicy:
@@ -351,7 +351,7 @@ export function isCanonicalGjcWorkflowSkill(skill: string): skill is CanonicalGj
351
351
  * rather than a generic synthetic compaction continuation.
352
352
  */
353
353
  const CONTINUATION_INERT_WORKFLOW_PHASES: Readonly<Partial<Record<CanonicalGjcWorkflowSkill, ReadonlySet<string>>>> = {
354
- team: new Set(["awaiting_integration"]),
354
+ autoresearch: new Set(["verdict"]),
355
355
  };
356
356
 
357
357
  /**
@@ -624,7 +624,7 @@ function dedupeVisibleBySkill(entries: SkillActiveEntry[], sessionId?: string):
624
624
  * The planning pipeline advances one stage at a time: `deep-interview →
625
625
  * ralplan → ultragoal`. Activating a downstream stage supersedes upstream
626
626
  * stages so stale rows cannot keep owning the HUD, gate, or primary active
627
- * snapshot. `team` is intentionally excluded — it runs alongside ultragoal —
627
+ * snapshot. `autoresearch` is intentionally excluded — it runs alongside ultragoal —
628
628
  * and every non-pipeline skill is left untouched.
629
629
  */
630
630
  const PLANNING_PIPELINE_SKILLS = new Set<string>(["deep-interview", "ralplan", "ultragoal"]);
@@ -1,4 +1,4 @@
1
1
  /** Native-free canonical GJC workflow skill identifiers. */
2
- export const CANONICAL_GJC_WORKFLOW_SKILLS = ["deep-interview", "ralplan", "ultragoal", "team"] as const;
2
+ export const CANONICAL_GJC_WORKFLOW_SKILLS = ["deep-interview", "ralplan", "ultragoal", "autoresearch"] as const;
3
3
 
4
4
  export type CanonicalGjcWorkflowSkill = (typeof CANONICAL_GJC_WORKFLOW_SKILLS)[number];
@@ -14,6 +14,6 @@ export function initialPhaseForSkill(skill: CanonicalGjcWorkflowSkill | string):
14
14
  if (skill === "deep-interview") return "interviewing";
15
15
  if (skill === "ultragoal") return "goal-planning";
16
16
  if (skill === "ralplan") return "planner";
17
- if (skill === "team") return "starting";
17
+ if (skill === "autoresearch") return "intake";
18
18
  return "planning";
19
19
  }
@@ -53,21 +53,6 @@ interface UltragoalHudState extends WorkflowGateHudState {
53
53
  updatedAt?: string;
54
54
  }
55
55
 
56
- interface TeamHudWorker {
57
- id: string;
58
- status?: string;
59
- }
60
-
61
- interface TeamHudState extends WorkflowGateHudState {
62
- phase: string;
63
- task_total: number;
64
- task_counts: Record<string, number>;
65
- workers: TeamHudWorker[];
66
- updated_at?: string;
67
- latestEvent?: { type?: string; worker?: string; message?: string };
68
- latestMessage?: { from_worker?: string; body?: string };
69
- }
70
-
71
56
  function percent(value: number | undefined): string | undefined {
72
57
  if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
73
58
  return `${Math.round(value * 100)}%`;
@@ -263,6 +248,47 @@ export function buildRalplanHudSummary(state: RalplanHudState): WorkflowHudSumma
263
248
  };
264
249
  }
265
250
 
251
+ export interface AutoresearchHudState extends WorkflowGateHudState {
252
+ phase: string;
253
+ mode?: string;
254
+ intake?: string;
255
+ slug?: string;
256
+ specPath?: string;
257
+ verdict?: string;
258
+ experimentCount?: number;
259
+ experimentStatuses?: string[];
260
+ updatedAt?: string;
261
+ }
262
+
263
+ export function buildAutoresearchHudSummary(state: AutoresearchHudState): WorkflowHudSummary {
264
+ const statuses = state.experimentStatuses ?? [];
265
+ const total = Math.max(state.experimentCount ?? statuses.length, statuses.length);
266
+ const kept = statuses.filter(status => status === "keep").length;
267
+ const crash = statuses.filter(status => status === "crash").length;
268
+ const checksFailed = statuses.filter(status => status === "checks_failed").length;
269
+ const chips: Array<WorkflowHudChip | null> = [
270
+ ...(state.phase === "verdict" ? [chip("verdict", state.verdict ?? "issued", 5)] : []),
271
+ chip("phase", state.phase, 10),
272
+ ...(total > 0 ? [chip("exp", `${kept}/${total}`, 20)] : []),
273
+ crash > 0 ? { label: "crash", value: String(crash), priority: 4, severity: "warning" } : null,
274
+ checksFailed > 0
275
+ ? { label: "checks_failed", value: String(checksFailed), priority: 5, severity: "warning" }
276
+ : null,
277
+ chip("mode", state.mode, 30),
278
+ chip("intake", state.intake, 40),
279
+ chip("spec", state.specPath, 45),
280
+ ...gateChips(state, 50),
281
+ ];
282
+ const visibleChips =
283
+ total === 0 && state.phase !== "verdict" ? compactChips([chip("phase", state.phase, 10)]) : compactChips(chips);
284
+ return {
285
+ version: 1,
286
+ ...(state.slug ? { summary: `autoresearch mission ${state.slug}` } : {}),
287
+ chips: visibleChips,
288
+ ...(state.updatedAt ? { updated_at: state.updatedAt } : {}),
289
+ };
290
+ }
291
+
266
292
  export function buildUltragoalHudSummary(state: UltragoalHudState): WorkflowHudSummary {
267
293
  const total = state.goals.length;
268
294
  const complete = state.counts.complete ?? 0;
@@ -288,29 +314,3 @@ export function buildUltragoalHudSummary(state: UltragoalHudState): WorkflowHudS
288
314
  ...(state.updatedAt ? { updated_at: state.updatedAt } : {}),
289
315
  };
290
316
  }
291
-
292
- export function buildTeamHudSummary(state: TeamHudState): WorkflowHudSummary {
293
- const failedWorkers = state.workers.filter(
294
- worker => worker.status === "failed" || worker.status === "blocked",
295
- ).length;
296
- const stoppedWorkers = state.workers.filter(worker => worker.status === "stopped").length;
297
- const completed = state.task_counts.completed ?? 0;
298
- const failedTasks = (state.task_counts.failed ?? 0) + (state.task_counts.blocked ?? 0);
299
- const latest = state.latestEvent?.message ?? state.latestEvent?.type ?? state.latestMessage?.body;
300
- return {
301
- version: 1,
302
- chips: compactChips([
303
- failedWorkers > 0 || failedTasks > 0
304
- ? { label: "blocked", value: String(failedWorkers + failedTasks), priority: 5, severity: "blocked" }
305
- : stoppedWorkers > 0
306
- ? { label: "stopped", value: String(stoppedWorkers), priority: 5, severity: "warning" }
307
- : null,
308
- chip("phase", state.phase, 10),
309
- chip("workers", `${state.workers.length - failedWorkers}/${state.workers.length}`, 20),
310
- chip("tasks", `${completed}/${state.task_total}`, 30),
311
- ...gateChips(state, 40),
312
- chip("latest", latest, 70),
313
- ]),
314
- ...(state.updated_at ? { updated_at: state.updated_at } : {}),
315
- };
316
- }
@@ -3,6 +3,7 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import type { AgentTool } from "@gajae-code/agent-core";
5
5
  import { logger } from "@gajae-code/utils";
6
+ import { isAutoresearchAuthorizedResearchPath } from "../autoresearch/git";
6
7
  import { expandApplyPatchToEntries } from "../edit/modes/apply-patch";
7
8
  import { GJC_SESSION_PREFIX, modeStatePath as sessionModeStatePath } from "../gjc-runtime/session-layout";
8
9
  import { resolveGjcSessionForRead } from "../gjc-runtime/session-resolution";
@@ -26,10 +27,13 @@ export const RALPLAN_MUTATION_BLOCK_MESSAGE =
26
27
  "Ralplan planning phase boundary: keep refining the consensus plan and persist plan artifacts through `gjc ralplan --write` (stage scratch files under a temp dir if needed). Product-code mutation tools and patch execution are blocked while ralplan is active; mutate only after the plan is approved and execution begins.";
27
28
  export const ULTRAGOAL_GOAL_PLANNING_MUTATION_BLOCK_MESSAGE =
28
29
  "Ultragoal goal-planning phase boundary: finish goal planning and record goals through `gjc ultragoal` before editing code. Product-code mutation tools and patch execution are blocked until goal planning completes and execution begins.";
30
+ export const AUTORESEARCH_MUTATION_BLOCK_MESSAGE =
31
+ "Autoresearch research-only boundary: this workflow produces findings, evidence, and a verdict — never product code. Product-code mutation tools and patch execution are blocked everywhere, on every branch. Mission research artifacts (`autoresearch.sh` at the workdir root and mission state through the sanctioned `gjc autoresearch` CLI) stay writable; run experiments through the `python` tool, or finish the mission and hand off to an approved implementation workflow.";
29
32
 
30
33
  /** Resolve the phase-boundary block message for the active planning skill. */
31
34
  function planningPhaseBlockMessage(skill: CanonicalGjcWorkflowSkill): string {
32
35
  if (skill === "ralplan") return RALPLAN_MUTATION_BLOCK_MESSAGE;
36
+ if (skill === "autoresearch") return AUTORESEARCH_MUTATION_BLOCK_MESSAGE;
33
37
  if (skill === "ultragoal") return ULTRAGOAL_GOAL_PLANNING_MUTATION_BLOCK_MESSAGE;
34
38
  return DEEP_INTERVIEW_MUTATION_BLOCK_MESSAGE;
35
39
  }
@@ -220,9 +224,12 @@ function modeStateMatchesContext(state: ModeState, sessionId?: string, threadId?
220
224
  return true;
221
225
  }
222
226
 
223
- /** Workflow skills that have a pre-approval planning posture this guard enforces. `team` never does. */
224
- function isPlanningSkill(skill: string): skill is "deep-interview" | "ralplan" | "ultragoal" {
225
- return skill === "deep-interview" || skill === "ralplan" || skill === "ultragoal";
227
+ /** Canonical workflow skills whose active posture gates product-code mutation. */
228
+ type MutationGatedSkill = "deep-interview" | "ralplan" | "ultragoal" | "autoresearch";
229
+
230
+ /** Workflow skills whose active posture this guard enforces against product-code mutation. */
231
+ function isPlanningSkill(skill: string): skill is MutationGatedSkill {
232
+ return skill === "deep-interview" || skill === "ralplan" || skill === "ultragoal" || skill === "autoresearch";
226
233
  }
227
234
 
228
235
  /**
@@ -233,14 +240,14 @@ function isPlanningSkill(skill: string): skill is "deep-interview" | "ralplan" |
233
240
  * `ultragoal` only blocks during `goal-planning`; once goals are created it is an
234
241
  * executor and mutates freely.
235
242
  */
236
- function isBlockingPlanningPhase(skill: "deep-interview" | "ralplan" | "ultragoal", phase: string): boolean {
243
+ function isBlockingPlanningPhase(skill: MutationGatedSkill, phase: string): boolean {
237
244
  const normalized = phase.trim().toLowerCase();
238
245
  if (skill === "ultragoal") return normalized === "goal-planning";
239
246
  return !getSkillManifest(skill).stopReleasingPhases.includes(normalized);
240
247
  }
241
248
 
242
249
  interface ActivePlanningSkill {
243
- skill: "deep-interview" | "ralplan" | "ultragoal";
250
+ skill: MutationGatedSkill;
244
251
  phase: string;
245
252
  }
246
253
 
@@ -251,7 +258,7 @@ interface ActivePlanningSkill {
251
258
  * to `active:false`, which `listActiveSkills` already filters out). If several
252
259
  * are momentarily active, prefer the most-recently-updated entry so a stale
253
260
  * planning row (e.g. a still-active ralplan `final`) can never be selected over a
254
- * newer executor (ultragoal/team), and a planning *return* (newer `updated_at`)
261
+ * newer executor (ultragoal/autoresearch), and a planning *return* (newer `updated_at`)
255
262
  * reliably wins. Ties fall back to the resolved top-level `skill`, then to the
256
263
  * first entry, matching how the HUD/chain guard pick `activeSkills[0]`.
257
264
  */
@@ -1053,7 +1060,7 @@ function blockedWorkflowStateSkill(cwd: string, rawPath: string): CanonicalGjcWo
1053
1060
  if (generatedRoot === "specs" || generatedRoot === "plans") return null;
1054
1061
  if (generatedRoot !== "state") return null;
1055
1062
  const fileName = segments.at(-1) ?? "";
1056
- for (const skillName of ["deep-interview", "ralplan", "ultragoal", "team"] as const) {
1063
+ for (const skillName of ["deep-interview", "ralplan", "ultragoal"] as const) {
1057
1064
  if (fileName === workflowModeStateFileName(skillName)) return skillName;
1058
1065
  }
1059
1066
  if (fileName === "skill-active-state.json") return "deep-interview";
@@ -1214,6 +1221,18 @@ export async function getWorkflowMutationDecision(
1214
1221
  return { blocked: false, targets: [] };
1215
1222
  }
1216
1223
  if (input.forceOverride) return { blocked: false, targets: [] };
1224
+ // Autoresearch is research-only: it never edits product code, on any branch.
1225
+ // The only agent-writable surface is the mission's own research artifact —
1226
+ // the `autoresearch.sh` harness at the workdir root (mission state under
1227
+ // `.gjc/**` is runtime-owned and only the sanctioned CLI writes it). A branch
1228
+ // name is not authorization: an `autoresearch/*` branch isolates keep/discard
1229
+ // bookkeeping, it does not turn product edits into research.
1230
+ if (
1231
+ planning.skill === "autoresearch" &&
1232
+ targets.paths.every(p => isAutoresearchAuthorizedResearchPath(input.cwd, p))
1233
+ ) {
1234
+ return { blocked: false, targets: targets.paths };
1235
+ }
1217
1236
  const message = planningPhaseBlockMessage(planning.skill);
1218
1237
  // Bash during a planning phase blocks only when a filesystem mutation was
1219
1238
  // actually recognized. Inspection, reads, and CLI invocations the scanner does
@@ -44,6 +44,7 @@ import {
44
44
  } from "../setup/provider-onboarding";
45
45
  import { parseThinkingLevel } from "../thinking";
46
46
  import { getDisplayChangelogEntries } from "../utils/changelog";
47
+ import { buildAutoroutingStatusReport } from "./helpers/autorouting-status";
47
48
  import { buildContextReportText } from "./helpers/context-report";
48
49
  import { switchSessionCredentialCommand } from "./helpers/credential-switch";
49
50
  import { buildFastStatusReport } from "./helpers/fast-status-report";
@@ -1117,6 +1118,82 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1117
1118
  runtime.ctx.editor.setText("");
1118
1119
  },
1119
1120
  },
1121
+ {
1122
+ name: "routing",
1123
+ description: "Show or set up sub-agent model autorouting",
1124
+ acpDescription: "Show or set up sub-agent model autorouting",
1125
+ inlineHint: "[on|off|status]",
1126
+ acpInputHint: "[on|off|status]",
1127
+ subcommands: [
1128
+ { name: "on", description: "Enable autorouting" },
1129
+ { name: "off", description: "Disable autorouting" },
1130
+ { name: "status", description: "Show effective autorouting tiers" },
1131
+ ],
1132
+ allowArgs: true,
1133
+ handle: async (command, runtime) => {
1134
+ const arg = command.args.trim().toLowerCase();
1135
+ if (arg === "on" || arg === "off") {
1136
+ try {
1137
+ // Mirror SelectorController#assertSmartRoutingWritable: the non-TUI (ACP/SDK)
1138
+ // dispatch path must honor the same scoped-session guard as the TUI controller,
1139
+ // otherwise a --models-scoped session can toggle routing through /routing on|off.
1140
+ if ((runtime.session.scopedModels?.length ?? 0) > 0) {
1141
+ throw new Error("Smart-routing settings are read-only in a --models-scoped session.");
1142
+ }
1143
+ runtime.settings.set("task.autorouting.enabled", arg === "on");
1144
+ } catch (err) {
1145
+ return usage(`Failed to change autorouting: ${errorMessage(err)}`, runtime);
1146
+ }
1147
+ await runtime.output(
1148
+ buildAutoroutingStatusReport({
1149
+ effective: runtime.settings.getEffectiveAutorouting(),
1150
+ tiers: runtime.settings.get("task.autorouting.tiers"),
1151
+ provenance: runtime.settings.get("task.autorouting.provenance"),
1152
+ }),
1153
+ );
1154
+
1155
+ return commandConsumed();
1156
+ }
1157
+ if (arg === "" || arg === "status") {
1158
+ await runtime.output(
1159
+ buildAutoroutingStatusReport({
1160
+ effective: runtime.settings.getEffectiveAutorouting(),
1161
+ tiers: runtime.settings.get("task.autorouting.tiers"),
1162
+ provenance: runtime.settings.get("task.autorouting.provenance"),
1163
+ }),
1164
+ );
1165
+
1166
+ return commandConsumed();
1167
+ }
1168
+ return usage("Usage: /routing [on|off|status]", runtime);
1169
+ },
1170
+ handleTui: async (command, runtime) => {
1171
+ const arg = command.args.trim().toLowerCase();
1172
+ runtime.ctx.editor.setText("");
1173
+ if (arg === "") {
1174
+ runtime.ctx.showModelSelector({ smartRoutingOnly: true });
1175
+ return;
1176
+ }
1177
+ if (arg === "on" || arg === "off") {
1178
+ // Route through the controller so the toggle honors the same
1179
+ // scoped-session and durable-config guards as the panel.
1180
+ await runtime.ctx.setAutoroutingEnabled(arg === "on");
1181
+ } else if (arg !== "status") {
1182
+ runtime.ctx.showStatus("Usage: /routing [on|off|status]");
1183
+ return;
1184
+ }
1185
+ const report = buildAutoroutingStatusReport({
1186
+ effective: runtime.ctx.settings.getEffectiveAutorouting(),
1187
+ tiers: runtime.ctx.settings.get("task.autorouting.tiers"),
1188
+ provenance: runtime.ctx.settings.get("task.autorouting.provenance"),
1189
+ });
1190
+ runtime.ctx.chatContainer.addChild(new Spacer(1));
1191
+ runtime.ctx.chatContainer.addChild(new DynamicBorder());
1192
+ runtime.ctx.chatContainer.addChild(new Text(report, 1, 0));
1193
+ runtime.ctx.chatContainer.addChild(new DynamicBorder());
1194
+ runtime.ctx.ui.requestRender();
1195
+ },
1196
+ },
1120
1197
  {
1121
1198
  name: "export",
1122
1199
  priority: 50,
@@ -0,0 +1,55 @@
1
+ import { replaceTabs, truncateToWidth } from "@gajae-code/tui";
2
+ import {
3
+ AUTOROUTING_TIERS,
4
+ type AutoroutingEffective,
5
+ type AutoroutingProvenance,
6
+ matchesRecordedTiersFingerprint,
7
+ validateAutoroutingProvenance,
8
+ } from "../../config/autorouting-contract";
9
+ import { validateDisplayLine } from "../../modes/components/ansi-display-validator";
10
+
11
+ /** Longest rendered chain/diagnostic before truncation. */
12
+ const MAX_STATUS_LINE_WIDTH = 200;
13
+
14
+ /**
15
+ * Selectors and diagnostics originate in user-editable config, so a hand-edited
16
+ * value can carry tabs or terminal control sequences. Strip them before the
17
+ * string reaches a renderer.
18
+ */
19
+ function displaySafe(text: string): string {
20
+ return truncateToWidth(validateDisplayLine(replaceTabs(text)), MAX_STATUS_LINE_WIDTH);
21
+ }
22
+
23
+ export type AutoroutingStatusSnapshot = {
24
+ effective: AutoroutingEffective;
25
+ tiers: unknown;
26
+ provenance: AutoroutingProvenance | undefined;
27
+ };
28
+
29
+ /** Render settings-derived autorouting state without consulting registry/auth. */
30
+ export function buildAutoroutingStatusReport(snapshot: AutoroutingStatusSnapshot): string {
31
+ const { effective, tiers, provenance } = snapshot;
32
+ if (!effective.active) {
33
+ const detail =
34
+ effective.issue?.detail ?? "Autorouting is disabled; every Task item uses manual model resolution.";
35
+ return `Autorouting: off\n${displaySafe(detail)}`;
36
+ }
37
+ const provenanceIssues = provenance === undefined ? [] : validateAutoroutingProvenance(provenance);
38
+ const malformed = provenanceIssues.length > 0;
39
+ const generated = !malformed && provenance !== undefined && matchesRecordedTiersFingerprint(provenance, tiers);
40
+ const label = malformed
41
+ ? "hand-authored tiers"
42
+ : generated
43
+ ? "generated"
44
+ : provenance === undefined
45
+ ? "hand-authored tiers"
46
+ : "generated, hand-edited";
47
+ const lines = [`Autorouting: on (${label})`];
48
+ if (malformed) lines.push("Recorded generation provenance is invalid; treating tiers as hand-authored.");
49
+ for (const tier of AUTOROUTING_TIERS) {
50
+ const chain = effective.map[tier];
51
+ const rendered = chain && chain.length > 0 ? displaySafe(chain.join(" -> ")) : "(unmapped, falls back to manual)";
52
+ lines.push(` ${tier}: ${rendered}`);
53
+ }
54
+ return lines.join("\n");
55
+ }
@@ -405,18 +405,116 @@ export interface BuildSystemPromptResult {
405
405
  /** Context-file discovery warnings visible to SDK and session callers. */
406
406
  warnings: string[];
407
407
  }
408
+ /** Host wall-clock facts injected with every turn. */
409
+ export interface LocalTimeContext {
410
+ /** Local calendar date with weekday, e.g. `2026-08-19 (Wed)`. */
411
+ date: string;
412
+ /** Local clock time with UTC offset and IANA zone, e.g. `21:04 UTC+09:00 (Asia/Seoul)`. */
413
+ time: string;
414
+ }
415
+
416
+ /**
417
+ * Render the host's local date and clock time for the volatile turn context.
418
+ *
419
+ * Every field is derived from a single `Intl.DateTimeFormat` pass over one
420
+ * resolved zone, so the date, clock, offset, and zone name can never disagree
421
+ * with each other the way `Date`'s local getters can. Falls back to UTC only
422
+ * when the runtime has no usable ICU zone.
423
+ *
424
+ * @param timeZone IANA zone override. Default: the host zone.
425
+ */
426
+ export function getLocalTimeContext(now: Date = new Date(), timeZone?: string): LocalTimeContext {
427
+ try {
428
+ const zone = timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
429
+ const parts = new Intl.DateTimeFormat("en-US", {
430
+ timeZone: zone,
431
+ year: "numeric",
432
+ month: "2-digit",
433
+ day: "2-digit",
434
+ weekday: "short",
435
+ hour: "2-digit",
436
+ minute: "2-digit",
437
+ hourCycle: "h23",
438
+ timeZoneName: "longOffset",
439
+ }).formatToParts(now);
440
+ const part = (type: Intl.DateTimeFormatPartTypes): string =>
441
+ parts.find(candidate => candidate.type === type)?.value ?? "";
442
+ const year = part("year");
443
+ const month = part("month");
444
+ const day = part("day");
445
+ const hour = part("hour");
446
+ const minute = part("minute");
447
+ if (!year || !month || !day || !hour || !minute) throw new Error("incomplete date parts");
448
+ // `longOffset` renders "GMT+09:00", or a bare "GMT" at zero offset.
449
+ const offset = part("timeZoneName").replace(/^GMT$/, "UTC+00:00").replace(/^GMT/, "UTC");
450
+ const weekday = part("weekday");
451
+ return {
452
+ date: weekday ? `${year}-${month}-${day} (${weekday})` : `${year}-${month}-${day}`,
453
+ time: `${hour}:${minute} ${offset}${zone ? ` (${zone})` : ""}`.trim(),
454
+ };
455
+ } catch (error) {
456
+ logger.warn("Could not resolve host timezone for volatile project context; falling back to UTC", {
457
+ error: String(error),
458
+ });
459
+ const fallbackNow = Number.isFinite(now.getTime()) ? now : new Date(0);
460
+ const iso = fallbackNow.toISOString();
461
+ return { date: iso.slice(0, 10), time: `${iso.slice(11, 16)} UTC+00:00 (UTC)` };
462
+ }
463
+ }
464
+
408
465
  export interface BuildVolatileProjectContextOptions {
409
466
  cwd?: string;
467
+ /** Clock source for the rendered local date/time. Default: `new Date()`. */
468
+ now?: Date;
469
+ /** Trusted/test-only override in the derived `YYYY-MM-DD (Www)` or `YYYY-MM-DD` format. */
410
470
  date?: string;
471
+ /** Trusted/test-only override in the derived `HH:MM UTC±HH:MM (IANA/Zone)` format. */
472
+ localTime?: string;
411
473
  workspaceTree?: WorkspaceTree;
412
474
  }
413
475
 
476
+ const LOCAL_DATE_OVERRIDE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})(?: \(([A-Z][a-z]{2})\))?$/u;
477
+ const LOCAL_TIME_OVERRIDE_PATTERN =
478
+ /^(\d{2}):(\d{2}) UTC([+-])(\d{2}):(\d{2}) \(([A-Za-z0-9_+-]+(?:\/[A-Za-z0-9_+-]+)*)\)$/u;
479
+
480
+ function isValidLocalDateOverride(value: string): boolean {
481
+ const match = LOCAL_DATE_OVERRIDE_PATTERN.exec(value);
482
+ if (!match) return false;
483
+ const year = Number(match[1]);
484
+ const month = Number(match[2]);
485
+ const day = Number(match[3]);
486
+ const instant = new Date(Date.UTC(2000, month - 1, day));
487
+ instant.setUTCFullYear(year);
488
+ if (instant.getUTCFullYear() !== year || instant.getUTCMonth() !== month - 1 || instant.getUTCDate() !== day)
489
+ return false;
490
+ const weekday = match[4];
491
+ if (!weekday) return true;
492
+ return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][instant.getUTCDay()] === weekday;
493
+ }
494
+
495
+ function isValidLocalTimeOverride(value: string): boolean {
496
+ const match = LOCAL_TIME_OVERRIDE_PATTERN.exec(value);
497
+ if (!match) return false;
498
+ const hour = Number(match[1]);
499
+ const minute = Number(match[2]);
500
+ const offsetHour = Number(match[4]);
501
+ const offsetMinute = Number(match[5]);
502
+ return hour < 24 && minute < 60 && offsetHour < 24 && offsetMinute < 60;
503
+ }
504
+
414
505
  export function buildVolatileProjectContext(options: BuildVolatileProjectContextOptions = {}): string {
415
506
  const resolvedCwd = options.cwd ?? getProjectDir();
416
- const date = options.date ?? new Date().toISOString().slice(0, 10);
507
+ const local = getLocalTimeContext(options.now ?? new Date());
508
+ const date = escapePromptMetadata(
509
+ options.date && isValidLocalDateOverride(options.date) ? options.date : local.date,
510
+ );
511
+ const localTime = escapePromptMetadata(
512
+ options.localTime && isValidLocalTimeOverride(options.localTime) ? options.localTime : local.time,
513
+ );
417
514
  return prompt
418
515
  .render(volatileProjectContextTemplate, {
419
516
  date,
517
+ localTime,
420
518
  cwd: escapePromptMetadata(shortenPath(resolvedCwd.replace(/\\/g, "/"))),
421
519
  workspaceTree: {
422
520
  ...(options.workspaceTree ?? {
@@ -556,8 +654,9 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
556
654
  }
557
655
  }
558
656
 
559
- const date = new Date().toISOString().slice(0, 10);
560
- const dateTime = date;
657
+ // Date/time deliberately absent: volatile clock facts live in the per-turn
658
+ // volatile project context (see buildVolatileProjectContext), never in this
659
+ // cached stable prefix.
561
660
  const promptCwd = shortenPath(resolvedCwd.replace(/\\/g, "/"));
562
661
 
563
662
  // Build tool metadata for system prompt rendering
@@ -618,8 +717,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
618
717
  workspaceTree,
619
718
  rules: rules ?? [],
620
719
  alwaysApplyRules: sanitizedAlwaysApplyRules,
621
- date,
622
- dateTime,
623
720
  cwd: promptCwd,
624
721
  intentTracing: !!intentField,
625
722
  intentField: intentField ?? "",
@@ -9,9 +9,8 @@
9
9
  * Agent files use markdown with YAML frontmatter.
10
10
  */
11
11
  import * as fs from "node:fs/promises";
12
- import * as os from "node:os";
13
12
  import * as path from "node:path";
14
- import { logger } from "@gajae-code/utils";
13
+ import { getTrustedHomeDir, logger } from "@gajae-code/utils";
15
14
  import { isProviderEnabled } from "../capability";
16
15
  import { findAllNearestProjectConfigDirs, getConfigDirs } from "../config";
17
16
  import type { Settings } from "../config/settings";
@@ -62,7 +61,7 @@ async function loadAgentsFromDir(dir: string, source: AgentSource): Promise<Agen
62
61
  */
63
62
  export async function discoverAgents(
64
63
  cwd: string,
65
- home: string = os.homedir(),
64
+ home: string = getTrustedHomeDir(),
66
65
  activeSettings?: Settings,
67
66
  ): Promise<DiscoveryResult> {
68
67
  const resolvedCwd = path.resolve(cwd);