@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,263 @@
1
+ /**
2
+ * Byte-safe JSON publication for files another application owns.
3
+ *
4
+ * Paseo owns `~/.paseo/config.json` and offers no lock API, so every write here
5
+ * is conservative by construction:
6
+ *
7
+ * - A round-trip fidelity self-check re-serializes the UNMODIFIED parse and
8
+ * refuses to write unless it is byte-identical to the original. Paseo writes
9
+ * 2-space JSON with a trailing newline; anything else means our formatting
10
+ * assumption no longer holds and guessing would silently rewrite the file.
11
+ * - A compare-and-swap re-reads the file immediately before publishing, so a
12
+ * concurrent write between our read and our rename is detected, not clobbered.
13
+ * - Publication is temp-write plus rename, never a direct write to the target.
14
+ * - Backups land beside the original at mode 0600, because `config.json` holds
15
+ * `daemon.auth.password`.
16
+ *
17
+ * This module carries NO ownership, seeding, or removal policy. Those live in
18
+ * the per-target adapters so this file stays small enough to audit.
19
+ */
20
+ import * as nodeCrypto from "node:crypto";
21
+ import * as fs from "node:fs/promises";
22
+ import * as path from "node:path";
23
+
24
+ /** Serialization Paseo itself produces. Verified byte-identical against the live config. */
25
+ export function serializeJson(value: unknown): string {
26
+ return `${JSON.stringify(value, null, 2)}\n`;
27
+ }
28
+
29
+ export function hashBytes(bytes: string): string {
30
+ return nodeCrypto.createHash("sha256").update(bytes).digest("hex");
31
+ }
32
+
33
+ /** Marker recorded when a target did not exist at preflight. */
34
+ export const ABSENT_IDENTITY = "absent";
35
+
36
+ export type PublishRefusal =
37
+ | { readonly reason: "parse-refusal"; readonly detail: string }
38
+ | { readonly reason: "format-drift"; readonly detail: string }
39
+ | { readonly reason: "cas-conflict"; readonly expected: string; readonly actual: string };
40
+
41
+ export class PaseoPublishError extends Error {
42
+ readonly refusal: PublishRefusal;
43
+ readonly targetPath: string;
44
+
45
+ constructor(targetPath: string, refusal: PublishRefusal) {
46
+ super(describeRefusal(targetPath, refusal));
47
+ this.name = "PaseoPublishError";
48
+ this.refusal = refusal;
49
+ this.targetPath = targetPath;
50
+ }
51
+ }
52
+
53
+ function describeRefusal(targetPath: string, refusal: PublishRefusal): string {
54
+ switch (refusal.reason) {
55
+ case "parse-refusal":
56
+ return `Refusing to write ${targetPath}: it is not parseable JSON (${refusal.detail}). Fix or remove the file, then re-run.`;
57
+ case "format-drift":
58
+ return `Refusing to write ${targetPath}: ${refusal.detail}. GJC only edits files it can rewrite byte-for-byte, so it will not reformat a file it did not author.`;
59
+ case "cas-conflict":
60
+ return `Refusing to write ${targetPath}: the file changed while GJC was preparing its update. Re-run to pick up the current contents.`;
61
+ }
62
+ }
63
+
64
+ export interface ReadTargetResult {
65
+ readonly exists: boolean;
66
+ /** Raw bytes as read, or `""` when absent. */
67
+ readonly raw: string;
68
+ /** Hash of `raw`, or `ABSENT_IDENTITY` when absent. */
69
+ readonly identity: string;
70
+ /** Parsed object; `{}` when absent. */
71
+ readonly parsed: Record<string, unknown>;
72
+ }
73
+
74
+ /**
75
+ * Read and validate a target without writing anything.
76
+ *
77
+ * Throws `PaseoPublishError` on unparseable JSON or on a formatting mismatch,
78
+ * so callers never have to decide whether a file is safe to touch.
79
+ */
80
+ export async function readTarget(targetPath: string): Promise<ReadTargetResult> {
81
+ let raw: string;
82
+ try {
83
+ raw = await Bun.file(targetPath).text();
84
+ } catch (error) {
85
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
86
+ return { exists: false, raw: "", identity: ABSENT_IDENTITY, parsed: {} };
87
+ }
88
+ throw error;
89
+ }
90
+
91
+ let parsed: unknown;
92
+ try {
93
+ parsed = JSON.parse(raw);
94
+ } catch (error) {
95
+ throw new PaseoPublishError(targetPath, {
96
+ reason: "parse-refusal",
97
+ detail: error instanceof Error ? error.message : String(error),
98
+ });
99
+ }
100
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
101
+ throw new PaseoPublishError(targetPath, { reason: "parse-refusal", detail: "root is not a JSON object" });
102
+ }
103
+
104
+ // Round-trip fidelity self-check: re-serialize the UNMODIFIED parse. If that
105
+ // is not byte-identical, our formatting assumption is wrong and any write
106
+ // would silently reformat regions we do not own.
107
+ const roundTrip = serializeJson(parsed);
108
+ if (roundTrip !== raw) {
109
+ throw new PaseoPublishError(targetPath, {
110
+ reason: "format-drift",
111
+ detail:
112
+ "re-serializing the file's own contents did not reproduce it byte-for-byte (expected 2-space indentation with a trailing newline)",
113
+ });
114
+ }
115
+
116
+ return { exists: true, raw, identity: hashBytes(raw), parsed: parsed as Record<string, unknown> };
117
+ }
118
+
119
+ export interface PublishPlan {
120
+ /** Bytes that will be published. */
121
+ readonly nextRaw: string;
122
+ /** Hash of `nextRaw` -- the expected post-publish identity, computable before any rename. */
123
+ readonly expectedIdentity: string;
124
+ /** True when the mutation produced no change and publication can be skipped. */
125
+ readonly unchanged: boolean;
126
+ }
127
+
128
+ /**
129
+ * Apply `mutate` to a validated read and compute the exact bytes to publish.
130
+ *
131
+ * Split out from {@link publishPlan} so the install saga can record the expected
132
+ * post-publish identity in its durable intent BEFORE anything is written.
133
+ */
134
+ export function planPublish(current: ReadTargetResult, mutate: (draft: Record<string, unknown>) => void): PublishPlan {
135
+ const draft = structuredClone(current.parsed);
136
+ mutate(draft);
137
+ const nextRaw = serializeJson(draft);
138
+ return { nextRaw, expectedIdentity: hashBytes(nextRaw), unchanged: nextRaw === current.raw };
139
+ }
140
+
141
+ export interface PublishOptions {
142
+ /** Identity the target must still carry at publication time. */
143
+ readonly expectedIdentity: string;
144
+ /** Take a mode-0600 backup beside the original before replacing it. */
145
+ readonly backup: boolean;
146
+ readonly now: Date;
147
+ }
148
+
149
+ export interface PublishResult {
150
+ readonly published: boolean;
151
+ readonly backupPath?: string;
152
+ readonly identity: string;
153
+ }
154
+
155
+ function backupSuffix(now: Date): string {
156
+ return now.toISOString().replace(/[:.]/g, "-");
157
+ }
158
+
159
+ /**
160
+ * Publish `plan.nextRaw` to `targetPath` under a compare-and-swap on
161
+ * `options.expectedIdentity`.
162
+ *
163
+ * The CAS is re-read immediately before the rename, which is the narrowest
164
+ * window GJC can achieve. It does not defend against Paseo re-writing the file
165
+ * later from its own stale in-memory copy -- Paseo exposes no lock or version
166
+ * API, so that remains a documented residual risk detected by `--check`.
167
+ */
168
+ export async function publishPlan(
169
+ targetPath: string,
170
+ plan: PublishPlan,
171
+ options: PublishOptions,
172
+ ): Promise<PublishResult> {
173
+ if (plan.unchanged) return { published: false, identity: options.expectedIdentity };
174
+
175
+ const directory = path.dirname(targetPath);
176
+ await fs.mkdir(directory, { recursive: true, mode: 0o700 });
177
+
178
+ // Compare-and-swap: re-read right before publishing so an external write
179
+ // between our original read and this rename is refused, not overwritten.
180
+ const observed = await currentIdentity(targetPath);
181
+ if (observed !== options.expectedIdentity) {
182
+ throw new PaseoPublishError(targetPath, {
183
+ reason: "cas-conflict",
184
+ expected: options.expectedIdentity,
185
+ actual: observed,
186
+ });
187
+ }
188
+
189
+ let backupPath: string | undefined;
190
+ if (options.backup && observed !== ABSENT_IDENTITY) {
191
+ backupPath = `${targetPath}.gjc-bak-${backupSuffix(options.now)}`;
192
+ await copyPrivately(targetPath, backupPath);
193
+ }
194
+
195
+ // Never write the final path directly: a crash mid-write would leave the
196
+ // user's config truncated. Stage beside the target, fsync, then rename.
197
+ const tempPath = path.join(directory, `.${path.basename(targetPath)}.${process.pid}.${nodeCrypto.randomUUID()}.tmp`);
198
+ const mode = await sourceMode(targetPath);
199
+ try {
200
+ const handle = await fs.open(tempPath, "wx", mode);
201
+ try {
202
+ await handle.writeFile(plan.nextRaw, "utf8");
203
+ await handle.sync();
204
+ } finally {
205
+ await handle.close();
206
+ }
207
+ await fs.rename(tempPath, targetPath);
208
+ } finally {
209
+ await fs.rm(tempPath, { force: true }).catch(() => undefined);
210
+ }
211
+
212
+ return { published: true, backupPath, identity: plan.expectedIdentity };
213
+ }
214
+
215
+ /** Current on-disk identity, or {@link ABSENT_IDENTITY} when the file does not exist. */
216
+ export async function currentIdentity(targetPath: string): Promise<string> {
217
+ try {
218
+ return hashBytes(await Bun.file(targetPath).text());
219
+ } catch (error) {
220
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return ABSENT_IDENTITY;
221
+ throw error;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Preserve the target's own permissions when republishing it.
227
+ *
228
+ * Narrowed to at most 0600 for group and other, never widened: a file that was
229
+ * already private must stay private, and one that was world-readable must not
230
+ * become more permissive because we rewrote it.
231
+ */
232
+ async function sourceMode(targetPath: string): Promise<number> {
233
+ try {
234
+ const stat = await fs.stat(targetPath);
235
+ return stat.mode & 0o777;
236
+ } catch {
237
+ return 0o600;
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Backups are ALWAYS 0600, regardless of the source mode.
243
+ *
244
+ * A backup of `~/.paseo/config.json` contains `daemon.auth.password`, and a
245
+ * backup generally duplicates content into a new path the user did not choose,
246
+ * so it must never inherit a permissive source mode.
247
+ */
248
+ const BACKUP_MODE = 0o600;
249
+
250
+ async function copyPrivately(from: string, to: string): Promise<void> {
251
+ const bytes = await Bun.file(from).text();
252
+ const mode = BACKUP_MODE;
253
+ const handle = await fs.open(to, "w", mode);
254
+ try {
255
+ await handle.writeFile(bytes, "utf8");
256
+ await handle.sync();
257
+ } finally {
258
+ await handle.close();
259
+ }
260
+ // `fs.open` honors the mode only on creation, so an existing backup path
261
+ // keeps its old permissions unless we set them explicitly.
262
+ await fs.chmod(to, mode);
263
+ }
@@ -0,0 +1,86 @@
1
+ import { ORCHESTRATION_ROLE_KEYS, PROVIDER_KEY } from "./setup-deps";
2
+
3
+ /** Every newly seeded orchestration role points at the base provider this setup registers. */
4
+ export const SEEDED_ROLE_VALUE = PROVIDER_KEY;
5
+
6
+ /** Paseo nests its role assignments under `providers`; the file's other top-level keys are not ours. */
7
+ const PROVIDERS_KEY = "providers";
8
+
9
+ export interface OrchestrationSeed {
10
+ readonly mutate: (draft: Record<string, unknown>) => void;
11
+ readonly seededKeys: readonly string[];
12
+ readonly seededValues: Record<string, string>;
13
+ }
14
+
15
+ function readRoles(preferences: Record<string, unknown>): Record<string, unknown> | undefined {
16
+ const providers = preferences[PROVIDERS_KEY];
17
+ if (!providers || typeof providers !== "object" || Array.isArray(providers)) return undefined;
18
+ return providers as Record<string, unknown>;
19
+ }
20
+
21
+ /**
22
+ * A role is empty only when it is absent, `""`, or `[]`.
23
+ *
24
+ * Every other value, including `null`, belongs to the user and is left alone --
25
+ * seeding must never overwrite an assignment somebody already made.
26
+ */
27
+ function isEmpty(roles: Record<string, unknown> | undefined, key: string): boolean {
28
+ if (!roles || !(key in roles)) return true;
29
+ const value = roles[key];
30
+ return value === "" || (Array.isArray(value) && value.length === 0);
31
+ }
32
+
33
+ /**
34
+ * Plan the seeding of `providers.{impl,ui,research,planning,audit}`.
35
+ *
36
+ * Only empty roles are seeded. `preferences[]` and any other top-level key are
37
+ * never read or assigned, so an existing array keeps its identity and content.
38
+ */
39
+ /** Read a role's current value, or `undefined` when unset. Shared by seeding and removal. */
40
+ export function readRoleValue(preferences: Record<string, unknown>, key: string): string | undefined {
41
+ const roles = readRoles(preferences);
42
+ const value = roles?.[key];
43
+ return typeof value === "string" ? value : undefined;
44
+ }
45
+
46
+ /**
47
+ * Delete seeded roles from the nested `providers` map.
48
+ *
49
+ * `shouldDelete` gates each key on provenance, so a role the user reassigned
50
+ * after install is left alone. The `providers` object itself is removed only
51
+ * when it becomes empty, so a file GJC fully seeded returns to its prior shape.
52
+ */
53
+ export function removeSeededRoles(
54
+ draft: Record<string, unknown>,
55
+ keys: readonly string[],
56
+ shouldDelete: (key: string, currentValue: string | undefined) => boolean,
57
+ ): void {
58
+ const roles = readRoles(draft);
59
+ if (!roles) return;
60
+ for (const key of keys) {
61
+ const value = roles[key];
62
+ if (shouldDelete(key, typeof value === "string" ? value : undefined)) delete roles[key];
63
+ }
64
+ if (Object.keys(roles).length === 0) delete draft[PROVIDERS_KEY];
65
+ }
66
+
67
+ export function createOrchestrationSeed(preferences: Record<string, unknown>): OrchestrationSeed {
68
+ const roles = readRoles(preferences);
69
+ const seededKeys = ORCHESTRATION_ROLE_KEYS.filter(key => isEmpty(roles, key));
70
+ const seededValues = Object.fromEntries(seededKeys.map(key => [key, SEEDED_ROLE_VALUE]));
71
+
72
+ return {
73
+ mutate: draft => {
74
+ if (seededKeys.length === 0) return;
75
+ const existing = draft[PROVIDERS_KEY];
76
+ const target =
77
+ existing && typeof existing === "object" && !Array.isArray(existing)
78
+ ? (existing as Record<string, unknown>)
79
+ : {};
80
+ for (const key of seededKeys) target[key] = SEEDED_ROLE_VALUE;
81
+ draft[PROVIDERS_KEY] = target;
82
+ },
83
+ seededKeys,
84
+ seededValues,
85
+ };
86
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * GJC-side ownership provenance and crash-recovery intent.
3
+ *
4
+ * Both records live under the GJC agent directory, never inside the user's
5
+ * Paseo files. Two separate concerns share this module because both answer the
6
+ * same question -- "did GJC actually do this?" -- and both are consumed by the
7
+ * install saga and by `--remove`.
8
+ *
9
+ * Provenance exists because "the current value equals what we would write" is
10
+ * NOT evidence that we wrote it: a user who hand-authored the same value would
11
+ * otherwise have it silently deleted by `--remove`.
12
+ *
13
+ * The intent record exists because a target file and the provenance ledger are
14
+ * separate files and cannot be renamed atomically together. It is written
15
+ * BEFORE either mutation and carries enough identity to classify the target on
16
+ * its own after a crash, without needing any later update.
17
+ */
18
+ import * as fs from "node:fs/promises";
19
+ import * as path from "node:path";
20
+ import { ABSENT_IDENTITY, currentIdentity, serializeJson } from "./json-publisher";
21
+
22
+ export const PROVENANCE_VERSION = 1;
23
+ export const INTENT_VERSION = 1;
24
+
25
+ export interface ProvenanceLedger {
26
+ readonly version: number;
27
+ /** `agents.providers` keys GJC created, mapped to the value hash it wrote. */
28
+ readonly providerKeys: Record<string, string>;
29
+ /** Orchestration role keys GJC actually seeded, mapped to the value it wrote. */
30
+ readonly seededOrchestrationKeys: Record<string, string>;
31
+ /** Bridge directory path GJC created, when it created it. */
32
+ readonly bridgePath?: string;
33
+ /** Bridge entries GJC created, so the inverse removes exactly those. */
34
+ readonly bridgeEntries?: readonly string[];
35
+ /** True when GJC created the bridge directory itself (as opposed to populating an existing one). */
36
+ readonly bridgeDirCreated?: boolean;
37
+ }
38
+
39
+ export const EMPTY_LEDGER: ProvenanceLedger = {
40
+ version: PROVENANCE_VERSION,
41
+ providerKeys: {},
42
+ seededOrchestrationKeys: {},
43
+ };
44
+
45
+ export async function readProvenance(provenancePath: string): Promise<ProvenanceLedger> {
46
+ let raw: string;
47
+ try {
48
+ raw = await Bun.file(provenancePath).text();
49
+ } catch (error) {
50
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return EMPTY_LEDGER;
51
+ throw error;
52
+ }
53
+ try {
54
+ const parsed = JSON.parse(raw) as Partial<ProvenanceLedger>;
55
+ return {
56
+ version: typeof parsed.version === "number" ? parsed.version : PROVENANCE_VERSION,
57
+ providerKeys: isStringRecord(parsed.providerKeys) ? parsed.providerKeys : {},
58
+ seededOrchestrationKeys: isStringRecord(parsed.seededOrchestrationKeys) ? parsed.seededOrchestrationKeys : {},
59
+ ...(typeof parsed.bridgePath === "string" ? { bridgePath: parsed.bridgePath } : {}),
60
+ ...(Array.isArray(parsed.bridgeEntries)
61
+ ? { bridgeEntries: parsed.bridgeEntries.filter((entry): entry is string => typeof entry === "string") }
62
+ : {}),
63
+ ...(typeof parsed.bridgeDirCreated === "boolean" ? { bridgeDirCreated: parsed.bridgeDirCreated } : {}),
64
+ };
65
+ } catch {
66
+ // A corrupt GJC-side ledger must not brick removal: treat it as empty so
67
+ // nothing is deleted on unproven ownership, which is the safe direction.
68
+ return EMPTY_LEDGER;
69
+ }
70
+ }
71
+
72
+ function isStringRecord(value: unknown): value is Record<string, string> {
73
+ return !!value && typeof value === "object" && !Array.isArray(value);
74
+ }
75
+
76
+ export async function writeProvenance(provenancePath: string, ledger: ProvenanceLedger): Promise<void> {
77
+ await fs.mkdir(path.dirname(provenancePath), { recursive: true, mode: 0o700 });
78
+ await Bun.write(provenancePath, serializeJson(ledger));
79
+ await fs.chmod(provenancePath, 0o600);
80
+ }
81
+
82
+ /**
83
+ * True only when GJC recorded this provider key AND the value still hashes to
84
+ * what GJC wrote. A user edit after install makes this false, which is what
85
+ * keeps `--remove` from destroying their change.
86
+ */
87
+ export function isProvenancedProvider(ledger: ProvenanceLedger, key: string, currentValueHash: string): boolean {
88
+ const recorded = ledger.providerKeys[key];
89
+ return recorded !== undefined && recorded === currentValueHash;
90
+ }
91
+
92
+ export function isProvenancedOrchestrationKey(ledger: ProvenanceLedger, key: string, currentValue: string): boolean {
93
+ const recorded = ledger.seededOrchestrationKeys[key];
94
+ return recorded !== undefined && recorded === currentValue;
95
+ }
96
+
97
+ /** Every `gjc`/`gjc-<preset>` key GJC recorded, so a plain `--remove` cleans up earlier `--mpreset` runs. */
98
+ export function provenancedProviderKeys(ledger: ProvenanceLedger): readonly string[] {
99
+ return Object.keys(ledger.providerKeys).sort();
100
+ }
101
+
102
+ export type IntentStep = "provider-config" | "orchestration-preferences";
103
+
104
+ /**
105
+ * Durable, credential-free intent record.
106
+ *
107
+ * Written before either the target publish or the ledger commit, and carrying
108
+ * BOTH identities for BOTH files, so recovery can classify each file as
109
+ * before / intended-after / divergent from this record alone. It stores only
110
+ * paths, key names, and hashes -- never file contents, diffs, or values -- so
111
+ * it stays credential-free even though `config.json` holds a bcrypt password.
112
+ */
113
+ export interface IntentRecord {
114
+ readonly version: number;
115
+ readonly step: IntentStep;
116
+ readonly targetPath: string;
117
+ readonly ownedKeys: readonly string[];
118
+ readonly targetPreflightIdentity: string;
119
+ readonly targetExpectedIdentity: string;
120
+ readonly provenancePath: string;
121
+ readonly provenancePreflightIdentity: string;
122
+ readonly provenanceExpectedIdentity: string;
123
+ /**
124
+ * The exact ledger this step intended to commit.
125
+ *
126
+ * Carried so recovery can finish the commit without re-running the step. That
127
+ * matters for seed-if-empty work: once the target publish has landed the roles
128
+ * are no longer empty, so a retry would skip the step and the ledger would
129
+ * never be written. Contains only key names and hashes, never values from the
130
+ * user's files, so the record stays credential-free.
131
+ */
132
+ readonly provenancePayload?: ProvenanceLedger;
133
+ readonly startedAt: string;
134
+ }
135
+
136
+ export async function writeIntent(intentPath: string, intent: IntentRecord): Promise<void> {
137
+ await fs.mkdir(path.dirname(intentPath), { recursive: true, mode: 0o700 });
138
+ await Bun.write(intentPath, serializeJson(intent));
139
+ await fs.chmod(intentPath, 0o600);
140
+ }
141
+
142
+ export async function readIntent(intentPath: string): Promise<IntentRecord | undefined> {
143
+ let raw: string;
144
+ try {
145
+ raw = await Bun.file(intentPath).text();
146
+ } catch (error) {
147
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
148
+ throw error;
149
+ }
150
+ try {
151
+ const parsed = JSON.parse(raw) as IntentRecord;
152
+ if (typeof parsed?.targetPath !== "string" || typeof parsed?.targetExpectedIdentity !== "string")
153
+ return undefined;
154
+ return parsed;
155
+ } catch {
156
+ return undefined;
157
+ }
158
+ }
159
+
160
+ export async function clearIntent(intentPath: string): Promise<void> {
161
+ await fs.rm(intentPath, { force: true }).catch(() => undefined);
162
+ }
163
+
164
+ /** Rebuild the ledger an interrupted step intended to commit, when it recorded one. */
165
+ export function pendingLedgerOf(intent: IntentRecord): ProvenanceLedger | undefined {
166
+ return intent.provenancePayload;
167
+ }
168
+
169
+ /** Where a file sits relative to an interrupted step. */
170
+ export type IntentFileState = "before" | "intended-after" | "divergent";
171
+
172
+ export function classifyIdentity(observed: string, preflight: string, expected: string): IntentFileState {
173
+ if (observed === expected) return "intended-after";
174
+ if (observed === preflight) return "before";
175
+ return "divergent";
176
+ }
177
+
178
+ export type IntentRecovery =
179
+ | { readonly action: "discard"; readonly detail: string }
180
+ | { readonly action: "complete-ledger"; readonly detail: string }
181
+ | { readonly action: "refuse"; readonly detail: string };
182
+
183
+ /**
184
+ * Classify BOTH the target and the ledger before deciding what to do.
185
+ *
186
+ * Deliberately exhaustive over the nine combinations: a divergent ledger always
187
+ * refuses, because replaying a recorded ledger output over a third party's
188
+ * change would destroy it.
189
+ */
190
+ export async function classifyIntent(intent: IntentRecord): Promise<IntentRecovery> {
191
+ const [targetObserved, ledgerObserved] = await Promise.all([
192
+ currentIdentity(intent.targetPath),
193
+ currentIdentity(intent.provenancePath),
194
+ ]);
195
+ const target = classifyIdentity(targetObserved, intent.targetPreflightIdentity, intent.targetExpectedIdentity);
196
+ const ledger = classifyIdentity(
197
+ ledgerObserved,
198
+ intent.provenancePreflightIdentity,
199
+ intent.provenanceExpectedIdentity,
200
+ );
201
+
202
+ if (ledger === "divergent") {
203
+ return {
204
+ action: "refuse",
205
+ detail: `the provenance ledger at ${intent.provenancePath} changed unexpectedly; GJC will not overwrite it`,
206
+ };
207
+ }
208
+ if (target === "divergent") {
209
+ return {
210
+ action: "refuse",
211
+ detail: `${intent.targetPath} was changed by another writer during an interrupted GJC step; GJC will not touch it`,
212
+ };
213
+ }
214
+ if (target === "intended-after" && ledger === "before") {
215
+ return { action: "complete-ledger", detail: `${intent.targetPath} was published; recording its provenance` };
216
+ }
217
+ if (target === "intended-after" && ledger === "intended-after") {
218
+ return { action: "discard", detail: "both writes landed; clearing the stale intent" };
219
+ }
220
+ // target === "before": the publish never landed, so nothing was mutated.
221
+ return { action: "discard", detail: `${intent.targetPath} was never modified; discarding the stale intent` };
222
+ }
223
+
224
+ export { ABSENT_IDENTITY };