@gajae-code/coding-agent 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. package/CHANGELOG.md +50 -2
  2. package/dist/types/append-only-mode.d.ts +1 -1
  3. package/dist/types/cli/args.d.ts +4 -5
  4. package/dist/types/cli/daemon-cli.d.ts +12 -1
  5. package/dist/types/cli/notify-cli.d.ts +21 -1
  6. package/dist/types/cli.d.ts +3 -0
  7. package/dist/types/commands/daemon.d.ts +39 -0
  8. package/dist/types/commands/launch.d.ts +3 -0
  9. package/dist/types/commands/notify.d.ts +28 -1
  10. package/dist/types/commands/sdk.d.ts +33 -0
  11. package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
  12. package/dist/types/config/model-profile-activation.d.ts +35 -15
  13. package/dist/types/config/model-profiles.d.ts +11 -5
  14. package/dist/types/config/model-registry.d.ts +3 -3
  15. package/dist/types/config/model-resolver.d.ts +39 -6
  16. package/dist/types/config/model-selector-value.d.ts +17 -0
  17. package/dist/types/config/models-config-schema.d.ts +10 -6
  18. package/dist/types/config/settings-schema.d.ts +133 -6
  19. package/dist/types/config/settings.d.ts +63 -35
  20. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
  21. package/dist/types/coordinator-mcp/server.d.ts +6 -49
  22. package/dist/types/daemon/builtin.d.ts +6 -5
  23. package/dist/types/daemon/control-types.d.ts +1 -1
  24. package/dist/types/discovery/mcp-json.d.ts +12 -1
  25. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  26. package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
  27. package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
  28. package/dist/types/extensibility/extensions/loader.d.ts +10 -1
  29. package/dist/types/extensibility/extensions/runner.d.ts +3 -3
  30. package/dist/types/extensibility/extensions/types.d.ts +141 -16
  31. package/dist/types/extensibility/hooks/types.d.ts +1 -1
  32. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
  33. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  34. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
  35. package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
  36. package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
  37. package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
  38. package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
  39. package/dist/types/harness-control-plane/operate.d.ts +5 -5
  40. package/dist/types/harness-control-plane/owner.d.ts +18 -4
  41. package/dist/types/harness-control-plane/receipts.d.ts +0 -1
  42. package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
  43. package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
  44. package/dist/types/harness-control-plane/types.d.ts +3 -4
  45. package/dist/types/lsp/index.d.ts +1 -1
  46. package/dist/types/main.d.ts +32 -38
  47. package/dist/types/modes/acp/acp-agent.d.ts +224 -53
  48. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
  49. package/dist/types/modes/acp/acp-mode.d.ts +6 -15
  50. package/dist/types/modes/acp/permission-mode.d.ts +4 -0
  51. package/dist/types/modes/acp/startup-options.d.ts +6 -0
  52. package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
  53. package/dist/types/modes/components/model-selector.d.ts +1 -0
  54. package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
  55. package/dist/types/modes/components/settings-selector.d.ts +6 -4
  56. package/dist/types/modes/components/welcome.d.ts +1 -1
  57. package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
  58. package/dist/types/modes/index.d.ts +0 -4
  59. package/dist/types/modes/interactive-mode.d.ts +3 -0
  60. package/dist/types/modes/runtime-init.d.ts +2 -2
  61. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
  62. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
  63. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
  64. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
  65. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
  66. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
  67. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
  68. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
  69. package/dist/types/modes/types.d.ts +5 -0
  70. package/dist/types/runtime-mcp/client.d.ts +2 -0
  71. package/dist/types/runtime-mcp/config.d.ts +5 -1
  72. package/dist/types/runtime-mcp/manager.d.ts +8 -1
  73. package/dist/types/runtime-mcp/types.d.ts +4 -0
  74. package/dist/types/sdk/acp/adapter.d.ts +67 -0
  75. package/dist/types/sdk/acp/index.d.ts +2 -0
  76. package/dist/types/sdk/broker/broker.d.ts +53 -0
  77. package/dist/types/sdk/broker/discovery.d.ts +30 -0
  78. package/dist/types/sdk/broker/ensure.d.ts +33 -0
  79. package/dist/types/sdk/broker/identity.d.ts +3 -0
  80. package/dist/types/sdk/broker/index.d.ts +8 -0
  81. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
  82. package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
  83. package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
  84. package/dist/types/sdk/broker/runtime.d.ts +33 -0
  85. package/dist/types/sdk/broker/session-index.d.ts +57 -0
  86. package/dist/types/sdk/broker/state-version.d.ts +8 -0
  87. package/dist/types/sdk/broker/transport.d.ts +9 -0
  88. package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
  89. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
  90. package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
  91. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
  92. package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
  93. package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
  94. package/dist/types/sdk/bus/config.d.ts +274 -0
  95. package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
  96. package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
  97. package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
  98. package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
  99. package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
  100. package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
  101. package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
  102. package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
  103. package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
  104. package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
  105. package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
  106. package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
  107. package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
  108. package/dist/types/sdk/bus/session-control.d.ts +83 -0
  109. package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
  110. package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
  111. package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
  112. package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
  113. package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
  114. package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
  115. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
  116. package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
  117. package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
  118. package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
  119. package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
  120. package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
  121. package/dist/types/sdk/cli/index.d.ts +1 -0
  122. package/dist/types/sdk/cli/session-cli.d.ts +19 -0
  123. package/dist/types/sdk/client/client.d.ts +1 -0
  124. package/dist/types/sdk/client/discovery.d.ts +27 -0
  125. package/dist/types/sdk/client/index.d.ts +2 -0
  126. package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
  127. package/dist/types/sdk/host/control/index.d.ts +2 -0
  128. package/dist/types/sdk/host/control/operations.d.ts +65 -0
  129. package/dist/types/sdk/host/events.d.ts +35 -0
  130. package/dist/types/sdk/host/host.d.ts +35 -0
  131. package/dist/types/sdk/host/index.d.ts +4 -0
  132. package/dist/types/sdk/host/query/cursor.d.ts +62 -0
  133. package/dist/types/sdk/host/query/handlers.d.ts +70 -0
  134. package/dist/types/sdk/host/query/index.d.ts +3 -0
  135. package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
  136. package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
  137. package/dist/types/sdk/host/types.d.ts +32 -0
  138. package/dist/types/sdk/index.d.ts +6 -0
  139. package/dist/types/sdk/lifecycle-session.d.ts +14 -0
  140. package/dist/types/sdk/mcp/index.d.ts +1 -0
  141. package/dist/types/sdk/mcp/server.d.ts +37 -0
  142. package/dist/types/sdk/models.d.ts +44 -0
  143. package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
  144. package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
  145. package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
  146. package/dist/types/sdk/startup-capability.d.ts +50 -0
  147. package/dist/types/session/agent-session.d.ts +114 -21
  148. package/dist/types/session/default-model-selection.d.ts +25 -0
  149. package/dist/types/session/fallback-chain-controller.d.ts +52 -0
  150. package/dist/types/session/session-manager.d.ts +58 -1
  151. package/dist/types/setup/credential-auto-import.d.ts +36 -6
  152. package/dist/types/tools/ask-answer-registry.d.ts +1 -1
  153. package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
  154. package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
  155. package/dist/types/tools/browser/launch.d.ts +22 -0
  156. package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
  157. package/dist/types/tools/browser/profile-posture.d.ts +37 -0
  158. package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
  159. package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
  160. package/dist/types/tools/browser/registry.d.ts +19 -0
  161. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  162. package/dist/types/tools/index.d.ts +8 -8
  163. package/package.json +34 -13
  164. package/scripts/build-sdk-package-smoke.ts +94 -0
  165. package/scripts/capture-notifications-settings-showcase.ts +435 -0
  166. package/scripts/compile-args.ts +4 -2
  167. package/scripts/g005-daemon-smoke.ts +4 -5
  168. package/scripts/g011-daemon-path-smoke.ts +5 -5
  169. package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
  170. package/scripts/generate-sdk-operation-inventory.ts +867 -0
  171. package/scripts/generate-telegram-baseline-manifest.ts +128 -0
  172. package/scripts/manifest-schema.ts +124 -0
  173. package/scripts/run-test-manifest.ts +153 -0
  174. package/scripts/run-threat-model-check.ts +157 -0
  175. package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
  176. package/scripts/verify-gjc-sdk-rename.ts +90 -0
  177. package/src/append-only-mode.ts +1 -1
  178. package/src/cli/args.ts +43 -12
  179. package/src/cli/auth-gateway-cli.ts +2 -2
  180. package/src/cli/config-cli.ts +2 -1
  181. package/src/cli/daemon-cli.ts +63 -14
  182. package/src/cli/list-models.ts +1 -1
  183. package/src/cli/notify-cli.ts +288 -286
  184. package/src/cli.ts +21 -2
  185. package/src/commands/daemon.ts +50 -6
  186. package/src/commands/harness.ts +93 -36
  187. package/src/commands/launch.ts +5 -2
  188. package/src/commands/mcp-serve.ts +19 -12
  189. package/src/commands/notify.ts +36 -3
  190. package/src/commands/sdk.ts +426 -0
  191. package/src/commit/agentic/agent.ts +1 -0
  192. package/src/config/atomic-yaml-patch.ts +423 -0
  193. package/src/config/model-profile-activation.ts +240 -89
  194. package/src/config/model-profiles.ts +18 -15
  195. package/src/config/model-registry.ts +51 -26
  196. package/src/config/model-resolver.ts +138 -51
  197. package/src/config/model-selector-value.ts +50 -0
  198. package/src/config/models-config-schema.ts +14 -21
  199. package/src/config/settings-schema.ts +128 -10
  200. package/src/config/settings.ts +1120 -158
  201. package/src/coordinator-mcp/model-preset.ts +74 -20
  202. package/src/coordinator-mcp/server.ts +1597 -3052
  203. package/src/daemon/builtin.ts +8 -5
  204. package/src/daemon/control-types.ts +1 -1
  205. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  206. package/src/discovery/mcp-json.ts +327 -20
  207. package/src/extensibility/custom-tools/types.ts +1 -1
  208. package/src/extensibility/extensions/compact-handler.ts +3 -3
  209. package/src/extensibility/extensions/get-commands-handler.ts +2 -2
  210. package/src/extensibility/extensions/loader.ts +69 -5
  211. package/src/extensibility/extensions/runner.ts +112 -4
  212. package/src/extensibility/extensions/types.ts +176 -17
  213. package/src/extensibility/hooks/types.ts +1 -1
  214. package/src/gjc-runtime/launch-tmux.ts +170 -86
  215. package/src/gjc-runtime/psmux-detect.ts +82 -4
  216. package/src/gjc-runtime/session-state-sidecar.ts +41 -31
  217. package/src/gjc-runtime/team-runtime.ts +54 -35
  218. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
  219. package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
  220. package/src/gjc-runtime/tmux-sessions.ts +19 -13
  221. package/src/gjc-runtime/ultragoal-guard.ts +53 -6
  222. package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
  223. package/src/gjc-runtime/windows-powershell-command.ts +58 -0
  224. package/src/harness-control-plane/operate.ts +9 -9
  225. package/src/harness-control-plane/owner.ts +283 -39
  226. package/src/harness-control-plane/receipts.ts +0 -1
  227. package/src/harness-control-plane/sdk-transport.ts +395 -0
  228. package/src/harness-control-plane/session-transport.ts +94 -0
  229. package/src/harness-control-plane/types.ts +1 -1
  230. package/src/internal-urls/docs-index.generated.ts +34 -35
  231. package/src/lsp/client.ts +42 -6
  232. package/src/lsp/index.ts +2 -2
  233. package/src/main.ts +299 -204
  234. package/src/modes/DESIGN.md +258 -0
  235. package/src/modes/acp/acp-agent.ts +1228 -2612
  236. package/src/modes/acp/acp-client-bridge.ts +2 -20
  237. package/src/modes/acp/acp-event-mapper.ts +18 -2
  238. package/src/modes/acp/acp-mode.ts +8 -36
  239. package/src/modes/acp/permission-mode.ts +25 -0
  240. package/src/modes/acp/startup-options.ts +6 -0
  241. package/src/modes/components/agent-dashboard.ts +37 -17
  242. package/src/modes/components/model-selector.ts +93 -35
  243. package/src/modes/components/notifications-settings-editor.ts +1379 -0
  244. package/src/modes/components/settings-selector.ts +60 -11
  245. package/src/modes/components/welcome.ts +4 -4
  246. package/src/modes/controllers/command-controller.ts +1 -2
  247. package/src/modes/controllers/event-controller.ts +11 -16
  248. package/src/modes/controllers/extension-ui-controller.ts +348 -4
  249. package/src/modes/controllers/selector-controller.ts +674 -36
  250. package/src/modes/index.ts +0 -22
  251. package/src/modes/interactive-mode.ts +59 -20
  252. package/src/modes/runtime-init.ts +298 -6
  253. package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
  254. package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
  255. package/src/modes/shared/agent-wire/event-contract.ts +7 -8
  256. package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
  257. package/src/modes/shared/agent-wire/event-observation.ts +16 -7
  258. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
  259. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
  260. package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
  261. package/src/modes/types.ts +5 -0
  262. package/src/runtime-mcp/client.ts +94 -19
  263. package/src/runtime-mcp/config.ts +27 -15
  264. package/src/runtime-mcp/manager.ts +343 -105
  265. package/src/runtime-mcp/oauth-flow.ts +42 -12
  266. package/src/runtime-mcp/transports/http.ts +49 -19
  267. package/src/runtime-mcp/transports/stdio.ts +47 -28
  268. package/src/runtime-mcp/types.ts +10 -0
  269. package/src/sdk/acp/adapter.ts +418 -0
  270. package/src/sdk/acp/index.ts +2 -0
  271. package/src/sdk/broker/broker.ts +642 -0
  272. package/src/sdk/broker/discovery.ts +94 -0
  273. package/src/sdk/broker/ensure.ts +249 -0
  274. package/src/sdk/broker/identity.ts +42 -0
  275. package/src/sdk/broker/index.ts +8 -0
  276. package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
  277. package/src/sdk/broker/internal-source.bunfig.toml +1 -0
  278. package/src/sdk/broker/lifecycle-ledger.ts +286 -0
  279. package/src/sdk/broker/lifecycle.ts +2157 -0
  280. package/src/sdk/broker/process-incarnation.ts +159 -0
  281. package/src/sdk/broker/runtime.ts +164 -0
  282. package/src/sdk/broker/session-index.ts +318 -0
  283. package/src/sdk/broker/state-version.ts +23 -0
  284. package/src/sdk/broker/transport.ts +125 -0
  285. package/src/sdk/bus/chat-command-policy.ts +107 -0
  286. package/src/sdk/bus/chat-daemon-cli.ts +214 -0
  287. package/src/sdk/bus/chat-daemon-control.ts +713 -0
  288. package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
  289. package/src/sdk/bus/chat-effect-journal.ts +353 -0
  290. package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
  291. package/src/sdk/bus/config.ts +433 -0
  292. package/src/sdk/bus/conversation-store.ts +391 -0
  293. package/src/sdk/bus/discord-conversation.ts +85 -0
  294. package/src/sdk/bus/discord-daemon.ts +2275 -0
  295. package/src/sdk/bus/discord-live-provider.ts +521 -0
  296. package/src/sdk/bus/discord-provider.ts +56 -0
  297. package/src/{notifications → sdk/bus}/html-format.ts +2 -2
  298. package/src/sdk/bus/index.ts +3943 -0
  299. package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
  300. package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
  301. package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
  302. package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
  303. package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
  304. package/src/sdk/bus/natives-augment.d.ts +38 -0
  305. package/src/sdk/bus/notification-orchestration.ts +535 -0
  306. package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
  307. package/src/sdk/bus/session-control.ts +333 -0
  308. package/src/sdk/bus/slack-conversation.ts +74 -0
  309. package/src/sdk/bus/slack-daemon.ts +2144 -0
  310. package/src/sdk/bus/slack-live-provider.ts +380 -0
  311. package/src/sdk/bus/slack-provider.ts +76 -0
  312. package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
  313. package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
  314. package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
  315. package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
  316. package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
  317. package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
  318. package/src/sdk/bus/telegram-setup.ts +663 -0
  319. package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
  320. package/src/sdk/cli/index.ts +1 -0
  321. package/src/sdk/cli/session-cli.ts +275 -0
  322. package/src/sdk/client/client.ts +1 -0
  323. package/src/sdk/client/discovery.ts +108 -0
  324. package/src/sdk/client/index.ts +2 -0
  325. package/src/sdk/host/control/dispatch.ts +367 -0
  326. package/src/sdk/host/control/index.ts +10 -0
  327. package/src/sdk/host/control/operations.ts +63 -0
  328. package/src/sdk/host/events.ts +91 -0
  329. package/src/sdk/host/host.ts +296 -0
  330. package/src/sdk/host/index.ts +4 -0
  331. package/src/sdk/host/query/cursor.ts +189 -0
  332. package/src/sdk/host/query/handlers.ts +533 -0
  333. package/src/sdk/host/query/index.ts +3 -0
  334. package/src/sdk/host/query/revision-store.ts +1126 -0
  335. package/src/sdk/host/reverse-leases.ts +325 -0
  336. package/src/sdk/host/types.ts +29 -0
  337. package/src/sdk/index.ts +13 -0
  338. package/src/sdk/lifecycle-session.ts +39 -0
  339. package/src/sdk/mcp/index.ts +1 -0
  340. package/src/sdk/mcp/server.ts +332 -0
  341. package/src/sdk/models.ts +168 -0
  342. package/src/sdk/protocol/adapter-validation.ts +48 -0
  343. package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
  344. package/src/sdk/protocol/operation-registry.ts +312 -0
  345. package/src/{sdk.ts → sdk/session.ts} +339 -212
  346. package/src/sdk/startup-capability.ts +189 -0
  347. package/src/session/agent-session.ts +2453 -894
  348. package/src/session/default-model-selection.ts +93 -0
  349. package/src/session/fallback-chain-controller.ts +136 -0
  350. package/src/session/session-manager.ts +438 -11
  351. package/src/session/session-storage.ts +8 -1
  352. package/src/setup/credential-auto-import.ts +290 -35
  353. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
  354. package/src/slash-commands/builtin-registry.ts +2 -1
  355. package/src/task/executor.ts +62 -13
  356. package/src/tools/ask-answer-registry.ts +1 -1
  357. package/src/tools/ask.ts +13 -8
  358. package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
  359. package/src/tools/browser/benchmark/detector-report.ts +157 -0
  360. package/src/tools/browser/benchmark/run-suite.ts +39 -0
  361. package/src/tools/browser/launch.ts +124 -19
  362. package/src/tools/browser/profile-discovery.ts +63 -0
  363. package/src/tools/browser/profile-posture.ts +59 -0
  364. package/src/tools/browser/profile-reuse.ts +102 -0
  365. package/src/tools/browser/profile-warmup.ts +109 -0
  366. package/src/tools/browser/registry.ts +87 -11
  367. package/src/tools/browser/tab-protocol.ts +1 -0
  368. package/src/tools/browser/tab-supervisor.ts +1 -0
  369. package/src/tools/browser/tab-worker.ts +1 -1
  370. package/src/tools/browser.ts +6 -0
  371. package/src/tools/index.ts +8 -8
  372. package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
  373. package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
  374. package/src/tools/telegram-send.ts +2 -2
  375. package/vendor/insane-search/MANIFEST.json +2 -1
  376. package/vendor/insane-search/engine/bias_check.py +4 -1
  377. package/vendor/insane-search/engine/phase0.py +26 -1
  378. package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
  379. package/dist/types/modes/bridge/auth.d.ts +0 -12
  380. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
  381. package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
  382. package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
  383. package/dist/types/modes/bridge/event-stream.d.ts +0 -8
  384. package/dist/types/modes/rpc/host-tools.d.ts +0 -1
  385. package/dist/types/modes/rpc/host-uris.d.ts +0 -1
  386. package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
  387. package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
  388. package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
  389. package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
  390. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
  391. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
  392. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
  393. package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
  394. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
  395. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
  396. package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
  397. package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
  398. package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
  399. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
  400. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
  401. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
  402. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
  403. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
  404. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
  405. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
  406. package/dist/types/notifications/config.d.ts +0 -132
  407. package/src/harness-control-plane/rpc-adapter.ts +0 -290
  408. package/src/modes/bridge/auth.ts +0 -41
  409. package/src/modes/bridge/bridge-client-bridge.ts +0 -47
  410. package/src/modes/bridge/bridge-mode.ts +0 -675
  411. package/src/modes/bridge/bridge-ui-context.ts +0 -200
  412. package/src/modes/bridge/event-stream.ts +0 -70
  413. package/src/modes/rpc/host-tools.ts +0 -1
  414. package/src/modes/rpc/host-uris.ts +0 -1
  415. package/src/modes/rpc/rpc-client.ts +0 -1166
  416. package/src/modes/rpc/rpc-mode.ts +0 -1029
  417. package/src/modes/rpc/rpc-socket-security.ts +0 -115
  418. package/src/modes/rpc/rpc-types.ts +0 -637
  419. package/src/modes/shared/agent-wire/command-contract.ts +0 -18
  420. package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
  421. package/src/modes/shared/agent-wire/command-validation.ts +0 -181
  422. package/src/modes/shared/agent-wire/handshake.ts +0 -157
  423. package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
  424. package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
  425. package/src/modes/shared/agent-wire/protocol.ts +0 -46
  426. package/src/modes/shared/agent-wire/responses.ts +0 -17
  427. package/src/modes/shared/agent-wire/scopes.ts +0 -94
  428. package/src/modes/shared/agent-wire/session-registry.ts +0 -109
  429. package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
  430. package/src/modes/shared/agent-wire/ui-result.ts +0 -48
  431. package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
  432. package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
  433. package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
  434. package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
  435. package/src/notifications/config.ts +0 -219
  436. package/src/notifications/index.ts +0 -1835
  437. package/src/notifications/telegram-daemon-cli.ts +0 -243
  438. package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
  439. /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
  440. /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
  441. /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
  442. /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
  443. /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
  444. /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
  445. /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
  446. /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
  447. /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
  448. /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
  449. /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
  450. /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
  451. /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
  452. /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
  453. /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
  454. /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
  455. /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
  456. /package/src/{notifications → sdk/bus}/engine.ts +0 -0
  457. /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
  458. /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
  459. /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
  460. /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
  461. /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
  462. /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
  463. /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
  464. /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
  465. /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
