@gajae-code/coding-agent 0.11.0 → 0.11.2

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 (418) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/dist/types/async/job-manager.d.ts +46 -7
  3. package/dist/types/cli/config-cli.d.ts +11 -1
  4. package/dist/types/cli/notify-cli.d.ts +1 -0
  5. package/dist/types/cli/session-picker.d.ts +2 -1
  6. package/dist/types/cli/setup-cli.d.ts +1 -1
  7. package/dist/types/commands/mcp-serve.d.ts +33 -0
  8. package/dist/types/commands/mcp.d.ts +1 -1
  9. package/dist/types/commands/notify.d.ts +0 -2
  10. package/dist/types/commands/sdk.d.ts +1 -1
  11. package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
  12. package/dist/types/config/file-lock.d.ts +14 -4
  13. package/dist/types/config/keybindings.d.ts +42 -1
  14. package/dist/types/config/model-auth.d.ts +3 -0
  15. package/dist/types/config/model-bindings-applier.d.ts +13 -0
  16. package/dist/types/config/model-discovery-manager.d.ts +49 -0
  17. package/dist/types/config/model-equivalence.d.ts +20 -0
  18. package/dist/types/config/model-registry.d.ts +16 -15
  19. package/dist/types/config/model-resolver.d.ts +28 -18
  20. package/dist/types/config/models-config-schema.d.ts +11 -0
  21. package/dist/types/config/settings-schema.d.ts +56 -1
  22. package/dist/types/config/settings.d.ts +14 -11
  23. package/dist/types/coordinator-mcp/policy.d.ts +5 -0
  24. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
  25. package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
  26. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  27. package/dist/types/discovery/helpers.d.ts +0 -6
  28. package/dist/types/edit/index.d.ts +3 -0
  29. package/dist/types/eval/py/env.d.ts +9 -0
  30. package/dist/types/extensibility/extensions/loader.d.ts +2 -1
  31. package/dist/types/extensibility/extensions/runner.d.ts +2 -2
  32. package/dist/types/extensibility/extensions/types.d.ts +34 -3
  33. package/dist/types/extensibility/hooks/runner.d.ts +1 -1
  34. package/dist/types/extensibility/shared-events.d.ts +2 -0
  35. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  36. package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
  37. package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
  38. package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
  39. package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
  40. package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
  41. package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
  42. package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
  43. package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
  44. package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
  45. package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
  46. package/dist/types/gjc-runtime/team-store.d.ts +160 -0
  47. package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
  48. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
  49. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
  50. package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
  51. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
  52. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
  53. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
  54. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
  55. package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
  56. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
  57. package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
  58. package/dist/types/goals/runtime.d.ts +1 -0
  59. package/dist/types/goals/state.d.ts +8 -0
  60. package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
  61. package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
  62. package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
  63. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  64. package/dist/types/hindsight/state.d.ts +11 -4
  65. package/dist/types/lsp/lspmux.d.ts +5 -3
  66. package/dist/types/lsp/path-trust.d.ts +1 -0
  67. package/dist/types/main.d.ts +3 -2
  68. package/dist/types/memories/storage.d.ts +1 -0
  69. package/dist/types/modes/action-registry.d.ts +29 -0
  70. package/dist/types/modes/components/chrome.d.ts +14 -0
  71. package/dist/types/modes/components/command-palette.d.ts +27 -0
  72. package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
  73. package/dist/types/modes/components/index.d.ts +2 -1
  74. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  75. package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
  76. package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
  77. package/dist/types/modes/components/queue-pane.d.ts +12 -0
  78. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  79. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
  80. package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
  81. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  82. package/dist/types/modes/components/show-images-selector.d.ts +1 -1
  83. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  84. package/dist/types/modes/components/status-line.d.ts +1 -94
  85. package/dist/types/modes/components/tasks-pane.d.ts +15 -0
  86. package/dist/types/modes/components/theme-selector.d.ts +1 -1
  87. package/dist/types/modes/components/tool-execution.d.ts +15 -0
  88. package/dist/types/modes/components/tool-status-header.d.ts +111 -0
  89. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
  90. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  91. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
  92. package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
  93. package/dist/types/modes/controllers/input-controller.d.ts +8 -1
  94. package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
  95. package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
  96. package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
  97. package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
  98. package/dist/types/modes/interactive-mode.d.ts +21 -19
  99. package/dist/types/modes/jobs-observer.d.ts +2 -0
  100. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
  101. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +21 -2
  102. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
  103. package/dist/types/modes/tasks-aggregator.d.ts +39 -0
  104. package/dist/types/modes/transcript-item-registry.d.ts +60 -0
  105. package/dist/types/modes/types.d.ts +22 -15
  106. package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
  107. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  108. package/dist/types/sdk/broker/broker.d.ts +78 -12
  109. package/dist/types/sdk/broker/ensure.d.ts +9 -0
  110. package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
  111. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
  112. package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
  113. package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
  114. package/dist/types/sdk/broker/session-index.d.ts +0 -1
  115. package/dist/types/sdk/broker/state-version.d.ts +2 -0
  116. package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
  117. package/dist/types/sdk/bus/config-commands.d.ts +1 -1
  118. package/dist/types/sdk/bus/config.d.ts +11 -0
  119. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  120. package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
  121. package/dist/types/sdk/bus/html-format.d.ts +3 -2
  122. package/dist/types/sdk/bus/index.d.ts +27 -0
  123. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  124. package/dist/types/sdk/bus/notification-service.d.ts +20 -0
  125. package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
  126. package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
  127. package/dist/types/sdk/bus/rich-document.d.ts +7 -0
  128. package/dist/types/sdk/bus/rich-render.d.ts +18 -41
  129. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  130. package/dist/types/sdk/bus/telegram-daemon.d.ts +69 -3
  131. package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
  132. package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
  133. package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
  134. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  135. package/dist/types/sdk/host/host.d.ts +2 -0
  136. package/dist/types/sdk/index.d.ts +1 -0
  137. package/dist/types/sdk/session-directory.d.ts +56 -0
  138. package/dist/types/session/agent-session.d.ts +82 -47
  139. package/dist/types/session/agent-storage.d.ts +7 -0
  140. package/dist/types/session/fallback-chain-controller.d.ts +10 -2
  141. package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
  142. package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
  143. package/dist/types/session/messages.d.ts +16 -0
  144. package/dist/types/session/session-manager-internal.d.ts +14 -0
  145. package/dist/types/session/session-manager.d.ts +82 -12
  146. package/dist/types/session/session-storage.d.ts +43 -3
  147. package/dist/types/session/volatile-context-pruning.d.ts +15 -0
  148. package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
  149. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
  150. package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
  151. package/dist/types/system-prompt.d.ts +2 -11
  152. package/dist/types/task/executor.d.ts +3 -1
  153. package/dist/types/thinking.d.ts +7 -0
  154. package/dist/types/tool-discovery/tool-index.d.ts +1 -50
  155. package/dist/types/tools/ask.d.ts +64 -2
  156. package/dist/types/tools/bash.d.ts +3 -0
  157. package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
  158. package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
  159. package/dist/types/tools/cron.d.ts +2 -0
  160. package/dist/types/tools/fetch.d.ts +1 -0
  161. package/dist/types/tools/index.d.ts +30 -15
  162. package/dist/types/tools/output-meta.d.ts +1 -0
  163. package/dist/types/tools/read.d.ts +3 -0
  164. package/dist/types/tools/search-tool-bm25.d.ts +3 -4
  165. package/dist/types/tools/subagent-render.d.ts +1 -0
  166. package/dist/types/tools/subagent.d.ts +6 -1
  167. package/dist/types/utils/event-bus.d.ts +1 -1
  168. package/dist/types/web/search/index.d.ts +3 -1
  169. package/package.json +21 -9
  170. package/scripts/build-sdk-package-smoke.ts +39 -1
  171. package/scripts/generate-hotkeys-docs.ts +74 -0
  172. package/scripts/generate-sdk-operation-inventory.ts +64 -20
  173. package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
  174. package/src/async/job-manager.ts +455 -69
  175. package/src/cli/config-cli.ts +75 -2
  176. package/src/cli/notify-cli.ts +92 -26
  177. package/src/cli/session-picker.ts +20 -3
  178. package/src/cli/setup-cli.ts +1 -1
  179. package/src/cli.ts +1 -1
  180. package/src/commands/config.ts +1 -1
  181. package/src/commands/harness.ts +34 -106
  182. package/src/commands/launch.ts +6 -4
  183. package/src/commands/mcp-serve.ts +85 -8
  184. package/src/commands/mcp.ts +2 -2
  185. package/src/commands/notify.ts +8 -4
  186. package/src/commands/sdk.ts +61 -40
  187. package/src/commands/ultragoal.ts +4 -1
  188. package/src/config/atomic-yaml-patch.ts +80 -13
  189. package/src/config/file-lock.ts +105 -43
  190. package/src/config/keybindings.ts +64 -20
  191. package/src/config/model-auth.ts +9 -0
  192. package/src/config/model-bindings-applier.ts +139 -0
  193. package/src/config/model-discovery-manager.ts +224 -0
  194. package/src/config/model-equivalence.ts +66 -0
  195. package/src/config/model-registry.ts +169 -302
  196. package/src/config/model-resolver.ts +361 -369
  197. package/src/config/models-config-schema.ts +5 -4
  198. package/src/config/settings-schema.ts +159 -2
  199. package/src/config/settings.ts +458 -482
  200. package/src/coordinator-mcp/policy.ts +76 -11
  201. package/src/coordinator-mcp/question-gate-codec.ts +274 -0
  202. package/src/coordinator-mcp/question-state.ts +778 -0
  203. package/src/coordinator-mcp/server.ts +1702 -238
  204. package/src/debug/index.ts +1 -1
  205. package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
  206. package/src/defaults/gjc/skills/team/SKILL.md +16 -0
  207. package/src/discovery/helpers.ts +1 -50
  208. package/src/edit/index.ts +64 -0
  209. package/src/eval/py/env.ts +26 -0
  210. package/src/eval/py/kernel.ts +5 -3
  211. package/src/extensibility/extensions/loader.ts +9 -1
  212. package/src/extensibility/extensions/runner.ts +12 -3
  213. package/src/extensibility/extensions/types.ts +39 -1
  214. package/src/extensibility/gjc-plugins/installer.ts +16 -16
  215. package/src/extensibility/hooks/runner.ts +2 -2
  216. package/src/extensibility/runtime-skill-discovery.ts +33 -13
  217. package/src/extensibility/shared-events.ts +2 -0
  218. package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
  219. package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
  220. package/src/gjc-runtime/deep-interview-state.ts +317 -5
  221. package/src/gjc-runtime/goal-mode-request.ts +55 -8
  222. package/src/gjc-runtime/launch-tmux.ts +2 -5
  223. package/src/gjc-runtime/launch-worktree.ts +5 -5
  224. package/src/gjc-runtime/linux-proc.ts +71 -0
  225. package/src/gjc-runtime/ralplan-runtime.ts +74 -90
  226. package/src/gjc-runtime/session-resolution.ts +18 -3
  227. package/src/gjc-runtime/session-state-sidecar.ts +6 -107
  228. package/src/gjc-runtime/state-migrations.ts +1 -1
  229. package/src/gjc-runtime/state-runtime.ts +320 -249
  230. package/src/gjc-runtime/state-writer.ts +36 -40
  231. package/src/gjc-runtime/team-launch.ts +319 -0
  232. package/src/gjc-runtime/team-notify.ts +326 -0
  233. package/src/gjc-runtime/team-runtime.ts +1099 -1482
  234. package/src/gjc-runtime/team-store.ts +828 -0
  235. package/src/gjc-runtime/team-workers.ts +962 -0
  236. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
  237. package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
  238. package/src/gjc-runtime/tmux-sessions.ts +4 -21
  239. package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
  240. package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
  241. package/src/gjc-runtime/ultragoal-guard.ts +83 -12
  242. package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
  243. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
  244. package/src/gjc-runtime/ultragoal-runtime.ts +156 -1319
  245. package/src/gjc-runtime/workflow-cli-common.ts +30 -0
  246. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  247. package/src/gjc-runtime/workflow-manifest.ts +18 -0
  248. package/src/goals/continuation-timeout-guard.ts +79 -0
  249. package/src/goals/runtime.ts +7 -5
  250. package/src/goals/state.ts +18 -0
  251. package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
  252. package/src/harness-control-plane/phase-rollup.ts +4 -7
  253. package/src/harness-control-plane/receipt-ingest.ts +5 -0
  254. package/src/harness-control-plane/receipt-spool.ts +51 -7
  255. package/src/harness-control-plane/session-lease.ts +12 -130
  256. package/src/hindsight/backend.ts +26 -27
  257. package/src/hindsight/state.ts +101 -32
  258. package/src/hooks/skill-state.ts +17 -11
  259. package/src/internal-urls/docs-index.generated.ts +21 -18
  260. package/src/lsp/client.ts +1 -1
  261. package/src/lsp/config.ts +145 -26
  262. package/src/lsp/index.ts +1 -1
  263. package/src/lsp/lspmux.ts +39 -10
  264. package/src/lsp/path-trust.ts +81 -0
  265. package/src/main.ts +28 -8
  266. package/src/memories/index.ts +1 -0
  267. package/src/memories/storage.ts +6 -2
  268. package/src/modes/action-registry.ts +165 -0
  269. package/src/modes/components/chrome.ts +40 -0
  270. package/src/modes/components/command-palette.ts +168 -0
  271. package/src/modes/components/custom-editor.ts +1 -1
  272. package/src/modes/components/custom-provider-wizard.ts +51 -14
  273. package/src/modes/components/index.ts +2 -1
  274. package/src/modes/components/pet-selector.ts +11 -17
  275. package/src/modes/components/plan-preview-overlay.ts +295 -0
  276. package/src/modes/components/queue-mode-selector.ts +10 -27
  277. package/src/modes/components/queue-pane.ts +79 -0
  278. package/src/modes/components/read-tool-group.ts +13 -0
  279. package/src/modes/components/session-observer-overlay.ts +207 -772
  280. package/src/modes/components/sessions-dashboard.ts +181 -0
  281. package/src/modes/components/settings-selector.ts +1 -1
  282. package/src/modes/components/show-images-selector.ts +10 -24
  283. package/src/modes/components/status-line/types.ts +1 -1
  284. package/src/modes/components/status-line.ts +1 -969
  285. package/src/modes/components/tasks-pane.ts +73 -0
  286. package/src/modes/components/theme-selector.ts +11 -31
  287. package/src/modes/components/tool-execution.ts +22 -0
  288. package/src/modes/components/tool-status-header.ts +1056 -0
  289. package/src/modes/components/transcript-viewer-overlay.ts +384 -0
  290. package/src/modes/controllers/btw-controller.ts +1 -0
  291. package/src/modes/controllers/command-controller.ts +9 -15
  292. package/src/modes/controllers/event-controller.ts +3 -2
  293. package/src/modes/controllers/extension-ui-controller.ts +72 -25
  294. package/src/modes/controllers/goal-mode-controller.ts +474 -0
  295. package/src/modes/controllers/input-controller.ts +764 -229
  296. package/src/modes/controllers/mode-gate.ts +24 -0
  297. package/src/modes/controllers/plan-mode-controller.ts +517 -0
  298. package/src/modes/controllers/selector-controller.ts +225 -10
  299. package/src/modes/controllers/stt-controller.ts +125 -0
  300. package/src/modes/interactive-mode.ts +302 -1151
  301. package/src/modes/jobs-observer.ts +3 -0
  302. package/src/modes/runtime-init.ts +8 -0
  303. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
  304. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +78 -19
  305. package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
  306. package/src/modes/tasks-aggregator.ts +199 -0
  307. package/src/modes/transcript-item-registry.ts +229 -0
  308. package/src/modes/types.ts +47 -20
  309. package/src/modes/utils/ui-helpers.ts +17 -9
  310. package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
  311. package/src/prompts/agent-fragments/restricted-bash.md +1 -0
  312. package/src/prompts/agents/architect.md +2 -8
  313. package/src/prompts/agents/critic.md +2 -8
  314. package/src/prompts/agents/executor.md +3 -0
  315. package/src/prompts/agents/planner.md +4 -12
  316. package/src/prompts/system/custom-system-prompt.md +1 -23
  317. package/src/prompts/system/plan-mode-approved.md +8 -0
  318. package/src/prompts/system/system-prompt.md +17 -96
  319. package/src/prompts/tools/bash.md +4 -4
  320. package/src/prompts/tools/browser.md +15 -52
  321. package/src/prompts/tools/find.md +2 -2
  322. package/src/prompts/tools/hashline.md +13 -55
  323. package/src/prompts/tools/read.md +3 -6
  324. package/src/prompts/tools/search-tool-bm25.md +0 -2
  325. package/src/prompts/tools/search.md +2 -4
  326. package/src/prompts/tools/skill-discovery.md +1 -2
  327. package/src/prompts/tools/task.md +1 -1
  328. package/src/runtime-mcp/manager.ts +1 -1
  329. package/src/sdk/broker/broker.ts +206 -78
  330. package/src/sdk/broker/discovery.ts +21 -2
  331. package/src/sdk/broker/ensure.ts +85 -25
  332. package/src/sdk/broker/lifecycle-codec.ts +11 -0
  333. package/src/sdk/broker/lifecycle-ledger.ts +371 -74
  334. package/src/sdk/broker/lifecycle.ts +1399 -142
  335. package/src/sdk/broker/process-incarnation.ts +5 -13
  336. package/src/sdk/broker/session-index.ts +145 -56
  337. package/src/sdk/broker/state-version.ts +21 -0
  338. package/src/sdk/broker/transport.ts +1 -1
  339. package/src/sdk/bus/chat-daemon-cli.ts +2 -0
  340. package/src/sdk/bus/chat-daemon-control.ts +8 -28
  341. package/src/sdk/bus/chat-effect-journal.ts +14 -4
  342. package/src/sdk/bus/config-commands.ts +5 -4
  343. package/src/sdk/bus/config.ts +98 -0
  344. package/src/sdk/bus/conversation-store.ts +5 -29
  345. package/src/sdk/bus/daemon-paths.ts +2 -0
  346. package/src/sdk/bus/discord-daemon.ts +25 -8
  347. package/src/sdk/bus/html-format.ts +20 -3
  348. package/src/sdk/bus/index.ts +834 -113
  349. package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
  350. package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
  351. package/src/sdk/bus/natives-augment.d.ts +26 -1
  352. package/src/sdk/bus/notification-service.ts +168 -20
  353. package/src/sdk/bus/rate-limit-pool.ts +58 -14
  354. package/src/sdk/bus/recent-activity.ts +188 -74
  355. package/src/sdk/bus/rich-document.ts +258 -0
  356. package/src/sdk/bus/rich-render.ts +62 -70
  357. package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
  358. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  359. package/src/sdk/bus/telegram-daemon.ts +1491 -247
  360. package/src/sdk/bus/threaded-inbound.ts +8 -3
  361. package/src/sdk/bus/threaded-render.ts +57 -1
  362. package/src/sdk/bus/topic-registry.ts +120 -13
  363. package/src/sdk/host/control/dispatch.ts +14 -2
  364. package/src/sdk/host/control/operations.ts +6 -1
  365. package/src/sdk/host/host.ts +31 -1
  366. package/src/sdk/host/query/handlers.ts +20 -22
  367. package/src/sdk/host/query/revision-store.ts +80 -57
  368. package/src/sdk/index.ts +1 -0
  369. package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
  370. package/src/sdk/session-directory.ts +141 -0
  371. package/src/sdk/session.ts +458 -479
  372. package/src/session/agent-session.ts +1901 -637
  373. package/src/session/agent-storage.ts +18 -0
  374. package/src/session/cache-economics.ts +25 -8
  375. package/src/session/fallback-chain-controller.ts +53 -3
  376. package/src/session/internal/managed-session-scope.ts +2226 -0
  377. package/src/session/internal/managed-session-storage.ts +453 -0
  378. package/src/session/messages.ts +91 -2
  379. package/src/session/session-manager-internal.ts +33 -0
  380. package/src/session/session-manager.ts +1112 -333
  381. package/src/session/session-storage.ts +444 -39
  382. package/src/session/volatile-context-pruning.ts +78 -0
  383. package/src/setup/credential-auto-import.ts +2 -1
  384. package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
  385. package/src/setup/provider-onboarding.ts +36 -61
  386. package/src/setup/provider-presets.json +5 -0
  387. package/src/skill-state/active-state.ts +3 -13
  388. package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
  389. package/src/skill-state/workflow-state-contract.ts +6 -41
  390. package/src/slash-commands/builtin-registry.ts +46 -11
  391. package/src/system-prompt.ts +37 -44
  392. package/src/task/agents.ts +19 -1
  393. package/src/task/executor.ts +90 -6
  394. package/src/task/index.ts +62 -37
  395. package/src/thinking.ts +21 -6
  396. package/src/tool-discovery/tool-index.ts +2 -134
  397. package/src/tools/ask.ts +157 -21
  398. package/src/tools/ast-edit.ts +1 -1
  399. package/src/tools/bash.ts +38 -0
  400. package/src/tools/browser/dead-tab-recovery.ts +78 -0
  401. package/src/tools/browser/tab-supervisor.ts +282 -36
  402. package/src/tools/computer.ts +10 -3
  403. package/src/tools/cron.ts +30 -16
  404. package/src/tools/eval.ts +1 -2
  405. package/src/tools/fetch.ts +11 -4
  406. package/src/tools/index.ts +76 -24
  407. package/src/tools/output-meta.ts +13 -5
  408. package/src/tools/read.ts +37 -1
  409. package/src/tools/resource-gc.ts +2 -2
  410. package/src/tools/search-tool-bm25.ts +11 -74
  411. package/src/tools/subagent-render.ts +19 -8
  412. package/src/tools/subagent.ts +84 -32
  413. package/src/utils/event-bus.ts +9 -4
  414. package/src/web/search/index.ts +3 -3
  415. package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
  416. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
  417. package/src/discovery/plugin-dir-roots.ts +0 -28
  418. package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
