@gajae-code/coding-agent 0.11.11 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/CHANGELOG.md +131 -4
  2. package/dist/types/async/job-manager.d.ts +17 -3
  3. package/dist/types/cli/read-cli.d.ts +1 -0
  4. package/dist/types/commands/deep-interview.d.ts +9 -0
  5. package/dist/types/commands/harness.d.ts +22 -0
  6. package/dist/types/commands/read.d.ts +6 -0
  7. package/dist/types/config/model-registry.d.ts +4 -0
  8. package/dist/types/config/provider-auth-health.d.ts +14 -0
  9. package/dist/types/config/provider-ranking.d.ts +59 -0
  10. package/dist/types/config/settings-schema.d.ts +83 -1
  11. package/dist/types/config/settings.d.ts +2 -0
  12. package/dist/types/exa/mcp-client.d.ts +10 -1
  13. package/dist/types/exec/bash-executor.d.ts +45 -12
  14. package/dist/types/extensibility/extensions/types.d.ts +38 -4
  15. package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
  16. package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
  17. package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
  18. package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
  19. package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
  20. package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
  21. package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
  22. package/dist/types/extensibility/shared-events.d.ts +6 -0
  23. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
  24. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
  25. package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
  26. package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
  27. package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
  28. package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
  29. package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
  30. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +70 -1
  31. package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
  32. package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
  33. package/dist/types/gjc-runtime/team-runtime.d.ts +27 -1
  34. package/dist/types/gjc-runtime/team-store.d.ts +12 -0
  35. package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
  36. package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
  37. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +17 -0
  38. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
  39. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +31 -80
  40. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
  41. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  42. package/dist/types/main.d.ts +1 -1
  43. package/dist/types/memories/index.d.ts +2 -0
  44. package/dist/types/modes/acp/acp-agent.d.ts +10 -19
  45. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  46. package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
  47. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  48. package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
  49. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  50. package/dist/types/modes/components/session-observer-overlay.d.ts +22 -0
  51. package/dist/types/modes/components/settings-selector.d.ts +11 -0
  52. package/dist/types/modes/components/status-line/gh.d.ts +5 -0
  53. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  54. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
  55. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  56. package/dist/types/modes/interactive-mode.d.ts +2 -0
  57. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  58. package/dist/types/modes/types.d.ts +2 -0
  59. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  60. package/dist/types/reminders/star-reminder.d.ts +3 -13
  61. package/dist/types/runtime-mcp/manager.d.ts +1 -0
  62. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  63. package/dist/types/sdk/acp/adapter.d.ts +3 -1
  64. package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
  65. package/dist/types/sdk/broker/transport.d.ts +2 -0
  66. package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
  67. package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -1
  68. package/dist/types/sdk/bus/config.d.ts +6 -2
  69. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  70. package/dist/types/sdk/bus/index.d.ts +44 -1
  71. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +44 -0
  72. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  73. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
  74. package/dist/types/sdk/bus/reconciliation-store.d.ts +69 -0
  75. package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
  76. package/dist/types/sdk/bus/rich-render.d.ts +2 -0
  77. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
  78. package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
  79. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -4
  80. package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
  81. package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
  82. package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
  83. package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
  84. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  85. package/dist/types/sdk/host/host.d.ts +2 -0
  86. package/dist/types/sdk/host/query/handlers.d.ts +5 -0
  87. package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
  88. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  89. package/dist/types/sdk/prompt-status.d.ts +23 -3
  90. package/dist/types/sdk/session.d.ts +6 -0
  91. package/dist/types/session/agent-session.d.ts +82 -12
  92. package/dist/types/session/artifacts.d.ts +8 -0
  93. package/dist/types/session/blob-store.d.ts +57 -4
  94. package/dist/types/session/internal/managed-session-storage.d.ts +8 -1
  95. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  96. package/dist/types/session/session-manager.d.ts +48 -19
  97. package/dist/types/session/session-storage.d.ts +4 -0
  98. package/dist/types/session/streaming-output.d.ts +96 -12
  99. package/dist/types/setup/credential-import.d.ts +12 -0
  100. package/dist/types/skill-state/active-state.d.ts +12 -4
  101. package/dist/types/skill-state/workflow-hud.d.ts +9 -0
  102. package/dist/types/task/executor.d.ts +3 -2
  103. package/dist/types/task/index.d.ts +3 -1
  104. package/dist/types/task/provider-retry-status.d.ts +8 -0
  105. package/dist/types/task/receipt.d.ts +7 -1
  106. package/dist/types/task/types.d.ts +23 -0
  107. package/dist/types/tools/ask-answer-registry.d.ts +2 -2
  108. package/dist/types/tools/bash-interactive.d.ts +4 -1
  109. package/dist/types/tools/bash.d.ts +3 -1
  110. package/dist/types/tools/browser/launch.d.ts +7 -0
  111. package/dist/types/tools/fetch.d.ts +17 -1
  112. package/dist/types/tools/image-gen.d.ts +4 -0
  113. package/dist/types/tools/index.d.ts +4 -0
  114. package/dist/types/tools/output-meta.d.ts +44 -3
  115. package/dist/types/tools/path-utils.d.ts +1 -0
  116. package/dist/types/tools/read-internals.d.ts +12 -0
  117. package/dist/types/tools/read.d.ts +45 -7
  118. package/dist/types/tools/resource-gc.d.ts +4 -0
  119. package/dist/types/tools/review.d.ts +5 -7
  120. package/dist/types/tools/subagent.d.ts +2 -0
  121. package/dist/types/tools/tool-result.d.ts +7 -1
  122. package/dist/types/utils/gh.d.ts +13 -0
  123. package/dist/types/utils/image-resize.d.ts +1 -0
  124. package/dist/types/web/search/providers/kimi.d.ts +2 -0
  125. package/dist/types/web/search/providers/searxng.d.ts +7 -0
  126. package/dist/types/web/search/providers/xai.d.ts +2 -0
  127. package/package.json +11 -13
  128. package/scripts/acp-conformance-agent.ts +218 -0
  129. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  130. package/scripts/capture-gjc-bundle-settings.ts +306 -0
  131. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  132. package/scripts/generate-sdk-operation-inventory.ts +5 -0
  133. package/scripts/resident-memory-bench.ts +1341 -0
  134. package/scripts/run-acp-conformance.ts +285 -0
  135. package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
  136. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  137. package/src/async/job-manager.ts +78 -20
  138. package/src/capability/index.ts +8 -0
  139. package/src/cli/fast-help.ts +1 -1
  140. package/src/cli/plugin-cli.ts +176 -18
  141. package/src/cli/read-cli.ts +2 -1
  142. package/src/cli.ts +1 -0
  143. package/src/commands/deep-interview.ts +29 -9
  144. package/src/commands/harness.ts +35 -12
  145. package/src/commands/read.ts +11 -2
  146. package/src/commands/team.ts +20 -2
  147. package/src/config/file-lock.ts +11 -2
  148. package/src/config/model-registry.ts +21 -2
  149. package/src/config/provider-auth-health.ts +42 -0
  150. package/src/config/provider-ranking.ts +119 -0
  151. package/src/config/settings-schema.ts +74 -1
  152. package/src/config/settings.ts +246 -108
  153. package/src/coordinator-mcp/question-gate-codec.ts +1 -1
  154. package/src/coordinator-mcp/server.ts +5 -12
  155. package/src/defaults/gjc/skills/deep-interview/SKILL.md +230 -82
  156. package/src/defaults/gjc/skills/ralplan/SKILL.md +77 -41
  157. package/src/defaults/gjc/skills/team/SKILL.md +3 -1
  158. package/src/defaults/gjc/skills/ultragoal/SKILL.md +62 -39
  159. package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
  160. package/src/defaults/gjc-defaults.ts +4 -4
  161. package/src/eval/py/executor.ts +7 -2
  162. package/src/exa/mcp-client.ts +12 -3
  163. package/src/exec/bash-executor.ts +144 -27
  164. package/src/extensibility/extensions/runner.ts +15 -0
  165. package/src/extensibility/extensions/types.ts +46 -5
  166. package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
  167. package/src/extensibility/gjc-plugins/index.ts +19 -2
  168. package/src/extensibility/gjc-plugins/installer.ts +189 -50
  169. package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
  170. package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
  171. package/src/extensibility/gjc-plugins/observability.ts +3 -3
  172. package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
  173. package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
  174. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
  175. package/src/extensibility/gjc-plugins/schema.ts +84 -12
  176. package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
  177. package/src/extensibility/gjc-plugins/types.ts +141 -0
  178. package/src/extensibility/gjc-plugins/validation.ts +9 -0
  179. package/src/extensibility/shared-events.ts +5 -0
  180. package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
  181. package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
  182. package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
  183. package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
  184. package/src/gjc-runtime/deep-interview-state.ts +12 -943
  185. package/src/gjc-runtime/gc-render.ts +1 -0
  186. package/src/gjc-runtime/gc-runtime.ts +11 -1
  187. package/src/gjc-runtime/launch-tmux.ts +25 -6
  188. package/src/gjc-runtime/managed-owner-admission.ts +1 -1
  189. package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
  190. package/src/gjc-runtime/ralplan-runtime.ts +1149 -98
  191. package/src/gjc-runtime/state-runtime.ts +2 -1
  192. package/src/gjc-runtime/state-writer.ts +70 -222
  193. package/src/gjc-runtime/team-launch.ts +42 -5
  194. package/src/gjc-runtime/team-runtime.ts +1085 -40
  195. package/src/gjc-runtime/team-store.ts +32 -0
  196. package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
  197. package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
  198. package/src/gjc-runtime/team-workers.ts +1 -0
  199. package/src/gjc-runtime/tmux-owner-isolation.ts +28 -0
  200. package/src/gjc-runtime/tmux-sessions.ts +58 -13
  201. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
  202. package/src/gjc-runtime/ultragoal-runtime.ts +414 -618
  203. package/src/gjc-runtime/workflow-manifest.generated.json +105 -449
  204. package/src/gjc-runtime/workflow-manifest.ts +38 -247
  205. package/src/hooks/native-skill-hook.ts +17 -4
  206. package/src/hooks/skill-state.ts +1 -23
  207. package/src/internal-urls/docs-index.generated.ts +21 -22
  208. package/src/internal-urls/local-root-gc.ts +127 -0
  209. package/src/main.ts +9 -5
  210. package/src/memories/index.ts +10 -0
  211. package/src/modes/DESIGN.md +98 -0
  212. package/src/modes/acp/acp-agent.ts +339 -191
  213. package/src/modes/acp/acp-event-mapper.ts +14 -7
  214. package/src/modes/acp/acp-mode.ts +39 -3
  215. package/src/modes/components/assistant-message.ts +123 -22
  216. package/src/modes/components/gjc-bundle-settings.ts +508 -0
  217. package/src/modes/components/irc-sidebar.ts +176 -62
  218. package/src/modes/components/model-selector.ts +61 -4
  219. package/src/modes/components/notifications-settings-editor.ts +12 -1
  220. package/src/modes/components/oauth-selector.ts +96 -28
  221. package/src/modes/components/read-tool-group.ts +82 -14
  222. package/src/modes/components/session-observer-overlay.ts +457 -83
  223. package/src/modes/components/settings-selector.ts +116 -23
  224. package/src/modes/components/status-line/gh.ts +16 -0
  225. package/src/modes/components/tool-execution.ts +138 -27
  226. package/src/modes/components/tool-status-header.ts +3 -14
  227. package/src/modes/components/transcript-viewer-overlay.ts +99 -24
  228. package/src/modes/controllers/command-controller.ts +4 -0
  229. package/src/modes/controllers/event-controller.ts +109 -27
  230. package/src/modes/controllers/extension-ui-controller.ts +26 -2
  231. package/src/modes/controllers/input-controller.ts +18 -3
  232. package/src/modes/controllers/selector-controller.ts +127 -36
  233. package/src/modes/interactive-mode.ts +62 -3
  234. package/src/modes/irc-observation-ledger.ts +71 -15
  235. package/src/modes/runtime-init.ts +22 -2
  236. package/src/modes/types.ts +2 -0
  237. package/src/modes/utils/ui-helpers.ts +94 -1
  238. package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
  239. package/src/prompts/agents/architect.md +9 -1
  240. package/src/prompts/agents/critic.md +9 -0
  241. package/src/prompts/system/eager-todo.md +8 -4
  242. package/src/prompts/system/system-prompt.md +11 -5
  243. package/src/prompts/tools/bash.md +5 -5
  244. package/src/prompts/tools/read.md +4 -2
  245. package/src/prompts/tools/search-tool-bm25.md +5 -0
  246. package/src/prompts/tools/task.md +1 -1
  247. package/src/reminders/star-reminder.ts +4 -42
  248. package/src/runtime-mcp/manager.ts +19 -4
  249. package/src/runtime-mcp/smithery-auth.ts +17 -2
  250. package/src/runtime-mcp/smithery-connect.ts +11 -1
  251. package/src/sdk/acp/adapter.ts +22 -6
  252. package/src/sdk/broker/lifecycle.ts +25 -6
  253. package/src/sdk/broker/transport.ts +35 -3
  254. package/src/sdk/bus/attachment-registry.ts +5 -1
  255. package/src/sdk/bus/chat-daemon-cli.ts +4 -39
  256. package/src/sdk/bus/chat-daemon-control.ts +6 -3
  257. package/src/sdk/bus/config.ts +18 -2
  258. package/src/sdk/bus/control-drain-lease.ts +104 -0
  259. package/src/sdk/bus/index.ts +984 -146
  260. package/src/sdk/bus/kind-aware-reconciliation.ts +317 -0
  261. package/src/sdk/bus/lifecycle-commands.ts +4 -1
  262. package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
  263. package/src/sdk/bus/prompt-reconciliation.ts +2 -1
  264. package/src/sdk/bus/reconciliation-store.ts +303 -0
  265. package/src/sdk/bus/rich-draft.ts +1 -1
  266. package/src/sdk/bus/rich-render.ts +2 -2
  267. package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
  268. package/src/sdk/bus/telegram-cli.ts +33 -8
  269. package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
  270. package/src/sdk/bus/telegram-daemon-contract.ts +11 -4
  271. package/src/sdk/bus/telegram-daemon.ts +916 -23
  272. package/src/sdk/bus/telegram-reference.ts +40 -3
  273. package/src/sdk/bus/threaded-render.ts +48 -3
  274. package/src/sdk/bus/topic-registry.ts +20 -0
  275. package/src/sdk/host/control/dispatch.ts +5 -1
  276. package/src/sdk/host/control/operations.ts +1 -1
  277. package/src/sdk/host/host.ts +23 -1
  278. package/src/sdk/host/query/handlers.ts +55 -0
  279. package/src/sdk/host/query/revision-store.ts +36 -2
  280. package/src/sdk/host/reverse-leases.ts +44 -7
  281. package/src/sdk/mcp/server.ts +46 -18
  282. package/src/sdk/prompt-status.ts +16 -3
  283. package/src/sdk/protocol/operation-inventory.generated.json +51 -0
  284. package/src/sdk/protocol/operation-registry.ts +7 -2
  285. package/src/sdk/session.ts +101 -10
  286. package/src/session/agent-session.ts +1286 -220
  287. package/src/session/artifacts.ts +22 -0
  288. package/src/session/auth-broker-config.ts +10 -3
  289. package/src/session/blob-store.ts +915 -24
  290. package/src/session/internal/managed-session-scope.ts +44 -6
  291. package/src/session/internal/managed-session-storage.ts +48 -6
  292. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  293. package/src/session/session-manager.ts +2364 -438
  294. package/src/session/session-storage.ts +9 -1
  295. package/src/session/streaming-output.ts +500 -111
  296. package/src/setup/credential-import.ts +75 -13
  297. package/src/setup/provider-onboarding.ts +19 -4
  298. package/src/skill-state/active-state.ts +32 -10
  299. package/src/skill-state/workflow-hud.ts +42 -2
  300. package/src/skill-state/workflow-mutation-guard.ts +560 -8
  301. package/src/task/executor.ts +122 -39
  302. package/src/task/index.ts +23 -14
  303. package/src/task/provider-retry-status.ts +16 -0
  304. package/src/task/receipt.ts +23 -7
  305. package/src/task/render.ts +6 -2
  306. package/src/task/types.ts +108 -0
  307. package/src/tools/ask-answer-registry.ts +12 -6
  308. package/src/tools/ask.ts +169 -118
  309. package/src/tools/ast-edit.ts +1 -0
  310. package/src/tools/ast-grep.ts +1 -0
  311. package/src/tools/bash-interactive.ts +8 -3
  312. package/src/tools/bash.ts +503 -60
  313. package/src/tools/browser/launch.ts +41 -6
  314. package/src/tools/fetch.ts +114 -23
  315. package/src/tools/find.ts +1 -0
  316. package/src/tools/image-gen.ts +29 -5
  317. package/src/tools/index.ts +4 -0
  318. package/src/tools/output-meta.ts +281 -20
  319. package/src/tools/path-utils.ts +8 -3
  320. package/src/tools/read-internals.ts +44 -0
  321. package/src/tools/read.ts +1560 -310
  322. package/src/tools/resource-gc.ts +182 -18
  323. package/src/tools/review.ts +5 -7
  324. package/src/tools/search.ts +1 -0
  325. package/src/tools/subagent-render.ts +6 -2
  326. package/src/tools/subagent.ts +6 -0
  327. package/src/tools/telegram-send.ts +6 -3
  328. package/src/tools/tool-result.ts +14 -1
  329. package/src/utils/gh.ts +39 -0
  330. package/src/utils/image-resize.ts +3 -0
  331. package/src/web/search/providers/anthropic.ts +4 -4
  332. package/src/web/search/providers/codex.ts +1 -1
  333. package/src/web/search/providers/kimi.ts +15 -5
  334. package/src/web/search/providers/searxng.ts +47 -4
  335. package/src/web/search/providers/xai.ts +7 -2
  336. package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
  337. package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
  338. package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
  339. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
  340. package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
  341. package/src/gjc-runtime/deep-interview-draft.ts +0 -843
  342. package/src/gjc-runtime/deep-interview-payload.ts +0 -262
  343. package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
  344. package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { getPuppeteerDir } from "@gajae-code/utils/dirs";