@@ -12,9 +12,10 @@
12
12
  */
13
13
  import * as fsPromises from "node:fs/promises";
14
14
  import * as path from "node:path";
15
- import type { Settings } from "../config/settings";
15
+ import type { Settings } from "../../config/settings";
16
16
  import {
17
17
  getNotificationConfig,
18
+ isDiscordConfigured,
18
19
  isGloballyConfigured,
19
20
  isTelegramConfigured,
20
21
  maskToken,
@@ -22,7 +23,7 @@ import {
22
23
  tokenFingerprint,
23
24
  } from "./config";
24
25
  import { type DaemonPaths, daemonPaths, HEARTBEAT_TTL_MS } from "./daemon-paths";
25
- import type { DaemonState } from "./telegram-daemon";
26
+ import { DAEMON_GENERATION } from "./telegram-daemon-contract";
26
27
 
27
28
  const DEFAULT_API_BASE = "https://api.telegram.org";
28
29
 
@@ -139,8 +140,8 @@ export function buildNotificationStatusReport(settings: Settings): NotificationS
139
140
  },
140
141
  discord: {
141
142
  botTokenMasked: maskToken(cfg.discord.botToken),
142
- channel: cfg.discord.channelId,
143
- configured: adapterConfigured(cfg.discord.botToken, cfg.discord.channelId),
143
+ channel: cfg.discord.parentChannelId,
144
+ configured: isDiscordConfigured(cfg),
144
145
  },