@@ -0,0 +1,258 @@
1
+ import { Marked, type Token, type Tokens } from "marked";
2
+
3
+ export type RichEligibility = { kind: "eligible" } | { kind: "ineligible" };
4
+
5
+ type MathToken = Token & { type: "btw_math"; expression: string };
6
+
7
+ const MAX_SCALARS = 32_768;
8
+ const MAX_UTF8_BYTES = 131_072;
9
+ const MAX_BLOCKS = 500;
10
+ const MAX_DEPTH = 16;
11
+ const MAX_TABLE_COLUMNS = 20;
12
+ function exceedsScalarLimit(source: string): boolean {
13
+ let count = 0;
14
+ for (const _scalar of source) {
15
+ count++;
16
+ if (count > MAX_SCALARS) return true;
17
+ }
18
+ return false;
19
+ }
20
+
21
+ function isEscaped(source: string, index: number): boolean {
22
+ let escapes = 0;
23
+ for (let cursor = index - 1; cursor >= 0 && source[cursor] === "\\"; cursor--) escapes++;
24
+ return escapes % 2 === 1;
25
+ }
26
+ function isPlainTextSafe(source: string): boolean {
27
+ for (let index = 0; index < source.length; index++) {
28
+ if (
29
+ !isEscaped(source, index) &&
30
+ (source[index] === "*" ||
31
+ source[index] === "_" ||
32
+ source[index] === "~" ||
33
+ source[index] === "`" ||
34
+ source[index] === "[" ||
35
+ source[index] === "]" ||
36
+ source[index] === "<" ||
37
+ source[index] === ">")
38
+ )
39
+ return false;
40
+ }
41
+ return true;
42
+ }
43
+
44
+ function hasLoneSurrogate(source: string): boolean {
45
+ for (let index = 0; index < source.length; index++) {
46
+ const code = source.charCodeAt(index);
47
+ if (code >= 0xd800 && code <= 0xdbff) {
48
+ if (
49
+ index + 1 >= source.length ||
50
+ source.charCodeAt(index + 1) < 0xdc00 ||
51
+ source.charCodeAt(index + 1) > 0xdfff
52
+ )
53
+ return true;
54
+ index++;
55
+ } else if (code >= 0xdc00 && code <= 0xdfff) {
56
+ return true;
57
+ }
58
+ }
59
+ return false;
60
+ }
61
+
62
+ function hasUnescapedDollar(source: string): boolean {
63
+ for (let cursor = 0; cursor < source.length; cursor++) {
64
+ if (source[cursor] === "$" && !isEscaped(source, cursor)) return true;
65
+ }
66
+ return false;
67
+ }
68
+
69
+ function isValidMathExpression(expression: string, multiline: boolean): boolean {
70
+ return (
71
+ expression.trim().length > 0 &&
72
+ (multiline || !expression.includes("\n")) &&
73
+ !expression.startsWith(" ") &&
74
+ !expression.endsWith(" ") &&
75
+ !hasUnescapedDollar(expression) &&
76
+ !expression.includes("\\(") &&
77
+ !expression.includes("\\)") &&
78
+ !expression.includes("\\[") &&
79
+ !expression.includes("\\]")
80
+ );
81
+ }
82
+
83
+ function validateInlineMath(source: string): boolean {
84
+ for (let cursor = 0; cursor < source.length; cursor++) {
85
+ if (source[cursor] === "$" && !isEscaped(source, cursor)) {
86
+ if (source[cursor + 1] === "$") return false;
87
+ let closing = cursor + 1;
88
+ while (closing < source.length && (source[closing] !== "$" || isEscaped(source, closing))) closing++;
89
+ if (closing === source.length || !isValidMathExpression(source.slice(cursor + 1, closing), false))
90
+ return false;
91
+ cursor = closing;
92
+ continue;
93
+ }
94
+ if (source.startsWith("\\(", cursor) && !isEscaped(source, cursor)) {
95
+ let closing = cursor + 2;
96
+ while (closing < source.length && (!source.startsWith("\\)", closing) || isEscaped(source, closing)))
97
+ closing++;
98
+ if (closing === source.length || !isValidMathExpression(source.slice(cursor + 2, closing), false))
99
+ return false;
100
+ cursor = closing + 1;
101
+ continue;
102
+ }
103
+ if (source.startsWith("\\)", cursor) && !isEscaped(source, cursor)) return false;
104
+ if ((source.startsWith("\\[", cursor) || source.startsWith("\\]", cursor)) && !isEscaped(source, cursor))
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+
110
+ function parseDisplayMath(source: string): boolean {
111
+ const block = source.trim();
112
+ const delimiter =
113
+ block.startsWith("$$") && block.endsWith("$$")
114
+ ? "$$"
115
+ : block.startsWith("\\[") && block.endsWith("\\]")
116
+ ? "\\["
117
+ : undefined;
118
+ if (delimiter === undefined) return false;
119
+ const closing = delimiter === "$$" ? "$$" : "\\]";
120
+ return (
121
+ !isEscaped(block, block.length - closing.length) &&
122
+ isValidMathExpression(block.slice(delimiter.length, -closing.length).trim(), true)
123
+ );
124
+ }
125
+
126
+ const marked = new Marked({
127
+ async: false,
128
+ breaks: false,
129
+ gfm: true,
130
+ pedantic: false,
131
+ silent: false,
132
+ extensions: [
133
+ {
134
+ name: "btw_math",
135
+ level: "inline",
136
+ start(src) {
137
+ const dollar = src.indexOf("$");
138
+ const parenthesized = src.indexOf("\\(");
139
+ if (dollar < 0) return parenthesized;
140
+ if (parenthesized < 0) return dollar;
141
+ return Math.min(dollar, parenthesized);
142
+ },
143
+ tokenizer(src) {
144
+ if (src.startsWith("$") && !src.startsWith("$$")) {
145
+ let closing = 1;
146
+ while (closing < src.length && (src[closing] !== "$" || isEscaped(src, closing))) closing++;
147
+ const expression = src.slice(1, closing);
148
+ if (closing < src.length && isValidMathExpression(expression, false))
149
+ return { type: "btw_math", raw: src.slice(0, closing + 1), expression };
150
+ }
151
+ if (src.startsWith("\\(")) {
152
+ let closing = 2;
153
+ while (closing < src.length && (!src.startsWith("\\)", closing) || isEscaped(src, closing))) closing++;
154
+ const expression = src.slice(2, closing);
155
+ if (closing < src.length && isValidMathExpression(expression, false))
156
+ return { type: "btw_math", raw: src.slice(0, closing + 2), expression };
157
+ }
158
+ return undefined;
159
+ },
160
+ },
161
+ ],
162
+ });
163
+
164
+ function recognizeText(tokens: Token[], depth: number): boolean {
165
+ if (depth > MAX_DEPTH) return false;
166
+ for (const token of tokens) {
167
+ switch (token.type) {
168
+ case "btw_math":
169
+ if (depth > 2 || !isValidMathExpression((token as MathToken).expression, false)) return false;
170
+ break;
171
+ case "text":
172
+ if (!isPlainTextSafe(token.raw)) return false;
173
+ break;
174
+ case "escape":
175
+ case "br":
176
+ case "codespan":
177
+ break;
178
+ case "strong":
179
+ case "em":
180
+ case "del":
181
+ if (token.tokens === undefined || !recognizeText(token.tokens, depth + 1)) return false;
182
+ break;
183
+ default:
184
+ return false;
185
+ }
186
+ }
187
+ return true;
188
+ }
189
+
190
+ function recognizeTable(token: Tokens.Table, count: { value: number }): boolean {
191
+ const columns = token.header.length;
192
+ if (
193
+ columns < 1 ||
194
+ columns > MAX_TABLE_COLUMNS ||
195
+ token.align.length !== columns ||
196
+ token.rows.some(row => row.length !== columns) ||
197
+ token.align.some(align => align !== "left" && align !== "center" && align !== "right" && align !== null)
198
+ )
199
+ return false;
200
+ for (const row of [token.header, ...token.rows]) {
201
+ for (const cell of row) {
202
+ if (!validateInlineMath(cell.text) || !recognizeText(cell.tokens, 0)) return false;
203
+ }
204
+ }
205
+ count.value += 1 + token.rows.length + 1;
206
+ return count.value <= MAX_BLOCKS;
207
+ }
208
+
209
+ /** Conservatively recognize `/btw` Markdown that can be represented as rich content. */
210
+ export function parseRichEligibility(markdown: string): RichEligibility {
211
+ try {
212
+ if (
213
+ hasLoneSurrogate(markdown) ||
214
+ exceedsScalarLimit(markdown) ||
215
+ Buffer.byteLength(markdown, "utf8") > MAX_UTF8_BYTES ||
216
+ markdown.includes("\u0000")
217
+ )
218
+ return { kind: "ineligible" };
219
+
220
+ const tokens = marked.lexer(markdown);
221
+ const count = { value: 0 };
222
+ let structured = false;
223
+ for (const token of tokens) {
224
+ if (token.type === "space") continue;
225
+ if (token.type === "heading") {
226
+ if (
227
+ token.tokens === undefined ||
228
+ token.depth < 1 ||
229
+ token.depth > 6 ||
230
+ !validateInlineMath(token.text) ||
231
+ !recognizeText(token.tokens, 0)
232
+ )
233
+ return { kind: "ineligible" };
234
+ count.value++;
235
+ } else if (token.type === "paragraph" || token.type === "text") {
236
+ if (token.type === "paragraph" && parseDisplayMath(token.text)) {
237
+ structured = true;
238
+ count.value++;
239
+ } else {
240
+ const inline = token.type === "paragraph" ? token.tokens : [token];
241
+ if (inline === undefined || !validateInlineMath(token.text) || !recognizeText(inline, 0))
242
+ return { kind: "ineligible" };
243
+ structured ||= inline.some(part => part.type === "btw_math");
244
+ count.value++;
245
+ }
246
+ } else if (token.type === "table") {
247
+ if (!recognizeTable(token as Tokens.Table, count)) return { kind: "ineligible" };
248
+ structured = true;
249
+ } else {
250
+ return { kind: "ineligible" };
251
+ }
252
+ if (count.value > MAX_BLOCKS) return { kind: "ineligible" };
253
+ }
254
+ return structured ? { kind: "eligible" } : { kind: "ineligible" };
255
+ } catch {
256
+ return { kind: "ineligible" };
257
+ }
258
+ }
@@ -2,28 +2,20 @@
2
2
  * Rich-message promotion for stable non-editable Telegram text sends.
3
3
  *
4
4
  * When enabled, the daemon promotes eligible finalized `sendMessage` payloads
5
- * carrying raw markdown to the Bot API `sendRichMessage` method. On any miss or
6
- * failure the daemon keeps the unchanged HTML `sendMessage` path, so the
7
- * off-state request bodies are byte-identical.
5
+ * carrying raw markdown to Bot API `sendRichMessage`. Only an explicit
6
+ * `{ ok: false }` response selects the unchanged HTML path; ambiguous transport
7
+ * outcomes remain single-attempt and never fall back.
8
8
  */
9
9
 
10
+ import { parseRichEligibility } from "./rich-document";
10
11
  import type { BotApi } from "./telegram-daemon";
11
12
  import type { ThreadedSend } from "./threaded-render";
12
13
 
13
14
  /**
14
- * Telegram's hard per-message character ceiling (4096). Surfaced here purely as
15
- * documentation and a marker for a future native rich-message splitter — it is
16
- * intentionally NON-BEHAVIORAL and MUST stay that way: nothing in the rich path
17
- * branches on this value.
18
- *
19
- * Overflow is already safe without it. The production final-answer text is capped
20
- * at 3500 chars upstream (`summaryFromMessage(..., 3500)`), so a promoted
21
- * `sendRichMessage` never approaches this ceiling; and if the Bot API ever rejects
22
- * an oversized rich payload it returns `{ ok: false }`, which
23
- * `deliverRichWithFallback` (below) turns into the chunked HTML `splitTelegramHtml`
24
- * fallback (each chunk ≤ TELEGRAM_MESSAGE_LIMIT). This constant only marks where a
25
- * future rich splitter would read its ceiling; wiring it into a branch would change
26
- * byte-for-byte behavior and is out of scope.
15
+ * Telegram's hard per-message character ceiling (4096). Final-answer promotion
16
+ * uses this value to keep oversized Markdown on the existing chunked HTML path.
17
+ * `/btw` eligibility has its own 32,768-scalar route guard; only a definite
18
+ * rich rejection may select the correlated HTML path.
27
19
  */
28
20
  export const RICH_MESSAGE_LIMIT = 4096;
29
21
 
@@ -31,8 +23,16 @@ export const RICH_MESSAGE_LIMIT = 4096;
31
23
  export function buildRichMessage(
32
24
  raw: string,
33
25
  extras: { reply_markup?: unknown } = {},
34
- ): { rich_message: { markdown: string }; reply_markup?: unknown } {
35
- return { rich_message: { markdown: raw }, ...extras };
26
+ ): { rich_message: { markdown: string; skip_entity_detection: true }; reply_markup?: unknown } {
27
+ return { rich_message: { markdown: raw, skip_entity_detection: true }, ...extras };
28
+ }
29
+ /** Telegram message identifiers are strictly positive safe integers. */
30
+ function validMessageId(value: unknown): value is number {
31
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
32
+ }
33
+ /** Whether `/btw` Markdown may use Bot API 10.1 rich delivery. */
34
+ export function isBtwRichEligible(markdown: string): boolean {
35
+ return parseRichEligibility(markdown).kind === "eligible";
36
36
  }
37
37
 
38
38
  /**
@@ -56,88 +56,80 @@ export function shouldPromoteRich(input: { enabled?: boolean; send: ThreadedSend
56
56
  }
57
57
 
58
58
  /**
59
- * Deliver the promoted rich message, falling back to `fallbackDeliver` (the
60
- * unchanged HTML `sendMessage` loop) on any failure. A failure is either a
61
- * thrown transport error or a `{ ok: false }` JSON response (the transport
62
- * returns `res.json()` for JSON methods, so `ok:false` does not throw). On
63
- * failure exactly one diagnostic is logged before the fallback runs; on success
64
- * the fallback never runs.
65
- *
66
- * Returns the sent message's `message_id` on success (when the response carries
67
- * one), otherwise `undefined` — including every failure/fallback path and a
68
- * success whose response omits `result.message_id`. Callers that ignore the
69
- * return value are unaffected.
59
+ * Deliver the promoted rich message, falling back to `fallbackDeliver` only
60
+ * after an explicit `{ ok: false }` response. Transport errors and malformed or
61
+ * ambiguous responses are logged but never retried or sent again through HTML.
70
62
  */
71
63
  export async function deliverRichWithFallback(
72
64
  botApi: BotApi,
73
65
  base: { chat_id: string | number; message_thread_id?: number },
74
66
  send: ThreadedSend,
67
+ signal: AbortSignal,
75
68
  fallbackDeliver: () => Promise<void>,
76
69
  log?: { warn(msg: string): void },
77
70
  ): Promise<number | undefined> {
78
- let failure: string | undefined;
79
- let messageId: number | undefined;
80
71
  try {
81
- const res = await botApi.call("sendRichMessage", { ...base, ...buildRichMessage(send.richMarkdown!) });
72
+ const res = await botApi.call(
73
+ "sendRichMessage",
74
+ { ...base, ...buildRichMessage(send.richMarkdown!) },
75
+ { noRetry: true, signal },
76
+ );
82
77
  if (res !== null && typeof res === "object" && (res as { ok?: unknown }).ok === false) {
83
78
  const description = (res as { description?: unknown }).description;
84
- failure = typeof description === "string" && description.length > 0 ? description : "ok:false";
85
- } else {
86
- const candidate = (res as { result?: { message_id?: unknown } } | null)?.result?.message_id;
87
- if (typeof candidate === "number") messageId = candidate;
79
+ const failure = typeof description === "string" && description.length > 0 ? description : "ok:false";
80
+ log?.warn(`notifications: sendRichMessage rejected (${failure}); falling back to HTML`);
81
+ await fallbackDeliver();
82
+ return undefined;
88
83
  }
84
+ const candidate = (res as { result?: { message_id?: unknown } } | null)?.result?.message_id;
85
+ if (validMessageId(candidate)) return candidate;
86
+ log?.warn("notifications: sendRichMessage outcome ambiguous; not falling back to HTML");
89
87
  } catch (err) {
90
- failure = err instanceof Error ? err.message : String(err);
88
+ const failure = err instanceof Error ? err.message : String(err);
89
+ log?.warn(`notifications: sendRichMessage failed (${failure}); not falling back to HTML`);
91
90
  }
92
- if (failure === undefined) return messageId;
93
- log?.warn(`notifications: sendRichMessage failed (${failure}); falling back to HTML`);
94
- await fallbackDeliver();
95
91
  return undefined;
96
92
  }
97
93
 
98
94
  /**
99
95
  * Deliver an action-needed (ask/idle) message via `sendRichMessage`, falling
100
- * back to the unchanged HTML chunk loop on any failure. Mirrors
101
- * {@link deliverRichWithFallback} but takes an explicit markdown body plus an
102
- * optional top-level `reply_markup` (probe-confirmed: `sendRichMessage` accepts
103
- * `reply_markup` alongside `rich_message`), and surfaces a structured outcome so
104
- * the daemon can route inbound replies to the resulting message id.
105
- *
106
- * On rich success: returns `{ messageId, usedRich: true, usedFallback: false }`
107
- * where `messageId` is `res.result.message_id` when present. On a `{ ok:false }`
108
- * response or a thrown transport error: warns exactly once, runs `htmlFallback`,
109
- * and returns `{ messageId, usedRich: false, usedFallback: true }` where
110
- * `messageId` is the fallback's return value (the last HTML chunk's id).
96
+ * back to the unchanged HTML chunk loop only after an explicit `{ ok:false }`
97
+ * response. Other outcomes are ambiguous, are logged, and preserve the single
98
+ * physical rich delivery.
111
99
  */
112
100
  export async function deliverRichActionWithFallback(
113
101
  botApi: BotApi,
114
102
  base: { chat_id: string | number; message_thread_id?: number },
115
- opts: { markdown: string; replyMarkup?: unknown; requireMessageId?: boolean },
103
+ opts: { markdown: string; replyMarkup?: unknown },
104
+ signal: AbortSignal,
116
105
  htmlFallback: () => Promise<number | undefined>,
117
106
  log?: { warn(msg: string): void },
118
107
  ): Promise<{ messageId?: number; usedRich: boolean; usedFallback: boolean }> {
119
- let failure: string | undefined;
120
- let messageId: number | undefined;
121
108
  try {
122
- const res = await botApi.call("sendRichMessage", {
123
- ...base,
124
- ...buildRichMessage(opts.markdown, opts.replyMarkup === undefined ? {} : { reply_markup: opts.replyMarkup }),
125
- });
109
+ const res = await botApi.call(
110
+ "sendRichMessage",
111
+ {
112
+ ...base,
113
+ ...buildRichMessage(
114
+ opts.markdown,
115
+ opts.replyMarkup === undefined ? {} : { reply_markup: opts.replyMarkup },
116
+ ),
117
+ },
118
+ { noRetry: true, signal },
119
+ );
126
120
  if (res !== null && typeof res === "object" && (res as { ok?: unknown }).ok === false) {
127
121
  const description = (res as { description?: unknown }).description;
128
- failure = typeof description === "string" && description.length > 0 ? description : "ok:false";
129
- } else {
130
- const candidate = (res as { result?: { message_id?: unknown } } | null)?.result?.message_id;
131
- if (typeof candidate === "number") messageId = candidate;
132
- // Ask messages MUST be reply-routable: if a rich success carries no numeric
133
- // message_id, fall back to HTML so a routable id is guaranteed.
134
- else if (opts.requireMessageId) failure = "rich response missing message_id";
122
+ const failure = typeof description === "string" && description.length > 0 ? description : "ok:false";
123
+ log?.warn(`notifications: sendRichMessage(action) rejected (${failure}); falling back to HTML`);
124
+ const fallbackId = await htmlFallback();
125
+ return { messageId: fallbackId, usedRich: false, usedFallback: true };
135
126
  }
127
+ const candidate = (res as { result?: { message_id?: unknown } } | null)?.result?.message_id;
128
+ if (validMessageId(candidate)) return { messageId: candidate, usedRich: true, usedFallback: false };
129
+ log?.warn("notifications: sendRichMessage(action) outcome ambiguous; not falling back to HTML");
136
130
  } catch (err) {
137
- failure = err instanceof Error ? err.message : String(err);
131
+ const failure = err instanceof Error ? err.message : String(err);
132
+ log?.warn(`notifications: sendRichMessage(action) failed (${failure}); not falling back to HTML`);
138
133
  }
139
- if (failure === undefined) return { messageId, usedRich: true, usedFallback: false };
140
- log?.warn(`notifications: sendRichMessage(action) failed (${failure}); falling back to HTML`);
141
- const fallbackId = await htmlFallback();
142
- return { messageId: fallbackId, usedRich: false, usedFallback: true };
134
+ return { messageId: undefined, usedRich: true, usedFallback: false };
143
135
  }
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { logger } from "@gajae-code/utils";
3
4
  import { YAML } from "bun";
4
5
  import { applyAtomicYamlPatches, setByPath } from "../../config/atomic-yaml-patch";
5
6
  import type { Settings } from "../../config/settings";
@@ -7,8 +8,7 @@ import {
7
8
  getNotificationConfig,
8
9
  isTelegramConfigured,
9
10
  type NotificationSettingsReader,
10
- type NotificationSettingsSnapshot,
11
- readTelegramActivationMarkers,
11
+ parseNotificationSettingsSnapshot,
12
12
  } from "./config";
13
13
  import { daemonPaths, HEARTBEAT_TTL_MS } from "./daemon-paths";
14
14
  import {
@@ -53,68 +53,21 @@ function argValue(argv: string[], name: string): string | undefined {
53
53
  const i = argv.indexOf(name);
54
54
  return i >= 0 ? argv[i + 1] : undefined;
55
55
  }
56
-
57
- function getByPath(obj: unknown, pathSegments: string[]): unknown {
58
- let current = obj;
59
- for (const segment of pathSegments) {
60
- if (!current || typeof current !== "object" || Array.isArray(current)) return undefined;
61
- current = (current as Record<string, unknown>)[segment];
62
- }
63
- return current;
64
- }
65
-
66
- function asString(value: unknown): string | undefined {
67
- return typeof value === "string" && value.length > 0 ? value : undefined;
68
- }
69
-
70
- function asBoolean(value: unknown, fallback: boolean): boolean {
71
- return typeof value === "boolean" ? value : fallback;
72
- }
73
-
74
- function asIdleTimeoutMs(value: unknown): number {
75
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 60_000;
56
+ const DAEMON_COMPATIBILITY_DIAGNOSTIC_LIMIT = 1;
57
+ let daemonCompatibilityDiagnosticCount = 0;
58
+ function recordDaemonCompatibilityDiagnostic(message: string): void {
59
+ if (daemonCompatibilityDiagnosticCount >= DAEMON_COMPATIBILITY_DIAGNOSTIC_LIMIT) return;
60
+ daemonCompatibilityDiagnosticCount++;
61
+ logger.warn(message);
76
62
  }
77
63
 
78
64
  export function createLightweightDaemonSettings(input: {
79
65
  agentDir: string;
80
66
  rawConfig?: unknown;
81
67
  }): LightweightDaemonSettings {
82
- const rawConfig = input.rawConfig && typeof input.rawConfig === "object" ? input.rawConfig : {};
83
- const activation = readTelegramActivationMarkers(getByPath(rawConfig, ["notifications", "telegram", "activation"]));
84
- const getNotificationSettingsSnapshot = (): NotificationSettingsSnapshot => ({
85
- enabled: asBoolean(getByPath(rawConfig, ["notifications", "enabled"]), false),
86
- telegram: {
87
- botToken: asString(getByPath(rawConfig, ["notifications", "telegram", "botToken"])),
88
- chatId: asString(getByPath(rawConfig, ["notifications", "telegram", "chatId"])),
89
- ...(Object.keys(activation).length === 0 ? {} : { activation }),
90
- rich: {
91
- enabled: asBoolean(getByPath(rawConfig, ["notifications", "telegram", "rich", "enabled"]), true),
92
- },
93
- richDraft: {
94
- enabled: asBoolean(getByPath(rawConfig, ["notifications", "telegram", "richDraft", "enabled"]), false),
95
- },
96
- topics: {
97
- nameTemplate: asString(getByPath(rawConfig, ["notifications", "telegram", "topics", "nameTemplate"])),
98
- },
99
- },
100
- discord: {
101
- botToken: asString(getByPath(rawConfig, ["notifications", "discord", "botToken"])),
102
- applicationId: asString(getByPath(rawConfig, ["notifications", "discord", "applicationId"])),
103
- guildId: asString(getByPath(rawConfig, ["notifications", "discord", "guildId"])),
104
- parentChannelId: asString(getByPath(rawConfig, ["notifications", "discord", "parentChannelId"])),
105
- },
106
- slack: {
107
- botToken: asString(getByPath(rawConfig, ["notifications", "slack", "botToken"])),
108
- appToken: asString(getByPath(rawConfig, ["notifications", "slack", "appToken"])),
109
- workspaceId: asString(getByPath(rawConfig, ["notifications", "slack", "workspaceId"])),
110
- channelId: asString(getByPath(rawConfig, ["notifications", "slack", "channelId"])),
111
- authorizedUserId: asString(getByPath(rawConfig, ["notifications", "slack", "authorizedUserId"])),
112
- },
113
- redact: asBoolean(getByPath(rawConfig, ["notifications", "redact"]), false),
114
- verbosity: getByPath(rawConfig, ["notifications", "verbosity"]) === "verbose" ? "verbose" : "lean",
115
- sessionScope: getByPath(rawConfig, ["notifications", "sessionScope"]) === "primary" ? "primary" : "all",
116
- idleTimeoutMs: asIdleTimeoutMs(getByPath(rawConfig, ["notifications", "daemon", "idleTimeoutMs"])),
117
- });
68
+ const rawConfig = input.rawConfig === undefined ? {} : input.rawConfig;
69
+ const getNotificationSettingsSnapshot = () => parseNotificationSettingsSnapshot(rawConfig);
70
+ getNotificationSettingsSnapshot();
118
71
 
119
72
  return {
120
73
  get(pathName: string): unknown {
@@ -126,6 +79,8 @@ export function createLightweightDaemonSettings(input: {
126
79
  return snapshot.telegram.botToken;
127
80
  case "notifications.telegram.chatId":
128
81
  return snapshot.telegram.chatId;
82
+ case "notifications.telegram.btw.enabled":
83
+ return snapshot.telegram.btw.enabled;
129
84
  case "notifications.discord.botToken":
130
85
  return snapshot.discord.botToken;
131
86
  case "notifications.discord.applicationId":
@@ -243,7 +198,7 @@ export async function runDaemonInternal(argv: string[], deps: RunDaemonInternalD
243
198
  const ownerId = argValue(argv, "--owner-id");
244
199
  if (!ownerId) throw new Error("missing --owner-id");
245
200
  if (!ownerProcessIsAlive(ownerId, deps)) {
246
- process.stderr.write(`GJC notify daemon exiting: owner process from --owner-id ${ownerId} is not alive.\n`);
201
+ recordDaemonCompatibilityDiagnostic("GJC notify daemon exiting because its owner is not alive");
247
202
  return;
248
203
  }
249
204
  const resolvedAgentDir = agentDir ?? process.env.GJC_CODING_AGENT_DIR ?? path.join(process.cwd(), ".gjc", "agent");
@@ -261,6 +216,7 @@ export async function runDaemonInternal(argv: string[], deps: RunDaemonInternalD
261
216
  rich: cfg.rich,
262
217
  richDraft: cfg.richDraft,
263
218
  topics: cfg.topics,
219
+ btw: cfg.btw,
264
220
  pid: deps.processPid ?? process.pid,
265
221
  control: {
266
222
  shouldStop: async owner => {
@@ -7,8 +7,13 @@
7
7
  export const NOTIFICATION_PROTOCOL_VERSION = 3;
8
8
 
9
9
  /**
10
- * Operational generation the current daemon build speaks. It is tied to the
11
- * wire protocol version so a freshly-upgraded host can identify an older,
12
- * still-live daemon without coupling status readers to the daemon runtime.
10
+ * Operational generation the current daemon build speaks. Decoupled from
11
+ * {@link NOTIFICATION_PROTOCOL_VERSION} (#2304): additive `tool_activity` /
12
+ * `reasoning_summary` frames do not bump the wire protocol version, but a
13
+ * freshly-upgraded host must still recognize an older, still-live daemon that
14
+ * predates capability-gated frame enforcement and trigger a reload. Bump this
15
+ * on every daemon-behavior change independent of the wire version.
16
+ * The current development baseline already includes #2299's generation 4;
17
+ * this behavior change therefore advances the operational generation to 5.
13
18
  */
14
- export const DAEMON_GENERATION = NOTIFICATION_PROTOCOL_VERSION;
19
+ export const DAEMON_GENERATION = 5;