@gajae-code/coding-agent 0.18.12 → 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 (305) hide show
  1. package/CHANGELOG.md +467 -0
  2. package/dist/types/capability/rule.d.ts +7 -2
  3. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  4. package/dist/types/cli/args.d.ts +7 -2
  5. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  6. package/dist/types/cli/customize-doctor.d.ts +4 -3
  7. package/dist/types/cli/public-command-errors.d.ts +1 -0
  8. package/dist/types/cli/root-flags.d.ts +0 -12
  9. package/dist/types/cli/update-cli.d.ts +7 -2
  10. package/dist/types/cli-main.d.ts +0 -6
  11. package/dist/types/commands/launch.d.ts +0 -6
  12. package/dist/types/commands/migrate.d.ts +1 -1
  13. package/dist/types/commands/sdk.d.ts +8 -0
  14. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  15. package/dist/types/config/autorouting-tier-map.d.ts +23 -1
  16. package/dist/types/config/config-hot-reload.d.ts +5 -0
  17. package/dist/types/config/file-lock.d.ts +44 -2
  18. package/dist/types/config/model-profile-activation.d.ts +19 -5
  19. package/dist/types/config/model-registry.d.ts +16 -3
  20. package/dist/types/config/model-resolver.d.ts +5 -4
  21. package/dist/types/config/settings-schema.d.ts +11 -1
  22. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  23. package/dist/types/customization/import.d.ts +2 -0
  24. package/dist/types/customization/types.d.ts +1 -1
  25. package/dist/types/dap/session.d.ts +11 -2
  26. package/dist/types/discovery/helpers.d.ts +7 -2
  27. package/dist/types/edit/index.d.ts +5 -0
  28. package/dist/types/edit/modes/patch.d.ts +9 -0
  29. package/dist/types/eval/py/runtime.d.ts +9 -0
  30. package/dist/types/export/ttsr.d.ts +19 -0
  31. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  32. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  33. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  34. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  35. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  36. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  37. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  38. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  39. package/dist/types/hindsight/content.d.ts +5 -6
  40. package/dist/types/internal-urls/types.d.ts +2 -0
  41. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  42. package/dist/types/main.d.ts +23 -18
  43. package/dist/types/migrate/action-planner.d.ts +10 -1
  44. package/dist/types/migrate/adapters/codex.d.ts +1 -1
  45. package/dist/types/migrate/adapters/index.d.ts +35 -3
  46. package/dist/types/migrate/hook-module.d.ts +23 -0
  47. package/dist/types/migrate/report.d.ts +2 -1
  48. package/dist/types/migrate/types.d.ts +69 -2
  49. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  50. package/dist/types/modes/components/model-selector.d.ts +43 -0
  51. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  52. package/dist/types/modes/utils/host-status.d.ts +15 -11
  53. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  54. package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
  55. package/dist/types/sdk/broker/broker.d.ts +22 -1
  56. package/dist/types/sdk/broker/ensure.d.ts +5 -2
  57. package/dist/types/sdk/broker/hop.d.ts +9 -0
  58. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  59. package/dist/types/sdk/broker/session-index.d.ts +2 -0
  60. package/dist/types/sdk/broker/transport.d.ts +2 -0
  61. package/dist/types/sdk/bus/index.d.ts +21 -0
  62. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
  63. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  64. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  65. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  66. package/dist/types/sdk/host/events.d.ts +4 -0
  67. package/dist/types/sdk/host/host.d.ts +2 -1
  68. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  69. package/dist/types/sdk/host/session-runtime.d.ts +8 -8
  70. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  71. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  72. package/dist/types/sdk/prompt-status.d.ts +11 -4
  73. package/dist/types/session/agent-session.d.ts +50 -8
  74. package/dist/types/session/session-manager.d.ts +16 -0
  75. package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
  76. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  77. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  78. package/dist/types/system-prompt.d.ts +3 -2
  79. package/dist/types/task/role-body.d.ts +11 -0
  80. package/dist/types/tools/awaiting-input.d.ts +28 -0
  81. package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
  82. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  83. package/dist/types/tools/bash.d.ts +7 -0
  84. package/dist/types/tools/descriptors.d.ts +13 -2
  85. package/dist/types/tools/fetch.d.ts +6 -0
  86. package/dist/types/tools/irc.d.ts +5 -3
  87. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  88. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  89. package/dist/types/tools/write.d.ts +2 -0
  90. package/dist/types/utils/file-mentions.d.ts +5 -0
  91. package/dist/types/utils/image-limits.d.ts +7 -0
  92. package/dist/types/utils/image-loading.d.ts +0 -1
  93. package/dist/types/utils/open.d.ts +0 -1
  94. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  95. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  96. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  97. package/dist/types/utils/title-generator.d.ts +13 -3
  98. package/dist/types/web/search/providers/codex.d.ts +6 -7
  99. package/package.json +9 -8
  100. package/scripts/acp-conformance-agent.ts +12 -2
  101. package/scripts/generate-sdk-operation-inventory.ts +16 -0
  102. package/src/capability/fs.ts +31 -3
  103. package/src/capability/rule.ts +11 -2
  104. package/src/cli/append-system-prompt.ts +12 -0
  105. package/src/cli/args.ts +11 -7
  106. package/src/cli/customize-doctor-precedence.ts +31 -0
  107. package/src/cli/customize-doctor.ts +49 -39
  108. package/src/cli/fast-help.ts +0 -2
  109. package/src/cli/migrate-cli.ts +14 -3
  110. package/src/cli/public-command-errors.ts +2 -0
  111. package/src/cli/root-flags.ts +0 -2
  112. package/src/cli/update-cli.ts +247 -6
  113. package/src/commands/harness.ts +25 -15
  114. package/src/commands/migrate.ts +3 -3
  115. package/src/commands/sdk.ts +81 -11
  116. package/src/commit/agentic/agent.ts +6 -6
  117. package/src/commit/analysis/conventional.ts +14 -5
  118. package/src/commit/map-reduce/index.ts +7 -7
  119. package/src/commit/pipeline.ts +12 -12
  120. package/src/config/autorouting-tier-map.ts +20 -1
  121. package/src/config/config-file.ts +106 -3
  122. package/src/config/config-hot-reload.ts +8 -0
  123. package/src/config/file-lock.ts +1431 -304
  124. package/src/config/keybindings.ts +154 -7
  125. package/src/config/model-profile-activation.ts +177 -39
  126. package/src/config/model-registry.ts +380 -55
  127. package/src/config/model-resolver.ts +49 -21
  128. package/src/config/settings-schema.ts +13 -1
  129. package/src/config/settings.ts +2 -0
  130. package/src/coordinator-mcp/server.ts +37 -2
  131. package/src/crash/sanitize.ts +3 -1
  132. package/src/customization/import.ts +276 -7
  133. package/src/customization/inventory.ts +24 -8
  134. package/src/customization/types.ts +1 -0
  135. package/src/dap/client.ts +3 -2
  136. package/src/dap/session.ts +161 -36
  137. package/src/debug/crash-diagnostics.ts +87 -5
  138. package/src/debug/report-bundle.ts +71 -2
  139. package/src/discovery/builtin.ts +4 -3
  140. package/src/discovery/helpers.ts +24 -14
  141. package/src/edit/index.ts +191 -5
  142. package/src/edit/modes/patch.ts +27 -0
  143. package/src/eval/py/runtime.ts +44 -4
  144. package/src/exec/non-interactive-env.ts +0 -1
  145. package/src/export/html/index.ts +60 -20
  146. package/src/export/ttsr.ts +176 -1
  147. package/src/extensibility/extensions/loader.ts +2 -0
  148. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  149. package/src/extensibility/extensions/types.ts +5 -1
  150. package/src/extensibility/hooks/loader.ts +10 -2
  151. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  152. package/src/extensibility/slash-commands.ts +5 -0
  153. package/src/gjc-runtime/gc-runtime.ts +4 -1
  154. package/src/gjc-runtime/launch-tmux.ts +23 -13
  155. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  156. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  157. package/src/gjc-runtime/state-writer.ts +109 -5
  158. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  159. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  160. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  161. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  162. package/src/harness-control-plane/finalize.ts +11 -0
  163. package/src/harness-control-plane/receipt-spool.ts +105 -28
  164. package/src/harness-control-plane/storage.ts +124 -6
  165. package/src/hindsight/content.ts +116 -16
  166. package/src/hindsight/mental-models.ts +22 -10
  167. package/src/hindsight/state.ts +7 -4
  168. package/src/hooks/native-skill-hook.ts +3 -1
  169. package/src/internal-urls/artifact-protocol.ts +3 -1
  170. package/src/internal-urls/docs-index.generated.ts +14 -14
  171. package/src/internal-urls/local-protocol.ts +28 -3
  172. package/src/internal-urls/types.ts +2 -0
  173. package/src/lsp/client.ts +2 -1
  174. package/src/lsp/clients/biome-client.ts +2 -0
  175. package/src/lsp/clients/swiftlint-client.ts +2 -0
  176. package/src/main.ts +330 -92
  177. package/src/memories/index.ts +25 -2
  178. package/src/migrate/action-planner.ts +517 -2
  179. package/src/migrate/adapters/claude-code.ts +141 -3
  180. package/src/migrate/adapters/codex.ts +57 -5
  181. package/src/migrate/adapters/index.ts +240 -5
  182. package/src/migrate/adapters/opencode.ts +4 -0
  183. package/src/migrate/executor.ts +141 -3
  184. package/src/migrate/hook-module.ts +193 -0
  185. package/src/migrate/report.ts +45 -1
  186. package/src/migrate/types.ts +73 -3
  187. package/src/modes/acp/acp-agent.ts +10 -1
  188. package/src/modes/components/agent-dashboard.ts +20 -4
  189. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  190. package/src/modes/components/customization/import-wizard.ts +29 -3
  191. package/src/modes/components/model-selector.ts +157 -103
  192. package/src/modes/components/welcome.ts +3 -1
  193. package/src/modes/controllers/command-controller.ts +271 -16
  194. package/src/modes/controllers/event-controller.ts +1 -1
  195. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  196. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  197. package/src/modes/controllers/input-controller.ts +2 -0
  198. package/src/modes/controllers/plan-mode-controller.ts +49 -4
  199. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  200. package/src/modes/controllers/selector-controller.ts +14 -1
  201. package/src/modes/interactive-mode.ts +55 -4
  202. package/src/modes/prompt-suggestion-controller.ts +1 -1
  203. package/src/modes/runtime-init.ts +5 -1
  204. package/src/modes/utils/host-status.ts +21 -10
  205. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  206. package/src/prompts/system/irc-incoming.md +3 -1
  207. package/src/prompts/tools/irc.md +1 -1
  208. package/src/runtime-mcp/manager.ts +8 -3
  209. package/src/runtime-mcp/project-host-values.ts +12 -8
  210. package/src/runtime-mcp/tool-bridge.ts +68 -4
  211. package/src/runtime-mcp/transports/stdio.ts +2 -1
  212. package/src/sdk/broker/broker-exit.ts +2 -0
  213. package/src/sdk/broker/broker.ts +143 -4
  214. package/src/sdk/broker/ensure.ts +74 -6
  215. package/src/sdk/broker/hop.ts +13 -1
  216. package/src/sdk/broker/lifecycle-ledger.ts +3 -0
  217. package/src/sdk/broker/lifecycle.ts +102 -6
  218. package/src/sdk/broker/session-index.ts +29 -4
  219. package/src/sdk/broker/transport.ts +17 -1
  220. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  221. package/src/sdk/bus/index.ts +408 -14
  222. package/src/sdk/bus/session-control.ts +2 -0
  223. package/src/sdk/bus/slack-daemon.ts +105 -9
  224. package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
  225. package/src/sdk/bus/telegram-daemon.ts +10 -2
  226. package/src/sdk/bus/threaded-render.ts +20 -1
  227. package/src/sdk/cli/session-cli.ts +2 -0
  228. package/src/sdk/host/control/dispatch.ts +13 -1
  229. package/src/sdk/host/control/operations.ts +10 -1
  230. package/src/sdk/host/events.ts +67 -4
  231. package/src/sdk/host/host.ts +8 -2
  232. package/src/sdk/host/prompt-image-upload.ts +438 -0
  233. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  234. package/src/sdk/host/session-runtime.test.ts +1537 -24
  235. package/src/sdk/host/session-runtime.ts +332 -36
  236. package/src/sdk/host/surface-policy.ts +4 -0
  237. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  238. package/src/sdk/host/websocket-transport.ts +19 -7
  239. package/src/sdk/lifecycle/service.ts +14 -1
  240. package/src/sdk/prompt-status.ts +6 -4
  241. package/src/sdk/protocol/adapter-validation.ts +28 -1
  242. package/src/sdk/protocol/operation-inventory.generated.json +160 -0
  243. package/src/sdk/protocol/operation-registry.ts +33 -3
  244. package/src/sdk/session.ts +123 -36
  245. package/src/session/agent-session.ts +1614 -329
  246. package/src/session/history-storage.ts +182 -12
  247. package/src/session/internal/managed-session-scope.ts +47 -18
  248. package/src/session/internal/managed-session-storage.ts +71 -1
  249. package/src/session/session-manager.ts +90 -5
  250. package/src/session-import/redact.ts +53 -2
  251. package/src/skill-state/workflow-mutation-guard.ts +1498 -18
  252. package/src/slash-commands/builtin-registry.ts +6 -3
  253. package/src/slash-commands/helpers/mcp.ts +11 -3
  254. package/src/ssh/ssh-executor.ts +9 -2
  255. package/src/stt/recorder.ts +26 -9
  256. package/src/system-prompt.ts +61 -16
  257. package/src/task/executor.ts +12 -8
  258. package/src/task/role-body.ts +84 -0
  259. package/src/task/token-log-mode.test.ts +162 -0
  260. package/src/task/token-log.ts +91 -2
  261. package/src/tools/archive-reader.ts +650 -37
  262. package/src/tools/ask.ts +1 -0
  263. package/src/tools/ast-grep.ts +7 -2
  264. package/src/tools/auto-generated-guard.ts +12 -3
  265. package/src/tools/awaiting-input.ts +114 -0
  266. package/src/tools/bash-allowed-prefixes.ts +61 -1
  267. package/src/tools/bash-skill-urls.ts +58 -5
  268. package/src/tools/bash.ts +99 -26
  269. package/src/tools/browser/tab-worker.ts +39 -13
  270. package/src/tools/browser.ts +17 -7
  271. package/src/tools/cron.ts +10 -1
  272. package/src/tools/debug.ts +9 -2
  273. package/src/tools/descriptors.test.ts +4 -4
  274. package/src/tools/descriptors.ts +16 -4
  275. package/src/tools/eval.ts +15 -0
  276. package/src/tools/fetch.ts +90 -2
  277. package/src/tools/gh.ts +27 -12
  278. package/src/tools/image-gen.ts +20 -6
  279. package/src/tools/irc.ts +5 -3
  280. package/src/tools/monitor.ts +9 -1
  281. package/src/tools/output-meta.ts +51 -3
  282. package/src/tools/read.ts +9 -2
  283. package/src/tools/recipe/runner.ts +23 -12
  284. package/src/tools/render-utils.ts +2 -2
  285. package/src/tools/search.ts +3 -2
  286. package/src/tools/ssh.ts +274 -8
  287. package/src/tools/subagent.ts +10 -4
  288. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  289. package/src/tools/tool-catalog.generated.ts +1 -1
  290. package/src/tools/ultragoal-ask-guard.ts +13 -1
  291. package/src/tools/write.ts +7 -0
  292. package/src/tui/tree-list.ts +15 -1
  293. package/src/utils/commit-message-generator.ts +3 -2
  294. package/src/utils/external-editor.ts +21 -7
  295. package/src/utils/file-mentions.ts +46 -6
  296. package/src/utils/git.ts +38 -7
  297. package/src/utils/image-limits.ts +8 -0
  298. package/src/utils/image-loading.ts +1 -1
  299. package/src/utils/open.ts +73 -4
  300. package/src/utils/pasted-image-loading.ts +9 -8
  301. package/src/utils/prompt-suggestion.ts +5 -4
  302. package/src/utils/shell-snapshot.ts +9 -1
  303. package/src/utils/title-generator.ts +65 -20
  304. package/src/web/search/index.ts +2 -1
  305. package/src/web/search/providers/codex.ts +29 -32