5
+ import { $credentialEnv } from "@gajae-code/utils/env";
5
6
  import * as logger from "@gajae-code/utils/logger";
6
7
  import { $which } from "@gajae-code/utils/which";
7
8
  import * as browsers from "@puppeteer/browsers";
@@ -80,6 +81,42 @@ export async function loadPuppeteerInWorker(safeDir: string): Promise<typeof Pup
80
81
  }
81
82
  }
82
83
 
84
+ /**
85
+ * Browser launch overrides resolved from trusted environment sources only.
86
+ *
87
+ * These choose the browser binary and route/verify its traffic, so whatever can
88
+ * set them controls what runs and who sees the session. `$env` merges the
89
+ * caller's `cwd/.env` into `process.env`, so reading them directly would let
90
+ * repository content pick the executable, point every request at an attacker's
91
+ * proxy, or disable certificate validation. Resolve them the same way provider
92
+ * credentials are: launching shell plus GJC/user-owned `.env` files, never the
93
+ * project `.env`.
94
+ */
95
+ function trustedBrowserEnv(name: string): string | undefined {
96
+ return $credentialEnv(name);
97
+ }
98
+
99
+ /** Boolean-like trusted browser env knob (`true`/`1`/`yes`/`on`). */
100
+ function browserLaunchFlagEnabled(name: string): boolean {
101
+ const value = trustedBrowserEnv(name)?.toLowerCase();
102
+ return value === "true" || value === "1" || value === "yes" || value === "on";
103
+ }
104
+
105
+ /** Test seam: the browser launch overrides as resolved from trusted env. */
106
+ export function resolveBrowserEnvOverridesForTest(): {
107
+ executablePath: string | undefined;
108
+ proxy: string | undefined;
109
+ proxyBypassLoopback: boolean;
110
+ ignoreCertErrors: boolean;
111
+ } {
112
+ return {
113
+ executablePath: trustedBrowserEnv("PUPPETEER_EXECUTABLE_PATH"),
114
+ proxy: trustedBrowserEnv("PUPPETEER_PROXY"),
115
+ proxyBypassLoopback: browserLaunchFlagEnabled("PUPPETEER_PROXY_BYPASS_LOOPBACK"),
116
+ ignoreCertErrors: browserLaunchFlagEnabled("PUPPETEER_PROXY_IGNORE_CERT_ERRORS"),
117
+ };
118
+ }
119
+
83
120
  /**
84
121
  * Lazily download Chromium on first browser launch via @puppeteer/browsers.
85
122
  * Skipped when a system Chromium (NixOS) or PUPPETEER_EXECUTABLE_PATH is set.
@@ -89,7 +126,7 @@ let chromiumExecutablePromise: Promise<string | undefined> | undefined;
89
126
  async function ensureChromiumExecutable(): Promise<string | undefined> {
90
127
  const sysChrome = resolveSystemChromium();
91
128
  if (sysChrome) return sysChrome;
92
- const envPath = process.env.PUPPETEER_EXECUTABLE_PATH;
129
+ const envPath = trustedBrowserEnv("PUPPETEER_EXECUTABLE_PATH");
93
130
  if (envPath) return envPath;
94
131
  if (chromiumExecutablePromise) return chromiumExecutablePromise;
95
132
 
@@ -272,13 +309,12 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
272
309
  // Isolated copy only (caller guarantees). Never point this at a live profile.
273
310
  launchArgs.push(`--user-data-dir=${opts.profileWarmupDir}`);
274
311
  }
275
- const proxy = process.env.PUPPETEER_PROXY;
312
+ const proxy = trustedBrowserEnv("PUPPETEER_PROXY");
276
313
  if (proxy) {
277
314
  launchArgs.push(`--proxy-server=${proxy}`);
278
315
  // Chrome (since v72) bypasses proxies for localhost by default. When PUPPETEER_PROXY_BYPASS_LOOPBACK
279
316
  // is true, add <-loopback> so traffic to localhost reaches the proxy (e.g. for mitmdump/auth capture).
280
- const bypassLoopback = process.env.PUPPETEER_PROXY_BYPASS_LOOPBACK?.toLowerCase();
281
- if (bypassLoopback === "true" || bypassLoopback === "1" || bypassLoopback === "yes" || bypassLoopback === "on") {
317
+ if (browserLaunchFlagEnabled("PUPPETEER_PROXY_BYPASS_LOOPBACK")) {
282
318
  launchArgs.push("--proxy-bypass-list=<-loopback>");
283
319
  }
284
320
  }
@@ -287,8 +323,7 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
287
323
  if (opts.geo?.locale) {
288
324
  launchArgs.push(`--lang=${opts.geo.locale}`);
289
325
  }
290
- const ignoreCert = process.env.PUPPETEER_PROXY_IGNORE_CERT_ERRORS?.toLowerCase();
291
- if (ignoreCert === "true" || ignoreCert === "1" || ignoreCert === "yes" || ignoreCert === "on") {
326
+ if (browserLaunchFlagEnabled("PUPPETEER_PROXY_IGNORE_CERT_ERRORS")) {
292
327
  launchArgs.push("--ignore-certificate-errors");
293
328
  }
294
329
  const launchEnv = opts.geo?.timezone ? { ...process.env, TZ: opts.geo.timezone } : undefined;
@@ -10,7 +10,7 @@ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
10
10
  import { type Theme, theme } from "../modes/theme/theme";
11
11
  import type { ToolSession } from "../sdk";
12
12
  import type { AgentStorage } from "../session/agent-storage";
13
- import { DEFAULT_MAX_BYTES, truncateHead } from "../session/streaming-output";
13
+ import { DEFAULT_MAX_BYTES, type TruncationDirection, truncateContent } from "../session/streaming-output";
14
14
  import { renderStatusLine } from "../tui";
15
15
  import { CachedOutputBlock } from "../tui/output-block";
16
16
  import { formatDimensionNote, resizeImage } from "../utils/image-resize";
@@ -1146,7 +1146,12 @@ interface ReadUrlCacheEntry {
1146
1146
  artifactId?: string;
1147
1147
  details: ReadUrlToolDetails;
1148
1148
  image?: FetchImagePayload;
1149
+ /** Complete URL output, including its preamble and body. */
1149
1150
  output: string;
