@gajae-code/coding-agent 0.18.11 → 0.19.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 (462) hide show
  1. package/CHANGELOG.md +760 -0
  2. package/dist/types/async/job-manager.d.ts +7 -3
  3. package/dist/types/async/result-formatting.d.ts +2 -0
  4. package/dist/types/autoresearch/git.d.ts +1 -1
  5. package/dist/types/capability/fs.d.ts +2 -2
  6. package/dist/types/capability/rule.d.ts +7 -2
  7. package/dist/types/cli/accounts-cli.d.ts +2 -2
  8. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  9. package/dist/types/cli/args.d.ts +7 -2
  10. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  11. package/dist/types/cli/customize-doctor.d.ts +4 -3
  12. package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
  13. package/dist/types/cli/install-activation.d.ts +10 -4
  14. package/dist/types/cli/public-command-errors.d.ts +2 -0
  15. package/dist/types/cli/root-flags.d.ts +0 -12
  16. package/dist/types/cli/update-cli.d.ts +21 -3
  17. package/dist/types/cli-main.d.ts +0 -6
  18. package/dist/types/cli-ordinary.d.ts +2 -2
  19. package/dist/types/commands/accounts.d.ts +2 -1
  20. package/dist/types/commands/launch.d.ts +0 -6
  21. package/dist/types/commands/migrate.d.ts +1 -1
  22. package/dist/types/commands/sdk.d.ts +13 -0
  23. package/dist/types/commit/changelog/index.d.ts +1 -0
  24. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  25. package/dist/types/config/autorouting-tier-map.d.ts +53 -1
  26. package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
  27. package/dist/types/config/config-hot-reload.d.ts +44 -0
  28. package/dist/types/config/file-lock.d.ts +52 -3
  29. package/dist/types/config/model-discovery-manager.d.ts +2 -0
  30. package/dist/types/config/model-profile-activation.d.ts +79 -9
  31. package/dist/types/config/model-registry.d.ts +72 -8
  32. package/dist/types/config/model-resolver.d.ts +7 -4
  33. package/dist/types/config/models-config-schema.d.ts +10 -0
  34. package/dist/types/config/settings-schema.d.ts +57 -4
  35. package/dist/types/config/settings.d.ts +33 -1
  36. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  37. package/dist/types/customization/import.d.ts +2 -0
  38. package/dist/types/customization/types.d.ts +1 -1
  39. package/dist/types/dap/config.d.ts +8 -4
  40. package/dist/types/dap/session.d.ts +11 -2
  41. package/dist/types/debug/system-info.d.ts +6 -1
  42. package/dist/types/discovery/helpers.d.ts +7 -2
  43. package/dist/types/discovery/mcp-compat.d.ts +3 -1
  44. package/dist/types/edit/index.d.ts +5 -0
  45. package/dist/types/edit/modes/patch.d.ts +14 -0
  46. package/dist/types/edit/modes/replace.d.ts +3 -0
  47. package/dist/types/eval/py/runtime.d.ts +9 -0
  48. package/dist/types/exec/bash-executor.d.ts +12 -0
  49. package/dist/types/export/ttsr.d.ts +19 -0
  50. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  51. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  52. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
  53. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  54. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  55. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  56. package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
  57. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  58. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  59. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  60. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  61. package/dist/types/hindsight/content.d.ts +5 -6
  62. package/dist/types/internal-urls/types.d.ts +2 -0
  63. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  64. package/dist/types/main.d.ts +23 -18
  65. package/dist/types/migrate/action-planner.d.ts +10 -1
  66. package/dist/types/migrate/adapters/codex.d.ts +2 -1
  67. package/dist/types/migrate/adapters/index.d.ts +42 -1
  68. package/dist/types/migrate/executor.d.ts +3 -1
  69. package/dist/types/migrate/hook-module.d.ts +23 -0
  70. package/dist/types/migrate/report.d.ts +2 -1
  71. package/dist/types/migrate/types.d.ts +82 -3
  72. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  73. package/dist/types/modes/components/model-selector.d.ts +43 -0
  74. package/dist/types/modes/components/settings-selector.d.ts +6 -0
  75. package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
  76. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  77. package/dist/types/modes/theme/theme.d.ts +1 -0
  78. package/dist/types/modes/utils/host-status.d.ts +15 -11
  79. package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
  80. package/dist/types/runtime-mcp/manager.d.ts +7 -0
  81. package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
  82. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  83. package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
  84. package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
  85. package/dist/types/sdk/broker/broker.d.ts +29 -2
  86. package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
  87. package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
  88. package/dist/types/sdk/broker/ensure.d.ts +141 -9
  89. package/dist/types/sdk/broker/hop.d.ts +45 -0
  90. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
  91. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  92. package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
  93. package/dist/types/sdk/broker/session-index.d.ts +6 -1
  94. package/dist/types/sdk/broker/transport.d.ts +2 -0
  95. package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
  96. package/dist/types/sdk/bus/index.d.ts +44 -0
  97. package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
  98. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
  99. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  100. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  101. package/dist/types/sdk/cli/session-cli.d.ts +0 -1
  102. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  103. package/dist/types/sdk/host/events.d.ts +4 -0
  104. package/dist/types/sdk/host/host.d.ts +2 -1
  105. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +40 -8
  107. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  109. package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
  111. package/dist/types/sdk/prompt-status.d.ts +11 -4
  112. package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
  113. package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
  114. package/dist/types/sdk/session-list.d.ts +1 -1
  115. package/dist/types/session/agent-session.d.ts +107 -9
  116. package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
  117. package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
  118. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
  119. package/dist/types/session/messages.d.ts +5 -1
  120. package/dist/types/session/session-manager.d.ts +29 -3
  121. package/dist/types/session/startup-auth-config.d.ts +24 -2
  122. package/dist/types/session/streaming-output.d.ts +11 -0
  123. package/dist/types/session/terminal-abort.d.ts +3 -1
  124. package/dist/types/session-import/service.d.ts +1 -1
  125. package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
  126. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  127. package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
  128. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  129. package/dist/types/system-prompt.d.ts +3 -2
  130. package/dist/types/task/executor.d.ts +19 -2
  131. package/dist/types/task/role-body.d.ts +11 -0
  132. package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
  133. package/dist/types/tools/atomic-file-write.d.ts +9 -7
  134. package/dist/types/tools/awaiting-input.d.ts +28 -0
  135. package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
  136. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  137. package/dist/types/tools/bash.d.ts +7 -0
  138. package/dist/types/tools/descriptors.d.ts +15 -2
  139. package/dist/types/tools/fetch.d.ts +7 -1
  140. package/dist/types/tools/index.d.ts +2 -0
  141. package/dist/types/tools/irc.d.ts +5 -3
  142. package/dist/types/tools/python.d.ts +1 -1
  143. package/dist/types/tools/report-issue.d.ts +41 -0
  144. package/dist/types/tools/subagent.d.ts +23 -0
  145. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  146. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  147. package/dist/types/tools/write.d.ts +2 -0
  148. package/dist/types/utils/file-mentions.d.ts +5 -0
  149. package/dist/types/utils/image-limits.d.ts +7 -0
  150. package/dist/types/utils/image-loading.d.ts +0 -1
  151. package/dist/types/utils/open.d.ts +0 -1
  152. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  153. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  154. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  155. package/dist/types/utils/title-generator.d.ts +13 -3
  156. package/dist/types/web/scrapers/types.d.ts +8 -1
  157. package/dist/types/web/search/providers/codex.d.ts +6 -7
  158. package/dist/types/web/search/providers/insane.d.ts +7 -1
  159. package/dist/types/web/search/providers/searxng.d.ts +5 -0
  160. package/package.json +10 -9
  161. package/scripts/acp-conformance-agent.ts +12 -2
  162. package/scripts/build-binary.ts +2 -1
  163. package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
  164. package/scripts/generate-docs-index.ts +8 -1
  165. package/scripts/generate-sdk-operation-inventory.ts +38 -4
  166. package/src/async/job-manager.ts +356 -56
  167. package/src/async/result-formatting.ts +40 -0
  168. package/src/autoresearch/git.ts +9 -2
  169. package/src/autoresearch/prompts/prompt-setup.md +1 -1
  170. package/src/capability/fs.ts +58 -22
  171. package/src/capability/index.ts +2 -2
  172. package/src/capability/rule.ts +11 -2
  173. package/src/cli/accounts-cli.ts +176 -8
  174. package/src/cli/append-system-prompt.ts +12 -0
  175. package/src/cli/args.ts +11 -7
  176. package/src/cli/auth-broker-cli.ts +21 -11
  177. package/src/cli/customize-doctor-precedence.ts +31 -0
  178. package/src/cli/customize-doctor.ts +49 -39
  179. package/src/cli/doctor/install-repairs.ts +35 -5
  180. package/src/cli/fast-help.ts +1 -3
  181. package/src/cli/install-activation.ts +47 -4
  182. package/src/cli/migrate-cli.ts +19 -6
  183. package/src/cli/public-command-errors.ts +6 -0
  184. package/src/cli/root-flags.ts +2 -4
  185. package/src/cli/stats-cli.ts +9 -5
  186. package/src/cli/update-cli.ts +471 -13
  187. package/src/cli-ordinary.ts +7 -2
  188. package/src/commands/accounts.ts +10 -5
  189. package/src/commands/harness.ts +25 -15
  190. package/src/commands/migrate.ts +3 -3
  191. package/src/commands/sdk.ts +277 -50
  192. package/src/commands/ultragoal.ts +16 -8
  193. package/src/commit/agentic/agent.ts +6 -6
  194. package/src/commit/analysis/conventional.ts +14 -5
  195. package/src/commit/changelog/index.ts +39 -1
  196. package/src/commit/map-reduce/index.ts +7 -7
  197. package/src/commit/message.ts +10 -3
  198. package/src/commit/pipeline.ts +12 -12
  199. package/src/config/autorouting-tier-map.ts +50 -1
  200. package/src/config/config-file.ts +106 -3
  201. package/src/config/config-hot-reload-trust.ts +148 -0
  202. package/src/config/config-hot-reload.ts +742 -0
  203. package/src/config/file-lock.ts +1615 -328
  204. package/src/config/keybindings.ts +154 -7
  205. package/src/config/model-bindings-applier.ts +85 -47
  206. package/src/config/model-discovery-manager.ts +10 -0
  207. package/src/config/model-profile-activation.ts +889 -148
  208. package/src/config/model-registry.ts +1706 -205
  209. package/src/config/model-resolver.ts +52 -22
  210. package/src/config/models-config-schema.ts +6 -0
  211. package/src/config/settings-schema.ts +73 -4
  212. package/src/config/settings.ts +460 -24
  213. package/src/coordinator-mcp/policy.ts +26 -1
  214. package/src/coordinator-mcp/server.ts +37 -2
  215. package/src/crash/sanitize.ts +7 -1
  216. package/src/customization/import.ts +279 -8
  217. package/src/customization/inventory.ts +24 -8
  218. package/src/customization/types.ts +1 -0
  219. package/src/dap/client.ts +3 -2
  220. package/src/dap/config.ts +31 -11
  221. package/src/dap/defaults.json +1 -1
  222. package/src/dap/session.ts +161 -36
  223. package/src/debug/crash-diagnostics.ts +89 -6
  224. package/src/debug/report-bundle.ts +90 -2
  225. package/src/debug/system-info.ts +8 -3
  226. package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
  227. package/src/discovery/agents-md.ts +1 -1
  228. package/src/discovery/builtin.ts +4 -3
  229. package/src/discovery/helpers.ts +25 -16
  230. package/src/discovery/mcp-compat.ts +120 -17
  231. package/src/edit/apply-patch/index.ts +12 -1
  232. package/src/edit/index.ts +204 -22
  233. package/src/edit/modes/patch.ts +214 -8
  234. package/src/edit/modes/replace.ts +136 -0
  235. package/src/eval/js/context-manager.ts +29 -3
  236. package/src/eval/js/worker-entry.ts +51 -18
  237. package/src/eval/py/executor.ts +14 -4
  238. package/src/eval/py/runner.py +144 -5
  239. package/src/eval/py/runtime.ts +44 -4
  240. package/src/exec/bash-executor.ts +81 -4
  241. package/src/exec/bash-shell-guardian.ts +4 -1
  242. package/src/exec/bash-shell-supervisor.ts +8 -4
  243. package/src/exec/isolated-shell.ts +5 -1
  244. package/src/exec/non-interactive-env.ts +0 -1
  245. package/src/export/html/index.ts +86 -6
  246. package/src/export/ttsr.ts +176 -1
  247. package/src/extensibility/custom-tools/wrapper.ts +3 -1
  248. package/src/extensibility/extensions/loader.ts +10 -1
  249. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  250. package/src/extensibility/extensions/types.ts +5 -1
  251. package/src/extensibility/hooks/loader.ts +10 -2
  252. package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
  253. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  254. package/src/extensibility/slash-commands.ts +5 -0
  255. package/src/gjc-runtime/gc-runtime.ts +37 -7
  256. package/src/gjc-runtime/launch-tmux.ts +30 -15
  257. package/src/gjc-runtime/launch-worktree.test.ts +148 -0
  258. package/src/gjc-runtime/launch-worktree.ts +101 -14
  259. package/src/gjc-runtime/managed-owner-admission.ts +46 -2
  260. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  261. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  262. package/src/gjc-runtime/session-resolution.ts +38 -6
  263. package/src/gjc-runtime/session-state-lock.ts +59 -14
  264. package/src/gjc-runtime/state-runtime.ts +23 -19
  265. package/src/gjc-runtime/state-writer.ts +109 -5
  266. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  267. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  268. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  269. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  270. package/src/harness-control-plane/finalize.ts +11 -0
  271. package/src/harness-control-plane/receipt-spool.ts +105 -28
  272. package/src/harness-control-plane/storage.ts +124 -6
  273. package/src/hindsight/content.ts +116 -16
  274. package/src/hindsight/mental-models.ts +22 -10
  275. package/src/hindsight/state.ts +7 -4
  276. package/src/hooks/native-skill-hook.ts +17 -12
  277. package/src/internal-urls/artifact-protocol.ts +3 -1
  278. package/src/internal-urls/docs-index.generated.ts +23 -22
  279. package/src/internal-urls/local-protocol.ts +167 -8
  280. package/src/internal-urls/types.ts +2 -0
  281. package/src/lsp/client.ts +2 -1
  282. package/src/lsp/clients/biome-client.ts +2 -0
  283. package/src/lsp/clients/swiftlint-client.ts +2 -0
  284. package/src/lsp/index.ts +29 -0
  285. package/src/lsp/workspace-path.ts +23 -0
  286. package/src/main.ts +330 -92
  287. package/src/memories/index.ts +25 -2
  288. package/src/migrate/action-planner.ts +574 -2
  289. package/src/migrate/adapters/claude-code.ts +162 -5
  290. package/src/migrate/adapters/codex.ts +77 -7
  291. package/src/migrate/adapters/index.ts +258 -5
  292. package/src/migrate/adapters/opencode.ts +10 -1
  293. package/src/migrate/executor.ts +222 -7
  294. package/src/migrate/hook-module.ts +193 -0
  295. package/src/migrate/report.ts +63 -2
  296. package/src/migrate/types.ts +84 -4
  297. package/src/modes/DESIGN.md +51 -0
  298. package/src/modes/acp/acp-agent.ts +10 -1
  299. package/src/modes/acp/permission-mode.ts +18 -1
  300. package/src/modes/components/agent-dashboard.ts +20 -4
  301. package/src/modes/components/assistant-message.ts +55 -1
  302. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  303. package/src/modes/components/customization/import-wizard.ts +29 -3
  304. package/src/modes/components/model-selector.ts +157 -103
  305. package/src/modes/components/settings-selector.ts +103 -37
  306. package/src/modes/components/welcome.ts +3 -1
  307. package/src/modes/controllers/command-controller.ts +300 -9
  308. package/src/modes/controllers/event-controller.ts +1 -1
  309. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  310. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  311. package/src/modes/controllers/input-controller.ts +2 -0
  312. package/src/modes/controllers/plan-mode-controller.ts +66 -11
  313. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  314. package/src/modes/controllers/selector-controller.ts +76 -7
  315. package/src/modes/interactive-mode.ts +123 -4
  316. package/src/modes/prompt-suggestion-controller.ts +1 -1
  317. package/src/modes/runtime-init.ts +5 -1
  318. package/src/modes/theme/theme.ts +15 -0
  319. package/src/modes/utils/host-status.ts +21 -10
  320. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  321. package/src/prompts/system/irc-incoming.md +3 -1
  322. package/src/prompts/tools/debug.md +1 -1
  323. package/src/prompts/tools/irc.md +1 -1
  324. package/src/prompts/tools/read.md +1 -1
  325. package/src/prompts/tools/report-issue.md +13 -0
  326. package/src/prompts/tools/task.md +1 -1
  327. package/src/runtime-credential-selector.ts +2 -2
  328. package/src/runtime-mcp/content-limits.ts +7 -0
  329. package/src/runtime-mcp/manager-pool.test.ts +5 -4
  330. package/src/runtime-mcp/manager.ts +40 -3
  331. package/src/runtime-mcp/oauth-flow.ts +32 -6
  332. package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
  333. package/src/runtime-mcp/project-host-values.ts +12 -8
  334. package/src/runtime-mcp/render.ts +16 -1
  335. package/src/runtime-mcp/tool-bridge.ts +194 -21
  336. package/src/runtime-mcp/transports/stdio.ts +2 -1
  337. package/src/sdk/broker/broker-exit.ts +10 -5
  338. package/src/sdk/broker/broker.ts +484 -85
  339. package/src/sdk/broker/daemon-entry.ts +338 -116
  340. package/src/sdk/broker/doctor-restart.ts +2 -1
  341. package/src/sdk/broker/ensure.ts +1214 -268
  342. package/src/sdk/broker/hop.ts +286 -0
  343. package/src/sdk/broker/lifecycle-ledger.ts +38 -11
  344. package/src/sdk/broker/lifecycle.ts +102 -6
  345. package/src/sdk/broker/lock-host-identity.ts +29 -0
  346. package/src/sdk/broker/session-index.ts +91 -16
  347. package/src/sdk/broker/transport.ts +17 -1
  348. package/src/sdk/bus/chat-command-policy.ts +16 -3
  349. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  350. package/src/sdk/bus/index.ts +460 -35
  351. package/src/sdk/bus/reconciliation-store.ts +201 -8
  352. package/src/sdk/bus/session-control.ts +2 -0
  353. package/src/sdk/bus/slack-daemon.ts +105 -9
  354. package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
  355. package/src/sdk/bus/telegram-daemon.ts +10 -2
  356. package/src/sdk/bus/threaded-render.ts +20 -1
  357. package/src/sdk/cli/session-cli.ts +23 -6
  358. package/src/sdk/host/control/dispatch.ts +13 -1
  359. package/src/sdk/host/control/operations.ts +10 -1
  360. package/src/sdk/host/events.ts +67 -4
  361. package/src/sdk/host/host.ts +8 -2
  362. package/src/sdk/host/prompt-image-upload.ts +438 -0
  363. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  364. package/src/sdk/host/session-runtime.test.ts +1737 -129
  365. package/src/sdk/host/session-runtime.ts +684 -83
  366. package/src/sdk/host/surface-policy.ts +4 -0
  367. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  368. package/src/sdk/host/websocket-transport.ts +19 -7
  369. package/src/sdk/lifecycle/service.ts +15 -1
  370. package/src/sdk/prompt-deadline-flush.ts +78 -5
  371. package/src/sdk/prompt-deadline-manager.ts +68 -14
  372. package/src/sdk/prompt-status.ts +6 -4
  373. package/src/sdk/protocol/adapter-validation.ts +81 -12
  374. package/src/sdk/protocol/operation-inventory.generated.json +261 -2
  375. package/src/sdk/protocol/operation-registry.ts +33 -3
  376. package/src/sdk/reconciliation-extensions.ts +1 -0
  377. package/src/sdk/session-list.ts +1 -0
  378. package/src/sdk/session.ts +237 -97
  379. package/src/session/agent-session.ts +2726 -479
  380. package/src/session/history-storage.ts +182 -12
  381. package/src/session/internal/managed-session-scope.ts +173 -57
  382. package/src/session/internal/managed-session-storage.ts +85 -12
  383. package/src/session/internal/native-publish-outcome.ts +5 -1
  384. package/src/session/messages.ts +47 -6
  385. package/src/session/session-manager.ts +326 -19
  386. package/src/session/startup-auth-config.ts +116 -8
  387. package/src/session/streaming-output.ts +262 -4
  388. package/src/session/terminal-abort.ts +14 -2
  389. package/src/session-import/redact.ts +53 -2
  390. package/src/session-import/service.ts +1 -0
  391. package/src/skill-state/workflow-mutation-guard.ts +2145 -206
  392. package/src/slash-commands/builtin-registry.ts +21 -6
  393. package/src/slash-commands/helpers/credential-switch.ts +10 -5
  394. package/src/slash-commands/helpers/mcp.ts +11 -3
  395. package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
  396. package/src/ssh/ssh-executor.ts +9 -2
  397. package/src/stt/recorder.ts +26 -9
  398. package/src/system-prompt.ts +61 -16
  399. package/src/task/executor.ts +115 -20
  400. package/src/task/index.ts +389 -175
  401. package/src/task/role-body.ts +84 -0
  402. package/src/task/token-log-mode.test.ts +162 -0
  403. package/src/task/token-log.ts +91 -2
  404. package/src/tools/archive-reader.ts +650 -37
  405. package/src/tools/ask.ts +1 -0
  406. package/src/tools/ast-edit-write-path.ts +64 -0
  407. package/src/tools/ast-edit.ts +18 -1
  408. package/src/tools/ast-grep.ts +7 -2
  409. package/src/tools/atomic-file-write.ts +46 -15
  410. package/src/tools/auto-generated-guard.ts +15 -3
  411. package/src/tools/awaiting-input.ts +114 -0
  412. package/src/tools/bash-allowed-prefixes.ts +214 -6
  413. package/src/tools/bash-interactive.ts +14 -2
  414. package/src/tools/bash-skill-urls.ts +58 -5
  415. package/src/tools/bash.ts +113 -26
  416. package/src/tools/browser/tab-worker.ts +39 -13
  417. package/src/tools/browser.ts +17 -7
  418. package/src/tools/cron.ts +10 -1
  419. package/src/tools/debug.ts +16 -8
  420. package/src/tools/descriptors.test.ts +44 -0
  421. package/src/tools/descriptors.ts +50 -6
  422. package/src/tools/eval.ts +15 -0
  423. package/src/tools/fetch.ts +93 -4
  424. package/src/tools/gh.ts +27 -12
  425. package/src/tools/image-gen.ts +20 -6
  426. package/src/tools/index.ts +2 -0
  427. package/src/tools/irc.ts +5 -3
  428. package/src/tools/monitor.ts +9 -1
  429. package/src/tools/output-meta.ts +74 -5
  430. package/src/tools/python.ts +1 -1
  431. package/src/tools/read.ts +26 -37
  432. package/src/tools/recipe/runner.ts +23 -12
  433. package/src/tools/render-utils.ts +2 -2
  434. package/src/tools/report-issue.ts +184 -0
  435. package/src/tools/search.ts +3 -2
  436. package/src/tools/ssh.ts +274 -8
  437. package/src/tools/subagent-render.ts +114 -12
  438. package/src/tools/subagent.ts +118 -5
  439. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  440. package/src/tools/todo-write.ts +41 -3
  441. package/src/tools/tool-catalog.generated.ts +32 -4
  442. package/src/tools/ultragoal-ask-guard.ts +13 -1
  443. package/src/tools/write.ts +42 -3
  444. package/src/tui/tree-list.ts +15 -1
  445. package/src/utils/commit-message-generator.ts +12 -4
  446. package/src/utils/external-editor.ts +21 -7
  447. package/src/utils/file-mentions.ts +75 -7
  448. package/src/utils/git.ts +38 -7
  449. package/src/utils/image-limits.ts +8 -0
  450. package/src/utils/image-loading.ts +1 -1
  451. package/src/utils/open.ts +73 -4
  452. package/src/utils/pasted-image-loading.ts +9 -8
  453. package/src/utils/prompt-suggestion.ts +5 -4
  454. package/src/utils/shell-snapshot.ts +9 -1
  455. package/src/utils/title-generator.ts +72 -22
  456. package/src/web/scrapers/index.ts +3 -1
  457. package/src/web/scrapers/types.ts +8 -0
  458. package/src/web/scrapers/youtube.ts +3 -2
  459. package/src/web/search/index.ts +2 -1
  460. package/src/web/search/providers/codex.ts +29 -32
  461. package/src/web/search/providers/insane.ts +91 -28
  462. package/src/web/search/providers/searxng.ts +62 -10
