@gajae-code/coding-agent 0.13.2 → 0.14.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 (610) hide show
  1. package/CHANGELOG.md +215 -8
  2. package/README.md +2 -0
  3. package/bin/gajaessi.js +4 -0
  4. package/dist/types/async/job-manager.d.ts +55 -2
  5. package/dist/types/async/support.d.ts +14 -0
  6. package/dist/types/capability/mcp.d.ts +6 -0
  7. package/dist/types/capability/types.d.ts +6 -0
  8. package/dist/types/cli/accounts-cli.d.ts +19 -0
  9. package/dist/types/cli/args.d.ts +3 -0
  10. package/dist/types/cli/config-cli.d.ts +6 -0
  11. package/dist/types/cli/crash-cli.d.ts +10 -0
  12. package/dist/types/cli/customize-doctor.d.ts +145 -0
  13. package/dist/types/cli/daemon-cli.d.ts +3 -1
  14. package/dist/types/cli/mcp-cli.d.ts +11 -0
  15. package/dist/types/cli/notify-cli.d.ts +1 -1
  16. package/dist/types/cli/root-flags.d.ts +12 -0
  17. package/dist/types/cli/setup-cli.d.ts +3 -1
  18. package/dist/types/cli/skills-cli.d.ts +2 -1
  19. package/dist/types/cli/update-cli.d.ts +19 -1
  20. package/dist/types/cli.d.ts +6 -0
  21. package/dist/types/commands/accounts.d.ts +40 -0
  22. package/dist/types/commands/crash.d.ts +24 -0
  23. package/dist/types/commands/customize.d.ts +22 -0
  24. package/dist/types/commands/daemon.d.ts +1 -1
  25. package/dist/types/commands/launch.d.ts +6 -0
  26. package/dist/types/commands/setup.d.ts +6 -0
  27. package/dist/types/commands/skills.d.ts +6 -1
  28. package/dist/types/commands/update.d.ts +7 -0
  29. package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
  30. package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
  31. package/dist/types/config/config-schema-version.d.ts +10 -0
  32. package/dist/types/config/file-lock.d.ts +18 -0
  33. package/dist/types/config/model-profile-activation.d.ts +3 -1
  34. package/dist/types/config/model-registry.d.ts +10 -1
  35. package/dist/types/config/models-config-schema.d.ts +2 -0
  36. package/dist/types/config/settings-schema.d.ts +73 -9
  37. package/dist/types/config/settings.d.ts +63 -0
  38. package/dist/types/config/skill-settings-defaults.d.ts +26 -0
  39. package/dist/types/config/task-delegation.d.ts +39 -0
  40. package/dist/types/config/update-channel.d.ts +10 -0
  41. package/dist/types/coordinator-mcp/server.d.ts +5 -1
  42. package/dist/types/crash/index-store.d.ts +87 -0
  43. package/dist/types/crash/nudge.d.ts +50 -0
  44. package/dist/types/crash/record-loader.d.ts +17 -0
  45. package/dist/types/crash/report.d.ts +134 -0
  46. package/dist/types/crash/sanitize.d.ts +25 -0
  47. package/dist/types/cursor.d.ts +13 -2
  48. package/dist/types/customization/import.d.ts +37 -0
  49. package/dist/types/customization/index.d.ts +4 -0
  50. package/dist/types/customization/inventory.d.ts +25 -0
  51. package/dist/types/customization/mutations.d.ts +32 -0
  52. package/dist/types/customization/types.d.ts +115 -0
  53. package/dist/types/daemon/control-types.d.ts +8 -1
  54. package/dist/types/daemon/operator-contract.d.ts +4 -4
  55. package/dist/types/discovery/claude.d.ts +39 -1
  56. package/dist/types/discovery/codex.d.ts +39 -1
  57. package/dist/types/discovery/index.d.ts +2 -0
  58. package/dist/types/discovery/mcp-compat.d.ts +30 -0
  59. package/dist/types/edit/renderer.d.ts +23 -0
  60. package/dist/types/extensibility/extensions/runner.d.ts +3 -0
  61. package/dist/types/extensibility/extensions/types.d.ts +21 -4
  62. package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
  63. package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
  64. package/dist/types/extensibility/hooks/loader.d.ts +19 -5
  65. package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
  66. package/dist/types/extensibility/skill-management.d.ts +107 -0
  67. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
  68. package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
  69. package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
  70. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
  71. package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
  72. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
  73. package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
  74. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
  75. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
  76. package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
  77. package/dist/types/hooks/events.d.ts +60 -0
  78. package/dist/types/hooks/normalize.d.ts +83 -0
  79. package/dist/types/hooks/skill-state.d.ts +9 -2
  80. package/dist/types/main.d.ts +2 -1
  81. package/dist/types/modes/acp/abort-scope.d.ts +13 -0
  82. package/dist/types/modes/acp/acp-agent.d.ts +7 -4
  83. package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
  84. package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
  85. package/dist/types/modes/components/customization/index.d.ts +1 -0
  86. package/dist/types/modes/components/dynamic-theme-text.d.ts +9 -0
  87. package/dist/types/modes/components/gajae-pet-widget.d.ts +6 -28
  88. package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
  89. package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
  90. package/dist/types/modes/components/oauth-selector.d.ts +10 -4
  91. package/dist/types/modes/components/pet-capability.d.ts +7 -21
  92. package/dist/types/modes/components/settings-selector.d.ts +5 -0
  93. package/dist/types/modes/components/status-line/gh.d.ts +6 -2
  94. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  95. package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
  96. package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
  97. package/dist/types/modes/interactive-mode.d.ts +1 -0
  98. package/dist/types/modes/theme/defaults/index.d.ts +118 -0
  99. package/dist/types/modes/types.d.ts +1 -0
  100. package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
  101. package/dist/types/runtime-mcp/config.d.ts +9 -0
  102. package/dist/types/runtime-mcp/manager.d.ts +26 -1
  103. package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
  104. package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
  105. package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
  106. package/dist/types/runtime-mcp/protocol.d.ts +278 -0
  107. package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
  108. package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
  109. package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
  110. package/dist/types/runtime-mcp/types.d.ts +102 -10
  111. package/dist/types/sdk/acp/adapter.d.ts +26 -5
  112. package/dist/types/sdk/broker/broker.d.ts +3 -55
  113. package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
  114. package/dist/types/sdk/broker/session-index.d.ts +14 -5
  115. package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
  116. package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
  117. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
  118. package/dist/types/sdk/bus/config.d.ts +11 -0
  119. package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
  120. package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
  121. package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
  122. package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
  123. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
  124. package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
  125. package/dist/types/sdk/bus/index.d.ts +85 -99
  126. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
  127. package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
  128. package/dist/types/sdk/bus/notification-service.d.ts +52 -0
  129. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
  130. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
  131. package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
  132. package/dist/types/sdk/bus/session-control.d.ts +25 -1
  133. package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
  134. package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
  135. package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
  136. package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
  137. package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
  138. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
  139. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
  140. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
  141. package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
  142. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
  143. package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
  144. package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
  145. package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
  146. package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
  147. package/dist/types/sdk/cli/rows.d.ts +2 -11
  148. package/dist/types/sdk/cli/session-cli.d.ts +15 -25
  149. package/dist/types/sdk/client/client.d.ts +97 -1
  150. package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
  151. package/dist/types/sdk/host/control/index.d.ts +1 -1
  152. package/dist/types/sdk/host/control/operations.d.ts +18 -5
  153. package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
  154. package/dist/types/sdk/host/host.d.ts +15 -1
  155. package/dist/types/sdk/host/query/handlers.d.ts +5 -0
  156. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  157. package/dist/types/sdk/host/session-runtime.d.ts +127 -7
  158. package/dist/types/sdk/host/types.d.ts +1 -1
  159. package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
  160. package/dist/types/sdk/index.d.ts +2 -1
  161. package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
  162. package/dist/types/sdk/lifecycle/client.d.ts +59 -0
  163. package/dist/types/sdk/lifecycle/index.d.ts +3 -0
  164. package/dist/types/sdk/lifecycle/service.d.ts +232 -0
  165. package/dist/types/sdk/mcp/server.d.ts +9 -6
  166. package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
  167. package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
  168. package/dist/types/sdk/prompt-status.d.ts +10 -3
  169. package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
  170. package/dist/types/sdk/receipt-state.d.ts +17 -0
  171. package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
  172. package/dist/types/sdk/router/index.d.ts +1 -0
  173. package/dist/types/sdk/router/session-router.d.ts +152 -0
  174. package/dist/types/sdk/session-list.d.ts +22 -0
  175. package/dist/types/sdk/session-reconnect.d.ts +43 -1
  176. package/dist/types/sdk/session.d.ts +34 -1
  177. package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
  178. package/dist/types/session/account-inventory.d.ts +86 -0
  179. package/dist/types/session/agent-session.d.ts +145 -6
  180. package/dist/types/session/agent-storage.d.ts +10 -0
  181. package/dist/types/session/coordinator-tool-label.d.ts +28 -0
  182. package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
  183. package/dist/types/session/messages.d.ts +1 -1
  184. package/dist/types/session/secure-token-file.d.ts +3 -0
  185. package/dist/types/session/session-manager.d.ts +47 -1
  186. package/dist/types/session/startup-auth-config.d.ts +44 -0
  187. package/dist/types/session/terminal-abort.d.ts +378 -0
  188. package/dist/types/session/yield-queue.d.ts +7 -0
  189. package/dist/types/session-import/claude.d.ts +26 -0
  190. package/dist/types/session-import/codex.d.ts +88 -0
  191. package/dist/types/session-import/command.d.ts +35 -0
  192. package/dist/types/session-import/detect.d.ts +22 -0
  193. package/dist/types/session-import/index.d.ts +7 -0
  194. package/dist/types/session-import/provider-codex.d.ts +22 -0
  195. package/dist/types/session-import/provider-service.d.ts +38 -0
  196. package/dist/types/session-import/redact.d.ts +23 -0
  197. package/dist/types/session-import/service.d.ts +47 -0
  198. package/dist/types/session-import/types.d.ts +120 -0
  199. package/dist/types/setup/paseo/check.d.ts +16 -0
  200. package/dist/types/setup/paseo/install-saga.d.ts +115 -0
  201. package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
  202. package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
  203. package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
  204. package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
  205. package/dist/types/setup/paseo/provider-config.d.ts +35 -0
  206. package/dist/types/setup/paseo/remove.d.ts +14 -0
  207. package/dist/types/setup/paseo/result-types.d.ts +59 -0
  208. package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
  209. package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
  210. package/dist/types/skill-state/active-state.d.ts +5 -1
  211. package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
  212. package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
  213. package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
  214. package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
  215. package/dist/types/slash-commands/types.d.ts +7 -1
  216. package/dist/types/task/executor.d.ts +10 -0
  217. package/dist/types/task/index.d.ts +3 -1
  218. package/dist/types/tools/ask-answer-registry.d.ts +6 -3
  219. package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
  220. package/dist/types/tools/bash.d.ts +2 -6
  221. package/dist/types/tools/browser/attach.d.ts +11 -0
  222. package/dist/types/tools/browser/launch.d.ts +20 -0
  223. package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
  224. package/dist/types/tools/browser/render.d.ts +1 -0
  225. package/dist/types/tools/browser.d.ts +3 -1
  226. package/dist/types/tools/image-gen.d.ts +10 -2
  227. package/dist/types/tools/index.d.ts +8 -0
  228. package/dist/types/tools/monitor.d.ts +1 -1
  229. package/dist/types/tools/skill-discovery.d.ts +8 -1
  230. package/dist/types/tools/subagent.d.ts +2 -0
  231. package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
  232. package/dist/types/utils/herdr-pane.d.ts +76 -0
  233. package/package.json +19 -9
  234. package/scripts/build-binary.ts +2 -1
  235. package/scripts/build-sdk-package-smoke.ts +2 -13
  236. package/scripts/capture-sticky-viewport-showcase.ts +0 -1
  237. package/scripts/compile-args.ts +2 -0
  238. package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
  239. package/scripts/generate-sdk-operation-inventory.ts +28 -0
  240. package/scripts/generate-telegram-baseline-manifest.ts +1 -0
  241. package/scripts/generate-tool-catalog.ts +6 -4
  242. package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
  243. package/src/async/job-manager.ts +352 -17
  244. package/src/async/support.ts +25 -0
  245. package/src/autoresearch/dashboard.ts +28 -13
  246. package/src/capability/index.ts +3 -1
  247. package/src/capability/mcp.ts +6 -0
  248. package/src/capability/types.ts +6 -0
  249. package/src/cli/accounts-cli.ts +395 -0
  250. package/src/cli/args.ts +23 -0
  251. package/src/cli/auth-broker-cli.ts +136 -66
  252. package/src/cli/auth-gateway-cli.ts +94 -70
  253. package/src/cli/config-cli.ts +27 -2
  254. package/src/cli/crash-cli.ts +78 -0
  255. package/src/cli/customize-doctor.ts +1605 -0
  256. package/src/cli/daemon-cli.ts +12 -3
  257. package/src/cli/fast-help.ts +1 -0
  258. package/src/cli/mcp-cli.ts +128 -60
  259. package/src/cli/notify-cli.ts +8 -14
  260. package/src/cli/root-flags.ts +8 -0
  261. package/src/cli/setup-cli.ts +145 -0
  262. package/src/cli/skills-cli.ts +38 -2
  263. package/src/cli/update-cli.ts +167 -12
  264. package/src/cli.ts +4 -0
  265. package/src/commands/accounts.ts +71 -0
  266. package/src/commands/crash.ts +35 -0
  267. package/src/commands/customize.ts +84 -0
  268. package/src/commands/daemon.ts +2 -2
  269. package/src/commands/deep-interview.ts +2 -0
  270. package/src/commands/launch.ts +1 -0
  271. package/src/commands/mcp.ts +12 -7
  272. package/src/commands/ralplan.ts +2 -0
  273. package/src/commands/sdk.ts +65 -32
  274. package/src/commands/setup.ts +5 -0
  275. package/src/commands/skills.ts +14 -4
  276. package/src/commands/ultragoal.ts +10 -1
  277. package/src/commands/update.ts +26 -15
  278. package/src/config/atomic-yaml-patch-worker.ts +57 -0
  279. package/src/config/atomic-yaml-patch.ts +769 -29
  280. package/src/config/config-schema-version.ts +10 -0
  281. package/src/config/file-lock.ts +70 -1
  282. package/src/config/mcp-schema.json +52 -14
  283. package/src/config/model-profile-activation.ts +23 -2
  284. package/src/config/model-profiles.ts +88 -21
  285. package/src/config/model-registry.ts +147 -21
  286. package/src/config/models-config-schema.ts +1 -1
  287. package/src/config/settings-schema.ts +77 -7
  288. package/src/config/settings.ts +4038 -199
  289. package/src/config/skill-settings-defaults.ts +41 -3
  290. package/src/config/task-delegation.ts +87 -0
  291. package/src/config/update-channel.ts +23 -0
  292. package/src/coordinator-mcp/server.ts +377 -308
  293. package/src/crash/index-store.ts +723 -0
  294. package/src/crash/nudge.ts +89 -0
  295. package/src/crash/record-loader.ts +149 -0
  296. package/src/crash/report.ts +551 -0
  297. package/src/crash/sanitize.ts +129 -0
  298. package/src/cursor.ts +36 -10
  299. package/src/customization/import.ts +946 -0
  300. package/src/customization/index.ts +4 -0
  301. package/src/customization/inventory.ts +335 -0
  302. package/src/customization/mutations.ts +141 -0
  303. package/src/customization/types.ts +223 -0
  304. package/src/daemon/control-types.ts +8 -1
  305. package/src/daemon/operator-contract.ts +6 -6
  306. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
  307. package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
  308. package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
  309. package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
  310. package/src/discovery/claude-hooks.ts +38 -0
  311. package/src/discovery/claude.ts +142 -209
  312. package/src/discovery/codex-hooks.ts +38 -0
  313. package/src/discovery/codex.ts +132 -198
  314. package/src/discovery/helpers.ts +8 -1
  315. package/src/discovery/index.ts +2 -0
  316. package/src/discovery/mcp-compat.ts +234 -0
  317. package/src/discovery/mcp-json.ts +6 -0
  318. package/src/edit/renderer.ts +40 -0
  319. package/src/edit/streaming.ts +4 -2
  320. package/src/exec/bash-executor.ts +24 -20
  321. package/src/export/html/index.ts +51 -2
  322. package/src/extensibility/extensions/loader.ts +9 -1
  323. package/src/extensibility/extensions/runner.ts +17 -4
  324. package/src/extensibility/extensions/types.ts +31 -6
  325. package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
  326. package/src/extensibility/gjc-plugins/installer.ts +9 -5
  327. package/src/extensibility/gjc-plugins/observability.ts +8 -2
  328. package/src/extensibility/gjc-plugins/registry.ts +13 -2
  329. package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
  330. package/src/extensibility/gjc-plugins/schema.ts +222 -39
  331. package/src/extensibility/hooks/loader.ts +154 -10
  332. package/src/extensibility/runtime-skill-discovery.ts +257 -60
  333. package/src/extensibility/skill-management.ts +368 -0
  334. package/src/extensibility/skills.ts +52 -16
  335. package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
  336. package/src/gjc-runtime/launch-worktree.ts +56 -2
  337. package/src/gjc-runtime/linux-proc.ts +7 -1
  338. package/src/gjc-runtime/ralplan-runtime.ts +100 -164
  339. package/src/gjc-runtime/session-state-lock.ts +992 -0
  340. package/src/gjc-runtime/session-state-sidecar.ts +644 -142
  341. package/src/gjc-runtime/state-writer.ts +39 -14
  342. package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
  343. package/src/gjc-runtime/ultragoal-guard.ts +11 -4
  344. package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
  345. package/src/gjc-runtime/workflow-settings.ts +596 -0
  346. package/src/goals/tools/goal-tool.ts +6 -1
  347. package/src/hooks/events.ts +291 -0
  348. package/src/hooks/native-skill-hook.ts +4 -0
  349. package/src/hooks/normalize.ts +369 -0
  350. package/src/hooks/skill-state.ts +165 -46
  351. package/src/internal-urls/docs-index.generated.ts +41 -32
  352. package/src/main.ts +36 -5
  353. package/src/migrate/mcp-mapper.ts +25 -2
  354. package/src/modes/acp/abort-scope.ts +29 -0
  355. package/src/modes/acp/acp-agent.ts +423 -98
  356. package/src/modes/components/assistant-message.ts +6 -1
  357. package/src/modes/components/chrome.ts +4 -3
  358. package/src/modes/components/customization/customization-dashboard.ts +443 -0
  359. package/src/modes/components/customization/import-wizard.ts +372 -0
  360. package/src/modes/components/customization/index.ts +1 -0
  361. package/src/modes/components/dynamic-theme-text.ts +22 -0
  362. package/src/modes/components/gajae-pet-widget.ts +607 -193
  363. package/src/modes/components/irc-sidebar.ts +11 -0
  364. package/src/modes/components/iterm-pet-transport.ts +585 -0
  365. package/src/modes/components/model-selector.ts +1 -6
  366. package/src/modes/components/oauth-selector.ts +277 -86
  367. package/src/modes/components/pet-capability.ts +35 -31
  368. package/src/modes/components/settings-selector.ts +29 -5
  369. package/src/modes/components/status-line/gh.ts +45 -1
  370. package/src/modes/components/status-line/segments.ts +19 -4
  371. package/src/modes/components/tool-status-header.ts +13 -3
  372. package/src/modes/controllers/command-controller.ts +20 -8
  373. package/src/modes/controllers/extension-ui-controller.ts +56 -257
  374. package/src/modes/controllers/input-controller.ts +10 -0
  375. package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
  376. package/src/modes/controllers/selector-controller.ts +238 -77
  377. package/src/modes/controllers/stt-controller.ts +3 -3
  378. package/src/modes/interactive-mode.ts +88 -16
  379. package/src/modes/print-mode.ts +35 -6
  380. package/src/modes/runtime-init.ts +21 -68
  381. package/src/modes/theme/defaults/index.ts +2 -0
  382. package/src/modes/theme/defaults/ouroboros.json +118 -0
  383. package/src/modes/types.ts +1 -0
  384. package/src/modes/utils/injected-user-submission.ts +25 -0
  385. package/src/modes/utils/terminal-bell.ts +15 -1
  386. package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
  387. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  388. package/src/prompts/agents/critic.md +1 -1
  389. package/src/prompts/system/rlm-research.md +1 -1
  390. package/src/prompts/system/system-prompt.md +0 -2
  391. package/src/prompts/tools/bash.md +7 -1
  392. package/src/prompts/tools/browser.md +1 -1
  393. package/src/prompts/tools/cron.md +6 -6
  394. package/src/prompts/tools/read.md +0 -2
  395. package/src/prompts/tools/skill-discovery.md +4 -2
  396. package/src/runtime-mcp/client.ts +427 -23
  397. package/src/runtime-mcp/config.ts +37 -5
  398. package/src/runtime-mcp/manager-pool.test.ts +56 -0
  399. package/src/runtime-mcp/manager.ts +52 -2
  400. package/src/runtime-mcp/oauth-discovery.ts +13 -0
  401. package/src/runtime-mcp/oauth-flow.ts +46 -1
  402. package/src/runtime-mcp/pool-key.ts +5 -1
  403. package/src/runtime-mcp/pool.test.ts +9 -0
  404. package/src/runtime-mcp/protocol.test.ts +353 -0
  405. package/src/runtime-mcp/protocol.ts +624 -0
  406. package/src/runtime-mcp/scope-config.ts +78 -0
  407. package/src/runtime-mcp/tool-bridge.ts +44 -10
  408. package/src/runtime-mcp/transports/http.ts +84 -13
  409. package/src/runtime-mcp/types.ts +123 -12
  410. package/src/sdk/acp/adapter.ts +328 -97
  411. package/src/sdk/broker/broker.ts +160 -741
  412. package/src/sdk/broker/ensure.ts +9 -1
  413. package/src/sdk/broker/lifecycle.ts +510 -116
  414. package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
  415. package/src/sdk/broker/process-incarnation.ts +19 -0
  416. package/src/sdk/broker/session-index.ts +516 -117
  417. package/src/sdk/broker/startup-failure.ts +28 -5
  418. package/src/sdk/broker/transport.ts +1 -13
  419. package/src/sdk/bus/chat-daemon-cli.ts +4 -2
  420. package/src/sdk/bus/chat-daemon-control.ts +67 -6
  421. package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
  422. package/src/sdk/bus/config.ts +13 -0
  423. package/src/sdk/bus/conversation-store.ts +59 -171
  424. package/src/sdk/bus/daemon-paths.ts +20 -0
  425. package/src/sdk/bus/discord-conversation.ts +2 -0
  426. package/src/sdk/bus/discord-daemon.ts +524 -126
  427. package/src/sdk/bus/discord-live-provider.ts +206 -79
  428. package/src/sdk/bus/existing-thread-readiness.ts +4 -97
  429. package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
  430. package/src/sdk/bus/index.ts +2480 -456
  431. package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
  432. package/src/sdk/bus/lifecycle-commands.ts +2 -2
  433. package/src/sdk/bus/managed-daemon.ts +2 -145
  434. package/src/sdk/bus/notification-service.ts +196 -0
  435. package/src/sdk/bus/operator-runtime.ts +1 -1
  436. package/src/sdk/bus/prompt-reconciliation.ts +24 -15
  437. package/src/sdk/bus/reconciliation-store.ts +553 -24
  438. package/src/sdk/bus/session-control.ts +49 -3
  439. package/src/sdk/bus/slack-conversation.ts +4 -0
  440. package/src/sdk/bus/slack-daemon.ts +733 -134
  441. package/src/sdk/bus/slack-live-provider.ts +65 -15
  442. package/src/sdk/bus/slack-provider.ts +4 -0
  443. package/src/sdk/bus/slack-thread-binding.ts +9 -104
  444. package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
  445. package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
  446. package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
  447. package/src/sdk/bus/telegram-daemon-control.ts +3 -13
  448. package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
  449. package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
  450. package/src/sdk/bus/telegram-daemon.ts +2552 -2280
  451. package/src/sdk/bus/telegram-reference.ts +1 -226
  452. package/src/sdk/bus/telegram-setup.ts +2 -2
  453. package/src/sdk/bus/threaded-render.ts +1 -0
  454. package/src/sdk/bus/topic-registry.ts +279 -146
  455. package/src/sdk/cli/rows.ts +2 -35
  456. package/src/sdk/cli/session-cli.ts +776 -700
  457. package/src/sdk/client/client.ts +892 -1
  458. package/src/sdk/host/control/dispatch.ts +93 -58
  459. package/src/sdk/host/control/index.ts +1 -1
  460. package/src/sdk/host/control/operations.ts +18 -10
  461. package/src/sdk/host/control/runtime-gate.ts +40 -0
  462. package/src/sdk/host/host.ts +63 -12
  463. package/src/sdk/host/query/handlers.ts +66 -7
  464. package/src/sdk/host/reverse-leases.ts +2 -2
  465. package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
  466. package/src/sdk/host/session-runtime.test.ts +2787 -34
  467. package/src/sdk/host/session-runtime.ts +1489 -101
  468. package/src/sdk/host/surface-policy.ts +12 -8
  469. package/src/sdk/host/types.ts +1 -1
  470. package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
  471. package/src/sdk/host/websocket-transport.ts +85 -50
  472. package/src/sdk/index.ts +2 -1
  473. package/src/sdk/lifecycle/broker-client.ts +53 -0
  474. package/src/sdk/lifecycle/client.ts +234 -0
  475. package/src/sdk/lifecycle/index.ts +3 -0
  476. package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
  477. package/src/sdk/lifecycle/service.ts +720 -0
  478. package/src/sdk/mcp/server.ts +226 -188
  479. package/src/sdk/prompt-deadline-lease.ts +55 -0
  480. package/src/sdk/prompt-deadline-manager.ts +148 -0
  481. package/src/sdk/prompt-status.ts +11 -3
  482. package/src/sdk/protocol/operation-inventory.generated.json +272 -129
  483. package/src/sdk/protocol/operation-registry.ts +71 -8
  484. package/src/sdk/receipt-state.ts +43 -0
  485. package/src/sdk/reconciliation-extensions.ts +14 -0
  486. package/src/sdk/router/index.ts +1 -0
  487. package/src/sdk/router/session-router.ts +1658 -0
  488. package/src/sdk/session-list.ts +93 -0
  489. package/src/sdk/session-reconnect.ts +45 -1
  490. package/src/sdk/session.ts +716 -120
  491. package/src/sdk/transport/serve-cli.ts +18 -34
  492. package/src/session/account-inventory.ts +603 -0
  493. package/src/session/agent-session.ts +2873 -559
  494. package/src/session/agent-storage.ts +35 -0
  495. package/src/session/blob-store.ts +32 -1
  496. package/src/session/coordinator-tool-label.ts +40 -0
  497. package/src/session/internal/managed-session-scope.ts +4 -0
  498. package/src/session/internal/managed-session-storage.ts +199 -31
  499. package/src/session/messages.ts +1 -1
  500. package/src/session/secure-token-file.ts +80 -0
  501. package/src/session/session-manager.ts +757 -57
  502. package/src/session/session-storage.ts +25 -16
  503. package/src/session/startup-auth-config.ts +297 -0
  504. package/src/session/terminal-abort.ts +1253 -0
  505. package/src/session/yield-queue.ts +57 -15
  506. package/src/session-import/claude.ts +334 -0
  507. package/src/session-import/codex.ts +1033 -0
  508. package/src/session-import/command.ts +77 -0
  509. package/src/session-import/detect.ts +133 -0
  510. package/src/session-import/index.ts +7 -0
  511. package/src/session-import/provider-codex.ts +327 -0
  512. package/src/session-import/provider-service.ts +457 -0
  513. package/src/session-import/redact.ts +128 -0
  514. package/src/session-import/service.ts +1264 -0
  515. package/src/session-import/types.ts +148 -0
  516. package/src/setup/paseo/check.ts +213 -0
  517. package/src/setup/paseo/install-saga.ts +293 -0
  518. package/src/setup/paseo/json-publisher.ts +263 -0
  519. package/src/setup/paseo/orchestration-preferences.ts +86 -0
  520. package/src/setup/paseo/paseo-ownership.ts +224 -0
  521. package/src/setup/paseo/paseo-setup.ts +253 -0
  522. package/src/setup/paseo/provider-config.ts +139 -0
  523. package/src/setup/paseo/remove.ts +183 -0
  524. package/src/setup/paseo/result-types.ts +79 -0
  525. package/src/setup/paseo/setup-deps.ts +175 -0
  526. package/src/setup/paseo/skills-bridge.ts +260 -0
  527. package/src/skill-state/active-state.ts +21 -11
  528. package/src/slash-commands/acp-builtins.ts +9 -1
  529. package/src/slash-commands/builtin-registry.ts +117 -19
  530. package/src/slash-commands/helpers/credential-switch.ts +143 -0
  531. package/src/slash-commands/helpers/mcp.ts +9 -3
  532. package/src/slash-commands/helpers/usage-report.ts +61 -56
  533. package/src/slash-commands/types.ts +7 -2
  534. package/src/task/executor.ts +30 -11
  535. package/src/task/index.ts +66 -7
  536. package/src/task/render.ts +23 -9
  537. package/src/tools/ask-answer-registry.ts +23 -7
  538. package/src/tools/ask.ts +8 -4
  539. package/src/tools/bash-sleep-advisory.ts +115 -0
  540. package/src/tools/bash.ts +98 -18
  541. package/src/tools/browser/attach.ts +71 -3
  542. package/src/tools/browser/launch.ts +86 -13
  543. package/src/tools/browser/profile-discovery.ts +57 -7
  544. package/src/tools/browser/render.ts +8 -1
  545. package/src/tools/browser.ts +130 -21
  546. package/src/tools/computer.enforcement.test.ts +1 -1
  547. package/src/tools/computer.ts +3 -2
  548. package/src/tools/cron.ts +12 -1
  549. package/src/tools/image-gen.ts +349 -142
  550. package/src/tools/index.ts +8 -0
  551. package/src/tools/job.ts +33 -6
  552. package/src/tools/monitor.ts +41 -2
  553. package/src/tools/skill-discovery.ts +21 -29
  554. package/src/tools/subagent.ts +142 -24
  555. package/src/tools/todo-contract.ts +55 -3
  556. package/src/tools/todo-write.ts +4 -3
  557. package/src/tools/tool-catalog.generated.ts +6 -6
  558. package/src/tools/ultragoal-ask-guard.ts +10 -3
  559. package/src/utils/herdr-pane.ts +345 -0
  560. package/src/utils/title-generator.ts +2 -0
  561. package/src/web/scrapers/index.ts +6 -4
  562. package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
  563. package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
  564. package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
  565. package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
  566. package/dist/types/runtime/lazy-service.test.d.ts +0 -1
  567. package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
  568. package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
  569. package/dist/types/runtime-mcp/config.test.d.ts +0 -1
  570. package/dist/types/runtime-mcp/http.test.d.ts +0 -1
  571. package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
  572. package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
  573. package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
  574. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
  575. package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
  576. package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
  577. package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
  578. package/dist/types/sdk/elevation/audit.d.ts +0 -93
  579. package/dist/types/sdk/elevation/capability.d.ts +0 -3
  580. package/dist/types/sdk/elevation/digest.d.ts +0 -72
  581. package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
  582. package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
  583. package/dist/types/sdk/elevation/owner.d.ts +0 -48
  584. package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
  585. package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
  586. package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
  587. package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
  588. package/dist/types/session/auth-broker-config.d.ts +0 -13
  589. package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
  590. package/dist/types/skills/skills.test.d.ts +0 -1
  591. package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
  592. package/dist/types/task/token-log.test.d.ts +0 -1
  593. package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
  594. package/dist/types/tools/descriptors.test.d.ts +0 -1
  595. package/scripts/g005-daemon-smoke.ts +0 -180
  596. package/scripts/g011-daemon-path-smoke.ts +0 -153
  597. package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
  598. package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
  599. package/src/sdk/bus/telegram-cli.ts +0 -212
  600. package/src/sdk/elevation/allowlist.ts +0 -54
  601. package/src/sdk/elevation/audit.ts +0 -208
  602. package/src/sdk/elevation/capability.ts +0 -41
  603. package/src/sdk/elevation/digest.ts +0 -129
  604. package/src/sdk/elevation/dispatch-receipt.ts +0 -184
  605. package/src/sdk/elevation/grant-ledger.ts +0 -1142
  606. package/src/sdk/elevation/owner.ts +0 -109
  607. package/src/session/auth-broker-config.ts +0 -109
  608. /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
  609. /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
  610. /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