1151
+ /** UTF-16 code-unit offset of the preamble in `output`. */
1152
+ preambleChars: number;
1153
+ /** Offset of the preamble after `wrapUntrustedContent(output)` is persisted. */
1154
+ wrappedPreambleChars?: number;
1150
1155
  }
1151
1156
 
1152
1157
  const readUrlCache = new Map<string, ReadUrlCacheEntry>();
@@ -1178,7 +1183,13 @@ async function materializeReadUrlCacheEntry(
1178
1183
  if (entry.artifactId) {
1179
1184
  const artifactOutput = await readArtifactOutput(session, entry.artifactId);
1180
1185
  if (artifactOutput !== null) {
1181
- return { ...entry, output: artifactOutput };
1186
+ // Artifacts retain their trust-boundary wrapper. Keep the raw preamble
1187
+ // coordinate alongside the escaped coordinate for live/rehydrated parity.
1188
+ return {
1189
+ ...entry,
1190
+ output: artifactOutput,
1191
+ ...(entry.wrappedPreambleChars !== undefined ? { wrappedPreambleChars: entry.wrappedPreambleChars } : {}),
1192
+ };
1182
1193
  }
1183
1194
  }
1184
1195
  return null;
@@ -1218,8 +1229,8 @@ async function buildReadUrlCacheEntry(
1218
1229
 
1219
1230
  const storage = session.settings.getStorage();
1220
1231
  const result = await renderUrl(url, effectiveTimeout, raw, session.settings, signal, storage);
1221
- const output = buildUrlReadOutput(result, result.content);
1222
- const artifactId = options?.ensureArtifact ? await persistReadUrlArtifact(session, output) : undefined;
1232
+ const built = buildUrlReadOutput(result, result.content);
1233
+ const artifactId = options?.ensureArtifact ? await persistReadUrlArtifact(session, built.output) : undefined;
1223
1234
 
1224
1235
  return {
1225
1236
  artifactId,
@@ -1233,7 +1244,9 @@ async function buildReadUrlCacheEntry(
1233
1244
  notes: result.notes,
1234
1245
  },
1235
1246
  image: result.image,
1236
- output,
1247
+ output: built.output,
1248
+ preambleChars: built.preambleChars,
1249
+ wrappedPreambleChars: built.wrappedPreambleChars,
1237
1250
  };
1238
1251
  }
1239
1252
 
@@ -1263,60 +1276,138 @@ export async function loadReadUrlCacheEntry(
1263
1276
 
1264
1277
  const UNTRUSTED_CONTENT_OPEN = "<untrusted-content>";
1265
1278
  const UNTRUSTED_CONTENT_CLOSE = "</untrusted-content>";
1279
+ export const WRAP_PREFIX_CHARS = `${UNTRUSTED_CONTENT_OPEN}\n`.length;
1280
+ const WRAP_SUFFIX = `\n${UNTRUSTED_CONTENT_CLOSE}`;
1281
+
1282
+ export function escapeUntrustedContent(content: string): string {
1283
+ return content.replace(/<\/untrusted-content>/gi, "&lt;/untrusted-content>");
1284
+ }
1266
1285
 
1267
1286
  export function wrapUntrustedContent(content: string): string {
1268
- return `${UNTRUSTED_CONTENT_OPEN}\n${content.replace(/<\/untrusted-content>/gi, "&lt;/untrusted-content>")}\n${UNTRUSTED_CONTENT_CLOSE}`;
1287
+ return `${UNTRUSTED_CONTENT_OPEN}\n${escapeUntrustedContent(content)}${WRAP_SUFFIX}`;
1288
+ }
1289
+
1290
+ function isWrappedReadUrlOutput(output: string): boolean {
1291
+ return output.startsWith(`${UNTRUSTED_CONTENT_OPEN}\n`) && output.endsWith(WRAP_SUFFIX);
1292
+ }
1293
+
1294
+ /**
1295
+ * Remove only the outer URL-output wrapper before line pagination. The content
1296
+ * remains escaped exactly as persisted; it is deliberately never unescaped.
1297
+ */
1298
+ export function prepareReadUrlSelectorInput(
1299
+ output: string,
1300
+ preambleChars: number,
1301
+ wrappedPreambleChars?: number,
1302
+ ): { text: string; preambleChars: number } {
1303
+ if (!isWrappedReadUrlOutput(output)) return { text: output, preambleChars };
1304
+ const bodyEnd = output.length - WRAP_SUFFIX.length;
1305
+ return {
1306
+ text: output.slice(WRAP_PREFIX_CHARS, Math.max(WRAP_PREFIX_CHARS, bodyEnd)),
1307
+ preambleChars: Math.max(0, (wrappedPreambleChars ?? preambleChars) - WRAP_PREFIX_CHARS),
1308
+ };
1309
+ }
1310
+ interface BuiltUrlReadOutput {
1311
+ output: string;
1312
+ preambleChars: number;
1313
+ wrappedPreambleChars: number;
1269
1314
  }
1270
1315
 
1271
- function buildUrlReadOutput(result: FetchRenderResult, content: string): string {
1272
- let output = "";
1273
- output += `URL: ${result.finalUrl}\n`;
1274
- output += `Content-Type: ${result.contentType}\n`;
1275
- output += `Method: ${result.method}\n`;
1316
+ function buildReadUrlPreamble(
1317
+ result: Pick<FetchRenderResult, "finalUrl" | "contentType" | "method" | "notes">,
1318
+ ): string {
1319
+ let preamble = "";
1320
+ preamble += `URL: ${result.finalUrl}\n`;
1321
+ preamble += `Content-Type: ${result.contentType}\n`;
1322
+ preamble += `Method: ${result.method}\n`;
1276
1323
  if (result.notes.length > 0) {
1277
- output += `Notes: ${result.notes.join("; ")}\n`;
1324
+ preamble += `Notes: ${result.notes.join("; ")}\n`;
1278
1325
  }
1279
- output += `\n---\n\n`;
1280
- output += content;
1281
- return output;
1326
+ preamble += "\n---\n\n";
1327
+ return preamble;
1328
+ }
1329
+
1330
+ function buildUrlReadOutput(result: FetchRenderResult, content: string): BuiltUrlReadOutput {
1331
+ const preamble = buildReadUrlPreamble(result);
1332
+ const preambleChars = preamble.length;
1333
+ const output = `${preamble}${content}`;
1334
+ return {
1335
+ output,
1336
+ preambleChars,
1337
+ wrappedPreambleChars: WRAP_PREFIX_CHARS + escapeUntrustedContent(preamble).length,
1338
+ };
1282
1339
  }
1283
1340
 
1284
1341
  export async function executeReadUrl(
1285
1342
  session: ToolSession,
1286
1343
  params: { path: string; raw?: boolean },
1287
1344
  signal?: AbortSignal,
1345
+ direction: TruncationDirection = "head",
1288
1346
  ): Promise<AgentToolResult<ReadUrlToolDetails>> {
1289
1347
  let cacheEntry = await loadReadUrlCacheEntry(session, params, signal, { preferCached: true });
1290
- const truncation = truncateHead(cacheEntry.output, {
1348
+ // `head` intentionally retains the historical whole-output accounting so the
1349
+ // existing URL golden remains byte-identical. Non-head directions account for
1350
+ // and truncate only the body; preamble bytes/chars are never part of that cap.
1351
+ const effectiveDirection =
1352
+ direction !== "head" && cacheEntry.wrappedPreambleChars === undefined ? "head" : direction;
1353
+ const isWrappedOutput = isWrappedReadUrlOutput(cacheEntry.output);
1354
+ const preambleChars = isWrappedOutput
1355
+ ? (cacheEntry.wrappedPreambleChars ??
1356
+ WRAP_PREFIX_CHARS + escapeUntrustedContent(buildReadUrlPreamble(cacheEntry.details)).length)
1357
+ : cacheEntry.preambleChars;
1358
+ const wrappedPrefix = isWrappedOutput ? WRAP_PREFIX_CHARS : 0;
1359
+ const wrappedSuffix = isWrappedOutput ? `\n${UNTRUSTED_CONTENT_CLOSE}` : "";
1360
+ const bodyEnd = cacheEntry.output.length - wrappedSuffix.length;
1361
+ const unwrappedOutput = isWrappedOutput
1362
+ ? cacheEntry.output.slice(wrappedPrefix, Math.max(wrappedPrefix, bodyEnd))
1363
+ : cacheEntry.output;
1364
+ const truncationInput =
1365
+ effectiveDirection === "head"
1366
+ ? unwrappedOutput
1367
+ : cacheEntry.output.slice(preambleChars, Math.max(preambleChars, bodyEnd));
1368
+ const truncation = truncateContent(truncationInput, {
1291
1369
  maxBytes: DEFAULT_MAX_BYTES,
1292
1370
  maxLines: FETCH_DEFAULT_MAX_LINES,
1371
+ direction: effectiveDirection,
1293
1372
  });
1294
1373
  const needsArtifact = truncation.truncated;
1295
1374
  if (needsArtifact && !cacheEntry.artifactId) {
1296
1375
  cacheEntry = await ensureReadUrlCacheArtifact(session, cacheEntry);
1297
1376
  cacheReadUrlEntry(session, params.path, params.raw ?? false, cacheEntry);
1298
1377
  }
1299
- const output = needsArtifact ? truncation.content : cacheEntry.output;
1378
+ const output =
1379
+ effectiveDirection !== "head"
1380
+ ? needsArtifact
1381
+ ? `${cacheEntry.output.slice(0, preambleChars)}${truncation.content}${wrappedSuffix}`
1382
+ : cacheEntry.output
1383
+ : truncation.content;
1300
1384
  const details: ReadUrlToolDetails = {
1301
1385
  ...cacheEntry.details,
1302
1386
  truncated: Boolean(cacheEntry.details.truncated || needsArtifact),
1303
1387
  };
1304
1388
 
1305
- const contentBlocks: Array<TextContent | ImageContent> = [{ type: "text", text: wrapUntrustedContent(output) }];
1389
+ const contentBlocks: Array<TextContent | ImageContent> = [
1390
+ { type: "text", text: isWrappedOutput && effectiveDirection !== "head" ? output : wrapUntrustedContent(output) },
1391
+ ];
1306
1392
  if (cacheEntry.image) {
1307
1393
  contentBlocks.push({ type: "image", data: cacheEntry.image.data, mimeType: cacheEntry.image.mimeType });
1308
1394
  }
1309
1395
 
1310
1396
  const resultBuilder = toolResult(details).content(contentBlocks).sourceUrl(details.finalUrl);
1311
1397
  if (needsArtifact) {
1312
- resultBuilder.truncation(truncation, { direction: "head", artifactId: cacheEntry.artifactId });
1398
+ resultBuilder.truncation(truncation, {
1399
+ direction: effectiveDirection === "both" ? "middle" : effectiveDirection === "last" ? "tail" : "head",
1400
+ artifactId: cacheEntry.artifactId,
1401
+ ...(effectiveDirection !== "head" ? { maxBytes: DEFAULT_MAX_BYTES } : {}),
1402
+ });
1313
1403
  } else if (cacheEntry.details.truncated) {
1314
- const outputLines = cacheEntry.output.split("\n").length;
1315
- const outputBytes = Buffer.byteLength(cacheEntry.output, "utf-8");
1404
+ const reportedText = effectiveDirection === "head" ? cacheEntry.output : truncationInput;
1405
+ const outputLines = reportedText.split("\n").length;
1406
+ const outputBytes = Buffer.byteLength(reportedText, "utf-8");
1316
1407
  const totalBytes = Math.max(outputBytes + 1, MAX_OUTPUT_CHARS + 1);
1317
1408
  const totalLines = outputLines + 1;
1318
- resultBuilder.truncationFromText(cacheEntry.output, {
1319
- direction: "tail",
1409
+ resultBuilder.truncationFromText(reportedText, {
1410
+ direction: effectiveDirection === "both" ? "middle" : effectiveDirection === "last" ? "tail" : "head",
1320
1411
  totalLines,
1321
1412
  totalBytes,
1322
1413
  maxBytes: MAX_OUTPUT_CHARS,
package/src/tools/find.ts CHANGED
@@ -158,6 +158,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
158
158
  const resource = await internalRouter.resolve(rawPattern, {
159
159
  cwd: this.session.cwd,
160
160
  getArtifactsDir: this.session.getArtifactsDir,
161
+ getAuthorizedArtifactsDirs: this.session.getAuthorizedArtifactsDirs,
161
162
  });
162
163
  if (!resource.sourcePath) {
163
164
  throw new ToolError(`Cannot find internal URL without a backing file: ${rawPattern}`);
@@ -12,7 +12,7 @@ import {
12
12
  URL_PATHS,
13
13
  } from "@gajae-code/ai/providers/openai-codex/constants";
14
14
  import {
15
- $env,
15
+ $credentialEnv,
16
16
  isEnoent,
17
17
  parseImageMetadata,
18
18
  prompt,
@@ -794,6 +794,23 @@ async function findAlibabaImageCredentials(
794
794
  return { provider: "alibaba", apiKey };
795
795
  }
796
796
 
797
+ /**
798
+ * Google image-provider key fallback, from trusted environment sources only.
799
+ *
800
+ * `$env` merges the caller's `cwd/.env`, so reading the key there would let
801
+ * repository content supply the credential these image requests authenticate
802
+ * with. Provider credentials resolve from the launching shell plus GJC/user-owned
803
+ * `.env` files, never the project `.env`.
804
+ */
805
+ function googleImageApiKeyFromEnv(): string | undefined {
806
+ return $credentialEnv("GOOGLE_API_KEY");
807
+ }
808
+
809
+ /** Test seam: the Google image key fallback as resolved from trusted env. */
810
+ export function googleImageApiKeyFromEnvForTest(): string | undefined {
811
+ return googleImageApiKeyFromEnv();
812
+ }
813
+
797
814
  async function findImageApiKey(
798
815
  modelRegistry?: ModelRegistry,
799
816
  activeModel?: Model,
@@ -827,7 +844,7 @@ async function findImageApiKey(
827
844
  if (config.provider === "gemini") {
828
845
  const geminiKey = getEnvApiKey("google");
829
846
  if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
830
- const googleKey = $env.GOOGLE_API_KEY;
847
+ const googleKey = googleImageApiKeyFromEnv();
831
848
  return googleKey ? { provider: "gemini", apiKey: googleKey } : null;
832
849
  }
833
850
  if (config.provider === "openrouter") {
@@ -850,7 +867,7 @@ async function findImageApiKey(
850
867
  } else if (preferredImageProvider === "gemini") {
851
868
  const geminiKey = getEnvApiKey("google");
852
869
  if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
853
- const googleKey = $env.GOOGLE_API_KEY;
870
+ const googleKey = googleImageApiKeyFromEnv();
854
871
  return googleKey ? { provider: "gemini", apiKey: googleKey } : null;
855
872
  } else if (preferredImageProvider === "openrouter") {
856
873
  const openRouterKey = getEnvApiKey("openrouter");
@@ -874,7 +891,7 @@ async function findImageApiKey(
874
891
  const geminiKey = getEnvApiKey("google");
875
892
  if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
876
893
 
877
- const googleKey = $env.GOOGLE_API_KEY;
894
+ const googleKey = googleImageApiKeyFromEnv();
878
895
  if (googleKey) return { provider: "gemini", apiKey: googleKey };
879
896
 
880
897
  const alibaba = await findAlibabaImageCredentials(modelRegistry, sessionId);
@@ -1106,7 +1123,9 @@ function getOpenAIBaseUrl(model: Model, authCredentialType?: "api_key" | "oauth"
1106
1123
  return (model.baseUrl || CODEX_BASE_URL).replace(/\/+$/, "");
1107
1124
  }
1108
1125
  if (authCredentialType === "oauth") return DEFAULT_OPENAI_BASE_URL;
1109
- const envBaseUrl = $env.OPENAI_BASE_URL?.trim();
1126
+ // Trusted sources only: this base URL becomes the endpoint for authenticated
1127
+ // image requests, and `$env` merges the caller's `cwd/.env`.
1128
+ const envBaseUrl = $credentialEnv("OPENAI_BASE_URL");
1110
1129
  const configuredBaseUrl = model.baseUrl?.trim();
1111
1130
  if (envBaseUrl && (!configuredBaseUrl || configuredBaseUrl.toLowerCase().includes("api.openai.com"))) {
1112
1131
  return envBaseUrl.replace(/\/+$/, "");
@@ -1114,6 +1133,11 @@ function getOpenAIBaseUrl(model: Model, authCredentialType?: "api_key" | "oauth"
1114
1133
  return (configuredBaseUrl || envBaseUrl || DEFAULT_OPENAI_BASE_URL).replace(/\/+$/, "");
1115
1134
  }
1116
1135
 
1136
+ /** Test seam: the image-request base URL as resolved from trusted env. */
1137
+ export function getOpenAIImageBaseUrlForTest(model: Model, authCredentialType?: "api_key" | "oauth"): string {
1138
+ return getOpenAIBaseUrl(model, authCredentialType);
1139
+ }
1140
+
1117
1141
  function getOpenAIResponsesUrl(model: Model, authCredentialType?: "api_key" | "oauth"): string {
1118
1142
  const baseUrl = getOpenAIBaseUrl(model, authCredentialType);
1119
1143
  if (model.api !== "openai-codex-responses" && model.provider !== "openai-codex") {
@@ -292,6 +292,8 @@ export interface ToolSession {
292
292
  strictToolNames?: boolean;
293
293
  /** Get artifacts directory for artifact:// URLs */
294
294
  getArtifactsDir?: () => string | null;
295
+ /** Additional artifacts directories explicitly authorized for this session's tree (parent/child/sibling reads). Derived only from the explicitly adopted/shared `ArtifactManager`; never enumerates unrelated live sessions. */
296
+ getAuthorizedArtifactsDirs?: () => readonly string[];
295
297
  /** Get the ArtifactManager backing this session (shared across parent + subagents). */
296
298
  getArtifactManager?: () => ArtifactManager | null;
297
299
  /** Allocate a new artifact path and ID for session-scoped truncated output. */
@@ -314,6 +316,8 @@ export interface ToolSession {
314
316
  settings: Settings;
315
317
  /** Live service-tier intent of the parent session, inherited by `inherit` subagents. */
316
318
  serviceTier?: ServiceTier;
319
+ /** Whether the effective subagent tier grants fast mode for a resolved provider. */
320
+ isFastForSubagentProvider?: (provider?: string) => boolean;
317
321
  /** Plan mode state (if active) */
318
322
  getPlanModeState?: () => PlanModeState | undefined;
319
323
  /** Goal mode state (if active or paused) */