145
146
  slack: {
146
147
  botTokenMasked: maskToken(cfg.slack.botToken),
@@ -164,7 +165,7 @@ export function formatNotificationStatusReport(report: NotificationStatusReport)
164
165
  ` telegram.fingerprint: ${report.telegram.tokenFingerprint ?? "(unset)"}`,
165
166
  ` telegram.configured: ${yesNo(report.telegram.configured)}`,
166
167
  ` discord.botToken: ${report.discord.botTokenMasked}`,
167
- ` discord.channelId: ${report.discord.channel ?? "(unset)"}`,
168
+ ` discord.parentChannelId: ${report.discord.channel ?? "(unset)"}`,
168
169
  ` slack.botToken: ${report.slack.botTokenMasked}`,
169
170
  ` slack.channelId: ${report.slack.channel ?? "(unset)"}`,
170
171
  ].join("\n");
@@ -215,7 +216,36 @@ async function readEndpointView(fs: NotificationServiceFs, file: string): Promis
215
216
  };
216
217
  }
217
218
 
218
- function parseDaemonState(raw: string): DaemonState | undefined {
219
+ interface NormalizedDaemonState {
220
+ pid: number;
221
+ ownerId: string;
222
+ tokenFingerprint: string | undefined;
223
+ chatId: string | undefined;
224
+ startedAt: number | undefined;
225
+ heartbeatAt: number | undefined;
226
+ stoppedAt: number | undefined;
227
+ roots: string[] | undefined;
228
+ generation: number | undefined;
229
+ generationStatus: "missing" | "valid" | "invalid";
230
+ }
231
+
232
+ function finiteNonNegativeNumber(value: unknown): number | undefined {
233
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
234
+ }
235
+
236
+ function safePositiveInteger(value: unknown): number | undefined {
237
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
238
+ }
239
+
240
+ function safeNonNegativeInteger(value: unknown): number | undefined {
241
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
242
+ }
243
+
244
+ function stringArray(value: unknown): string[] | undefined {
245
+ return Array.isArray(value) && value.every(item => typeof item === "string") ? value : undefined;
246
+ }
247
+
248
+ function parseDaemonState(raw: string): NormalizedDaemonState | undefined {
219
249
  let parsed: unknown;
220
250
  try {
221
251
  parsed = JSON.parse(raw);
@@ -224,11 +254,33 @@ function parseDaemonState(raw: string): DaemonState | undefined {
224
254
  }
225
255
  if (!parsed || typeof parsed !== "object") return undefined;
226
256
  const rec = parsed as Record<string, unknown>;
227
- if (typeof rec.pid !== "number" || typeof rec.ownerId !== "string") return undefined;
228
- return parsed as DaemonState;
257
+ const pid = safePositiveInteger(rec.pid);
258
+ if (pid === undefined || typeof rec.ownerId !== "string" || rec.ownerId.length === 0) return undefined;
259
+
260
+ const generation = safeNonNegativeInteger(rec.generation);
261
+ const generationStatus = !Object.hasOwn(rec, "generation")
262
+ ? "missing"
263
+ : generation === undefined
264
+ ? "invalid"
265
+ : "valid";
266
+ return {
267
+ pid,
268
+ ownerId: rec.ownerId,
269
+ tokenFingerprint: typeof rec.tokenFingerprint === "string" ? rec.tokenFingerprint : undefined,
270
+ chatId: typeof rec.chatId === "string" ? rec.chatId : undefined,
271
+ startedAt: finiteNonNegativeNumber(rec.startedAt),
272
+ heartbeatAt: finiteNonNegativeNumber(rec.heartbeatAt),
273
+ stoppedAt: finiteNonNegativeNumber(rec.stoppedAt),
274
+ roots: stringArray(rec.roots),
275
+ generation,
276
+ generationStatus,
277
+ };
229
278
  }
230
279
 
231
- async function readDaemonStateFile(fs: NotificationServiceFs, file: string): Promise<DaemonState | undefined> {
280
+ async function readDaemonStateFile(
281
+ fs: NotificationServiceFs,
282
+ file: string,
283
+ ): Promise<NormalizedDaemonState | undefined> {
232
284
  try {
233
285
  return parseDaemonState(await fs.readFile(file, "utf8"));
234
286
  } catch {
@@ -262,8 +314,28 @@ export interface DaemonHealth {
262
314
  heartbeatFresh: boolean;
263
315
  identityMatches: boolean;
264
316
  stopped: boolean;
317
+ heartbeatAt: number | undefined;
318
+ heartbeatAgeMs: number | undefined;
319
+ generation: number | undefined;
320
+ currentGeneration: number;
321
+ generationRelation: DaemonGenerationRelation;
322
+ }
323
+
324
+ export type DaemonGenerationRelation = "pre_generation" | "older" | "current" | "newer" | "unknown";
325
+
326
+ function daemonGenerationRelation(state: NormalizedDaemonState | undefined): DaemonGenerationRelation {
327
+ if (!state) return "unknown";
328
+ if (state.generationStatus === "missing") return "pre_generation";
329
+ if (state.generation === undefined) return "unknown";
330
+ if (state.generation < DAEMON_GENERATION) return "older";
331
+ if (state.generation === DAEMON_GENERATION) return "current";
332
+ return "newer";
265
333
  }
266
334
 
335
+ function displayHeartbeatAgeMs(now: number, heartbeatAt: number): number | undefined {
336
+ const age = now - heartbeatAt;
337
+ return Number.isFinite(age) ? Math.max(0, age) : undefined;
338
+ }
267
339
  export interface EndpointHealth {
268
340
  total: number;
269
341
  live: number;
@@ -349,18 +421,24 @@ export async function checkNotificationHealth(opts: HealthOptions): Promise<Noti
349
421
  // Daemon ownership state (offline; read the persisted state file directly).
350
422
  const paths = daemonPaths(opts.settings.getAgentDir());
351
423
  const state = await readDaemonStateFile(fs, paths.state);
424
+ const heartbeatAt = state?.heartbeatAt;
352
425
  const daemon: DaemonHealth = {
353
426
  present: Boolean(state),
354
427
  ownerId: state?.ownerId,
355
428
  pid: state?.pid,
356
429
  alive: state ? pidAlive(state.pid) : false,
357
- heartbeatFresh: state ? now - state.heartbeatAt <= HEARTBEAT_TTL_MS : false,
430
+ heartbeatFresh: heartbeatAt !== undefined ? now - heartbeatAt <= HEARTBEAT_TTL_MS : false,
358
431
  identityMatches:
359
432
  Boolean(state) &&
360
433
  telegramConfigured &&
361
434
  state?.tokenFingerprint === tokenFingerprint(cfg.botToken) &&
362
435
  state?.chatId === cfg.chatId,
363
- stopped: typeof state?.stoppedAt === "number",
436
+ stopped: state?.stoppedAt !== undefined,
437
+ heartbeatAt,
438
+ heartbeatAgeMs: heartbeatAt === undefined ? undefined : displayHeartbeatAgeMs(now, heartbeatAt),
439
+ generation: state?.generation,
440
+ currentGeneration: DAEMON_GENERATION,
441
+ generationRelation: daemonGenerationRelation(state),
364
442
  };
365
443
  if (!state) {
366
444
  checks.push({ name: "daemon", level: "ok", detail: "no daemon ownership record (none running)" });
@@ -421,6 +499,22 @@ export async function checkNotificationHealth(opts: HealthOptions): Promise<Noti
421
499
  detail: `${live} live, ${unknownEndpoints} unverified endpoint file(s)`,
422
500
  });
423
501
  }
502
+ if (
503
+ telegramConfigured &&
504
+ daemon.present &&
505
+ daemon.alive &&
506
+ daemon.heartbeatFresh &&
507
+ daemon.identityMatches &&
508
+ !daemon.stopped &&
509
+ endpoints.total === 0
510
+ ) {
511
+ checks.push({
512
+ name: "local_endpoint",
513
+ level: "warn",
514
+ detail:
515
+ "No local notification endpoint for this working directory. In this GJC terminal run /notify on; if it does not report notifications enabled, start a new local GJC session. Do not re-pair Telegram.",
516
+ });
517
+ }
424
518
 
425
519
  // Optional network reachability probe.
426
520
  let reachability = { probed: false, ok: false, detail: "not probed" };
@@ -566,7 +660,7 @@ async function removeDeadOwnerLock(
566
660
  fs: NotificationServiceFs,
567
661
  paths: DaemonPaths,
568
662
  pidAlive: (pid: number) => boolean,
569
- expected: DaemonState,
663
+ expected: NormalizedDaemonState,
570
664
  ): Promise<"cleared" | "contended" | "superseded" | "now-alive" | "unlink-failed"> {
571
665
  if (!(await fs.createExclusive(paths.steal))) return "contended";
572
666
  try {
@@ -0,0 +1,333 @@
1
+ import {
2
+ getCurrentTelegramActivationMarker,
3
+ isSessionNotificationsEnabled,
4
+ isTelegramConfigured,
5
+ type NotificationConfig,
6
+ } from "./config";
7
+
8
+ /** Minimal session-manager surface shared by extension, TUI, and headless hosts. */
9
+ export interface NotificationSessionContext {
10
+ sessionManager: {
11
+ getCwd(): string;
12
+ getSessionId(): string;
13
+ };
14
+ }
15
+
16
+ /** A snapshot of the current session, resolved from the session manager per operation. */
17
+ export interface BoundNotificationSession<Context extends NotificationSessionContext = NotificationSessionContext> {
18
+ readonly context: Context;
19
+ readonly cwd: string;
20
+ readonly sessionId: string;
21
+ unbind(): void;
22
+ }
23
+
24
+ export type NotificationEndpointStartResult = "started" | "already" | "disabled" | "failed";
25
+ export type TelegramDaemonPreflightResult = "ready" | "blocked_identity";
26
+
27
+ /**
28
+ * The endpoint implementation is deliberately injected. The controller owns
29
+ * policy and session-local state while the extension continues to own its
30
+ * concrete NotificationServer resources.
31
+ */
32
+ export interface NotificationSessionRuntime<Context extends NotificationSessionContext = NotificationSessionContext> {
33
+ isRunning(binding: BoundNotificationSession<Context>): boolean;
34
+ start(binding: BoundNotificationSession<Context>): Promise<NotificationEndpointStartResult>;
35
+ stop(binding: BoundNotificationSession<Context>): Promise<boolean>;
36
+ /**
37
+ * Proves the complete Telegram owner identity before a generic endpoint can
38
+ * emit a frame. `blocked_identity` is fail-closed and starts nothing.
39
+ */
40
+ ensureTelegramDaemon?(binding: BoundNotificationSession<Context>): Promise<TelegramDaemonPreflightResult>;
41
+ }
42
+
43
+ export interface NotificationSessionStatus {
44
+ eligible: boolean;
45
+ locallyEnabled: boolean;
46
+ effectiveEnabled: boolean;
47
+ running: boolean;
48
+ environment: "off" | "explicit" | "token" | "default";
49
+ }
50
+
51
+ export interface NotificationSessionReconcileResult {
52
+ outcome: NotificationEndpointStartResult | "stopped";
53
+ status: NotificationSessionStatus;
54
+ }
55
+
56
+ export interface NotificationSessionControllerOptions {
57
+ /** Gate A result, resolved once by the SDK from the canonical host predicate. */
58
+ eligible: boolean;
59
+ /** Reads the global-only, schema-default-resolved notification configuration. */
60
+ getConfig(): NotificationConfig;
61
+ /** Kept as a reference so test and embedding hosts can supply their own environment. */
62
+ env?: NodeJS.ProcessEnv;
63
+ }
64
+
65
+ /**
66
+ * Shared owner of notification session policy.
67
+ *
68
+ * Gate A is captured at creation. Gate B is evaluated for each session
69
+ * operation with `isSessionNotificationsEnabled`. Gate C verifies complete
70
+ * Telegram ownership before a generic endpoint is allowed to start.
71
+ */
72
+ export class NotificationSessionController {
73
+ readonly #eligible: boolean;
74
+ readonly #getConfig: () => NotificationConfig;
75
+ readonly #env: NodeJS.ProcessEnv;
76
+ readonly #disabledSessions = new Set<string>();
77
+ /** Sessions held inactive after a post-commit foreign daemon identity race. */
78
+ readonly #blockedRuntimeSessions = new Set<string>();
79
+ /** Sessions closed during host shutdown; no queued operation may restart them. */
80
+ readonly #shuttingDownSessions = new Set<string>();
81
+ /** Serializes endpoint mutations for each bound session snapshot. */
82
+ readonly #sessionOperations = new Map<string, Promise<void>>();
83
+ #runtime: NotificationSessionRuntime<NotificationSessionContext> | undefined;
84
+
85
+ constructor(options: NotificationSessionControllerOptions) {
86
+ this.#eligible = options.eligible;
87
+ this.#getConfig = options.getConfig;
88
+ this.#env = options.env ?? process.env;
89
+ }
90
+
91
+ /** Attach the concrete generic endpoint implementation used by this host. */
92
+ attachRuntime<Context extends NotificationSessionContext>(runtime: NotificationSessionRuntime<Context>): () => void {
93
+ const attached = runtime as unknown as NotificationSessionRuntime<NotificationSessionContext>;
94
+ this.#runtime = attached;
95
+ return () => {
96
+ if (this.#runtime === attached) this.#runtime = undefined;
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Bind a fresh session snapshot. Callers should not cache it: cwd and session
102
+ * id may change on `/new`, fork, or resume.
103
+ */
104
+ bind<Context extends NotificationSessionContext>(context: Context): BoundNotificationSession<Context> {
105
+ let bound = true;
106
+ const getCwd = context.sessionManager.getCwd;
107
+ const cwd =
108
+ typeof getCwd === "function" ? getCwd.call(context.sessionManager) : (context as { cwd?: unknown }).cwd;
109
+ if (typeof cwd !== "string" || cwd.length === 0) {
110
+ throw new Error("Notification session context does not expose a cwd.");
111
+ }
112
+ return {
113
+ cwd,
114
+ sessionId: context.sessionManager.getSessionId(),
115
+ unbind: () => {
116
+ bound = false;
117
+ },
118
+ get context() {
119
+ if (!bound) throw new Error("Notification session binding has been released.");
120
+ return context;
121
+ },
122
+ };
123
+ }
124
+
125
+ /** Preserve session-local safety state and pending-operation ownership across a session rekey. */
126
+ rekeySession(previousSessionId: string, nextSessionId: string): void {
127
+ if (previousSessionId === nextSessionId) return;
128
+ if (this.#disabledSessions.delete(previousSessionId)) this.#disabledSessions.add(nextSessionId);
129
+ if (this.#blockedRuntimeSessions.delete(previousSessionId)) this.#blockedRuntimeSessions.add(nextSessionId);
130
+ if (this.#shuttingDownSessions.delete(previousSessionId)) this.#shuttingDownSessions.add(nextSessionId);
131
+
132
+ const previousOperation = this.#sessionOperations.get(previousSessionId);
133
+ if (!previousOperation) return;
134
+ const nextOperation = this.#sessionOperations.get(nextSessionId);
135
+ const completion = nextOperation ? previousOperation.then(() => nextOperation) : previousOperation;
136
+ this.#sessionOperations.delete(previousSessionId);
137
+ this.#sessionOperations.set(nextSessionId, completion);
138
+ void completion.then(() => {
139
+ if (this.#sessionOperations.get(nextSessionId) === completion) {
140
+ this.#sessionOperations.delete(nextSessionId);
141
+ }
142
+ });
143
+ }
144
+
145
+ query<Context extends NotificationSessionContext>(context: Context): NotificationSessionStatus {
146
+ const binding = this.bind(context);
147
+ try {
148
+ return this.#query(binding);
149
+ } finally {
150
+ binding.unbind();
151
+ }
152
+ }
153
+
154
+ /** Stop the current endpoint during host shutdown without changing local preference. */
155
+ async stopCurrentSession<Context extends NotificationSessionContext>(context: Context): Promise<boolean> {
156
+ const binding = this.bind(context);
157
+ try {
158
+ // Set this before joining the queue so an in-flight start self-stops before
159
+ // shutdown resolves, and no later operation can restart this session.
160
+ this.#shuttingDownSessions.add(binding.sessionId);
161
+ return await this.#enqueue(binding, async () => {
162
+ const runtime = this.#runtime as NotificationSessionRuntime<Context> | undefined;
163
+ return runtime?.isRunning(binding) ? await runtime.stop(binding) : false;
164
+ });
165
+ } finally {
166
+ binding.unbind();
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Hold this session's endpoint inactive after a foreign-daemon identity race.
172
+ * The block remains until an explicit same-identity reconnect or CAS restore clears it.
173
+ */
174
+ async enterBlockedRuntime<Context extends NotificationSessionContext>(context: Context): Promise<boolean> {
175
+ const binding = this.bind(context);
176
+ try {
177
+ return await this.#enqueue(binding, async () => {
178
+ const runtime = this.#runtime as NotificationSessionRuntime<Context> | undefined;
179
+ const stopped = runtime?.isRunning(binding) ? await runtime.stop(binding) : false;
180
+ this.#blockedRuntimeSessions.add(binding.sessionId);
181
+ return stopped;
182
+ });
183
+ } finally {
184
+ binding.unbind();
185
+ }
186
+ }
187
+
188
+ /** Clear a block only after the caller has verified a safe same-identity reconnect or restore. */
189
+ async clearBlockedRuntime<Context extends NotificationSessionContext>(context: Context): Promise<void> {
190
+ const binding = this.bind(context);
191
+ try {
192
+ await this.#enqueue(binding, async () => {
193
+ this.#blockedRuntimeSessions.delete(binding.sessionId);
194
+ });
195
+ } finally {
196
+ binding.unbind();
197
+ }
198
+ }
199
+
200
+ async setLocalEnabled<Context extends NotificationSessionContext>(
201
+ context: Context,
202
+ enabled: boolean,
203
+ ): Promise<NotificationSessionReconcileResult> {
204
+ const binding = this.bind(context);
205
+ try {
206
+ return await this.#enqueue(binding, async () => {
207
+ if (enabled) {
208
+ this.#disabledSessions.delete(binding.sessionId);
209
+ this.#shuttingDownSessions.delete(binding.sessionId);
210
+ } else {
211
+ this.#disabledSessions.add(binding.sessionId);
212
+ }
213
+ return await this.#reconcile(binding);
214
+ });
215
+ } finally {
216
+ binding.unbind();
217
+ }
218
+ }
219
+
220
+ async reconcileCurrentSession<Context extends NotificationSessionContext>(
221
+ context: Context,
222
+ ): Promise<NotificationSessionReconcileResult> {
223
+ const binding = this.bind(context);
224
+ try {
225
+ return await this.#enqueue(binding, () => this.#reconcile(binding));
226
+ } finally {
227
+ binding.unbind();
228
+ }
229
+ }
230
+
231
+ async #reconcile<Context extends NotificationSessionContext>(
232
+ binding: BoundNotificationSession<Context>,
233
+ ): Promise<NotificationSessionReconcileResult> {
234
+ const cfg = this.#getConfig();
235
+ const runtime = this.#runtime as NotificationSessionRuntime<Context> | undefined;
236
+ const status = this.#status(binding, cfg, runtime);
237
+ if (!status.effectiveEnabled) {
238
+ if (runtime && status.running) await runtime.stop(binding);
239
+ return { outcome: status.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
240
+ }
241
+
242
+ if (!runtime) return { outcome: "disabled", status };
243
+ if (getCurrentTelegramActivationMarker(cfg)) {
244
+ if (runtime.isRunning(binding)) await runtime.stop(binding);
245
+ this.#blockedRuntimeSessions.add(binding.sessionId);
246
+ return { outcome: "disabled", status: this.#status(binding, cfg, runtime) };
247
+ }
248
+ if (isTelegramConfigured(cfg)) {
249
+ try {
250
+ const ensured = await runtime.ensureTelegramDaemon?.(binding);
251
+ if (ensured !== "ready") {
252
+ if (runtime.isRunning(binding)) await runtime.stop(binding);
253
+ this.#blockedRuntimeSessions.add(binding.sessionId);
254
+ return { outcome: "disabled", status: this.#status(binding, cfg, runtime) };
255
+ }
256
+ } catch {
257
+ if (runtime.isRunning(binding)) await runtime.stop(binding);
258
+ this.#blockedRuntimeSessions.add(binding.sessionId);
259
+ return { outcome: "failed", status: this.#status(binding, cfg, runtime) };
260
+ }
261
+ }
262
+
263
+ const current = this.#status(binding, cfg, runtime);
264
+ if (!current.effectiveEnabled) {
265
+ if (current.running) await runtime.stop(binding);
266
+ return { outcome: current.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
267
+ }
268
+ const outcome = current.running ? "already" : await runtime.start(binding);
269
+ const afterStart = this.#status(binding, cfg, runtime);
270
+ if (!afterStart.effectiveEnabled) {
271
+ if (afterStart.running) await runtime.stop(binding);
272
+ return { outcome: afterStart.running ? "stopped" : "disabled", status: this.#status(binding, cfg, runtime) };
273
+ }
274
+ return { outcome, status: this.#status(binding, cfg, runtime) };
275
+ }
276
+
277
+ #enqueue<Context extends NotificationSessionContext, Result>(
278
+ binding: BoundNotificationSession<Context>,
279
+ operation: () => Promise<Result>,
280
+ ): Promise<Result> {
281
+ const previous = this.#sessionOperations.get(binding.sessionId) ?? Promise.resolve();
282
+ const result = previous.then(operation, operation);
283
+ const completion = result.then(
284
+ () => undefined,
285
+ () => undefined,
286
+ );
287
+ this.#sessionOperations.set(binding.sessionId, completion);
288
+ void completion.then(() => {
289
+ if (this.#sessionOperations.get(binding.sessionId) === completion) {
290
+ this.#sessionOperations.delete(binding.sessionId);
291
+ }
292
+ });
293
+ return result;
294
+ }
295
+
296
+ #query<Context extends NotificationSessionContext>(
297
+ binding: BoundNotificationSession<Context>,
298
+ ): NotificationSessionStatus {
299
+ const cfg = this.#getConfig();
300
+ return this.#status(binding, cfg, this.#runtime as NotificationSessionRuntime<Context> | undefined);
301
+ }
302
+
303
+ #status<Context extends NotificationSessionContext>(
304
+ binding: BoundNotificationSession<Context>,
305
+ cfg: NotificationConfig,
306
+ runtime: NotificationSessionRuntime<Context> | undefined,
307
+ ): NotificationSessionStatus {
308
+ const locallyEnabled = !this.#disabledSessions.has(binding.sessionId);
309
+ const blockedRuntime = this.#blockedRuntimeSessions.has(binding.sessionId);
310
+ const shuttingDown = this.#shuttingDownSessions.has(binding.sessionId);
311
+ const effectiveEnabled =
312
+ !blockedRuntime &&
313
+ !shuttingDown &&
314
+ !getCurrentTelegramActivationMarker(cfg) &&
315
+ this.#eligible &&
316
+ isSessionNotificationsEnabled({ cfg, env: this.#env, sessionDisabled: !locallyEnabled });
317
+ const environment =
318
+ this.#env.GJC_NOTIFICATIONS === "0"
319
+ ? "off"
320
+ : this.#env.GJC_NOTIFICATIONS === "1"
321
+ ? "explicit"
322
+ : this.#env.GJC_NOTIFICATIONS_TOKEN
323
+ ? "token"
324
+ : "default";
325
+ return {
326
+ eligible: this.#eligible,
327
+ locallyEnabled,
328
+ effectiveEnabled,
329
+ running: runtime?.isRunning(binding) ?? false,
330
+ environment,
331
+ };
332
+ }
333
+ }
@@ -0,0 +1,74 @@
1
+ import { boundedDedupe, type ConversationRecord } from "./conversation-store";
2
+
3
+ export interface SlackInboundDispatchReceipt {
4
+ key: string;
5
+ eventId: string;
6
+ interactionId: string;
7
+ retryKey: string;
8
+ eventContext?: string;
9
+ kind: "action" | "command";
10
+ actionId?: string;
11
+ /** The endpoint generation and SDK effect captured before Socket Mode ACK. */
12
+ endpointGeneration: number;
13
+ /** Identifier of the protected journal payload; mappings never retain message bodies. */
14
+ effectId: string;
15
+ idempotencyKey: string;
16
+ }
17
+
18
+ export type SlackConversationState = "absent" | "posting_root" | "active" | "closed_marker" | "resumed_root" | "error";
19
+
20
+ /** Durable Slack mapping. It contains identifiers and receipt metadata only; protected journal effects own message bodies. */
21
+ export interface SlackConversation extends ConversationRecord {
22
+ state: SlackConversationState;
23
+ teamId: string;
24
+ channelId: string;
25
+ rootTs?: string;
26
+ sessionId?: string;
27
+ endpointGeneration?: number;
28
+ clientMsgId?: string;
29
+ /** Exclusive, expiring authority for provider reconciliation/publication of the root intent. */
30
+ rootPublicationOwner?: string;
31
+ rootPublicationLeaseExpiresAt?: number;
32
+ /** Monotonic fencing token; stale root publishers may not commit after takeover. */
33
+ rootPublicationFence?: number;
34
+
35
+ /** Exclusive, expiring authority for provider reconciliation/publication of the outbound action intent. */
36
+ outboundActionOwner?: string;
37
+ outboundActionLeaseExpiresAt?: number;
38
+ /** Monotonic fencing token; stale action publishers may not commit after takeover. */
39
+ outboundActionFence?: number;
40
+ /** Action authority only; action bodies and endpoint credentials are never persisted. */
41
+ pendingActionId?: string;
42
+ /** Durable, unpublished action intent. The action body is supplied only by the caller. */
43
+ outboundActionId?: string;
44
+ outboundActionClientMsgId?: string;
45
+ lastError?: string;
46
+ updatedAt: number;
47
+ seenEventIds: string[];
48
+ seenContextIds: string[];
49
+ seenRetryKeys: string[];
50
+ seenInteractionIds: string[];
51
+ /** Durable inbound effect identifiers. The protected journal owns replay payloads. */
52
+ inboundDispatches?: SlackInboundDispatchReceipt[];
53
+ }
54
+
55
+ export function slackConversationKey(input: { teamId: string; channelId: string; rootTs: string }): string {
56
+ return `${input.teamId}:${input.channelId}:${input.rootTs}`;
57
+ }
58
+
59
+ export function normalizeSlackConversation(record: SlackConversation): SlackConversation {
60
+ const inboundDispatches = record.inboundDispatches ?? [];
61
+ return {
62
+ ...record,
63
+ seenEventIds: boundedDedupe(record.seenEventIds),
64
+ seenContextIds: boundedDedupe(record.seenContextIds),
65
+ seenRetryKeys: boundedDedupe(record.seenRetryKeys),
66
+ seenInteractionIds: boundedDedupe(record.seenInteractionIds),
67
+ // Replay lifecycle and terminal retention are journal-owned; mappings retain identifiers.
68
+ inboundDispatches,
69
+ };
70
+ }
71
+
72
+ export function acceptsSlackInbound(record: SlackConversation, rootTs: string, endpointGeneration: number): boolean {
73
+ return record.state === "active" && record.rootTs === rootTs && record.endpointGeneration === endpointGeneration;
74
+ }