@@ -0,0 +1,498 @@
1
+ import * as path from "node:path";
2
+ import { nativeProcessBindings } from "@gajae-code/utils/native-process";
3
+ import { $ } from "bun";
4
+ import { isProcessIncarnation } from "../broker/process-incarnation";
5
+ import type { TelegramDaemonFs } from "./telegram-daemon";
6
+ import {
7
+ listTelegramOwnerMarkers,
8
+ removeTelegramOwnerMarker,
9
+ type TelegramOwnerMarker,
10
+ } from "./telegram-daemon-owner-registry";
11
+
12
+ /**
13
+ * A stable, identity-bound reference to a process opened BEFORE the incarnation
14
+ * check and used for ALL subsequent operations. This closes the PID-reuse race:
15
+ * the native handle pins the exact process incarnation, so a reused PID cannot
16
+ * be signaled through a stale reference — the OS will reject the operation.
17
+ *
18
+ * `signalRoot` signals only the pinned root process (root-only).
19
+ * `terminateTree` signals the process AND its descendants / process group.
20
+ */
21
+ export interface TelegramOrphanProcessRef {
22
+ /** The incarnation of the process this reference was opened against. */
23
+ incarnation: string;
24
+ /**
25
+ * Gracefully terminate this process and its entire descendant tree / process
26
+ * group (TERM → wait → KILL escalation). Returns true if the process (and
27
+ * its children) exited within the bounded wait.
28
+ */
29
+ terminateTree(signal?: NodeJS.Signals): boolean;
30
+ }
31
+
32
+ /**
33
+ * A pinned reference that can additionally prove the process's kernel-derived
34
+ * launch arguments. Used only by the legacy-stray sweep: candidate PIDs come
35
+ * from an untrusted enumeration, but every authorization fact (incarnation,
36
+ * argv) is read from this pinned reference, never from the enumeration.
37
+ */
38
+ export interface TelegramStrayProcessRef extends TelegramOrphanProcessRef {
39
+ /** Launch arguments reported by the kernel for the pinned incarnation. */
40
+ args(): string[];
41
+ }
42
+
43
+ export interface TelegramOrphanReapDeps {
44
+ fs?: TelegramDaemonFs;
45
+ now?: () => number;
46
+ pidAlive: (pid: number) => boolean;
47
+ pidIncarnation: (pid: number) => string | undefined;
48
+ /**
49
+ * Opens a stable process reference bound to the exact process incarnation at
50
+ * open time. The reference must be opened BEFORE the incarnation check so
51
+ * that termination operates on the same identity that was proven stale.
52
+ */
53
+ processReference?: (pid: number) => TelegramOrphanProcessRef | undefined;
54
+ platform?: NodeJS.Platform;
55
+ /**
56
+ * Enumerates candidate PIDs for the legacy-stray sweep. The list is
57
+ * untrusted discovery input only; authorization derives exclusively from
58
+ * the pinned {@link TelegramStrayProcessRef}. Defaults to a bounded POSIX
59
+ * `ps` listing; returns nothing on Windows or enumeration failure.
60
+ */
61
+ listCandidatePids?: () => Promise<number[]>;
62
+ /** Opens a pinned argv-capable reference for stray verification. */
63
+ strayReference?: (pid: number) => TelegramStrayProcessRef | undefined;
64
+ /**
65
+ * First-sighting ledger for stray confirmation, keyed by
66
+ * `pid|incarnation` with the first-seen timestamp. Injectable for tests;
67
+ * defaults to a process-lifetime module ledger.
68
+ */
69
+ straySightings?: Map<string, number>;
70
+ }
71
+
72
+ export interface TelegramOrphanCandidate {
73
+ marker: TelegramOwnerMarker;
74
+ executablePath?: string;
75
+ argv?: string[];
76
+ }
77
+
78
+ export type OrphanReapDecision =
79
+ | { kind: "reaped"; pid: number; acquisitionId: string }
80
+ | { kind: "refused"; pid: number; acquisitionId: string; reason: string }
81
+ | { kind: "inert"; pid: number; acquisitionId: string };
82
+
83
+ export interface TelegramOrphanRecoveryReceipt {
84
+ version: 1;
85
+ agentDir: string;
86
+ currentOwnerId: string;
87
+ currentAcquisitionId: string;
88
+ currentPid: number;
89
+ createdAt: number;
90
+ candidates: number;
91
+ terminated: number;
92
+ refused: number;
93
+ inert: number;
94
+ // bounded, secret-free
95
+ reasons: Record<string, number>;
96
+ // no command lines, tokens, chatIds, env dumps
97
+ }
98
+
99
+ /**
100
+ * Maximum number of candidate markers the sweep will inspect and attempt to
101
+ * reap. A runaway registry cannot turn the sweep into unbounded wall time.
102
+ */
103
+ const MAX_REAP_CANDIDATES = 64;
104
+
105
+ /** Bounded cooperative-termination wait before escalating to hard kill (ms). */
106
+ const TERM_GRACE_MS = 2_000;
107
+ /** Bounded hard-kill wait before declaring termination failed (ms). */
108
+ const KILL_WAIT_MS = 1_500;
109
+
110
+ /** Maximum PIDs the legacy-stray enumeration will inspect per sweep. */
111
+ const MAX_STRAY_SCAN_PIDS = 4096;
112
+ /** Maximum stray terminations per sweep; the rest wait for the next cadence. */
113
+ const MAX_STRAY_TERMINATIONS = 8;
114
+ /**
115
+ * A stray must be sighted twice with the same pinned incarnation at least this
116
+ * far apart before it may be terminated. A legitimate successor daemon reaches
117
+ * ownership (and writes its marker) within seconds of spawning, so it can
118
+ * never accumulate a confirmed unmarked sighting; a pre-registry zombie
119
+ * persists across sweeps and is confirmed on the second one.
120
+ */
121
+ const STRAY_CONFIRMATION_MS = 45_000;
122
+ /** Bounded size of the first-sighting ledger. */
123
+ const MAX_STRAY_SIGHTINGS = 256;
124
+
125
+ /** Process-lifetime default first-sighting ledger for the stray sweep. */
126
+ const moduleStraySightings = new Map<string, number>();
127
+
128
+ /**
129
+ * Exact invocation-signature test for a legacy Telegram daemon owner:
130
+ * a `notify daemon-internal` subcommand bound to exactly this agent dir.
131
+ * This is deliberately an exact-token match on kernel-reported argv from a
132
+ * pinned reference — never a substring similarity over an untrusted dump.
133
+ */
134
+ export function isLegacyStrayDaemonArgs(args: readonly string[], agentDir: string): boolean {
135
+ const resolvedAgentDir = path.resolve(agentDir);
136
+ let hasSubcommand = false;
137
+ let agentDirMatches = false;
138
+ for (let i = 0; i < args.length; i++) {
139
+ if (args[i] === "notify" && args[i + 1] === "daemon-internal") hasSubcommand = true;
140
+ if (args[i] === "--agent-dir") {
141
+ const value = args[i + 1];
142
+ if (typeof value === "string" && path.resolve(value) === resolvedAgentDir) agentDirMatches = true;
143
+ }
144
+ }
145
+ return hasSubcommand && agentDirMatches;
146
+ }
147
+
148
+ /** Bounded POSIX PID enumeration; empty on Windows or on any failure. */
149
+ async function defaultListCandidatePids(platform: NodeJS.Platform): Promise<number[]> {
150
+ if (platform === "win32") return [];
151
+ try {
152
+ const out = await $`ps -axo pid=`.quiet().text();
153
+ const pids: number[] = [];
154
+ for (const line of out.split("\n")) {
155
+ const pid = Number.parseInt(line.trim(), 10);
156
+ if (Number.isSafeInteger(pid) && pid > 0) pids.push(pid);
157
+ if (pids.length >= MAX_STRAY_SCAN_PIDS) break;
158
+ }
159
+ return pids;
160
+ } catch {
161
+ return [];
162
+ }
163
+ }
164
+
165
+ /** Default pinned argv-capable reference over the native process bindings. */
166
+ function defaultStrayReference(pid: number): TelegramStrayProcessRef | undefined {
167
+ try {
168
+ const ref = nativeProcessBindings().Process.fromPid(pid);
169
+ if (!ref || !isProcessIncarnation(ref.incarnation)) return undefined;
170
+ return {
171
+ incarnation: ref.incarnation,
172
+ args: () => {
173
+ try {
174
+ return ref.args();
175
+ } catch {
176
+ return [];
177
+ }
178
+ },
179
+ terminateTree: (signal?: NodeJS.Signals) => {
180
+ try {
181
+ return ref.killTree(signal === "SIGKILL" ? 9 : 15) > 0;
182
+ } catch {
183
+ return false;
184
+ }
185
+ },
186
+ };
187
+ } catch {
188
+ return undefined;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Identity-bound, process-group-aware termination.
194
+ *
195
+ * Uses the stable process reference (opened before the incarnation check) so
196
+ * that a reused PID cannot be signaled through a stale handle. The native
197
+ * `terminateTree` signals the process AND its descendants / process group,
198
+ * proving complete owned process-group cleanup rather than root-only signal.
199
+ *
200
+ * Falls back to POSIX `process.kill(-pgid)` when a native reference is
201
+ * unavailable, attempting the negative-pid group signal first.
202
+ */
203
+ async function terminateOwnedProcessTree(
204
+ pid: number,
205
+ ref: TelegramOrphanProcessRef | undefined,
206
+ deps: TelegramOrphanReapDeps,
207
+ ): Promise<boolean> {
208
+ // Preferred path: native stable reference with process-group termination.
209
+ if (ref) {
210
+ try {
211
+ const exited = await boundedTerminationWait(pid, deps, () => {
212
+ try {
213
+ return ref.terminateTree("SIGTERM");
214
+ } catch {
215
+ return false;
216
+ }
217
+ });
218
+ if (exited) return true;
219
+ // Escalate to hard kill via the same stable reference.
220
+ try {
221
+ ref.terminateTree("SIGKILL");
222
+ } catch {
223
+ return !deps.pidAlive(pid);
224
+ }
225
+ return await boundedTerminationWait(pid, deps, () => false, KILL_WAIT_MS);
226
+ } catch {
227
+ return !deps.pidAlive(pid);
228
+ }
229
+ }
230
+
231
+ // Fallback: POSIX process-group signal via negative-pid.
232
+ try {
233
+ try {
234
+ process.kill(-pid, "SIGTERM");
235
+ } catch (e) {
236
+ const code = (e as NodeJS.ErrnoException).code;
237
+ if (code === "ESRCH") return true;
238
+ // EPERM or ESRCH on the group: try root-only signal as last resort.
239
+ try {
240
+ process.kill(pid, "SIGTERM");
241
+ } catch (e2) {
242
+ if ((e2 as NodeJS.ErrnoException).code === "ESRCH") return true;
243
+ return false;
244
+ }
245
+ }
246
+ const exited = await boundedTerminationWait(pid, deps, () => false);
247
+ if (exited) return true;
248
+ try {
249
+ process.kill(-pid, "SIGKILL");
250
+ } catch (e) {
251
+ const code = (e as NodeJS.ErrnoException).code;
252
+ if (code === "ESRCH") return true;
253
+ try {
254
+ process.kill(pid, "SIGKILL");
255
+ } catch (e2) {
256
+ if ((e2 as NodeJS.ErrnoException).code === "ESRCH") return true;
257
+ return false;
258
+ }
259
+ }
260
+ return await boundedTerminationWait(pid, deps, () => false, KILL_WAIT_MS);
261
+ } catch {
262
+ return false;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Poll process liveness within a bounded deadline. The optional `signalFn`
268
+ * fires the initial signal; it is called once, then liveness is polled.
269
+ * Incarnation drift also proves exit (the reference's process is gone even if
270
+ * the PID was reused).
271
+ */
272
+ async function boundedTerminationWait(
273
+ pid: number,
274
+ deps: TelegramOrphanReapDeps,
275
+ signalFn: () => boolean,
276
+ budgetMs = TERM_GRACE_MS,
277
+ ): Promise<boolean> {
278
+ signalFn();
279
+ const clock = deps.now ?? Date.now;
280
+ const deadline = clock() + budgetMs;
281
+ const step = Math.max(Math.floor(budgetMs / 40), 25);
282
+ while (clock() < deadline) {
283
+ if (!deps.pidAlive(pid)) return true;
284
+ await new Promise<void>(r => setTimeout(r, step));
285
+ }
286
+ return !deps.pidAlive(pid);
287
+ }
288
+
289
+ /**
290
+ * Bounded stale-owner sweep. Authorizes termination only from product-owned
291
+ * marker registry bound to exact agentDir digest + acquisitionId + pid + incarnation.
292
+ * Never authorizes from bare /proc cmdline similarity; markers are the trust anchor.
293
+ *
294
+ * Safety invariants:
295
+ * 1. The stable process reference is opened BEFORE the incarnation check and
296
+ * used for termination, so a reused PID cannot be signaled through a stale
297
+ * handle.
298
+ * 2. Process-group termination (not root-only signal) ensures reparented
299
+ * descendants of an orphaned daemon are also cleaned up.
300
+ * 3. Zombies and dead processes are classified inert without any signaling.
301
+ * 4. The sweep is bounded: at most MAX_REAP_CANDIDATES markers are inspected.
302
+ */
303
+ export async function reapTelegramDaemonOrphans(input: {
304
+ agentDir: string;
305
+ currentOwnerId: string;
306
+ currentAcquisitionId: string;
307
+ currentPid: number;
308
+ currentIncarnation: string;
309
+ fsImpl: TelegramDaemonFs;
310
+ deps: TelegramOrphanReapDeps;
311
+ }): Promise<{ decisions: OrphanReapDecision[]; receipt: TelegramOrphanRecoveryReceipt }> {
312
+ const fsImpl = input.fsImpl;
313
+ const deps = input.deps;
314
+ const candidates = await listTelegramOwnerMarkers(fsImpl, input.agentDir);
315
+ const decisions: OrphanReapDecision[] = [];
316
+ const reasons: Record<string, number> = {};
317
+ let terminated = 0;
318
+ let refused = 0;
319
+ let inert = 0;
320
+
321
+ // Bound the sweep: process at most MAX_REAP_CANDIDATES markers.
322
+ const bounded = candidates.slice(0, MAX_REAP_CANDIDATES);
323
+
324
+ for (const entry of bounded) {
325
+ if (!entry.marker) {
326
+ decisions.push({
327
+ kind: "refused",
328
+ pid: -1,
329
+ acquisitionId: entry.acquisitionId,
330
+ reason: "malformed_or_foreign",
331
+ });
332
+ refused += 1;
333
+ reasons.malformed_or_foreign = (reasons.malformed_or_foreign ?? 0) + 1;
334
+ continue;
335
+ }
336
+ const m = entry.marker;
337
+ if (
338
+ m.acquisitionId === input.currentAcquisitionId &&
339
+ m.ownerId === input.currentOwnerId &&
340
+ m.pid === input.currentPid &&
341
+ m.incarnation === input.currentIncarnation
342
+ ) {
343
+ // current owner — never signal
344
+ continue;
345
+ }
346
+ // Skip markers whose pid/incarnation proves they are the current live owner
347
+ if (m.pid === input.currentPid && m.incarnation === input.currentIncarnation) {
348
+ decisions.push({ kind: "refused", pid: m.pid, acquisitionId: m.acquisitionId, reason: "current_incarnation" });
349
+ refused += 1;
350
+ reasons.current_incarnation = (reasons.current_incarnation ?? 0) + 1;
351
+ continue;
352
+ }
353
+ // Open the stable process reference BEFORE the incarnation check.
354
+ // This pins the exact process identity for all subsequent operations.
355
+ const ref = deps.processReference?.(m.pid);
356
+
357
+ // Require stable incarnation authority; if unavailable fail closed.
358
+ // BUT: if pidAlive says absent, classify as inert (dead/zombie) without signaling.
359
+ if (!deps.pidAlive(m.pid)) {
360
+ // Process is dead or a zombie — inert, never signal.
361
+ decisions.push({ kind: "inert", pid: m.pid, acquisitionId: m.acquisitionId });
362
+ inert += 1;
363
+ // Clean stale marker for absent/dead pid.
364
+ await removeTelegramOwnerMarker(fsImpl, input.agentDir, m.acquisitionId).catch(() => undefined);
365
+ continue;
366
+ }
367
+ const curIncarnation = deps.pidIncarnation(m.pid);
368
+ if (!isProcessIncarnation(curIncarnation)) {
369
+ // Without stable proof of incarnation, fail closed — do not signal.
370
+ decisions.push({
371
+ kind: "refused",
372
+ pid: m.pid,
373
+ acquisitionId: m.acquisitionId,
374
+ reason: "incarnation_unavailable",
375
+ });
376
+ refused += 1;
377
+ reasons.incarnation_unavailable = (reasons.incarnation_unavailable ?? 0) + 1;
378
+ continue;
379
+ }
380
+ if (curIncarnation !== m.incarnation) {
381
+ // PID reused — old owner is inert, marker is stale. Never signal a
382
+ // live process that now belongs to a different incarnation.
383
+ decisions.push({ kind: "inert", pid: m.pid, acquisitionId: m.acquisitionId });
384
+ inert += 1;
385
+ await removeTelegramOwnerMarker(fsImpl, input.agentDir, m.acquisitionId).catch(() => undefined);
386
+ continue;
387
+ }
388
+ // Attempt bounded process-group TERM then KILL using the stable reference
389
+ // opened before the incarnation check.
390
+ const exited = await terminateOwnedProcessTree(m.pid, ref, deps);
391
+ if (exited || !deps.pidAlive(m.pid)) {
392
+ decisions.push({ kind: "reaped", pid: m.pid, acquisitionId: m.acquisitionId });
393
+ terminated += 1;
394
+ await removeTelegramOwnerMarker(fsImpl, input.agentDir, m.acquisitionId).catch(() => undefined);
395
+ } else {
396
+ decisions.push({ kind: "refused", pid: m.pid, acquisitionId: m.acquisitionId, reason: "termination_failed" });
397
+ refused += 1;
398
+ reasons.termination_failed = (reasons.termination_failed ?? 0) + 1;
399
+ }
400
+ }
401
+
402
+ // Legacy-stray sweep: daemons spawned by builds that predate the marker
403
+ // registry never registered a marker and may not own the state file, so the
404
+ // marker sweep above cannot see them — they keep polling getUpdates and
405
+ // 409-starve every fresh owner. Candidate PIDs come from an untrusted
406
+ // enumeration; ALL authorization facts (incarnation, argv) come from the
407
+ // pinned native reference. Termination additionally requires a confirmed
408
+ // second sighting of the same pinned incarnation, so a legitimate successor
409
+ // mid-handoff (which reaches ownership and writes its marker within
410
+ // seconds) can never be killed.
411
+ try {
412
+ const markerPids = new Set<number>();
413
+ for (const entry of candidates) if (entry.marker) markerPids.add(entry.marker.pid);
414
+ const clock = deps.now ?? Date.now;
415
+ const sightings = deps.straySightings ?? moduleStraySightings;
416
+ const listPids = deps.listCandidatePids ?? (() => defaultListCandidatePids(deps.platform ?? process.platform));
417
+ const strayRef = deps.strayReference ?? defaultStrayReference;
418
+ const liveKeys = new Set<string>();
419
+ let strayTerminations = 0;
420
+ for (const pid of (await listPids()).slice(0, MAX_STRAY_SCAN_PIDS)) {
421
+ if (pid === input.currentPid || pid === process.pid || markerPids.has(pid)) continue;
422
+ const ref = strayRef(pid);
423
+ if (!ref || !isProcessIncarnation(ref.incarnation)) continue;
424
+ if (ref.incarnation === input.currentIncarnation) continue;
425
+ if (!isLegacyStrayDaemonArgs(ref.args(), input.agentDir)) continue;
426
+ const key = `${pid}|${ref.incarnation}`;
427
+ liveKeys.add(key);
428
+ const firstSeenAt = sightings.get(key);
429
+ if (firstSeenAt === undefined) {
430
+ if (sightings.size < MAX_STRAY_SIGHTINGS) sightings.set(key, clock());
431
+ reasons.legacy_stray_pending_confirmation = (reasons.legacy_stray_pending_confirmation ?? 0) + 1;
432
+ continue;
433
+ }
434
+ if (clock() - firstSeenAt < STRAY_CONFIRMATION_MS) {
435
+ reasons.legacy_stray_pending_confirmation = (reasons.legacy_stray_pending_confirmation ?? 0) + 1;
436
+ continue;
437
+ }
438
+ if (strayTerminations >= MAX_STRAY_TERMINATIONS) break;
439
+ const acquisitionId = `legacy-stray:${pid}`;
440
+ const exited = await terminateOwnedProcessTree(pid, ref, deps);
441
+ if (exited || !deps.pidAlive(pid)) {
442
+ decisions.push({ kind: "reaped", pid, acquisitionId });
443
+ terminated += 1;
444
+ strayTerminations += 1;
445
+ sightings.delete(key);
446
+ liveKeys.delete(key);
447
+ reasons.legacy_stray_reaped = (reasons.legacy_stray_reaped ?? 0) + 1;
448
+ } else {
449
+ decisions.push({ kind: "refused", pid, acquisitionId, reason: "termination_failed" });
450
+ refused += 1;
451
+ reasons.legacy_stray_termination_failed = (reasons.legacy_stray_termination_failed ?? 0) + 1;
452
+ }
453
+ }
454
+ // Drop ledger entries whose process no longer matched this sweep, so the
455
+ // bounded ledger cannot fill with dead incarnations.
456
+ for (const key of [...sightings.keys()]) if (!liveKeys.has(key)) sightings.delete(key);
457
+ } catch {
458
+ reasons.legacy_stray_scan_failed = (reasons.legacy_stray_scan_failed ?? 0) + 1;
459
+ }
460
+
461
+ // If the registry exceeded the bound, record the overflow.
462
+ if (candidates.length > MAX_REAP_CANDIDATES) {
463
+ reasons.registry_overflow = candidates.length;
464
+ }
465
+
466
+ const receipt: TelegramOrphanRecoveryReceipt = {
467
+ version: 1,
468
+ agentDir: input.agentDir,
469
+ currentOwnerId: input.currentOwnerId,
470
+ currentAcquisitionId: input.currentAcquisitionId,
471
+ currentPid: input.currentPid,
472
+ createdAt: (deps.now ?? Date.now)(),
473
+ candidates: candidates.length,
474
+ terminated,
475
+ refused,
476
+ inert,
477
+ reasons,
478
+ };
479
+ // Bound receipt: ensure secret-free (no tokens, no chatIds, no env)
480
+ return { decisions, receipt };
481
+ }
482
+
483
+ export async function writeTelegramOrphanRecoveryReceipt(
484
+ fsImpl: TelegramDaemonFs,
485
+ agentDir: string,
486
+ receipt: TelegramOrphanRecoveryReceipt,
487
+ ): Promise<void> {
488
+ const { daemonPaths } = await import("./daemon-paths");
489
+ const file = daemonPaths(agentDir).recoveryReceipt;
490
+ const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
491
+ // Bounded size: JSON stringify once, truncate to 4KiB if needed (secret-free so truncation is safe)
492
+ let data = `${JSON.stringify(receipt, null, 2)}\n`;
493
+ if (data.length > 4096) data = data.slice(0, 4096);
494
+ await fsImpl.mkdir(path.dirname(file), { recursive: true, mode: 0o700 }).catch(() => undefined);
495
+ await fsImpl.writeFile(tmp, data, { mode: 0o600 });
496
+ await fsImpl.chmod(tmp, 0o600).catch(() => undefined);
497
+ await fsImpl.rename(tmp, file);
498
+ }
@@ -0,0 +1,176 @@
1
+ import * as path from "node:path";
2
+ import { isProcessIncarnation } from "../broker/process-incarnation";
3
+ import { agentDirDigest, telegramDaemonOwnerMarkerPath } from "./daemon-paths";
4
+ import type { TelegramDaemonFs } from "./telegram-daemon";
5
+
6
+ export interface TelegramOwnerMarker {
7
+ version: 1;
8
+ agentDir: string;
9
+ agentDirDigest: string;
10
+ ownerId: string;
11
+ acquisitionId: string;
12
+ pid: number;
13
+ incarnation: string;
14
+ createdAt: number;
15
+ startedAt: number;
16
+ }
17
+
18
+ function validMarker(value: unknown): value is TelegramOwnerMarker {
19
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
20
+ const v = value as Record<string, unknown>;
21
+ return (
22
+ v.version === 1 &&
23
+ typeof v.agentDir === "string" &&
24
+ typeof v.agentDirDigest === "string" &&
25
+ typeof v.ownerId === "string" &&
26
+ v.ownerId.length > 0 &&
27
+ typeof v.acquisitionId === "string" &&
28
+ v.acquisitionId.length > 0 &&
29
+ typeof v.pid === "number" &&
30
+ Number.isSafeInteger(v.pid) &&
31
+ v.pid > 0 &&
32
+ typeof v.incarnation === "string" &&
33
+ isProcessIncarnation(v.incarnation) &&
34
+ typeof v.createdAt === "number" &&
35
+ Number.isSafeInteger(v.createdAt) &&
36
+ typeof v.startedAt === "number" &&
37
+ Number.isSafeInteger(v.startedAt)
38
+ );
39
+ }
40
+
41
+ export async function writeTelegramOwnerMarker(
42
+ fsImpl: TelegramDaemonFs,
43
+ agentDir: string,
44
+ marker: TelegramOwnerMarker,
45
+ ): Promise<void> {
46
+ const file = telegramDaemonOwnerMarkerPath(agentDir, marker.acquisitionId);
47
+ const dir = path.dirname(file);
48
+ await fsImpl.mkdir(dir, { recursive: true, mode: 0o700 });
49
+ await fsImpl.chmod(dir, 0o700).catch(() => undefined);
50
+ const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
51
+ await fsImpl.writeFile(tmp, `${JSON.stringify(marker, null, 2)}\n`, { mode: 0o600 });
52
+ await fsImpl.chmod(tmp, 0o600).catch(() => undefined);
53
+ await fsImpl.rename(tmp, file);
54
+ }
55
+
56
+ export async function removeTelegramOwnerMarker(
57
+ fsImpl: TelegramDaemonFs,
58
+ agentDir: string,
59
+ acquisitionId: string,
60
+ ): Promise<void> {
61
+ const file = telegramDaemonOwnerMarkerPath(agentDir, acquisitionId);
62
+ await fsImpl.unlink(file).catch(() => undefined);
63
+ // Best-effort directory cleanup; do not fail if markers remain.
64
+ try {
65
+ const entries = await fsImpl.readdir(path.dirname(file));
66
+ if (entries.length === 0) await fsImpl.unlink(path.dirname(file)).catch(() => undefined);
67
+ } catch {}
68
+ }
69
+
70
+ export async function readTelegramOwnerMarker(
71
+ fsImpl: TelegramDaemonFs,
72
+ agentDir: string,
73
+ acquisitionId: string,
74
+ ): Promise<TelegramOwnerMarker | undefined> {
75
+ const file = telegramDaemonOwnerMarkerPath(agentDir, acquisitionId);
76
+ try {
77
+ const raw = await fsImpl.readFile(file, "utf8");
78
+ const parsed = JSON.parse(raw) as unknown;
79
+ if (!validMarker(parsed)) return undefined;
80
+ // Digest must match the path's agent dir; otherwise fail closed (foreign).
81
+ if (parsed.agentDirDigest !== agentDirDigest(agentDir)) return undefined;
82
+ if (path.resolve(parsed.agentDir) !== path.resolve(agentDir)) return undefined;
83
+ return parsed;
84
+ } catch (error) {
85
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
86
+ return undefined;
87
+ }
88
+ }
89
+
90
+ export interface ListedOwnerMarker {
91
+ acquisitionId: string;
92
+ marker: TelegramOwnerMarker | undefined;
93
+ // undefined marker means malformed/unreadable/foreign-digest-mismatch (fail-closed).
94
+ raw?: string;
95
+ }
96
+
97
+ export async function listTelegramOwnerMarkers(
98
+ fsImpl: TelegramDaemonFs,
99
+ agentDir: string,
100
+ ): Promise<ListedOwnerMarker[]> {
101
+ const { daemonPaths } = await import("./daemon-paths");
102
+ const dir = daemonPaths(agentDir).ownerRegistry;
103
+ let entries: string[];
104
+ try {
105
+ entries = await fsImpl.readdir(dir);
106
+ } catch (error) {
107
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
108
+ throw error;
109
+ }
110
+ const out: ListedOwnerMarker[] = [];
111
+ for (const entry of entries) {
112
+ if (!entry.endsWith(".json")) continue;
113
+ const acquisitionId = entry.slice(0, -5);
114
+ const file = path.join(dir, entry);
115
+ try {
116
+ const raw = await fsImpl.readFile(file, "utf8");
117
+ let parsed: unknown;
118
+ try {
119
+ parsed = JSON.parse(raw);
120
+ } catch {
121
+ out.push({ acquisitionId, marker: undefined, raw });
122
+ continue;
123
+ }
124
+ if (!validMarker(parsed)) {
125
+ out.push({ acquisitionId, marker: undefined, raw });
126
+ continue;
127
+ }
128
+ if (parsed.agentDirDigest !== agentDirDigest(agentDir)) {
129
+ out.push({ acquisitionId, marker: undefined, raw });
130
+ continue;
131
+ }
132
+ if (path.resolve(parsed.agentDir) !== path.resolve(agentDir)) {
133
+ out.push({ acquisitionId, marker: undefined, raw });
134
+ continue;
135
+ }
136
+ out.push({ acquisitionId, marker: parsed });
137
+ } catch (error) {
138
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
139
+ out.push({ acquisitionId, marker: undefined });
140
+ }
141
+ }
142
+ return out;
143
+ }
144
+
145
+ export async function clearStaleTelegramOwnerMarkers(
146
+ fsImpl: TelegramDaemonFs,
147
+ agentDir: string,
148
+ pidAlive: (pid: number) => boolean,
149
+ pidIncarnation: (pid: number) => string | undefined,
150
+ now: number,
151
+ ttlMs = 86_400_000,
152
+ ): Promise<number> {
153
+ const markers = await listTelegramOwnerMarkers(fsImpl, agentDir);
154
+ let removed = 0;
155
+ for (const entry of markers) {
156
+ if (!entry.marker) {
157
+ // Malformed markers are not auto-removed here; the reaper counts them as refused.
158
+ // But very old malformed cruft beyond TTL may be removed if its mtime is ancient and pid proves absent.
159
+ // For now fail closed and leave it.
160
+ continue;
161
+ }
162
+ const m = entry.marker;
163
+ const alive = pidAlive(m.pid);
164
+ const currentIncarnation = pidIncarnation(m.pid);
165
+ const pidReused =
166
+ isProcessIncarnation(currentIncarnation) &&
167
+ isProcessIncarnation(m.incarnation) &&
168
+ currentIncarnation !== m.incarnation;
169
+ const isStaleTime = now - m.createdAt > ttlMs;
170
+ if ((!alive || pidReused) && isStaleTime) {
171
+ await removeTelegramOwnerMarker(fsImpl, agentDir, m.acquisitionId);
172
+ removed += 1;
173
+ }
174
+ }
175
+ return removed;
176
+ }