@@ -4,7 +4,12 @@
4
4
  * Manages rules that get injected mid-stream when their condition pattern matches
5
5
  * the agent's output. When a match occurs, the stream is aborted, the rule is
6
6
  * injected as a system reminder, and the request is retried.
7
+ *
8
+ * Rules may also carry `astCondition` ast-grep patterns, which are evaluated
9
+ * against the source a write/edit tool call introduces, before it executes.
7
10
  */
11
+ import * as path from "node:path";
12
+ import { AstMatchStrictness, astMatch } from "@gajae-code/natives";
8
13
  import { logger } from "@gajae-code/utils";
9
14
  import type { Rule } from "../capability/rule";
10
15
  import type { TtsrSettings } from "../config/settings";
@@ -22,6 +27,14 @@ export interface TtsrMatchContext {
22
27
  streamKey?: string;
23
28
  }
24
29
 
30
+ /** An astCondition pattern parse issue observed while checking a target language. */
31
+ export interface TtsrAstConditionDiagnostic {
32
+ ruleName: string;
33
+ lang: string;
34
+ patterns: string[];
35
+ parseErrors: string[];
36
+ }
37
+
25
38
  interface ToolScope {
26
39
  toolName?: string;
27
40
  pathGlob?: Bun.Glob;
@@ -39,6 +52,7 @@ interface TtsrEntry {
39
52
  rule: Rule;
40
53
  conditions: RegExp[];
41
54
  conditionWindows: Array<number | undefined>;
55
+ astConditions?: string[];
42
56
 
43
57
  scope: TtsrScope;
44
58
  globalPathGlobs?: Bun.Glob[];
@@ -69,6 +83,7 @@ const DEFAULT_SETTINGS: Required<TtsrSettings> = {
69
83
 
70
84
  const SAFE_REGEX_WINDOW_CAP = 4096;
71
85
  const SAFE_REGEX_WINDOW_MULTIPLIER = 4;
86
+ const SOURCE_SYNTAX_PARSE_ERROR = "parse error (syntax tree contains error nodes)";
72
87
 
73
88
  const DEFAULT_SCOPE: TtsrScope = {
74
89
  allowText: true,
@@ -81,7 +96,9 @@ export class TtsrManager {
81
96
  readonly #settings: Required<TtsrSettings>;
82
97
  readonly #rules = new Map<string, TtsrEntry>();
83
98
  readonly #injectionRecords = new Map<string, InjectionRecord>();
99
+ readonly #astConditionDiagnostics = new Map<string, TtsrAstConditionDiagnostic>();
84
100
  readonly #buffers = new Map<string, string>();
101
+ readonly #lastAstSnapshots = new Map<string, string>();
85
102
  #messageCount = 0;
86
103
 
87
104
  constructor(settings?: Partial<TtsrSettings>) {
@@ -339,7 +356,10 @@ export class TtsrManager {
339
356
  }
340
357
 
341
358
  const conditions = this.#compileConditions(rule);
342
- if (conditions.length === 0) {
359
+ const astConditions = rule.astCondition ?? [];
360
+
361
+ // Rule must have either regex or AST conditions
362
+ if (conditions.length === 0 && astConditions.length === 0) {
343
363
  return false;
344
364
  }
345
365
 
@@ -357,6 +377,7 @@ export class TtsrManager {
357
377
  rule,
358
378
  conditions,
359
379
  conditionWindows,
380
+ astConditions: astConditions.length > 0 ? astConditions : undefined,
360
381
  scope,
361
382
  globalPathGlobs,
362
383
  });
@@ -364,6 +385,7 @@ export class TtsrManager {
364
385
  logger.debug("TTSR rule registered", {
365
386
  ruleName: rule.name,
366
387
  conditions: rule.condition,
388
+ astConditions: rule.astCondition,
367
389
  scope: rule.scope,
368
390
  globs: rule.globs,
369
391
  });
@@ -446,6 +468,25 @@ export class TtsrManager {
446
468
  return Array.from(this.#injectionRecords.keys());
447
469
  }
448
470
 
471
+ /**
472
+ * Undo a provisional claim whose reminder was never delivered: restore the
473
+ * record the rule had before the claim, or forget it when it had none, so the
474
+ * rule is eligible again exactly as before.
475
+ */
476
+ releaseInjected(ruleName: string, previous: TtsrInjectionRecord | undefined): void {
477
+ const name = ruleName.trim();
478
+ if (name.length === 0) return;
479
+ if (!previous) {
480
+ this.#injectionRecords.delete(name);
481
+ return;
482
+ }
483
+ this.#injectionRecords.set(name, {
484
+ lastInjectedAt: previous.lastInjectedAt,
485
+ repeatMode: previous.repeatMode,
486
+ repeatGap: previous.repeatGap,
487
+ });
488
+ }
489
+
449
490
  /** Get full injected rule records (for persistence). */
450
491
  getInjectedRecords(): TtsrInjectionRecord[] {
451
492
  return Array.from(this.#injectionRecords, ([name, record]) => ({ name, ...record }));
@@ -477,6 +518,7 @@ export class TtsrManager {
477
518
  /** Reset stream buffers (called on new turn). */
478
519
  resetBuffer(): void {
479
520
  this.#buffers.clear();
521
+ this.#lastAstSnapshots.clear();
480
522
  }
481
523
 
482
524
  /** Check if any TTSR rules are registered. */
@@ -509,4 +551,137 @@ export class TtsrManager {
509
551
  getSettings(): Required<TtsrSettings> {
510
552
  return this.#settings;
511
553
  }
554
+
555
+ /** Check if any registered rule carries an `astCondition`. */
556
+ hasAstRules(): boolean {
557
+ if (this.#isDisabled()) {
558
+ return false;
559
+ }
560
+ for (const entry of this.#rules.values()) {
561
+ if (entry.astConditions) {
562
+ return true;
563
+ }
564
+ }
565
+ return false;
566
+ }
567
+
568
+ /** Get observed astCondition pattern parse issues for rule diagnostics. */
569
+ getAstConditionDiagnostics(): TtsrAstConditionDiagnostic[] {
570
+ return Array.from(this.#astConditionDiagnostics.values(), diagnostic => ({
571
+ ...diagnostic,
572
+ patterns: [...diagnostic.patterns],
573
+ parseErrors: [...diagnostic.parseErrors],
574
+ }));
575
+ }
576
+
577
+ /** Derive an ast-grep language alias from the candidate file extensions. */
578
+ #deriveLang(filePaths: string[] | undefined): string | undefined {
579
+ for (const filePath of filePaths ?? []) {
580
+ const ext = path.extname(this.#normalizePath(filePath));
581
+ if (ext.length > 1) return ext.slice(1).toLowerCase();
582
+ }
583
+ return undefined;
584
+ }
585
+
586
+ /** Match the source introduced by finalized tool arguments, before execution. */
587
+ async checkAstSnapshot(snapshot: string, context: TtsrMatchContext, signal?: AbortSignal): Promise<Rule[]> {
588
+ if (this.#isDisabled() || context.source !== "tool" || signal?.aborted) {
589
+ return [];
590
+ }
591
+ const lang = this.#deriveLang(context.filePaths);
592
+ if (!lang) return [];
593
+
594
+ const candidates: TtsrEntry[] = [];
595
+ for (const [name, entry] of this.#rules) {
596
+ if (!entry.astConditions?.length) continue;
597
+ if (
598
+ !this.#canTrigger(name) ||
599
+ !this.#matchesScope(entry, context) ||
600
+ !this.#matchesGlobalPaths(entry, context)
601
+ ) {
602
+ continue;
603
+ }
604
+ candidates.push(entry);
605
+ }
606
+ if (candidates.length === 0) return [];
607
+
608
+ const bufferKey = this.#bufferKey(context);
609
+ if (this.#lastAstSnapshots.get(bufferKey) === snapshot) return [];
610
+ this.#lastAstSnapshots.set(bufferKey, snapshot);
611
+
612
+ const matches: Rule[] = [];
613
+ for (const entry of candidates) {
614
+ if (!entry.astConditions) continue;
615
+ const matched = await this.#astConditionsMatch(entry.rule.name, entry.astConditions, snapshot, lang, signal);
616
+ if (signal?.aborted) return [];
617
+ if (matched) {
618
+ matches.push(entry.rule);
619
+ logger.debug("TTSR ast condition matched", {
620
+ ruleName: entry.rule.name,
621
+ astConditions: entry.rule.astCondition,
622
+ toolName: context.toolName,
623
+ filePaths: context.filePaths,
624
+ });
625
+ }
626
+ }
627
+ return matches;
628
+ }
629
+
630
+ #recordAstConditionDiagnostics(
631
+ ruleName: string,
632
+ patterns: string[],
633
+ lang: string,
634
+ parseErrors: string[] | undefined,
635
+ ): void {
636
+ const patternParseErrors = parseErrors?.filter(parseError => parseError !== SOURCE_SYNTAX_PARSE_ERROR) ?? [];
637
+ if (patternParseErrors.length === 0) return;
638
+
639
+ const key = JSON.stringify([ruleName, lang]);
640
+ if (this.#astConditionDiagnostics.has(key)) return;
641
+
642
+ const diagnostic: TtsrAstConditionDiagnostic = {
643
+ ruleName,
644
+ lang,
645
+ patterns: [...patterns],
646
+ parseErrors: patternParseErrors,
647
+ };
648
+ this.#astConditionDiagnostics.set(key, diagnostic);
649
+ logger.warn("TTSR astCondition pattern is invalid for target language", {
650
+ ruleName,
651
+ lang,
652
+ patterns: diagnostic.patterns,
653
+ parseErrors: diagnostic.parseErrors,
654
+ });
655
+ }
656
+
657
+ async #astConditionsMatch(
658
+ ruleName: string,
659
+ patterns: string[],
660
+ source: string,
661
+ lang: string,
662
+ signal?: AbortSignal,
663
+ ): Promise<boolean> {
664
+ try {
665
+ const result = await astMatch({
666
+ patterns,
667
+ source,
668
+ lang,
669
+ strictness: AstMatchStrictness.Smart,
670
+ limit: 1,
671
+ signal,
672
+ });
673
+ if (!signal?.aborted) {
674
+ this.#recordAstConditionDiagnostics(ruleName, patterns, lang, result.parseErrors);
675
+ }
676
+ return result.totalMatches > 0;
677
+ } catch (error) {
678
+ logger.warn("TTSR ast match failed, treating as no match", {
679
+ ruleName,
680
+ patterns,
681
+ lang,
682
+ error: error instanceof Error ? error.message : String(error),
683
+ });
684
+ return false;
685
+ }
686
+ }
512
687
  }
@@ -21,9 +21,11 @@ export class CustomToolAdapter<TParams extends TSchema = TSchema, TDetails = any
21
21
  private tool: CustomTool<TParams, TDetails>,
22
22
  private getContext: () => CustomToolContext,
23
23
  ) {
24
- applyToolProxy(tool, this);
24
+ // Own these before proxying: with useDefineForClassFields off, the uninitialized fields do not
25
+ // exist yet, so applyToolProxy would install getter-only accessors that reject assignment.
25
26
  this.strict = tool.strict;
26
27
  this.concurrency = tool.concurrency;
28
+ applyToolProxy(tool, this);
27
29
  }
28
30
 
29
31
  execute(
@@ -15,6 +15,7 @@ import { loadCapability } from "../../discovery";
15
15
  import { getExtensionNameFromPath } from "../../discovery/helpers";
16
16
  import type { ExecOptions } from "../../exec/exec";
17
17
  import { execCommand } from "../../exec/exec";
18
+ import type { QueuedRunStartFailure } from "../../session/agent-session";
18
19
  import type { CustomMessage } from "../../session/messages";
19
20
  import { EventBus } from "../../utils/event-bus";
20
21
  import { installLegacyPiSpecifierShim, loadLegacyPiModule } from "../plugins/legacy-pi-compat";
@@ -394,6 +395,7 @@ class ConcreteExtensionAPI implements ExtensionAPI {
394
395
  onPreflightAccepted?: () => void;
395
396
  onPreflightAcceptCommit?: () => void | Promise<void>;
396
397
  onQueuedPromoted?: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
398
+ onQueuedRunStartFailed?: (failure: QueuedRunStartFailure) => void;
397
399
  onDispatchDisposition?: (promotion: { startsOwnRun: boolean }) => void;
398
400
  preflightSignal?: AbortSignal;
399
401
  },
@@ -791,7 +793,14 @@ export async function discoverAndLoadExtensions(
791
793
  ...(options.profileAuthority === undefined ? {} : { profileAuthority: options.profileAuthority }),
792
794
  ...(options.settings === undefined ? {} : { settings: options.settings }),
793
795
  });
794
- for (const ext of discovered.items) {
796
+ // Filter project modules before first-wins name deduplication. `items` has
797
+ // already deduplicated, so a project module could otherwise shadow the
798
+ // same-named user module before being rejected here.
799
+ const seenDiscoveredNames = new Set<string>();
800
+ for (const ext of discovered.all) {
801
+ if (ext._source.provider === "native" && ext.level === "project") continue;
802
+ if (seenDiscoveredNames.has(ext.name)) continue;
803
+ seenDiscoveredNames.add(ext.name);
795
804
  if (ext._source.provider !== "native") continue;
796
805
  addPath(ext.path);
797
806
  }
@@ -1,9 +1,14 @@
1
+ import { ptree } from "@gajae-code/utils";
2
+ import { $credentialEnv, startupProjectEnvSnapshot } from "@gajae-code/utils/env";
3
+ import { canonicalEnvKey } from "@gajae-code/utils/env-file";
4
+ import type { ExecResult } from "../../exec/exec";
1
5
  import type { MCPServerConnection, MCPToolCallResult } from "../../runtime-mcp";
2
6
  import { callTool, connectToServer, disconnectServer } from "../../runtime-mcp";
3
7
  import { createExactPrefixCommandBridge } from "./prefix-command-bridge";
4
8
  import type { ExtensionContext, InputEvent, InputEventResult } from "./types";
5
9
 
6
10
  const OUROBOROS_CLI_ENV = "OUROBOROS_CLI";
11
+ const DEFAULT_OUROBOROS_COMMAND = "ouroboros";
7
12
  const INTERVIEW_COMMAND = "ooo interview";
8
13
  const INTERVIEW_TOOL = "ouroboros_interview";
9
14
  const INTERVIEW_SESSION_PATTERN = /^interview_[A-Za-z0-9_-]+$/;
@@ -23,8 +28,64 @@ interface OuroborosOooBridgeHandler {
23
28
  reset(): Promise<void>;
24
29
  }
25
30
 
31
+ function projectSuppliedOuroborosCli(): boolean {
32
+ const snapshot = startupProjectEnvSnapshot();
33
+ const key = canonicalEnvKey(OUROBOROS_CLI_ENV);
34
+ return Object.hasOwn(snapshot.values, key) || snapshot.dynamic.has(key);
35
+ }
36
+
37
+ /**
38
+ * Executable for `ooo` dispatch and the interview MCP server.
39
+ *
40
+ * The string is spawned directly, so a repository `.env` must not choose it.
41
+ * `$credentialEnv` keeps a launching-shell or user-owned setting and ignores
42
+ * the project dotenv, including a dynamic declaration. Anything else stays the
43
+ * default command name. A rejected project value is removed from `process.env`
44
+ * before either spawn. Prefix dispatch also passes that cleaned object: Bun
45
+ * keeps the startup environment for a spawn that omits `env`, so deleting the
46
+ * key alone would still hand the project value to the child.
47
+ */
26
48
  function resolveOuroborosCommand(): string {
27
- return process.env[OUROBOROS_CLI_ENV]?.trim() || "ouroboros";
49
+ const trusted = $credentialEnv(OUROBOROS_CLI_ENV)?.trim();
50
+ if (trusted) {
51
+ const live = process.env[OUROBOROS_CLI_ENV];
52
+ if (live !== undefined && live.trim() !== trusted) process.env[OUROBOROS_CLI_ENV] = trusted;
53
+ return trusted;
54
+ }
55
+ if (projectSuppliedOuroborosCli()) delete process.env[OUROBOROS_CLI_ENV];
56
+ return DEFAULT_OUROBOROS_COMMAND;
57
+ }
58
+
59
+ function ouroborosSpawnEnv(): Record<string, string> {
60
+ resolveOuroborosCommand();
61
+ const env: Record<string, string> = {};
62
+ for (const key of Object.keys(process.env)) {
63
+ const value = process.env[key];
64
+ if (typeof value === "string") env[key] = value;
65
+ }
66
+ return env;
67
+ }
68
+
69
+ async function dispatchOuroborosCommand(
70
+ command: string,
71
+ args: string[],
72
+ ctx: ExtensionContext,
73
+ execOptions: { timeout?: number },
74
+ ): Promise<ExecResult> {
75
+ const result = await ptree.exec([command, ...args], {
76
+ cwd: ctx.cwd,
77
+ timeout: execOptions.timeout,
78
+ allowNonZero: true,
79
+ allowAbort: true,
80
+ stderr: "full",
81
+ env: ouroborosSpawnEnv(),
82
+ });
83
+ return {
84
+ stdout: result.stdout,
85
+ stderr: result.stderr,
86
+ code: result.exitCode ?? 0,
87
+ killed: Boolean(result.exitError?.aborted),
88
+ };
28
89
  }
29
90
 
30
91
  function interviewArgument(text: string): string | undefined {
@@ -89,6 +150,7 @@ export function createOuroborosOooBridge(options: OuroborosOooBridgeOptions = {}
89
150
  prefix: "ooo",
90
151
  command: resolveOuroborosCommand(),
91
152
  args: ["dispatch", "--runtime", "gjc"],
153
+ dispatch: dispatchOuroborosCommand,
92
154
  });
93
155
 
94
156
  function assertCurrent(generation: number, signal: AbortSignal | undefined): void {
@@ -50,7 +50,7 @@ import type { CustomEditor } from "../../modes/components/custom-editor";
50
50
  import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/workflow-gate-broker";
51
51
  import type { Theme } from "../../modes/theme/theme";
52
52
  import type { ProjectProgressSnapshot } from "../../progress/progress-contract";
53
- import type { AgentSessionEventListener } from "../../session/agent-session";
53
+ import type { AgentSessionEventListener, QueuedRunStartFailure } from "../../session/agent-session";
54
54
  import type {
55
55
  ClientBridge,
56
56
  ClientBridgePermissionOption,
@@ -1347,6 +1347,8 @@ export interface ExtensionAPI {
1347
1347
  onPreflightAccepted?: () => void;
1348
1348
  onPreflightAcceptCommit?: () => void | Promise<void>;
1349
1349
  onQueuedPromoted?: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
1350
+ /** Internal positive refusal before an exact queued invocation can start. */
1351
+ onQueuedRunStartFailed?: (failure: QueuedRunStartFailure) => void;
1350
1352
  onDispatchDisposition?: (promotion: { startsOwnRun: boolean }) => void;
1351
1353
  preflightSignal?: AbortSignal;
1352
1354
  /** Internal SDK correlation owner for an exact queued follow-up. */
@@ -1577,6 +1579,8 @@ export type SendUserMessageHandler = (
1577
1579
  * grants root abort ownership.
1578
1580
  */
1579
1581
  onQueuedPromoted?: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
1582
+ /** Internal positive refusal, not an inference from a dispatch error or missing event. */
1583
+ onQueuedRunStartFailed?: (failure: QueuedRunStartFailure) => void;
1580
1584
  /**
1581
1585
  * Fired synchronously when a plain prompt's dispatch resolves at queue
1582
1586
  * time instead of run end (diverted into an in-flight run's steering).
@@ -346,9 +346,17 @@ export async function discoverAndLoadHooks(configuredPaths: string[], cwd: strin
346
346
  };
347
347
 
348
348
  // 1. Discover hooks via capability API and validate the provider descriptor
349
- // against the canonical model before importing project-controlled code.
349
+ // against the canonical model before importing. A repository
350
+ // `<cwd>/.gjc/hooks` module is not imported. Skip those entries before
351
+ // dedup so a same-named user-agent hook still loads. An explicit path
352
+ // below remains a consent path.
350
353
  const discovered = await loadCapability<Hook>(hookCapability.id, { cwd, providers: ["native"] });
351
- for (const hook of discovered.items) {
354
+ const seenHookKeys = new Set<string>();
355
+ for (const hook of discovered.all) {
356
+ if (hook.level === "project") continue;
357
+ const hookKey = `${hook.type}:${hook.tool}:${hook.name}`;
358
+ if (seenHookKeys.has(hookKey)) continue;
359
+ seenHookKeys.add(hookKey);
352
360
  const convention =
353
361
  hook._source.provider === "native"
354
362
  ? HookSourceConvention.NativeGjc
@@ -10,7 +10,7 @@ import * as https from "node:https";
10
10
  import * as net from "node:net";
11
11
  import * as os from "node:os";
12
12
  import * as path from "node:path";
13
- import { isEnoent, logger } from "@gajae-code/utils";
13
+ import { isEnoent, logger, redactCrashSecrets } from "@gajae-code/utils";
14
14
  import * as git from "../../../utils/git";
15
15
  import { isPrivateOrSpecialAddress, validatePublicHttpUrl } from "../../../web/insane/url-guard";
16
16
 
@@ -461,7 +461,7 @@ async function cloneAndReadCatalog(url: string, cacheDir: string): Promise<Fetch
461
461
  const tmpDir = path.join(cacheDir, `.tmp-clone-${Date.now()}`);
462
462
  await fs.mkdir(cacheDir, { recursive: true });
463
463
 
464
- logger.debug(`[marketplace] cloning ${url} → ${tmpDir}`);
464
+ logger.debug(redactCrashSecrets(`[marketplace] cloning ${url} → ${tmpDir}`));
465
465
  await git.clone(url, tmpDir);
466
466
 
467
467
  const catalogPath = path.join(tmpDir, CATALOG_RELATIVE_PATH);
@@ -15,6 +15,7 @@ import {
15
15
  type NativeDirectoryTreeSnapshot,
16
16
  type NativeExactUnlinkResult,
17
17
  renameDirectoryNoReplacePathAsync,
18
+ renameNoReplacePathAsync,
18
19
  snapshotDirectoryTree,
19
20
  } from "@gajae-code/natives";
20
21
  import { isEnoent, logger, pathIsWithin } from "@gajae-code/utils";
@@ -175,6 +176,13 @@ async function snapshotIfPresent(targetPath: string): Promise<NativeDirectoryTre
175
176
  return snapshot.ok ? snapshot.snapshot : undefined;
176
177
  }
177
178
 
179
+ /** Windows supports directory no-replace through the handle-based path API, not the POSIX directory fallback. */
180
+ function renameDirectoryNoReplace(sourcePath: string, destinationPath: string) {
181
+ return process.platform === "win32"
182
+ ? renameNoReplacePathAsync(sourcePath, destinationPath)
183
+ : renameDirectoryNoReplacePathAsync(sourcePath, destinationPath);
184
+ }
185
+
178
186
  /**
179
187
  * Full identity comparison between two directory tree snapshots. A same-
180
188
  * filesystem `renameat`-class namespace operation changes neither the root's
@@ -850,9 +858,10 @@ export class MarketplaceManager {
850
858
  });
851
859
  if (finalStat?.isSymbolicLink()) throw new MarketplaceRestoreError("final_path_is_symlink");
852
860
  const hadFinal = finalStat !== undefined;
853
- // Single-component name; never renamed elsewhere on collision since the
854
- // native no-replace primitive is what actually refuses an occupied slot.
855
- const backup = `${finalPath}.restore-backup-${process.pid}-${randomBytes(6).toString("hex")}`;
861
+ // Keep the private backup component bounded independently of the canonical
862
+ // cache basename, which can itself be near NAME_MAX through a long version.
863
+ const backupName = `.gjc-restore-backup-${createHash("sha256").update(finalPath).digest("hex")}-${randomBytes(12).toString("hex")}`;
864
+ const backup = path.join(pluginsCacheDir, backupName);
856
865
 
857
866
  const parentRecheck = await fs.lstat(pluginsCacheDir, { bigint: true }).catch(() => undefined);
858
867
  if (!parentRecheck || parentRecheck.dev !== parentBefore.dev || parentRecheck.ino !== parentBefore.ino)
@@ -878,12 +887,29 @@ export class MarketplaceManager {
878
887
  // merely a decision about whether it is safe to proceed. sideEffectStarted
879
888
  // stays false — effects are physical, not procedural.
880
889
  if (!finalSnapshotBefore) throw new MarketplaceRestoreError("final_path_snapshot_unavailable");
881
- const detached = exactRemoveDirectoryTree(finalPath, finalSnapshotBefore, {
882
- dev: parentBefore.dev,
883
- ino: parentBefore.ino,
884
- });
890
+ const detached = exactRemoveDirectoryTree(
891
+ finalPath,
892
+ finalSnapshotBefore,
893
+ {
894
+ dev: parentBefore.dev,
895
+ ino: parentBefore.ino,
896
+ },
897
+ true,
898
+ );
885
899
  const detachedPath = detachedTree(detached);
886
900
  if (!detachedPath) {
901
+ if (detached.code === "restore_failed" && detached.detachedPath) {
902
+ const retainedSnapshot = await snapshotIfPresent(detached.detachedPath).catch(() => undefined);
903
+ const retainedBackupPath =
904
+ retainedSnapshot && sameDirectoryTreeIdentity(finalSnapshotBefore, retainedSnapshot)
905
+ ? detached.detachedPath
906
+ : undefined;
907
+ throw new MarketplaceRestoreError("backup_detach_unverified", {
908
+ sideEffectStarted: true,
909
+ outcome: "uncertain",
910
+ ...(retainedBackupPath ? { retainedBackupPath } : {}),
911
+ });
912
+ }
887
913
  // identity_mismatch means finalPath already changed before we ever
888
914
  // touched it — exactRemoveDirectoryTree refused the mutation entirely,
889
915
  // so this is a pure precheck refusal: nothing moved, sideEffectStarted
@@ -906,7 +932,7 @@ export class MarketplaceManager {
906
932
  // later detach of the same canonical path inside this same transaction (the
907
933
  // rollback path) collides with our own leftover and is misread as a foreign
908
934
  // claim. The rename below is what vacates it; failure is reported, never assumed.
909
- const backupPublish = await renameDirectoryNoReplacePathAsync(detachedPath, backup);
935
+ const backupPublish = await renameDirectoryNoReplace(detachedPath, backup);
910
936
  if (!backupPublish.ok) {
911
937
  // mutationState "not_committed" proves the rename never moved the
912
938
  // object; it is still at the detached path, so that path is real
@@ -962,7 +988,7 @@ export class MarketplaceManager {
962
988
  // devices on cross_device, the dev/ino-stability assumption behind
963
989
  // `stagedSnapshot` breaks silently and every identity-bound check in this
964
990
  // method becomes unsound.
965
- const publish = await renameDirectoryNoReplacePathAsync(token.stagedArtifactPath, finalPath);
991
+ const publish = await renameDirectoryNoReplace(token.stagedArtifactPath, finalPath);
966
992
  // Test seam only (no-op in production): lets a test deterministically
967
993
  // simulate a concurrent second writer republishing finalPath here,
968
994
  // exercising the foreign-owner branch below without weakening it.
@@ -1233,7 +1259,7 @@ export class MarketplaceManager {
1233
1259
  );
1234
1260
  }
1235
1261
  }
1236
- const restorePublish = await renameDirectoryNoReplacePathAsync(backup, finalPath);
1262
+ const restorePublish = await renameDirectoryNoReplace(backup, finalPath);
1237
1263
  if (!restorePublish.ok) {
1238
1264
  // Restore failed: confirm what actually remains at `backup` right now
1239
1265
  // (the no-replace rename may have left it there proven-untouched, or
@@ -116,6 +116,11 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<
116
116
  return cmd;
117
117
  });
118
118
 
119
+ export function getBuiltinSlashCommandsForSession(hasExactMcpControls: boolean): typeof BUILTIN_SLASH_COMMANDS {
120
+ if (hasExactMcpControls) return BUILTIN_SLASH_COMMANDS;
121
+ return BUILTIN_SLASH_COMMANDS.filter(command => command.name !== "mcp");
122
+ }
123
+
119
124
  /**
120
125
  * Represents a custom slash command loaded from a file
121
126
  */
@@ -18,7 +18,15 @@ import * as fsp from "node:fs/promises";
18
18
  import * as os from "node:os";
19
19
  import * as path from "node:path";
20
20
  import { exactRemoveDirectoryTree, exactUnlink, snapshotDirectoryTree } from "@gajae-code/natives";
21
- import { getAgentDir, getBlobsDir, getSessionsDir, isEnoent, VERSION } from "@gajae-code/utils";
21
+ import {
22
+ getAgentDir,
23
+ getBlobsDir,
24
+ getSessionsDir,
25
+ isEnoent,
26
+ loadedProjectEnvSnapshot,
27
+ VERSION,
28
+ } from "@gajae-code/utils";
29
+ import { type ProjectEnvSnapshot, projectEnvSnapshot } from "@gajae-code/utils/env-file";
22
30
  import { getDefault } from "../config/settings-schema";
23
31
  import { listHarnessRootRegistriesForGc } from "../harness-control-plane/storage";
24
32
  import { SessionIndex } from "../sdk/broker/session-index";
@@ -54,6 +62,7 @@ import {
54
62
  } from "./empty-delete-gc";
55
63
  import { buildGcReportText } from "./gc-render";
56
64
  import { collectSessionScopeUsage, type GcSessionScopeUsage, shouldReportSessionScope } from "./gc-session-scope";
65
+ import { trustedCoordinatorEnv } from "./sidecar-bootstrap-env";
57
66
 
58
67
  export type GcStore = "harness_leases" | "file_locks" | "tmux_sessions" | "registry_entries" | "local_roots";
59
68
 
@@ -456,8 +465,21 @@ export function computeExitCode(report: GcReport): number {
456
465
  return 0;
457
466
  }
458
467
 
459
- function resolveGcAgentDir(env: NodeJS.ProcessEnv): string {
460
- return env.GJC_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || getAgentDir();
468
+ /**
469
+ * Operator override only. Same project-dotenv rule as `getAgentDir()`: a value
470
+ * the checkout declares is ignored, and an operator value it does not declare
471
+ * is kept. A repository `.env` must not choose the tree `gjc gc` reclaims.
472
+ *
473
+ * `snapshot` is the dotenv provenance from when that environment was loaded.
474
+ * `process.env` uses the module-load snapshot. A separate env map uses the
475
+ * snapshot taken with it before awaited GC work. Neither path rereads files later.
476
+ */
477
+ function resolveGcAgentDir(env: NodeJS.ProcessEnv, snapshot: ProjectEnvSnapshot): string {
478
+ return (
479
+ trustedCoordinatorEnv("GJC_CODING_AGENT_DIR", env, snapshot) ||
480
+ trustedCoordinatorEnv("PI_CODING_AGENT_DIR", env, snapshot) ||
481
+ getAgentDir()
482
+ );
461
483
  }
462
484
 
463
485
  /**
@@ -572,6 +594,10 @@ export async function runGjcGcCommand(
572
594
  adapters?: GcStoreAdapter[],
573
595
  diskPolicy?: Partial<GcDiskPolicy>,
574
596
  ): Promise<GcRunResult> {
597
+ // process.env was loaded before this module. Reusing that snapshot keeps a
598
+ // project value rejected after its declaration is removed or changed.
599
+ // A caller-supplied env is not that load: bind its cwd files once, here.
600
+ const loadedProjectEnv = env === process.env ? loadedProjectEnvSnapshot() : projectEnvSnapshot(cwd);
575
601
  let parsed: ParsedGcArgs;
576
602
  try {
577
603
  parsed = parseGcArgs(argv);
@@ -656,17 +682,18 @@ export async function runGjcGcCommand(
656
682
  const ordinaryPrune = parsed.prune && (emptyDeleteReceipts?.errors.length ?? 0) === 0;
657
683
  const report = await collectGcReport(resolvedAdapters, ctx, ordinaryPrune);
658
684
  report.operation = parsed.repairSessionIndex ? "repair_session_index" : parsed.prune ? "prune" : "dry_run";
659
- report.session_index = await collectSessionIndexHealth(parsed.repairSessionIndex, resolveGcAgentDir(env));
685
+ const agentDir = resolveGcAgentDir(env, loadedProjectEnv);
686
+ report.session_index = await collectSessionIndexHealth(parsed.repairSessionIndex, agentDir);
660
687
  if (parsed.disk) {
661
688
  report.disk = await collectGcDiskReport({
662
- agentDir: resolveGcAgentDir(env),
689
+ agentDir,
663
690
  env,
664
691
  policy: resolveGcDiskPolicy(diskPolicy),
665
692
  prune: ordinaryPrune,
666
693
  });
667
694
  }
668
695
  if (emptyDeleteReceipts) report.empty_delete_receipts = emptyDeleteReceipts;
669
- const scopeUsage = await collectGcSessionScope(cwd, resolveGcAgentDir(env));
696
+ const scopeUsage = await collectGcSessionScope(cwd, agentDir);
670
697
  if (scopeUsage && shouldReportSessionScope(scopeUsage)) report.session_scope = scopeUsage;
671
698
  const sessionIndexFailed =
672
699
  report.session_index?.status === "corrupt" ||
@@ -2272,7 +2299,10 @@ async function removeGcDiskEntry(
2272
2299
  return { removed: false, reason: "entry_changed" };
2273
2300
  }
2274
2301
  const retainedAtOriginal = !removal.detachedPath || path.resolve(removal.detachedPath) === path.resolve(target);
2275
- if ((removal.code === "permission_denied" || removal.code === "io_error") && retainedAtOriginal) {
2302
+ if (
2303
+ (removal.code === "permission_denied" || removal.code === "access_denied" || removal.code === "io_error") &&
2304
+ retainedAtOriginal
2305
+ ) {
2276
2306
  return { removed: false, reason: "withheld_evidence_incomplete: tree_unreadable", withheld: true };
2277
2307
  }
2278
2308
  return {