@gajae-code/coding-agent 0.13.3 → 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 (599) hide show
  1. package/CHANGELOG.md +184 -2
  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 +9 -1
  35. package/dist/types/config/models-config-schema.d.ts +2 -0
  36. package/dist/types/config/settings-schema.d.ts +70 -6
  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 +1 -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/gajae-pet-widget.d.ts +8 -30
  87. package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
  88. package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
  89. package/dist/types/modes/components/oauth-selector.d.ts +10 -4
  90. package/dist/types/modes/components/pet-capability.d.ts +11 -25
  91. package/dist/types/modes/components/status-line/gh.d.ts +6 -2
  92. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  93. package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
  94. package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
  95. package/dist/types/modes/interactive-mode.d.ts +1 -0
  96. package/dist/types/modes/types.d.ts +1 -0
  97. package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
  98. package/dist/types/runtime-mcp/config.d.ts +9 -0
  99. package/dist/types/runtime-mcp/manager.d.ts +26 -1
  100. package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
  101. package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
  102. package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
  103. package/dist/types/runtime-mcp/protocol.d.ts +278 -0
  104. package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
  105. package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
  106. package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
  107. package/dist/types/runtime-mcp/types.d.ts +102 -10
  108. package/dist/types/sdk/acp/adapter.d.ts +26 -5
  109. package/dist/types/sdk/broker/broker.d.ts +3 -55
  110. package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
  111. package/dist/types/sdk/broker/session-index.d.ts +14 -5
  112. package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
  113. package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
  114. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
  115. package/dist/types/sdk/bus/config.d.ts +11 -0
  116. package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
  117. package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
  118. package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
  119. package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
  120. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
  121. package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
  122. package/dist/types/sdk/bus/index.d.ts +85 -99
  123. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
  124. package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
  125. package/dist/types/sdk/bus/notification-service.d.ts +52 -0
  126. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
  127. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
  128. package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
  129. package/dist/types/sdk/bus/session-control.d.ts +25 -1
  130. package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
  131. package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
  132. package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
  133. package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
  134. package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
  135. package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
  136. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
  137. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
  138. package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
  139. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
  140. package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
  141. package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
  142. package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
  143. package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
  144. package/dist/types/sdk/cli/rows.d.ts +2 -11
  145. package/dist/types/sdk/cli/session-cli.d.ts +15 -25
  146. package/dist/types/sdk/client/client.d.ts +97 -1
  147. package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
  148. package/dist/types/sdk/host/control/index.d.ts +1 -1
  149. package/dist/types/sdk/host/control/operations.d.ts +18 -5
  150. package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
  151. package/dist/types/sdk/host/host.d.ts +15 -1
  152. package/dist/types/sdk/host/query/handlers.d.ts +5 -0
  153. package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
  154. package/dist/types/sdk/host/session-runtime.d.ts +127 -7
  155. package/dist/types/sdk/host/types.d.ts +1 -1
  156. package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
  157. package/dist/types/sdk/index.d.ts +2 -1
  158. package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
  159. package/dist/types/sdk/lifecycle/client.d.ts +59 -0
  160. package/dist/types/sdk/lifecycle/index.d.ts +3 -0
  161. package/dist/types/sdk/lifecycle/service.d.ts +232 -0
  162. package/dist/types/sdk/mcp/server.d.ts +9 -6
  163. package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
  164. package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
  165. package/dist/types/sdk/prompt-status.d.ts +10 -3
  166. package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
  167. package/dist/types/sdk/receipt-state.d.ts +17 -0
  168. package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
  169. package/dist/types/sdk/router/index.d.ts +1 -0
  170. package/dist/types/sdk/router/session-router.d.ts +152 -0
  171. package/dist/types/sdk/session-list.d.ts +22 -0
  172. package/dist/types/sdk/session-reconnect.d.ts +43 -1
  173. package/dist/types/sdk/session.d.ts +34 -1
  174. package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
  175. package/dist/types/session/account-inventory.d.ts +86 -0
  176. package/dist/types/session/agent-session.d.ts +145 -6
  177. package/dist/types/session/agent-storage.d.ts +10 -0
  178. package/dist/types/session/coordinator-tool-label.d.ts +28 -0
  179. package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
  180. package/dist/types/session/messages.d.ts +1 -1
  181. package/dist/types/session/secure-token-file.d.ts +3 -0
  182. package/dist/types/session/session-manager.d.ts +40 -1
  183. package/dist/types/session/startup-auth-config.d.ts +44 -0
  184. package/dist/types/session/terminal-abort.d.ts +378 -0
  185. package/dist/types/session/yield-queue.d.ts +7 -0
  186. package/dist/types/session-import/claude.d.ts +26 -0
  187. package/dist/types/session-import/codex.d.ts +88 -0
  188. package/dist/types/session-import/command.d.ts +35 -0
  189. package/dist/types/session-import/detect.d.ts +22 -0
  190. package/dist/types/session-import/index.d.ts +7 -0
  191. package/dist/types/session-import/provider-codex.d.ts +22 -0
  192. package/dist/types/session-import/provider-service.d.ts +38 -0
  193. package/dist/types/session-import/redact.d.ts +23 -0
  194. package/dist/types/session-import/service.d.ts +47 -0
  195. package/dist/types/session-import/types.d.ts +120 -0
  196. package/dist/types/setup/paseo/check.d.ts +16 -0
  197. package/dist/types/setup/paseo/install-saga.d.ts +115 -0
  198. package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
  199. package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
  200. package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
  201. package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
  202. package/dist/types/setup/paseo/provider-config.d.ts +35 -0
  203. package/dist/types/setup/paseo/remove.d.ts +14 -0
  204. package/dist/types/setup/paseo/result-types.d.ts +59 -0
  205. package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
  206. package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
  207. package/dist/types/skill-state/active-state.d.ts +5 -1
  208. package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
  209. package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
  210. package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
  211. package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
  212. package/dist/types/slash-commands/types.d.ts +7 -1
  213. package/dist/types/task/executor.d.ts +10 -0
  214. package/dist/types/task/index.d.ts +3 -1
  215. package/dist/types/tools/ask-answer-registry.d.ts +6 -3
  216. package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
  217. package/dist/types/tools/bash.d.ts +2 -6
  218. package/dist/types/tools/browser/attach.d.ts +11 -0
  219. package/dist/types/tools/browser/launch.d.ts +20 -0
  220. package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
  221. package/dist/types/tools/browser/render.d.ts +1 -0
  222. package/dist/types/tools/browser.d.ts +3 -1
  223. package/dist/types/tools/image-gen.d.ts +9 -2
  224. package/dist/types/tools/index.d.ts +8 -0
  225. package/dist/types/tools/monitor.d.ts +1 -1
  226. package/dist/types/tools/skill-discovery.d.ts +8 -1
  227. package/dist/types/tools/subagent.d.ts +2 -0
  228. package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
  229. package/dist/types/utils/herdr-pane.d.ts +76 -0
  230. package/package.json +19 -9
  231. package/scripts/build-binary.ts +2 -1
  232. package/scripts/build-sdk-package-smoke.ts +2 -13
  233. package/scripts/capture-sticky-viewport-showcase.ts +0 -1
  234. package/scripts/compile-args.ts +2 -0
  235. package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
  236. package/scripts/generate-sdk-operation-inventory.ts +28 -0
  237. package/scripts/generate-telegram-baseline-manifest.ts +1 -0
  238. package/scripts/generate-tool-catalog.ts +6 -4
  239. package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
  240. package/src/async/job-manager.ts +352 -17
  241. package/src/async/support.ts +25 -0
  242. package/src/autoresearch/dashboard.ts +28 -13
  243. package/src/capability/index.ts +3 -1
  244. package/src/capability/mcp.ts +6 -0
  245. package/src/capability/types.ts +6 -0
  246. package/src/cli/accounts-cli.ts +395 -0
  247. package/src/cli/args.ts +23 -0
  248. package/src/cli/auth-broker-cli.ts +136 -66
  249. package/src/cli/auth-gateway-cli.ts +94 -70
  250. package/src/cli/config-cli.ts +27 -2
  251. package/src/cli/crash-cli.ts +78 -0
  252. package/src/cli/customize-doctor.ts +1605 -0
  253. package/src/cli/daemon-cli.ts +12 -3
  254. package/src/cli/fast-help.ts +1 -0
  255. package/src/cli/mcp-cli.ts +128 -60
  256. package/src/cli/notify-cli.ts +8 -14
  257. package/src/cli/root-flags.ts +8 -0
  258. package/src/cli/setup-cli.ts +145 -0
  259. package/src/cli/skills-cli.ts +38 -2
  260. package/src/cli/update-cli.ts +167 -12
  261. package/src/cli.ts +4 -0
  262. package/src/commands/accounts.ts +71 -0
  263. package/src/commands/crash.ts +35 -0
  264. package/src/commands/customize.ts +84 -0
  265. package/src/commands/daemon.ts +2 -2
  266. package/src/commands/deep-interview.ts +2 -0
  267. package/src/commands/launch.ts +1 -0
  268. package/src/commands/mcp.ts +12 -7
  269. package/src/commands/ralplan.ts +2 -0
  270. package/src/commands/sdk.ts +65 -32
  271. package/src/commands/setup.ts +5 -0
  272. package/src/commands/skills.ts +14 -4
  273. package/src/commands/ultragoal.ts +10 -1
  274. package/src/commands/update.ts +26 -15
  275. package/src/config/atomic-yaml-patch-worker.ts +57 -0
  276. package/src/config/atomic-yaml-patch.ts +769 -29
  277. package/src/config/config-schema-version.ts +10 -0
  278. package/src/config/file-lock.ts +70 -1
  279. package/src/config/mcp-schema.json +52 -14
  280. package/src/config/model-profile-activation.ts +20 -1
  281. package/src/config/model-profiles.ts +29 -19
  282. package/src/config/model-registry.ts +132 -21
  283. package/src/config/models-config-schema.ts +1 -1
  284. package/src/config/settings-schema.ts +76 -6
  285. package/src/config/settings.ts +4038 -199
  286. package/src/config/skill-settings-defaults.ts +41 -3
  287. package/src/config/task-delegation.ts +87 -0
  288. package/src/config/update-channel.ts +23 -0
  289. package/src/coordinator-mcp/server.ts +377 -308
  290. package/src/crash/index-store.ts +723 -0
  291. package/src/crash/nudge.ts +89 -0
  292. package/src/crash/record-loader.ts +149 -0
  293. package/src/crash/report.ts +551 -0
  294. package/src/crash/sanitize.ts +129 -0
  295. package/src/cursor.ts +36 -10
  296. package/src/customization/import.ts +946 -0
  297. package/src/customization/index.ts +4 -0
  298. package/src/customization/inventory.ts +335 -0
  299. package/src/customization/mutations.ts +141 -0
  300. package/src/customization/types.ts +223 -0
  301. package/src/daemon/control-types.ts +8 -1
  302. package/src/daemon/operator-contract.ts +6 -6
  303. package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
  304. package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
  305. package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
  306. package/src/discovery/claude-hooks.ts +38 -0
  307. package/src/discovery/claude.ts +142 -209
  308. package/src/discovery/codex-hooks.ts +38 -0
  309. package/src/discovery/codex.ts +132 -198
  310. package/src/discovery/helpers.ts +8 -1
  311. package/src/discovery/index.ts +2 -0
  312. package/src/discovery/mcp-compat.ts +234 -0
  313. package/src/discovery/mcp-json.ts +6 -0
  314. package/src/edit/renderer.ts +40 -0
  315. package/src/edit/streaming.ts +4 -2
  316. package/src/exec/bash-executor.ts +24 -20
  317. package/src/export/html/index.ts +51 -2
  318. package/src/extensibility/extensions/loader.ts +9 -1
  319. package/src/extensibility/extensions/runner.ts +17 -4
  320. package/src/extensibility/extensions/types.ts +31 -6
  321. package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
  322. package/src/extensibility/gjc-plugins/installer.ts +9 -5
  323. package/src/extensibility/gjc-plugins/observability.ts +8 -2
  324. package/src/extensibility/gjc-plugins/registry.ts +13 -2
  325. package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
  326. package/src/extensibility/gjc-plugins/schema.ts +222 -39
  327. package/src/extensibility/hooks/loader.ts +154 -10
  328. package/src/extensibility/runtime-skill-discovery.ts +257 -60
  329. package/src/extensibility/skill-management.ts +368 -0
  330. package/src/extensibility/skills.ts +52 -16
  331. package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
  332. package/src/gjc-runtime/launch-worktree.ts +56 -2
  333. package/src/gjc-runtime/linux-proc.ts +7 -1
  334. package/src/gjc-runtime/ralplan-runtime.ts +100 -164
  335. package/src/gjc-runtime/session-state-lock.ts +992 -0
  336. package/src/gjc-runtime/session-state-sidecar.ts +644 -142
  337. package/src/gjc-runtime/state-writer.ts +39 -14
  338. package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
  339. package/src/gjc-runtime/ultragoal-guard.ts +11 -4
  340. package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
  341. package/src/gjc-runtime/workflow-settings.ts +596 -0
  342. package/src/goals/tools/goal-tool.ts +6 -1
  343. package/src/hooks/events.ts +291 -0
  344. package/src/hooks/native-skill-hook.ts +4 -0
  345. package/src/hooks/normalize.ts +369 -0
  346. package/src/hooks/skill-state.ts +165 -46
  347. package/src/internal-urls/docs-index.generated.ts +40 -32
  348. package/src/main.ts +32 -5
  349. package/src/migrate/mcp-mapper.ts +25 -2
  350. package/src/modes/acp/abort-scope.ts +29 -0
  351. package/src/modes/acp/acp-agent.ts +423 -98
  352. package/src/modes/components/assistant-message.ts +6 -1
  353. package/src/modes/components/customization/customization-dashboard.ts +443 -0
  354. package/src/modes/components/customization/import-wizard.ts +372 -0
  355. package/src/modes/components/customization/index.ts +1 -0
  356. package/src/modes/components/gajae-pet-widget.ts +539 -213
  357. package/src/modes/components/irc-sidebar.ts +11 -0
  358. package/src/modes/components/iterm-pet-transport.ts +585 -0
  359. package/src/modes/components/oauth-selector.ts +277 -86
  360. package/src/modes/components/pet-capability.ts +39 -40
  361. package/src/modes/components/settings-selector.ts +9 -0
  362. package/src/modes/components/status-line/gh.ts +45 -1
  363. package/src/modes/components/status-line/segments.ts +16 -1
  364. package/src/modes/components/tool-status-header.ts +13 -3
  365. package/src/modes/controllers/command-controller.ts +20 -8
  366. package/src/modes/controllers/extension-ui-controller.ts +56 -257
  367. package/src/modes/controllers/input-controller.ts +10 -0
  368. package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
  369. package/src/modes/controllers/selector-controller.ts +177 -72
  370. package/src/modes/controllers/stt-controller.ts +3 -3
  371. package/src/modes/interactive-mode.ts +88 -16
  372. package/src/modes/print-mode.ts +35 -6
  373. package/src/modes/runtime-init.ts +21 -68
  374. package/src/modes/types.ts +1 -0
  375. package/src/modes/utils/injected-user-submission.ts +25 -0
  376. package/src/modes/utils/terminal-bell.ts +15 -1
  377. package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
  378. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  379. package/src/prompts/agents/critic.md +1 -1
  380. package/src/prompts/system/rlm-research.md +1 -1
  381. package/src/prompts/system/system-prompt.md +0 -2
  382. package/src/prompts/tools/bash.md +7 -1
  383. package/src/prompts/tools/browser.md +1 -1
  384. package/src/prompts/tools/cron.md +6 -6
  385. package/src/prompts/tools/read.md +0 -2
  386. package/src/prompts/tools/skill-discovery.md +4 -2
  387. package/src/runtime-mcp/client.ts +427 -23
  388. package/src/runtime-mcp/config.ts +37 -5
  389. package/src/runtime-mcp/manager-pool.test.ts +56 -0
  390. package/src/runtime-mcp/manager.ts +52 -2
  391. package/src/runtime-mcp/oauth-discovery.ts +13 -0
  392. package/src/runtime-mcp/oauth-flow.ts +46 -1
  393. package/src/runtime-mcp/pool-key.ts +5 -1
  394. package/src/runtime-mcp/pool.test.ts +9 -0
  395. package/src/runtime-mcp/protocol.test.ts +353 -0
  396. package/src/runtime-mcp/protocol.ts +624 -0
  397. package/src/runtime-mcp/scope-config.ts +78 -0
  398. package/src/runtime-mcp/tool-bridge.ts +44 -10
  399. package/src/runtime-mcp/transports/http.ts +84 -13
  400. package/src/runtime-mcp/types.ts +123 -12
  401. package/src/sdk/acp/adapter.ts +328 -97
  402. package/src/sdk/broker/broker.ts +160 -741
  403. package/src/sdk/broker/ensure.ts +9 -1
  404. package/src/sdk/broker/lifecycle.ts +510 -116
  405. package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
  406. package/src/sdk/broker/process-incarnation.ts +19 -0
  407. package/src/sdk/broker/session-index.ts +516 -117
  408. package/src/sdk/broker/startup-failure.ts +28 -5
  409. package/src/sdk/broker/transport.ts +1 -13
  410. package/src/sdk/bus/chat-daemon-cli.ts +4 -2
  411. package/src/sdk/bus/chat-daemon-control.ts +67 -6
  412. package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
  413. package/src/sdk/bus/config.ts +13 -0
  414. package/src/sdk/bus/conversation-store.ts +59 -171
  415. package/src/sdk/bus/daemon-paths.ts +20 -0
  416. package/src/sdk/bus/discord-conversation.ts +2 -0
  417. package/src/sdk/bus/discord-daemon.ts +524 -126
  418. package/src/sdk/bus/discord-live-provider.ts +206 -79
  419. package/src/sdk/bus/existing-thread-readiness.ts +4 -97
  420. package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
  421. package/src/sdk/bus/index.ts +2480 -456
  422. package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
  423. package/src/sdk/bus/lifecycle-commands.ts +2 -2
  424. package/src/sdk/bus/managed-daemon.ts +2 -145
  425. package/src/sdk/bus/notification-service.ts +196 -0
  426. package/src/sdk/bus/operator-runtime.ts +1 -1
  427. package/src/sdk/bus/prompt-reconciliation.ts +24 -15
  428. package/src/sdk/bus/reconciliation-store.ts +553 -24
  429. package/src/sdk/bus/session-control.ts +49 -3
  430. package/src/sdk/bus/slack-conversation.ts +4 -0
  431. package/src/sdk/bus/slack-daemon.ts +733 -134
  432. package/src/sdk/bus/slack-live-provider.ts +65 -15
  433. package/src/sdk/bus/slack-provider.ts +4 -0
  434. package/src/sdk/bus/slack-thread-binding.ts +9 -104
  435. package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
  436. package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
  437. package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
  438. package/src/sdk/bus/telegram-daemon-control.ts +3 -13
  439. package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
  440. package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
  441. package/src/sdk/bus/telegram-daemon.ts +2552 -2280
  442. package/src/sdk/bus/telegram-reference.ts +1 -226
  443. package/src/sdk/bus/telegram-setup.ts +2 -2
  444. package/src/sdk/bus/threaded-render.ts +1 -0
  445. package/src/sdk/bus/topic-registry.ts +279 -146
  446. package/src/sdk/cli/rows.ts +2 -35
  447. package/src/sdk/cli/session-cli.ts +776 -700
  448. package/src/sdk/client/client.ts +892 -1
  449. package/src/sdk/host/control/dispatch.ts +93 -58
  450. package/src/sdk/host/control/index.ts +1 -1
  451. package/src/sdk/host/control/operations.ts +18 -10
  452. package/src/sdk/host/control/runtime-gate.ts +40 -0
  453. package/src/sdk/host/host.ts +63 -12
  454. package/src/sdk/host/query/handlers.ts +66 -7
  455. package/src/sdk/host/reverse-leases.ts +2 -2
  456. package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
  457. package/src/sdk/host/session-runtime.test.ts +2787 -34
  458. package/src/sdk/host/session-runtime.ts +1489 -101
  459. package/src/sdk/host/surface-policy.ts +12 -8
  460. package/src/sdk/host/types.ts +1 -1
  461. package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
  462. package/src/sdk/host/websocket-transport.ts +85 -50
  463. package/src/sdk/index.ts +2 -1
  464. package/src/sdk/lifecycle/broker-client.ts +53 -0
  465. package/src/sdk/lifecycle/client.ts +234 -0
  466. package/src/sdk/lifecycle/index.ts +3 -0
  467. package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
  468. package/src/sdk/lifecycle/service.ts +720 -0
  469. package/src/sdk/mcp/server.ts +226 -188
  470. package/src/sdk/prompt-deadline-lease.ts +55 -0
  471. package/src/sdk/prompt-deadline-manager.ts +148 -0
  472. package/src/sdk/prompt-status.ts +11 -3
  473. package/src/sdk/protocol/operation-inventory.generated.json +272 -129
  474. package/src/sdk/protocol/operation-registry.ts +71 -8
  475. package/src/sdk/receipt-state.ts +43 -0
  476. package/src/sdk/reconciliation-extensions.ts +14 -0
  477. package/src/sdk/router/index.ts +1 -0
  478. package/src/sdk/router/session-router.ts +1658 -0
  479. package/src/sdk/session-list.ts +93 -0
  480. package/src/sdk/session-reconnect.ts +45 -1
  481. package/src/sdk/session.ts +716 -120
  482. package/src/sdk/transport/serve-cli.ts +18 -34
  483. package/src/session/account-inventory.ts +603 -0
  484. package/src/session/agent-session.ts +2857 -582
  485. package/src/session/agent-storage.ts +35 -0
  486. package/src/session/blob-store.ts +32 -1
  487. package/src/session/coordinator-tool-label.ts +40 -0
  488. package/src/session/internal/managed-session-scope.ts +4 -0
  489. package/src/session/internal/managed-session-storage.ts +173 -12
  490. package/src/session/messages.ts +1 -1
  491. package/src/session/secure-token-file.ts +80 -0
  492. package/src/session/session-manager.ts +730 -58
  493. package/src/session/session-storage.ts +25 -16
  494. package/src/session/startup-auth-config.ts +297 -0
  495. package/src/session/terminal-abort.ts +1253 -0
  496. package/src/session/yield-queue.ts +57 -15
  497. package/src/session-import/claude.ts +334 -0
  498. package/src/session-import/codex.ts +1033 -0
  499. package/src/session-import/command.ts +77 -0
  500. package/src/session-import/detect.ts +133 -0
  501. package/src/session-import/index.ts +7 -0
  502. package/src/session-import/provider-codex.ts +327 -0
  503. package/src/session-import/provider-service.ts +457 -0
  504. package/src/session-import/redact.ts +128 -0
  505. package/src/session-import/service.ts +1264 -0
  506. package/src/session-import/types.ts +148 -0
  507. package/src/setup/paseo/check.ts +213 -0
  508. package/src/setup/paseo/install-saga.ts +293 -0
  509. package/src/setup/paseo/json-publisher.ts +263 -0
  510. package/src/setup/paseo/orchestration-preferences.ts +86 -0
  511. package/src/setup/paseo/paseo-ownership.ts +224 -0
  512. package/src/setup/paseo/paseo-setup.ts +253 -0
  513. package/src/setup/paseo/provider-config.ts +139 -0
  514. package/src/setup/paseo/remove.ts +183 -0
  515. package/src/setup/paseo/result-types.ts +79 -0
  516. package/src/setup/paseo/setup-deps.ts +175 -0
  517. package/src/setup/paseo/skills-bridge.ts +260 -0
  518. package/src/skill-state/active-state.ts +21 -11
  519. package/src/slash-commands/acp-builtins.ts +9 -1
  520. package/src/slash-commands/builtin-registry.ts +98 -16
  521. package/src/slash-commands/helpers/credential-switch.ts +143 -0
  522. package/src/slash-commands/helpers/mcp.ts +9 -3
  523. package/src/slash-commands/helpers/usage-report.ts +61 -56
  524. package/src/slash-commands/types.ts +7 -2
  525. package/src/task/executor.ts +30 -11
  526. package/src/task/index.ts +66 -7
  527. package/src/task/render.ts +23 -9
  528. package/src/tools/ask-answer-registry.ts +23 -7
  529. package/src/tools/ask.ts +8 -4
  530. package/src/tools/bash-sleep-advisory.ts +115 -0
  531. package/src/tools/bash.ts +98 -18
  532. package/src/tools/browser/attach.ts +71 -3
  533. package/src/tools/browser/launch.ts +86 -13
  534. package/src/tools/browser/profile-discovery.ts +57 -7
  535. package/src/tools/browser/render.ts +8 -1
  536. package/src/tools/browser.ts +130 -21
  537. package/src/tools/computer.enforcement.test.ts +1 -1
  538. package/src/tools/computer.ts +3 -2
  539. package/src/tools/cron.ts +12 -1
  540. package/src/tools/image-gen.ts +27 -16
  541. package/src/tools/index.ts +8 -0
  542. package/src/tools/job.ts +33 -6
  543. package/src/tools/monitor.ts +41 -2
  544. package/src/tools/skill-discovery.ts +21 -29
  545. package/src/tools/subagent.ts +142 -24
  546. package/src/tools/tool-catalog.generated.ts +6 -6
  547. package/src/tools/ultragoal-ask-guard.ts +10 -3
  548. package/src/utils/herdr-pane.ts +345 -0
  549. package/src/utils/title-generator.ts +2 -0
  550. package/src/web/scrapers/index.ts +6 -4
  551. package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
  552. package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
  553. package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
  554. package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
  555. package/dist/types/runtime/lazy-service.test.d.ts +0 -1
  556. package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
  557. package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
  558. package/dist/types/runtime-mcp/config.test.d.ts +0 -1
  559. package/dist/types/runtime-mcp/http.test.d.ts +0 -1
  560. package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
  561. package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
  562. package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
  563. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
  564. package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
  565. package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
  566. package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
  567. package/dist/types/sdk/elevation/audit.d.ts +0 -93
  568. package/dist/types/sdk/elevation/capability.d.ts +0 -3
  569. package/dist/types/sdk/elevation/digest.d.ts +0 -72
  570. package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
  571. package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
  572. package/dist/types/sdk/elevation/owner.d.ts +0 -48
  573. package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
  574. package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
  575. package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
  576. package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
  577. package/dist/types/session/auth-broker-config.d.ts +0 -13
  578. package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
  579. package/dist/types/skills/skills.test.d.ts +0 -1
  580. package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
  581. package/dist/types/task/token-log.test.d.ts +0 -1
  582. package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
  583. package/dist/types/tools/descriptors.test.d.ts +0 -1
  584. package/scripts/g005-daemon-smoke.ts +0 -180
  585. package/scripts/g011-daemon-path-smoke.ts +0 -153
  586. package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
  587. package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
  588. package/src/sdk/bus/telegram-cli.ts +0 -212
  589. package/src/sdk/elevation/allowlist.ts +0 -54
  590. package/src/sdk/elevation/audit.ts +0 -208
  591. package/src/sdk/elevation/capability.ts +0 -41
  592. package/src/sdk/elevation/digest.ts +0 -129
  593. package/src/sdk/elevation/dispatch-receipt.ts +0 -184
  594. package/src/sdk/elevation/grant-ledger.ts +0 -1142
  595. package/src/sdk/elevation/owner.ts +0 -109
  596. package/src/session/auth-broker-config.ts +0 -109
  597. /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
  598. /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
  599. /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
+ }