@@ -102,21 +102,31 @@ server = Bun.serve({
102
102
  if (prompt.includes("sleep")) {
103
103
  const requested = Number(/\bsleep\s+(\d+)/.exec(prompt)?.[1] ?? 0);
104
104
  const delay = Math.min(Math.max(requested, 0), 30_000);
105
+ let closed = false;
106
+ let close = (): void => {};
105
107
  const stream = new ReadableStream<Uint8Array>({
106
108
  start(controller) {
107
- let closed = false;
108
- const close = () => {
109
+ close = () => {
109
110
  if (closed) return;
110
111
  closed = true;
112
+ request.signal.removeEventListener("abort", close);
111
113
  controller.close();
112
114
  };
113
115
  request.signal.addEventListener("abort", close, { once: true });
116
+ // The body parse above may yield long enough for the client to abort before
117
+ // this response stream installs its listener. Signals do not replay abort
118
+ // events to late listeners, so close immediately when that already happened.
119
+ if (request.signal.aborted) close();
114
120
  void Bun.sleep(delay).then(() => {
115
121
  if (closed) return;
116
122
  controller.enqueue(new TextEncoder().encode(textCompletion("slept")));
117
123
  close();
118
124
  });
119
125
  },
126
+ cancel() {
127
+ closed = true;
128
+ request.signal.removeEventListener("abort", close);
129
+ },
120
130
  });
121
131
  return new Response(stream, { headers: { "content-type": "text/event-stream" } });
122
132
  }
@@ -27,6 +27,10 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
27
27
  "internal read-only run-resource-ledger view, threaded via terminalAbortSeams so the prompt deadline can find a tool-call boundary; not a user-facing SDK control seam",
28
28
  "agent_session:markProfileRoleOverrideManual":
29
29
  "internal profile role-layer ownership tracking after explicit user assignments; not a user-facing SDK control seam",
30
+ "agent_session:notifySessionIdentityChanged":
31
+ "internal session-identity listener fan-out for in-process session surfaces; not a user-facing SDK control seam",
32
+ "agent_session:subscribeSessionIdentityChanges":
33
+ "internal session-identity subscription used by in-process adapters to follow session replacement; not a user-facing SDK control seam",
30
34
  "slash_command:routing":
31
35
  "visual/local-only autorouting settings toggle and smart-routing panel entry, not a user-facing SDK control seam",
32
36
  "slash_command:mcp":
@@ -85,6 +89,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
85
89
  "internal session-scoped startup-profile recovery marker, not a user-facing SDK control seam",
86
90
  "agent_session:getUnavailableModelProfile":
87
91
  "internal session-scoped startup-profile recovery status for UI rendering, not a public SDK operation",
92
+ "agent_session:getActivatedModelProfile":
93
+ "internal model-profile activation provenance used to gate role-only materialization, not a user-facing SDK control seam",
88
94
  "agent_session:getConfigurationPaths":
89
95
  "internal config hot-reload path identity projection, not a user-facing SDK control seam",
90
96
  "agent_session:validateConfiguration":
@@ -121,6 +127,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
121
127
  "internal tool-selection predicate used to preserve the --no-tools boundary, not a user-facing SDK control seam",
122
128
  "agent_session:getEffectiveActiveWorkflowSkillState":
123
129
  "internal restored-or-live workflow guard for session rescope, not a user-facing SDK control seam",
130
+ "agent_session:refreshEffectiveActiveWorkflowSkillState":
131
+ "internal durable workflow identity refresh before session safety checks, not an independent public SDK operation",
124
132
  "agent_session:replaceOwnedMcpManager":
125
133
  "internal cwd-rebinding helper for owned MCP authority after move_session, not a user-facing SDK control seam",
126
134
  "agent_session:replaceNamedCustomTools":
@@ -159,6 +167,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
159
167
  "test-only parked terminal persistence seam, not a user-facing SDK control seam",
160
168
  "agent_session:flushParkedAgentEndForCoordinatorPersistForTests":
161
169
  "test-only parked terminal flush seam, not a user-facing SDK control seam",
170
+ "agent_session:waitForAgentEndHandlingForTests":
171
+ "test-only SDK terminal-event handler completion barrier, not a user-facing SDK control seam",
162
172
  "agent_session:setCancelAndSubmitAbortOutcomeProviderForTests":
163
173
  "test-only cancellation seam, not a user-facing SDK control seam",
164
174
  "agent_session:awaitDisposeCompletion": "test-only disposal completion seam, not a user-facing SDK control seam",
@@ -193,6 +203,12 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
193
203
  "agent_session:onJobFold": "internal fold-event plumbing feeding the bash_folded event frame, not a control seam",
194
204
 
195
205
  "agent_session:getSelectedMCPToolNames": "internal accessor/plumbing, not a user-facing control seam",
206
+ "agent_session:getActiveMCPToolNames":
207
+ "internal MCP bridge subset used by mode controllers to preserve live selections across tool swaps; not a user-facing SDK control seam",
208
+ "agent_session:hasMCPToolSelectionAuthority":
209
+ "internal MCP-selection authority predicate used by catalog and mode-transition reconciliation; not a user-facing SDK control seam",
210
+ "agent_session:hasCommittedMCPToolSelectionAuthority":
211
+ "internal committed MCP-selection authority predicate used by catalog reconciliation; not a user-facing SDK control seam",
196
212
  "agent_session:isToolDiscoveryEnabled": "internal accessor/plumbing, not a user-facing control seam",
197
213
  "agent_session:getDiscoverableToolSearchIndex": "internal accessor/plumbing, not a user-facing control seam",
198
214
  "agent_session:getSelectedDiscoveredToolNames": "internal accessor/plumbing, not a user-facing control seam",
@@ -241,6 +241,28 @@ async function openIsolatedFile(abs: string, options?: ReadFileOptions): Promise
241
241
  }
242
242
  }
243
243
 
244
+ /**
245
+ * A lexical path outside the boundary keeps the historical ancestor read.
246
+ * A path inside the boundary must still be inside after realpath, so a
247
+ * repository symlink cannot be opened as its external target.
248
+ */
249
+ async function containedReadPath(abs: string, options?: ReadFileOptions): Promise<string | null> {
250
+ const root = options?.containmentRoot;
251
+ if (!root || options?.isolatedHome) return abs;
252
+ const lexical = path.resolve(abs);
253
+ const boundary = path.resolve(root);
254
+ if (!isWithinOrEqual(boundary, lexical)) return lexical;
255
+ try {
256
+ const [boundaryReal, targetReal] = await Promise.all([
257
+ fs.promises.realpath(boundary),
258
+ fs.promises.realpath(lexical),
259
+ ]);
260
+ return isWithinOrEqual(boundaryReal, targetReal) ? targetReal : null;
261
+ } catch {
262
+ return null;
263
+ }
264
+ }
265
+
244
266
  export async function readFile(filePath: string, options?: ReadFileOptions): Promise<string | null> {
245
267
  let abs: string | null;
246
268
  try {
@@ -250,13 +272,19 @@ export async function readFile(filePath: string, options?: ReadFileOptions): Pro
250
272
  }
251
273
  if (abs === null) return null;
252
274
  if (!(await isCurrentIsolatedPath(abs, options))) return null;
253
- const useCache = !options?.bypassCache && !options?.isolatedHome;
275
+ // A contained read must not share the lexical cache with a different boundary.
276
+ const useCache = !options?.bypassCache && !options?.isolatedHome && !options?.containmentRoot;
254
277
  if (useCache && contentCache.has(abs)) {
255
278
  return contentCache.get(abs) ?? null;
256
279
  }
257
280
  if (!options?.isolatedHome) {
281
+ const readable = await containedReadPath(abs, options);
282
+ if (readable === null) {
283
+ if (useCache) contentCache.set(abs, null);
284
+ return null;
285
+ }
258
286
  try {
259
- const content = await Bun.file(abs).text();
287
+ const content = await Bun.file(readable).text();
260
288
  if (useCache) contentCache.set(abs, content);
261
289
  return content;
262
290
  } catch {
@@ -460,7 +488,7 @@ export async function findRepoRoot(
460
488
  let current = resolvePath(startDir);
461
489
  const stop = stopAt ? resolvePath(stopAt) : undefined;
462
490
  while (true) {
463
- const entries = await readDirEntries(current, options);
491
+ const entries = await readDirEntries(current, { ...options, bypassCache: true });
464
492
  if (entries.some(e => e.name === ".git")) {
465
493
  return current;
466
494
  }
@@ -18,6 +18,8 @@ export interface RuleFrontmatter {
18
18
  alwaysApply?: boolean;
19
19
  /** New key for TTSR match conditions. */
20
20
  condition?: string | string[];
21
+ /** AST-grep pattern(s) for TTSR AST matching (evaluated on completed tool args). */
22
+ astCondition?: string | string[];
21
23
  /** New key for TTSR stream scope. */
22
24
  scope?: string | string[];
23
25
  /** Per-rule TTSR interrupt mode override. */
@@ -48,6 +50,8 @@ export interface Rule {
48
50
  description?: string;
49
51
  /** Regex condition(s) that can trigger TTSR interruption. */
50
52
  condition?: string[];
53
+ /** AST-grep pattern(s) for TTSR AST matching (evaluated on completed tool args). */
54
+ astCondition?: string[];
51
55
  /** Optional stream scope tokens (for example: text, thinking, tool:edit(*.ts)). */
52
56
  scope?: string[];
53
57
  /** Per-rule TTSR interrupt mode override (falls back to global ttsr.interruptMode). */
@@ -182,17 +186,21 @@ function isLikelyFileGlob(value: string): boolean {
182
186
  }
183
187
 
184
188
  /**
185
- * Parse `condition` + `scope` from rule frontmatter.
189
+ * Parse `condition`, `astCondition`, and `scope` from rule frontmatter.
186
190
  *
187
191
  * - `condition` accepts string or string[]
192
+ * - `astCondition` accepts string or string[] (ast-grep patterns)
188
193
  * - `scope` accepts string or string[]
189
194
  * - legacy `ttsr_trigger` / `ttsrTrigger` are accepted as a `condition` fallback
190
195
  * - condition tokens that look like file globs become scope shorthands:
191
196
  * `*.rs` => `tool:edit(*.rs)`, `tool:write(*.rs)` and a catch-all condition `.*`
192
197
  */
193
- export function parseRuleConditionAndScope(frontmatter: RuleFrontmatter): Pick<Rule, "condition" | "scope"> {
198
+ export function parseRuleConditionAndScope(
199
+ frontmatter: RuleFrontmatter,
200
+ ): Pick<Rule, "condition" | "astCondition" | "scope"> {
194
201
  const rawCondition = frontmatter.condition ?? frontmatter.ttsr_trigger ?? frontmatter.ttsrTrigger;
195
202
  const parsedCondition = normalizeRuleField(rawCondition);
203
+ const parsedAstCondition = normalizeRuleField(frontmatter.astCondition);
196
204
  const parsedScope = normalizeScopeField(frontmatter.scope);
197
205
 
198
206
  const inferredScope: string[] = [];
@@ -214,6 +222,7 @@ export function parseRuleConditionAndScope(frontmatter: RuleFrontmatter): Pick<R
214
222
  const scope = [...(parsedScope ?? []), ...inferredScope];
215
223
  return {
216
224
  condition: condition.length > 0 ? Array.from(new Set(condition)) : undefined,
225
+ astCondition: parsedAstCondition ? Array.from(new Set(parsedAstCondition)) : undefined,
217
226
  scope: scope.length > 0 ? Array.from(new Set(scope)) : undefined,
218
227
  };
219
228
  }
@@ -0,0 +1,12 @@
1
+ export interface AppendSystemPromptCandidate {
2
+ level: "user" | "project";
3
+ path: string;
4
+ }
5
+
6
+ /** First user file. A repository copy is untrusted project text and is never selected. */
7
+ export function selectAppendSystemPromptFile(candidates: readonly AppendSystemPromptCandidate[]): string | undefined {
8
+ for (const candidate of candidates) {
9
+ if (candidate.level === "user") return candidate.path;
10
+ }
11
+ return undefined;
12
+ }
package/src/cli/args.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import * as path from "node:path";
5
5
  import { ThinkingLevel } from "@gajae-code/agent-core";
6
- import { logger } from "@gajae-code/utils";
6
+ import { $pickenv, logger } from "@gajae-code/utils";
7
7
  import { CliParseError } from "@gajae-code/utils/cli";
8
8
  import { parseThinkingLevel } from "../thinking";
9
9
  import { BUILTIN_TOOLS } from "../tools";
@@ -46,8 +46,6 @@ export interface Args {
46
46
  allowHome?: boolean;
47
47
  provider?: string;
48
48
  model?: string;
49
- smol?: string;
50
- slow?: string;
51
49
  plan?: string;
52
50
  mpreset?: string;
53
51
  default?: boolean;
@@ -228,10 +226,6 @@ export function parseArgs(args: string[], authority: ParseArgsAuthority = "local
228
226
  result.provider = takeFlagValue(args, i++, "--provider");
229
227
  } else if (arg === "--model") {
230
228
  result.model = takeFlagValue(args, i++, "--model");
231
- } else if (arg === "--smol") {
232
- result.smol = takeFlagValue(args, i++, "--smol");
233
- } else if (arg === "--slow") {
234
- result.slow = takeFlagValue(args, i++, "--slow");
235
229
  } else if (arg === "--plan") {
236
230
  result.plan = takeFlagValue(args, i++, "--plan");
237
231
  } else if (arg === "--mpreset") {
@@ -427,3 +421,13 @@ export function assertLocalLaunchArgs(parsed: Args): void {
427
421
  if (!launchFlagIsOwnedBy(name, "local")) throw new CliParseError(`Unknown option: ${token}`);
428
422
  }
429
423
  }
424
+
425
+ /**
426
+ * Resolve the ephemeral `plan` model-role override that plan mode switches to.
427
+ *
428
+ * Precedence is CLI `--plan` > `GJC_PLAN_MODEL` > legacy `PI_PLAN_MODEL`.
429
+ * `$pickenv` trims values and treats empty/whitespace as unset.
430
+ */
431
+ export function resolvePlanModelOverride(parsed: Pick<Args, "plan">): string | undefined {
432
+ return parsed.plan ?? $pickenv("GJC_PLAN_MODEL", "PI_PLAN_MODEL");
433
+ }
@@ -0,0 +1,31 @@
1
+ export interface DiscoveryPrecedenceCandidate {
2
+ key: string | undefined;
3
+ provider: string;
4
+ scope: "user" | "project" | "native";
5
+ eligible: boolean;
6
+ enabled: boolean;
7
+ }
8
+
9
+ export interface DiscoveryPrecedenceResult {
10
+ shadowed: boolean;
11
+ shadowedBy?: { provider: string; scope: DiscoveryPrecedenceCandidate["scope"] };
12
+ }
13
+
14
+ /** Resolve ordered discovery candidates without allowing ineligible or disabled entries to own keys. */
15
+ export function resolveDiscoveryPrecedence(
16
+ candidates: readonly DiscoveryPrecedenceCandidate[],
17
+ ): DiscoveryPrecedenceResult[] {
18
+ const winners = new Map<string, { provider: string; scope: DiscoveryPrecedenceCandidate["scope"] }>();
19
+
20
+ return candidates.map(candidate => {
21
+ if (!candidate.eligible || candidate.key === undefined) return { shadowed: false };
22
+
23
+ const winner = winners.get(candidate.key);
24
+ if (winner) return { shadowed: true, shadowedBy: winner };
25
+
26
+ if (candidate.enabled) {
27
+ winners.set(candidate.key, { provider: candidate.provider, scope: candidate.scope });
28
+ }
29
+ return { shadowed: false };
30
+ });
31
+ }
@@ -54,6 +54,7 @@ import {
54
54
  isCanonicalGjcWorkflowSkillFilesystemAlias,
55
55
  } from "../skill-state/canonical-skills";
56
56
  import { expandTilde } from "../tools/path-utils";
57
+ import { resolveDiscoveryPrecedence } from "./customize-doctor-precedence";
57
58
 
58
59
  // =============================================================================
59
60
  // Public types (stable JSON contract)
@@ -67,9 +68,10 @@ export type CustomizeSurfaceKind = "mcp" | "skill" | "hook" | "tool" | "extensio
67
68
  *
68
69
  * - `canonical`: project/global `.gjc` entries and GJC bundled defaults — the
69
70
  * primary load path and the persisted authority for sessions.
70
- * - `convention`: items from registered non-native conventions that are part of
71
- * the discovery load path (claude-plugins, claude/codex hooks, agents,
72
- * cursor, gemini, opencode, windsurf, cline, github, mcp-json, ssh).
71
+ * - `convention`: items exposed by registered non-native discovery providers
72
+ * (claude-plugins, claude/codex hooks, agents, cursor, gemini, opencode,
73
+ * windsurf, cline, github, mcp-json, ssh). The item status identifies whether
74
+ * a discovered item is active, stored-only, or otherwise excluded at runtime.
73
75
  * - `import-candidate`: Claude Code / Codex project (+ global) files on
74
76
  * surfaces GJC deliberately never loads. Reported for provenance only; never
75
77
  * active runtime authority. Candidates for a future import flow (#4291).
@@ -267,7 +269,7 @@ const SOURCE_CLASS_DESCRIPTIONS: Array<{ sourceClass: CustomizeSourceClass; desc
267
269
  {
268
270
  sourceClass: "convention",
269
271
  description:
270
- "Items from other registered conventions (claude-plugins, claude/codex hooks, agents, cursor, gemini, opencode, windsurf, cline, github, mcp-json, ssh) that are part of the discovery load path.",
272
+ "Items exposed by registered convention providers (claude-plugins, claude/codex hooks, agents, cursor, gemini, opencode, windsurf, cline, github, mcp-json, ssh); item status identifies runtime eligibility.",
271
273
  },
272
274
  {
273
275
  sourceClass: "import-candidate",
@@ -394,6 +396,7 @@ async function discoverCapability<T extends { _source: SourceMeta }>(
394
396
  nameOf: (item: T) => string;
395
397
  pathOf: (item: T) => string;
396
398
  extensionIdOf?: (item: T) => string | undefined;
399
+ precedenceEligible?: (item: T) => boolean;
397
400
  },
398
401
  ): Promise<DiscoveredSet<T>> {
399
402
  const disabledProviders = new Set(activeSettings.get("disabledProviders"));
@@ -405,41 +408,34 @@ async function discoverCapability<T extends { _source: SourceMeta }>(
405
408
  includeDisabledProviders: true,
406
409
  });
407
410
 
408
- // First item per dedup key wins (highest priority provider first in `all`).
409
- const winners = new Map<string, { provider: string; scope: "user" | "project" | "native" }>();
410
- const entries: DiscoveredEntry<T>[] = [];
411
- for (const item of result.all) {
412
- const key = capability.key(item);
413
- const scope = scopeOf(item._source);
414
- let shadowed = (item as { _shadowed?: boolean })._shadowed === true;
415
- let shadowedBy: { provider: string; scope: string } | undefined;
416
- if (key !== undefined) {
417
- const winner = winners.get(key);
418
- if (winner) {
419
- shadowed = true;
420
- shadowedBy = winner;
421
- } else if (!disabledProviders.has(item._source.provider)) {
422
- // Only enabled providers can own a dedup key; a disabled
423
- // higher-priority provider must never shadow an enabled
424
- // lower-priority source (includeDisabledProviders reports it
425
- // as a listed item without giving it effective precedence).
426
- winners.set(key, { provider: item._source.provider, scope });
427
- }
428
- }
429
- entries.push({
411
+ // Resolve provider-order winners only after applying surface eligibility and
412
+ // the configured provider-disabled policy.
413
+ const resolvedPrecedence = resolveDiscoveryPrecedence(
414
+ result.all.map(item => ({
415
+ key: capability.key(item),
416
+ provider: item._source.provider,
417
+ scope: scopeOf(item._source),
418
+ eligible: options.precedenceEligible?.(item) ?? true,
419
+ enabled: !disabledProviders.has(item._source.provider),
420
+ })),
421
+ );
422
+ const entries: DiscoveredEntry<T>[] = result.all.map((item, index) => {
423
+ const precedence = resolvedPrecedence[index];
424
+ if (!precedence) throw new Error("discovery precedence result is missing an item");
425
+ return {
430
426
  item,
431
427
  name: options.nameOf(item),
432
428
  path: options.pathOf(item),
433
429
  provider: item._source.provider,
434
430
  providerName: item._source.providerName,
435
- scope,
431
+ scope: scopeOf(item._source),
436
432
  priority: capability.providers.find(p => p.id === item._source.provider)?.priority ?? 0,
437
- shadowed,
438
- shadowedBy,
433
+ shadowed: precedence.shadowed,
434
+ shadowedBy: precedence.shadowedBy,
439
435
  invalidReason: capability.validate?.(item),
440
436
  extensionId: options.extensionIdOf?.(item),
441
- });
442
- }
437
+ };
438
+ });
443
439
 
444
440
  entries.sort((a, b) => a.name.localeCompare(b.name) || a.provider.localeCompare(b.provider));
445
441
 
@@ -954,6 +950,7 @@ async function collectHooks(cwd: string, activeSettings: SettingsInstance): Prom
954
950
  return collectNotExecutedAtStartup("hook", hookCapability, cwd, activeSettings, {
955
951
  nameOf: hook => hook.name,
956
952
  pathOf: hook => hook.path,
953
+ precedenceEligible: hook => hook.level !== "project",
957
954
  notExecutedDetail:
958
955
  "Trusted filesystem hook module discovered for session-start activation. Doctor does not execute the module, so dynamic Function Hook registrations and effective runtime order remain opaque until a session loads it.",
959
956
  notExecutedRemediation: [
@@ -989,6 +986,7 @@ async function collectExtensions(cwd: string, activeSettings: SettingsInstance):
989
986
  interface NotExecutedOptions<T extends { _source: SourceMeta }> {
990
987
  nameOf: (item: T) => string;
991
988
  pathOf: (item: T) => string;
989
+ precedenceEligible?: (item: T) => boolean;
992
990
  notExecutedDetail: string;
993
991
  notExecutedRemediation: string[];
994
992
  }
@@ -1004,6 +1002,7 @@ async function collectNotExecutedAtStartup<T extends { _source: SourceMeta }>(
1004
1002
  nameOf: options.nameOf,
1005
1003
  pathOf: options.pathOf,
1006
1004
  extensionIdOf: item => capability.toExtensionId?.(item),
1005
+ precedenceEligible: options.precedenceEligible,
1007
1006
  });
1008
1007
  const disabledExts = disabledExtensionIds(activeSettings);
1009
1008
  const disabledProviders = new Set(activeSettings.get("disabledProviders"));
@@ -1019,6 +1018,18 @@ async function collectNotExecutedAtStartup<T extends { _source: SourceMeta }>(
1019
1018
  [`Fix ${entry.path}`],
1020
1019
  );
1021
1020
  }
1021
+ if (kind === "hook" && entry.scope === "project") {
1022
+ return finalizeItem(
1023
+ base,
1024
+ "stored-only",
1025
+ "storage-only",
1026
+ "Project-scoped hooks are stored for inspection, but ordinary sessions do not load repository hook modules.",
1027
+ [
1028
+ "Import the hook into Global .gjc to load it for future sessions",
1029
+ "or configure an explicit hook path if repository-local activation is intentional",
1030
+ ],
1031
+ );
1032
+ }
1022
1033
  if (entry.extensionId !== undefined && disabledExts.has(entry.extensionId)) {
1023
1034
  return finalizeItem(
1024
1035
  base,
@@ -1360,13 +1371,12 @@ async function collectPluginBundles(cwd: string): Promise<CustomizeDoctorSurface
1360
1371
  // Foreign conventions (Claude project / Codex project)
1361
1372
  //
1362
1373
  // Claude Code / Codex project hooks are discovered by the registered
1363
- // claude-hooks/codex-hooks providers (see discovery/index.ts) and appear in the
1364
- // Hooks surface above; sessions surface them in the extension dashboard but do
1365
- // not execute hook files. Every OTHER `.claude`/`.codex` surface (MCP, skills,
1366
- // tools, extensions, commands, prompts, settings) is deliberately not part of
1367
- // the load path: the doctor reports those files by invoking the canonical
1368
- // provider load functions directly (without registering the providers) and
1369
- // marks every such item as `ignored`.
1374
+ // claude-hooks/codex-hooks providers (see discovery/index.ts), but like native
1375
+ // project hooks are stored-only and are not imported by ordinary sessions.
1376
+ // Every OTHER `.claude`/`.codex` surface (MCP, skills, tools, extensions,
1377
+ // commands, prompts, settings) is deliberately not part of the load path: the
1378
+ // doctor reports those files by invoking canonical provider load functions
1379
+ // directly (without registering the providers) and marks them `ignored`.
1370
1380
  // =============================================================================
1371
1381
 
1372
1382
  interface ForeignConventionDescriptor {
@@ -1468,7 +1478,7 @@ function foreignItem(
1468
1478
  async function collectForeignConventions(cwd: string): Promise<ForeignConventionCollection> {
1469
1479
  const ctx: LoadContext = { cwd, home: os.homedir(), repoRoot: await findRepoRoot(cwd) };
1470
1480
  const itemsByKind: Map<CustomizeSurfaceKind, CustomizeDoctorItem[]> = new Map();
1471
- const policyNote = `${FOREIGN_CONVENTIONS.map(d => d.dirName).join(" and ")} project hooks are discovered by GJC (see the Hooks surface); all other ${FOREIGN_CONVENTIONS.map(d => d.providerName).join(" / ")} project config surfaces (MCP servers, skills, tools, extensions, commands, prompts, settings) are not part of the GJC load path. Those files are reported below as import candidates for provenance but are never discovered by sessions.`;
1481
+ const policyNote = `${FOREIGN_CONVENTIONS.map(d => d.dirName).join(" and ")} project hooks are shown as stored-only (see the Hooks surface); ordinary sessions do not load repository hook modules. All other ${FOREIGN_CONVENTIONS.map(d => d.providerName).join(" / ")} project config surfaces (MCP servers, skills, tools, extensions, commands, prompts, settings) are not part of the GJC load path and are reported below as import candidates for provenance.`;
1472
1482
 
1473
1483
  for (const desc of FOREIGN_CONVENTIONS) {
1474
1484
  const inspection = await desc.inspect(ctx);
@@ -81,8 +81,6 @@ Environment Variables:
81
81
  GJC_CODING_AGENT_DIR - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
82
82
  GJC_UI_LANGUAGE - Explicit first-use onboarding language (en, ko, zh, or ja); invalid values use English without prompting
83
83
  GJC_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
84
- GJC_SMOL_MODEL - Override smol/fast model (see --smol)
85
- GJC_SLOW_MODEL - Override slow/reasoning model (see --slow)
86
84
  GJC_PLAN_MODEL - Override planning model (see --plan)
87
85
  GJC_MODEL_PRESET_REGISTRY_URL - HTTPS latest.json override for the signed preset registry
88
86
  GJC_MODEL_PRESET_REGISTRY_DISABLED - Disable registry fetch/use (1/true/yes/on)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `gjc migrate` — import MCP servers, skills, and instruction files from other coding agents.
2
+ * `gjc migrate` — import MCP servers, skills, slash commands, and instruction files from other coding agents.
3
3
  */
4
4
  import * as os from "node:os";
5
5
  import * as path from "node:path";
@@ -55,8 +55,18 @@ function resolveDestinations(project: boolean, cwd: string): MigrateDestinations
55
55
  const scope = project ? "project" : "user";
56
56
  const agentDir = project ? getProjectAgentDir(cwd) : getAgentDir();
57
57
  const skillsDir = path.join(agentDir, "skills");
58
+ const commandsDir = path.join(agentDir, "commands");
59
+ const agentsDir = path.join(agentDir, "agents");
60
+ const hooksDir = path.join(agentDir, "hooks");
58
61
  const instructionsPath = path.join(agentDir, "AGENTS.md");
59
- return { mcpConfigPath: getMCPConfigPath(scope, cwd), skillsDir, instructionsPath };
62
+ return {
63
+ mcpConfigPath: getMCPConfigPath(scope, cwd),
64
+ skillsDir,
65
+ commandsDir,
66
+ agentsDir,
67
+ hooksDir,
68
+ instructionsPath,
69
+ };
60
70
  }
61
71
 
62
72
  /** Run the migration and return the report (does not set process.exitCode). */
@@ -71,12 +81,13 @@ export async function runMigrate(args: MigrateCommandArgs): Promise<MigrateRepor
71
81
  results.push(await getAdapter(source).collect({ homeDir, cwd, scope: args.project ? "project" : "user" }));
72
82
  }
73
83
 
74
- const { actions, warnings } = await planMigration({ results, destinations, force: args.force });
84
+ const { actions, warnings, manualActions } = await planMigration({ results, destinations, force: args.force });
75
85
  const finalActions = args.dryRun ? actions : await executeActions(actions, { project: args.project });
76
86
 
77
87
  return buildReport({
78
88
  actions: finalActions,
79
89
  warnings,
90
+ manualActions,
80
91
  sources,
81
92
  destinations,
82
93
  dryRun: args.dryRun,
@@ -111,6 +111,8 @@ export const PUBLIC_COMMAND_DIAGNOSTICS = {
111
111
  // of them and never implies that another key would be safe.
112
112
  lifecycle_idempotency_conflict:
113
113
  "The broker reported an idempotency conflict. Reconcile existing lifecycle state before deciding whether another request is safe.",
114
+ lifecycle_legacy_history_ambiguous:
115
+ "The broker refused admission because legacy lifecycle history cannot prove this target is unrelated. This request was not dispatched; this refusal does not settle earlier effects. Preserve the ledger and original operation, request key, and target. For a prior session.create, use read-only session.lookup with its original key and create target; not_found is not proof of no effect. An empty transcript, stopped host, or fresh key does not resolve this fence. Do not edit the ledger, force deletion, or retry with another key; missing identity or terminal effect evidence requires operator investigation.",
114
116
  // Refusals of the request payload itself: the broker's own text names the missing field
115
117
  // (a required `sessionPath`, for one), and it is never rendered, so the class is named here.
116
118
  lifecycle_input_refused:
@@ -10,8 +10,6 @@ export type RootThinkingLevel = (typeof ROOT_THINKING_LEVELS)[number];
10
10
  /** Public launch flags shared by root help, completion, and the launch command. */
11
11
  export const ROOT_LAUNCH_FLAGS = {
12
12
  model: Flags.string({ description: 'Model to use (fuzzy match: "opus", "gpt-5.2", or "openai/gpt-5.2")' }),
13
- smol: Flags.string({ description: "Smol/fast model for lightweight tasks (or GJC_SMOL_MODEL env)" }),
14
- slow: Flags.string({ description: "Slow/reasoning model for thorough analysis (or GJC_SLOW_MODEL env)" }),
15
13
  plan: Flags.string({ description: "Plan model for architectural planning (or GJC_PLAN_MODEL env)" }),
16
14
  mpreset: Flags.string({ description: "Model profile preset to activate for this session" }),
17
15
  default: Flags.boolean({ description: "Persist --mpreset as the default model profile" }),