@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
@@ -1,1142 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import * as fsSync from "node:fs";
3
- import * as fs from "node:fs/promises";
4
- import * as path from "node:path";
5
- import { withFileLock } from "../../config/file-lock";
6
- import { assertSupportedStateVersion, SDK_STATE_VERSION } from "../broker/state-version";
7
- import { ElevationAudit, type ElevationAuditEntry } from "./audit";
8
- import {
9
- ELEVATION_OPERATION_KINDS,
10
- type ElevationOperation,
11
- elevationRequestDigest,
12
- isKnownElevationOperation,
13
- } from "./digest";
14
- import {
15
- claimReceipt,
16
- dispatchReceipt,
17
- type ElevationAnswerAuthority,
18
- type ElevationClaimIdentity,
19
- type ElevationDispatchOutcome,
20
- type ElevationDispatchReceipt,
21
- isElevationAnswerAuthority,
22
- isElevationClaimIdentity,
23
- isElevationDispatchOutcome,
24
- isElevationDispatchReceipt,
25
- isElevationRequestId,
26
- receiptClaimMatches,
27
- uncertainReceipt,
28
- } from "./dispatch-receipt";
29
- import {
30
- type BrokerOwnerPrincipal,
31
- classifyProcessIncarnationLiveness,
32
- isBrokerOwnerPrincipal,
33
- sameBrokerOwnerPrincipal,
34
- } from "./owner";
35
-
36
- /**
37
- * Broker-owned elevation grant ledger.
38
- *
39
- * Durable state machine (per elevation request):
40
- * `requested -> granted -> claimed -> dispatched(outcome)`; terminal
41
- * `denied`/`expired`/`misused`/`target_unavailable`; crash truth
42
- * `consumed` (claimed but outcome unknown). Grants are single-use: the claim
43
- * CAS consumes the grant before dispatch, and a crash between consume and
44
- * dispatch is replayed truthfully as `consumed` with outcome `unknown`
45
- * (retry requires a new grant — fail closed).
46
- *
47
- * All mutations and reads serialize on one elevation-local
48
- * `withFileLock` transaction covering grants (`grants.jsonl`), dispatch
49
- * receipts (`receipts.jsonl`), the issue index (`index.json`), and the
50
- * audit trail (`audit.jsonl`), so a claim, its receipt, and the issue
51
- * sequence commit together.
52
- *
53
- * The answer boundary is internal operator-only: there is no public
54
- * `elevation.answer` operation; answers are admitted only from the internal
55
- * operator attestation, so requester self-approval is impossible by
56
- * construction.
57
- */
58
- export type ElevationGrantState =
59
- | "requested"
60
- | "granted"
61
- | "claimed"
62
- | "dispatched"
63
- | "consumed"
64
- | "denied"
65
- | "expired"
66
- | "misused"
67
- | "target_unavailable";
68
-
69
- export interface ElevationSessionIdentity {
70
- sessionId: string;
71
- /** Exact endpoint state root bound at issue time; never the repo root. */
72
- endpointStateRoot: string;
73
- endpointGeneration: number;
74
- endpointIncarnation: string;
75
- }
76
-
77
- /** Audit-only requester binding; never an identity claim. */
78
- export interface ElevationRequester {
79
- source: "broker_connection";
80
- connectionId: string;
81
- }
82
-
83
- export interface ElevationGrantRecord {
84
- version: typeof SDK_STATE_VERSION;
85
- /** Durable correlation ID; UUID, path-safe. */
86
- elevationRequestId: string;
87
- /** Monotonic path-safe issue sequence, allocated under the ledger lock. */
88
- issueIndex: number;
89
- state: ElevationGrantState;
90
- operation: ElevationOperation;
91
- requestDigest: string;
92
- sessionIdentity: ElevationSessionIdentity;
93
- principal: BrokerOwnerPrincipal;
94
- requester: ElevationRequester;
95
- requestedAt: number;
96
- expiresAt: number;
97
- outcome?: ElevationDispatchOutcome;
98
- ts: number;
99
- }
100
-
101
- export type ElevationErrorCode =
102
- | "not_found"
103
- | "invalid_input"
104
- | "idempotency_conflict"
105
- | "grant_spent"
106
- | "elevation_claim_in_progress"
107
- | "elevation_required"
108
- | "elevation_unavailable"
109
- | "elevation_not_required"
110
- | "endpoint_stale"
111
- | "target_unavailable"
112
- | "expired"
113
- | "misused"
114
- | "duplicate_answer"
115
- | "terminal_uncertain"
116
- | (string & {});
117
-
118
- export type ElevationResult<T> =
119
- | { ok: true; value: T }
120
- | { ok: false; error: { code: ElevationErrorCode; message: string } };
121
-
122
- export interface ElevationIssueParams {
123
- /** Optional caller-supplied correlation ID for idempotent retry; the broker generates a UUID otherwise. */
124
- elevationRequestId?: string;
125
- operation: ElevationOperation;
126
- input: Record<string, unknown>;
127
- sessionIdentity: ElevationSessionIdentity;
128
- principal: BrokerOwnerPrincipal;
129
- requester: ElevationRequester;
130
- expiresInMs?: number;
131
- }
132
-
133
- export interface ElevationIssueValue {
134
- replay: boolean;
135
- elevationRequestId: string;
136
- issueIndex: number;
137
- requestDigest: string;
138
- state: ElevationGrantState;
139
- requestedAt: number;
140
- expiresAt: number;
141
- grant: ElevationGrantRecord;
142
- }
143
-
144
- export interface ElevationAnswerParams {
145
- elevationRequestId: string;
146
- answer: "approve" | "deny";
147
- /** Digest the answering surface presented from the gate payload. */
148
- presentedDigest: string;
149
- /** Broker-owner principal attesting the answer authority. */
150
- principal: BrokerOwnerPrincipal;
151
- answerer: ElevationAnswerAuthority;
152
- /** Current broker-resolved target identity; undefined means the session is unreachable. */
153
- currentSessionIdentity?: ElevationSessionIdentity;
154
- }
155
-
156
- export type ElevationAnswerOutcome =
157
- | "granted"
158
- | "denied"
159
- | "expired"
160
- | "misused"
161
- | "target_unavailable"
162
- | "duplicate_answer";
163
-
164
- export interface ElevationAnswerValue {
165
- outcome: ElevationAnswerOutcome;
166
- grant: ElevationGrantRecord;
167
- }
168
-
169
- export interface ElevationClaimParams {
170
- elevationRequestId: string;
171
- claimIdentity: ElevationClaimIdentity;
172
- /** Current broker-resolved target identity; undefined means the session is unreachable. */
173
- currentSessionIdentity?: ElevationSessionIdentity;
174
- }
175
-
176
- export interface ElevationClaimValue {
177
- grant: ElevationGrantRecord;
178
- receipt: ElevationDispatchReceipt;
179
- }
180
-
181
- export interface ElevationDispatchParams {
182
- elevationRequestId: string;
183
- dispatchIdentity: ElevationClaimIdentity;
184
- outcome: ElevationDispatchOutcome;
185
- }
186
-
187
- export interface ElevationDispatchValue {
188
- grant: ElevationGrantRecord;
189
- receipt: ElevationDispatchReceipt;
190
- }
191
-
192
- export interface ElevationReadValue {
193
- grant: ElevationGrantRecord;
194
- receipt?: ElevationDispatchReceipt;
195
- }
196
-
197
- export interface ElevationListValue {
198
- grants: ElevationReadValue[];
199
- }
200
-
201
- export interface ElevationLedgerOptions {
202
- /** Defaults to the GJC_SDK_ELEVATION_ENABLED environment flag. */
203
- enabled?: boolean;
204
- /** Request lifetime before the grant expires. */
205
- ttlMs?: number;
206
- /** Injectable clock. */
207
- now?: () => number;
208
- /**
209
- * Injectable tri-state process-incarnation liveness classifier (defaults to
210
- * `classifyProcessIncarnationLiveness`). Test seam for crash-state replay:
211
- * "unknown" must always fail closed as `elevation_claim_in_progress`.
212
- */
213
- classifyLiveness?: typeof classifyProcessIncarnationLiveness;
214
- }
215
-
216
- const ELEVATION_DIR_NAME = "elevation";
217
- const MAX_ELEVATION_FILE_BYTES = 64 * 1024 * 1024;
218
- const MAX_ELEVATION_LINE_BYTES = 8 * 1024 * 1024;
219
- const ELEVATION_REQUEST_TTL_MS = 60_000;
220
- const SHA256_HEX = /^[a-f0-9]{64}$/;
221
- const CANONICAL_SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
222
- const ELEVATION_GRANT_STATES = [
223
- "requested",
224
- "granted",
225
- "claimed",
226
- "dispatched",
227
- "consumed",
228
- "denied",
229
- "expired",
230
- "misused",
231
- "target_unavailable",
232
- ] as const;
233
-
234
- const elevationUtf8Decoder = new TextDecoder("utf-8", { fatal: true });
235
-
236
- function parseElevationJson(bytes: Uint8Array): unknown {
237
- return JSON.parse(elevationUtf8Decoder.decode(bytes));
238
- }
239
-
240
- function elevationEnabledByDefault(): boolean {
241
- const raw = (process.env.GJC_SDK_ELEVATION_ENABLED ?? "").trim().toLowerCase();
242
- return raw === "1" || raw === "true";
243
- }
244
-
245
- function elevationFailure<T>(code: ElevationErrorCode, message: string): ElevationResult<T> {
246
- return { ok: false, error: { code, message } };
247
- }
248
-
249
- function isElevationOperation(value: unknown): value is ElevationOperation {
250
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
251
- const operation = value as { kind?: unknown; sdkId?: unknown };
252
- return (
253
- typeof operation.kind === "string" &&
254
- (ELEVATION_OPERATION_KINDS as readonly string[]).includes(operation.kind) &&
255
- typeof operation.sdkId === "string" &&
256
- operation.sdkId.length > 0 &&
257
- operation.sdkId.length <= 128 &&
258
- isKnownElevationOperation(operation.kind, operation.sdkId)
259
- );
260
- }
261
-
262
- function isElevationGrantState(value: unknown): value is ElevationGrantState {
263
- return typeof value === "string" && (ELEVATION_GRANT_STATES as readonly string[]).includes(value);
264
- }
265
-
266
- function isElevationSessionIdentity(value: unknown): value is ElevationSessionIdentity {
267
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
268
- const identity = value as {
269
- sessionId?: unknown;
270
- endpointStateRoot?: unknown;
271
- endpointGeneration?: unknown;
272
- endpointIncarnation?: unknown;
273
- };
274
- return (
275
- typeof identity.sessionId === "string" &&
276
- CANONICAL_SESSION_ID.test(identity.sessionId) &&
277
- typeof identity.endpointStateRoot === "string" &&
278
- identity.endpointStateRoot.length > 0 &&
279
- Number.isSafeInteger(identity.endpointGeneration) &&
280
- (identity.endpointGeneration as number) > 0 &&
281
- typeof identity.endpointIncarnation === "string" &&
282
- SHA256_HEX.test(identity.endpointIncarnation)
283
- );
284
- }
285
-
286
- function isElevationRequester(value: unknown): value is ElevationRequester {
287
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
288
- const requester = value as { source?: unknown; connectionId?: unknown };
289
- return (
290
- requester.source === "broker_connection" &&
291
- typeof requester.connectionId === "string" &&
292
- requester.connectionId.length > 0
293
- );
294
- }
295
-
296
- function isElevationGrantRecord(value: unknown): value is ElevationGrantRecord {
297
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
298
- const record = value as Partial<ElevationGrantRecord>;
299
- if (
300
- record.version !== SDK_STATE_VERSION ||
301
- !isElevationRequestId(record.elevationRequestId) ||
302
- !Number.isSafeInteger(record.issueIndex) ||
303
- (record.issueIndex as number) <= 0 ||
304
- !isElevationGrantState(record.state) ||
305
- !isElevationOperation(record.operation) ||
306
- typeof record.requestDigest !== "string" ||
307
- !SHA256_HEX.test(record.requestDigest) ||
308
- !isElevationSessionIdentity(record.sessionIdentity) ||
309
- !isBrokerOwnerPrincipal(record.principal) ||
310
- !isElevationRequester(record.requester) ||
311
- !Number.isSafeInteger(record.requestedAt) ||
312
- (record.requestedAt as number) <= 0 ||
313
- !Number.isSafeInteger(record.expiresAt) ||
314
- (record.expiresAt as number) <= 0 ||
315
- !Number.isSafeInteger(record.ts) ||
316
- (record.ts as number) <= 0
317
- )
318
- return false;
319
- const outcome = record.outcome;
320
- if (record.state === "dispatched") return isElevationDispatchOutcome(outcome) && outcome.status !== "unknown";
321
- if (record.state === "consumed") return isElevationDispatchOutcome(outcome) && outcome.status === "unknown";
322
- return outcome === undefined;
323
- }
324
-
325
- function sameSessionIdentity(left: ElevationSessionIdentity, right: ElevationSessionIdentity): boolean {
326
- return (
327
- left.sessionId === right.sessionId &&
328
- path.resolve(left.endpointStateRoot) === path.resolve(right.endpointStateRoot) &&
329
- left.endpointGeneration === right.endpointGeneration &&
330
- left.endpointIncarnation === right.endpointIncarnation
331
- );
332
- }
333
-
334
- type ReconcileOutcome = "ok" | "expired" | "uncertain" | "in_progress";
335
- export class ElevationLedger {
336
- #dir: string;
337
- #agentDir: string;
338
- #grantsFile: string;
339
- #receiptsFile: string;
340
- #indexFile: string;
341
- #audit: ElevationAudit;
342
- #enabled: boolean;
343
- #ttlMs: number;
344
- #now: () => number;
345
- #grants: ElevationGrantRecord[] = [];
346
- #receipts: ElevationDispatchReceipt[] = [];
347
- #byRequestId = new Map<string, ElevationGrantRecord>();
348
- #byReceiptRequestId = new Map<string, ElevationDispatchReceipt>();
349
- #nextIssueIndex = 1;
350
- #warnings: string[] = [];
351
- #classifyLiveness: typeof classifyProcessIncarnationLiveness;
352
-
353
- constructor(agentDir: string, options: ElevationLedgerOptions = {}) {
354
- this.#agentDir = agentDir;
355
- this.#dir = path.join(agentDir, "sdk", ELEVATION_DIR_NAME);
356
- this.#grantsFile = path.join(this.#dir, "grants.jsonl");
357
- this.#receiptsFile = path.join(this.#dir, "receipts.jsonl");
358
- this.#indexFile = path.join(this.#dir, "index.json");
359
- this.#audit = new ElevationAudit(agentDir, { now: options.now, classifyLiveness: options.classifyLiveness });
360
- this.#enabled = options.enabled ?? elevationEnabledByDefault();
361
- this.#ttlMs = options.ttlMs ?? ELEVATION_REQUEST_TTL_MS;
362
- this.#now = options.now ?? Date.now;
363
- this.#classifyLiveness = options.classifyLiveness ?? classifyProcessIncarnationLiveness;
364
- }
365
-
366
- get warnings(): readonly string[] {
367
- return this.#warnings;
368
- }
369
-
370
- /**
371
- * One elevation-local lock serializes grants, receipts, and the issue
372
- * index: the same transaction covers every file the ledger owns.
373
- */
374
- async #mutate<T>(operation: () => Promise<T>): Promise<T> {
375
- return withFileLock(this.#grantsFile, operation);
376
- }
377
-
378
- async open(): Promise<this> {
379
- await this.#mutate(async () => this.#openUnderLock());
380
- return this;
381
- }
382
-
383
- async #openUnderLock(): Promise<void> {
384
- await fs.mkdir(this.#dir, { recursive: true, mode: 0o700 });
385
- this.#audit = await new ElevationAudit(this.#agentDir, {
386
- now: this.#now,
387
- classifyLiveness: this.#classifyLiveness,
388
- }).open();
389
- this.#grants = [];
390
- this.#receipts = [];
391
- this.#byRequestId.clear();
392
- this.#byReceiptRequestId.clear();
393
- this.#warnings = [];
394
- this.#warnings.push(...this.#audit.warnings);
395
- let nextIssueIndex = 1;
396
- const indexRaw = await this.#readIndex();
397
- if (indexRaw !== undefined) {
398
- let parsed: unknown;
399
- try {
400
- parsed = JSON.parse(indexRaw);
401
- } catch {
402
- throw new Error("Invalid elevation issue index");
403
- }
404
- assertSupportedStateVersion(this.#indexFile, parsed);
405
- const index = parsed as { nextIssueIndex?: unknown };
406
- if (!Number.isSafeInteger(index.nextIssueIndex) || (index.nextIssueIndex as number) < 1)
407
- throw new Error("Invalid elevation issue index");
408
- nextIssueIndex = index.nextIssueIndex as number;
409
- }
410
- const grantSource = await this.#readBoundedSource(this.#grantsFile);
411
- let maxIssueIndex = 0;
412
- if (grantSource) maxIssueIndex = await this.#loadGrantRows(grantSource);
413
- const receiptSource = await this.#readBoundedSource(this.#receiptsFile);
414
- if (receiptSource) await this.#loadReceiptRows(receiptSource);
415
- // Grants are the source of truth for issued indexes: a crash between
416
- // appending a grant row and rewriting index.json must never reuse it.
417
- const healed = Math.max(nextIssueIndex, maxIssueIndex + 1);
418
- if (healed !== nextIssueIndex) await this.#writeIndex(healed);
419
- this.#nextIssueIndex = healed;
420
- }
421
-
422
- async assertSupportedStateVersions(): Promise<void> {
423
- for (const file of [this.#grantsFile, this.#receiptsFile]) {
424
- const source = await this.#readBoundedSource(file);
425
- if (!source) continue;
426
- let lineStart = 0;
427
- for (let offset = 0; offset <= source.length; offset += 1) {
428
- if (offset !== source.length && source[offset] !== 0x0a) continue;
429
- const line = source.subarray(lineStart, offset);
430
- lineStart = offset + 1;
431
- if (line.length === 0 || line.length > MAX_ELEVATION_LINE_BYTES) continue;
432
- try {
433
- assertSupportedStateVersion(file, parseElevationJson(line));
434
- } catch (error) {
435
- if (error instanceof Error && "code" in error && error.code === "unsupported_state_version") throw error;
436
- }
437
- }
438
- }
439
- }
440
-
441
- async #readIndex(): Promise<string | undefined> {
442
- let handle: fs.FileHandle | undefined;
443
- try {
444
- handle = await fs.open(this.#indexFile, fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW);
445
- const stat = await handle.stat();
446
- if (!stat.isFile()) throw new Error("Elevation issue index is not a regular file");
447
- if (stat.size > MAX_ELEVATION_LINE_BYTES)
448
- throw new Error("Elevation issue index exceeds the maximum byte length");
449
- const bytes = Buffer.alloc(stat.size + 1);
450
- const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);
451
- return elevationUtf8Decoder.decode(bytes.subarray(0, bytesRead));
452
- } catch (error) {
453
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
454
- throw error;
455
- } finally {
456
- if (handle) await handle.close();
457
- }
458
- }
459
-
460
- async #readBoundedSource(file: string): Promise<Buffer | undefined> {
461
- let handle: fs.FileHandle | undefined;
462
- try {
463
- handle = await fs.open(file, fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW);
464
- const stat = await handle.stat({ bigint: true });
465
- if (!stat.isFile()) throw new Error(`Elevation ledger target is not a regular file: ${file}`);
466
- if (stat.size > BigInt(MAX_ELEVATION_FILE_BYTES))
467
- throw new Error(`Elevation ledger file exceeds the maximum byte length: ${file}`);
468
- const bytes = Buffer.alloc(Number(stat.size) + 1);
469
- const { bytesRead } = await handle.read(bytes, 0, bytes.length, 0);
470
- return bytes.subarray(0, bytesRead);
471
- } catch (error) {
472
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
473
- throw error;
474
- } finally {
475
- if (handle) await handle.close();
476
- }
477
- }
478
-
479
- async #loadGrantRows(source: Buffer): Promise<number> {
480
- let maxIssueIndex = 0;
481
- const tornTail = source.length > 0 && source.at(-1) !== 0x0a;
482
- let lineStart = 0;
483
- for (let offset = 0; offset <= source.length; offset += 1) {
484
- if (offset !== source.length && source[offset] !== 0x0a) continue;
485
- const line = source.subarray(lineStart, offset);
486
- lineStart = offset + 1;
487
- if (line.length === 0) continue;
488
- if (line.length > MAX_ELEVATION_LINE_BYTES)
489
- throw new Error("Elevation grant row exceeds the maximum byte length");
490
- try {
491
- const value = parseElevationJson(line);
492
- assertSupportedStateVersion(this.#grantsFile, value);
493
- if (!isElevationGrantRecord(value)) throw new Error("invalid elevation grant record");
494
- this.#grants.push(value);
495
- this.#byRequestId.set(value.elevationRequestId, value);
496
- if (value.issueIndex > maxIssueIndex) maxIssueIndex = value.issueIndex;
497
- } catch (error) {
498
- if (error instanceof Error && "code" in error && error.code === "unsupported_state_version") throw error;
499
- await this.#quarantine(this.#grantsFile, line);
500
- }
501
- }
502
- if (tornTail) await this.#sealTornTail(this.#grantsFile);
503
- return maxIssueIndex;
504
- }
505
-
506
- async #loadReceiptRows(source: Buffer): Promise<void> {
507
- const tornTail = source.length > 0 && source.at(-1) !== 0x0a;
508
- let lineStart = 0;
509
- for (let offset = 0; offset <= source.length; offset += 1) {
510
- if (offset !== source.length && source[offset] !== 0x0a) continue;
511
- const line = source.subarray(lineStart, offset);
512
- lineStart = offset + 1;
513
- if (line.length === 0) continue;
514
- if (line.length > MAX_ELEVATION_LINE_BYTES)
515
- throw new Error("Elevation receipt row exceeds the maximum byte length");
516
- try {
517
- const value = parseElevationJson(line);
518
- assertSupportedStateVersion(this.#receiptsFile, value);
519
- if (!isElevationDispatchReceipt(value)) throw new Error("invalid elevation dispatch receipt");
520
- this.#receipts.push(value);
521
- this.#byReceiptRequestId.set(value.elevationRequestId, value);
522
- } catch (error) {
523
- if (error instanceof Error && "code" in error && error.code === "unsupported_state_version") throw error;
524
- await this.#quarantine(this.#receiptsFile, line);
525
- }
526
- }
527
- if (tornTail) await this.#sealTornTail(this.#receiptsFile);
528
- }
529
-
530
- async #openAppendRegular(file: string): Promise<fs.FileHandle> {
531
- const handle = await fs.open(
532
- file,
533
- fsSync.constants.O_WRONLY | fsSync.constants.O_APPEND | fsSync.constants.O_CREAT | fsSync.constants.O_NOFOLLOW,
534
- 0o600,
535
- );
536
- try {
537
- if (!(await handle.stat()).isFile()) throw new Error("Elevation ledger write target is not a regular file.");
538
- return handle;
539
- } catch (error) {
540
- await handle.close();
541
- throw error;
542
- }
543
- }
544
-
545
- async #sealTornTail(file: string): Promise<void> {
546
- const h = await this.#openAppendRegular(file);
547
- try {
548
- await h.writeFile("\n");
549
- await h.sync();
550
- } finally {
551
- await h.close();
552
- }
553
- }
554
-
555
- async #quarantine(file: string, line: Uint8Array): Promise<void> {
556
- const h = await this.#openAppendRegular(`${file}.corrupt`);
557
- try {
558
- await h.writeFile(line);
559
- await h.writeFile("\n");
560
- await h.sync();
561
- } finally {
562
- await h.close();
563
- }
564
- this.#warnings.push("Malformed elevation ledger entry quarantined");
565
- }
566
-
567
- async #syncDirectory(): Promise<void> {
568
- const directory = await fs.open(this.#dir, fsSync.constants.O_RDONLY);
569
- try {
570
- await directory.sync();
571
- } finally {
572
- await directory.close();
573
- }
574
- }
575
-
576
- async #writeIndex(nextIssueIndex: number): Promise<void> {
577
- const contents = Buffer.from(`${JSON.stringify({ version: SDK_STATE_VERSION, nextIssueIndex })}\n`);
578
- const temporary = path.join(
579
- this.#dir,
580
- `.index.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`,
581
- );
582
- let renamed = false;
583
- try {
584
- const h = await fs.open(
585
- temporary,
586
- fsSync.constants.O_WRONLY |
587
- fsSync.constants.O_CREAT |
588
- fsSync.constants.O_EXCL |
589
- fsSync.constants.O_NOFOLLOW,
590
- 0o600,
591
- );
592
- try {
593
- await h.writeFile(contents);
594
- await h.sync();
595
- } finally {
596
- await h.close();
597
- }
598
- await fs.rename(temporary, this.#indexFile);
599
- renamed = true;
600
- await this.#syncDirectory();
601
- } finally {
602
- if (!renamed) await fs.unlink(temporary).catch(() => {});
603
- }
604
- }
605
-
606
- async #appendGrant(record: ElevationGrantRecord): Promise<void> {
607
- const line = Buffer.from(`${JSON.stringify(record)}\n`);
608
- if (line.length - 1 > MAX_ELEVATION_LINE_BYTES)
609
- throw new Error("Elevation grant row exceeds the maximum byte length");
610
- const h = await this.#openAppendRegular(this.#grantsFile);
611
- try {
612
- await h.writeFile(line);
613
- await h.sync();
614
- } finally {
615
- await h.close();
616
- }
617
- this.#grants.push(record);
618
- this.#byRequestId.set(record.elevationRequestId, record);
619
- }
620
-
621
- async #appendReceipt(receipt: ElevationDispatchReceipt): Promise<void> {
622
- const line = Buffer.from(`${JSON.stringify(receipt)}\n`);
623
- if (line.length - 1 > MAX_ELEVATION_LINE_BYTES)
624
- throw new Error("Elevation receipt row exceeds the maximum byte length");
625
- const h = await this.#openAppendRegular(this.#receiptsFile);
626
- try {
627
- await h.writeFile(line);
628
- await h.sync();
629
- } finally {
630
- await h.close();
631
- }
632
- this.#receipts.push(receipt);
633
- this.#byReceiptRequestId.set(receipt.elevationRequestId, receipt);
634
- }
635
-
636
- /**
637
- * Appends one audit row for an elevation transition. Must be called under
638
- * the same elevation-local lock as the grant/receipt appends so the audit
639
- * row and the durable grant row commit together.
640
- */
641
- async #appendAudit(entry: Omit<ElevationAuditEntry, "version">): Promise<void> {
642
- await this.#audit.append(entry);
643
- }
644
-
645
- /**
646
- * Truthful reconciliation before any access: heals a grant row that a
647
- * crash left behind a terminal receipt, lazily expires overdue requests,
648
- * and terminalizes claims left by provably dead broker tenures to
649
- * `consumed`/`uncertain`. Unknown liveness always resolves to
650
- * `in_progress` (fail closed — never fabricate an outcome).
651
- */
652
- async #reconcile(
653
- grant: ElevationGrantRecord,
654
- receipt: ElevationDispatchReceipt | undefined,
655
- claimIdentity: ElevationClaimIdentity,
656
- now: number,
657
- ): Promise<ReconcileOutcome> {
658
- if (receipt?.state === "dispatched") {
659
- if (grant.state !== "dispatched")
660
- await this.#appendGrant({ ...grant, state: "dispatched", outcome: receipt.outcome, ts: now });
661
- return "ok";
662
- }
663
- if (receipt?.state === "uncertain") {
664
- if (grant.state !== "consumed")
665
- await this.#appendGrant({ ...grant, state: "consumed", outcome: receipt.outcome, ts: now });
666
- return "uncertain";
667
- }
668
- if (receipt?.state === "claimed") {
669
- if (receiptClaimMatches(receipt, claimIdentity)) return "in_progress";
670
- const liveness = this.#classifyLiveness(receipt.claim.pid, receipt.claim.incarnation);
671
- if (liveness === "dead") {
672
- if (grant.state !== "consumed")
673
- await this.#appendGrant({
674
- ...grant,
675
- state: "consumed",
676
- outcome: {
677
- status: "unknown",
678
- message: "Claiming broker tenure ended before dispatch outcome was recorded",
679
- dispatchedAt: now,
680
- },
681
- ts: now,
682
- });
683
- await this.#appendReceipt(uncertainReceipt(receipt, now));
684
- await this.#appendAudit({
685
- ts: now,
686
- elevationRequestId: grant.elevationRequestId,
687
- issueIndex: grant.issueIndex,
688
- event: {
689
- kind: "consumed",
690
- message: "Claiming broker tenure ended before dispatch outcome was recorded",
691
- },
692
- });
693
- return "uncertain";
694
- }
695
- // Alive or unknown liveness: the claim may still be in flight.
696
- return "in_progress";
697
- }
698
- if ((grant.state === "requested" || grant.state === "granted") && now > grant.expiresAt) {
699
- await this.#appendGrant({ ...grant, state: "expired", ts: now });
700
- await this.#appendAudit({
701
- ts: now,
702
- elevationRequestId: grant.elevationRequestId,
703
- issueIndex: grant.issueIndex,
704
- event: { kind: "expired" },
705
- });
706
- return "expired";
707
- }
708
- return "ok";
709
- }
710
-
711
- async issue(params: ElevationIssueParams): Promise<ElevationResult<ElevationIssueValue>> {
712
- if (!this.#enabled)
713
- return elevationFailure("elevation_unavailable", "Elevation is not enabled (GJC_SDK_ELEVATION_ENABLED)");
714
- if (!isElevationOperation(params.operation))
715
- return elevationFailure("invalid_input", "operation must be an allowlisted control/query/global operation");
716
- if (typeof params.input !== "object" || params.input === null || Array.isArray(params.input))
717
- return elevationFailure("invalid_input", "input must be a JSON object");
718
- if (!isElevationSessionIdentity(params.sessionIdentity))
719
- return elevationFailure("invalid_input", "sessionIdentity is malformed");
720
- if (!isBrokerOwnerPrincipal(params.principal))
721
- return elevationFailure("invalid_input", "principal must be the broker-owner principal");
722
- if (!isElevationRequester(params.requester)) return elevationFailure("invalid_input", "requester is malformed");
723
- if (params.elevationRequestId !== undefined && !isElevationRequestId(params.elevationRequestId))
724
- return elevationFailure("invalid_input", "elevationRequestId must be a UUID");
725
- if (
726
- params.expiresInMs !== undefined &&
727
- (!Number.isSafeInteger(params.expiresInMs) || params.expiresInMs <= 0 || params.expiresInMs > 15 * 60_000)
728
- )
729
- return elevationFailure(
730
- "invalid_input",
731
- "expiresInMs must be a positive safe integer no greater than 15 minutes",
732
- );
733
- const digestResult = elevationRequestDigest({
734
- kind: params.operation.kind,
735
- sdkId: params.operation.sdkId,
736
- input: params.input,
737
- });
738
- if (!digestResult.ok)
739
- return elevationFailure(digestResult.error.code as ElevationErrorCode, digestResult.error.message);
740
- const ttlMs = params.expiresInMs ?? this.#ttlMs;
741
- const requestId = params.elevationRequestId ?? randomUUID();
742
- return this.#mutate(async () => {
743
- const now = this.#now();
744
- const existing = this.#byRequestId.get(requestId);
745
- if (existing) {
746
- const identical =
747
- existing.requestDigest === digestResult.digest &&
748
- existing.operation.kind === params.operation.kind &&
749
- existing.operation.sdkId === params.operation.sdkId &&
750
- sameSessionIdentity(existing.sessionIdentity, params.sessionIdentity) &&
751
- sameBrokerOwnerPrincipal(existing.principal, params.principal);
752
- if (!identical)
753
- return elevationFailure(
754
- "idempotency_conflict",
755
- "elevationRequestId was used with different request content",
756
- );
757
- return {
758
- ok: true,
759
- value: {
760
- replay: true,
761
- elevationRequestId: existing.elevationRequestId,
762
- issueIndex: existing.issueIndex,
763
- requestDigest: existing.requestDigest,
764
- state: existing.state,
765
- requestedAt: existing.requestedAt,
766
- expiresAt: existing.expiresAt,
767
- grant: existing,
768
- },
769
- };
770
- }
771
- const grant: ElevationGrantRecord = {
772
- version: SDK_STATE_VERSION,
773
- elevationRequestId: requestId,
774
- issueIndex: this.#nextIssueIndex,
775
- state: "requested",
776
- operation: params.operation,
777
- requestDigest: digestResult.digest,
778
- sessionIdentity: params.sessionIdentity,
779
- principal: params.principal,
780
- requester: params.requester,
781
- requestedAt: now,
782
- expiresAt: now + ttlMs,
783
- ts: now,
784
- };
785
- this.#nextIssueIndex += 1;
786
- await this.#appendGrant(grant);
787
- await this.#appendAudit({
788
- ts: now,
789
- elevationRequestId: grant.elevationRequestId,
790
- issueIndex: grant.issueIndex,
791
- event: { kind: "issued" },
792
- });
793
- await this.#writeIndex(this.#nextIssueIndex);
794
- return {
795
- ok: true,
796
- value: {
797
- replay: false,
798
- elevationRequestId: grant.elevationRequestId,
799
- issueIndex: grant.issueIndex,
800
- requestDigest: grant.requestDigest,
801
- state: grant.state,
802
- requestedAt: grant.requestedAt,
803
- expiresAt: grant.expiresAt,
804
- grant,
805
- },
806
- };
807
- });
808
- }
809
-
810
- async answer(params: ElevationAnswerParams): Promise<ElevationResult<ElevationAnswerValue>> {
811
- if (!this.#enabled)
812
- return elevationFailure("elevation_unavailable", "Elevation is not enabled (GJC_SDK_ELEVATION_ENABLED)");
813
- if (!isElevationRequestId(params.elevationRequestId))
814
- return elevationFailure("invalid_input", "elevationRequestId must be a UUID");
815
- if (params.answer !== "approve" && params.answer !== "deny")
816
- return elevationFailure("invalid_input", "answer must be approve or deny");
817
- if (typeof params.presentedDigest !== "string" || !SHA256_HEX.test(params.presentedDigest))
818
- return elevationFailure("invalid_input", "presentedDigest must be a SHA-256 hash");
819
- if (!isBrokerOwnerPrincipal(params.principal))
820
- return elevationFailure("invalid_input", "principal must be the broker-owner principal");
821
- if (!isElevationAnswerAuthority(params.answerer))
822
- return elevationFailure("invalid_input", "answerer is malformed");
823
- if (params.currentSessionIdentity !== undefined && !isElevationSessionIdentity(params.currentSessionIdentity))
824
- return elevationFailure("invalid_input", "currentSessionIdentity is malformed");
825
- return this.#mutate(async () => {
826
- const now = this.#now();
827
- const grant = this.#byRequestId.get(params.elevationRequestId);
828
- if (!grant) return elevationFailure("not_found", "elevation request was not found");
829
- const receipt = this.#byReceiptRequestId.get(params.elevationRequestId);
830
- if (receipt !== undefined) {
831
- await this.#appendAudit({
832
- ts: now,
833
- elevationRequestId: grant.elevationRequestId,
834
- issueIndex: grant.issueIndex,
835
- event: { kind: "duplicate_answer" },
836
- });
837
- return { ok: true, value: { outcome: "duplicate_answer", grant } };
838
- }
839
- if ((grant.state === "requested" || grant.state === "granted") && now > grant.expiresAt) {
840
- const expired: ElevationGrantRecord = { ...grant, state: "expired", ts: now };
841
- await this.#appendGrant(expired);
842
- await this.#appendAudit({
843
- ts: now,
844
- elevationRequestId: grant.elevationRequestId,
845
- issueIndex: grant.issueIndex,
846
- event: { kind: "answered", outcome: "expired" },
847
- });
848
- return { ok: true, value: { outcome: "expired", grant: expired } };
849
- }
850
- if (grant.state !== "requested") {
851
- await this.#appendAudit({
852
- ts: now,
853
- elevationRequestId: grant.elevationRequestId,
854
- issueIndex: grant.issueIndex,
855
- event: { kind: "duplicate_answer" },
856
- });
857
- return { ok: true, value: { outcome: "duplicate_answer", grant } };
858
- }
859
- if (!sameBrokerOwnerPrincipal(params.principal, grant.principal)) {
860
- const misused: ElevationGrantRecord = { ...grant, state: "misused", ts: now };
861
- await this.#appendGrant(misused);
862
- await this.#appendAudit({
863
- ts: now,
864
- elevationRequestId: grant.elevationRequestId,
865
- issueIndex: grant.issueIndex,
866
- event: { kind: "answered", outcome: "misused" },
867
- });
868
- return { ok: true, value: { outcome: "misused", grant: misused } };
869
- }
870
- if (params.presentedDigest !== grant.requestDigest) {
871
- const misused: ElevationGrantRecord = { ...grant, state: "misused", ts: now };
872
- await this.#appendGrant(misused);
873
- await this.#appendAudit({
874
- ts: now,
875
- elevationRequestId: grant.elevationRequestId,
876
- issueIndex: grant.issueIndex,
877
- event: { kind: "answered", outcome: "misused" },
878
- });
879
- return { ok: true, value: { outcome: "misused", grant: misused } };
880
- }
881
- if (params.currentSessionIdentity === undefined) {
882
- const unavailable: ElevationGrantRecord = { ...grant, state: "target_unavailable", ts: now };
883
- await this.#appendGrant(unavailable);
884
- await this.#appendAudit({
885
- ts: now,
886
- elevationRequestId: grant.elevationRequestId,
887
- issueIndex: grant.issueIndex,
888
- event: { kind: "answered", outcome: "target_unavailable" },
889
- });
890
- return { ok: true, value: { outcome: "target_unavailable", grant: unavailable } };
891
- }
892
- if (!sameSessionIdentity(params.currentSessionIdentity, grant.sessionIdentity)) {
893
- const misused: ElevationGrantRecord = { ...grant, state: "misused", ts: now };
894
- await this.#appendGrant(misused);
895
- await this.#appendAudit({
896
- ts: now,
897
- elevationRequestId: grant.elevationRequestId,
898
- issueIndex: grant.issueIndex,
899
- event: { kind: "answered", outcome: "misused" },
900
- });
901
- return { ok: true, value: { outcome: "misused", grant: misused } };
902
- }
903
- if (params.answer === "deny") {
904
- const denied: ElevationGrantRecord = { ...grant, state: "denied", ts: now };
905
- await this.#appendGrant(denied);
906
- await this.#appendAudit({
907
- ts: now,
908
- elevationRequestId: grant.elevationRequestId,
909
- issueIndex: grant.issueIndex,
910
- event: { kind: "answered", outcome: "denied" },
911
- });
912
- return { ok: true, value: { outcome: "denied", grant: denied } };
913
- }
914
- const granted: ElevationGrantRecord = { ...grant, state: "granted", ts: now };
915
- await this.#appendGrant(granted);
916
- await this.#appendAudit({
917
- ts: now,
918
- elevationRequestId: grant.elevationRequestId,
919
- issueIndex: grant.issueIndex,
920
- event: { kind: "answered", outcome: "granted" },
921
- });
922
- return { ok: true, value: { outcome: "granted", grant: granted } };
923
- });
924
- }
925
-
926
- async claim(params: ElevationClaimParams): Promise<ElevationResult<ElevationClaimValue>> {
927
- if (!isElevationRequestId(params.elevationRequestId))
928
- return elevationFailure("invalid_input", "elevationRequestId must be a UUID");
929
- if (!isElevationClaimIdentity(params.claimIdentity))
930
- return elevationFailure("invalid_input", "claimIdentity is malformed");
931
- if (params.currentSessionIdentity !== undefined && !isElevationSessionIdentity(params.currentSessionIdentity))
932
- return elevationFailure("invalid_input", "currentSessionIdentity is malformed");
933
- return this.#mutate(async () => {
934
- const now = this.#now();
935
- const grant = this.#byRequestId.get(params.elevationRequestId);
936
- if (!grant) return elevationFailure("not_found", "elevation request was not found");
937
- const receipt = this.#byReceiptRequestId.get(params.elevationRequestId);
938
- const reconciled = await this.#reconcile(grant, receipt, params.claimIdentity, now);
939
- if (reconciled === "in_progress")
940
- return elevationFailure("elevation_claim_in_progress", "elevation claim is already in progress");
941
- if (reconciled === "uncertain")
942
- return elevationFailure(
943
- "terminal_uncertain",
944
- "elevation grant was consumed with an unknown dispatch outcome; retry requires a new grant",
945
- );
946
- if (reconciled === "expired") return elevationFailure("expired", "elevation grant has expired");
947
- const current = this.#byRequestId.get(params.elevationRequestId) ?? grant;
948
- if (current.state === "requested")
949
- return elevationFailure("elevation_required", "elevation request has not been granted");
950
- if (current.state === "claimed")
951
- return elevationFailure("elevation_claim_in_progress", "elevation claim is already in progress");
952
- if (current.state !== "granted")
953
- return elevationFailure("grant_spent", "elevation grant is already spent or terminal");
954
- if (params.currentSessionIdentity === undefined)
955
- return elevationFailure("target_unavailable", "session endpoint is unreachable at claim time");
956
- if (!sameSessionIdentity(params.currentSessionIdentity, current.sessionIdentity))
957
- return elevationFailure(
958
- "endpoint_stale",
959
- "session endpoint identity no longer matches the elevation grant",
960
- );
961
- const claimed: ElevationGrantRecord = { ...current, state: "claimed", ts: now };
962
- // Receipt before grant: if a crash lands between the two appends
963
- // the receipt is authoritative, and the next access truthfully
964
- // terminalizes the claim.
965
- const receiptRecord = claimReceipt(current.elevationRequestId, current.issueIndex, params.claimIdentity, now);
966
- await this.#appendReceipt(receiptRecord);
967
- await this.#appendGrant(claimed);
968
- await this.#appendAudit({
969
- ts: now,
970
- elevationRequestId: current.elevationRequestId,
971
- issueIndex: current.issueIndex,
972
- event: {
973
- kind: "claimed",
974
- claim: {
975
- ownerId: params.claimIdentity.ownerId,
976
- epoch: params.claimIdentity.epoch,
977
- pid: params.claimIdentity.pid,
978
- incarnation: params.claimIdentity.incarnation,
979
- },
980
- },
981
- });
982
- return { ok: true, value: { grant: claimed, receipt: receiptRecord } };
983
- });
984
- }
985
-
986
- async dispatch(params: ElevationDispatchParams): Promise<ElevationResult<ElevationDispatchValue>> {
987
- if (!isElevationRequestId(params.elevationRequestId))
988
- return elevationFailure("invalid_input", "elevationRequestId must be a UUID");
989
- if (!isElevationClaimIdentity(params.dispatchIdentity))
990
- return elevationFailure("invalid_input", "dispatchIdentity is malformed");
991
- if (!isElevationDispatchOutcome(params.outcome)) return elevationFailure("invalid_input", "outcome is malformed");
992
- if (params.outcome.status === "unknown")
993
- return elevationFailure("invalid_input", "dispatch outcome unknown is reserved for crash replay");
994
- return this.#mutate(async () => {
995
- const now = this.#now();
996
- const grant = this.#byRequestId.get(params.elevationRequestId);
997
- if (!grant) return elevationFailure("not_found", "elevation request was not found");
998
- const receipt = this.#byReceiptRequestId.get(params.elevationRequestId);
999
- if (!receipt)
1000
- return elevationFailure("elevation_claim_in_progress", "elevation grant was not claimed before dispatch");
1001
- if (receipt.state === "claimed" && !receiptClaimMatches(receipt, params.dispatchIdentity)) {
1002
- const liveness = this.#classifyLiveness(receipt.claim.pid, receipt.claim.incarnation);
1003
- if (liveness === "dead") {
1004
- if (grant.state !== "consumed")
1005
- await this.#appendGrant({
1006
- ...grant,
1007
- state: "consumed",
1008
- outcome: {
1009
- status: "unknown",
1010
- message: "Claiming broker tenure ended before dispatch outcome was recorded",
1011
- dispatchedAt: now,
1012
- },
1013
- ts: now,
1014
- });
1015
- await this.#appendReceipt(uncertainReceipt(receipt, now));
1016
- await this.#appendAudit({
1017
- ts: now,
1018
- elevationRequestId: grant.elevationRequestId,
1019
- issueIndex: grant.issueIndex,
1020
- event: {
1021
- kind: "consumed",
1022
- message: "Claiming broker tenure ended before dispatch outcome was recorded",
1023
- },
1024
- });
1025
- return elevationFailure(
1026
- "terminal_uncertain",
1027
- "elevation grant dispatch outcome is unknown; retry requires a new grant",
1028
- );
1029
- }
1030
- return elevationFailure(
1031
- "elevation_claim_in_progress",
1032
- "elevation grant is claimed by another broker tenure",
1033
- );
1034
- }
1035
- if (receipt.state === "dispatched") {
1036
- if (
1037
- receipt.dispatch === undefined ||
1038
- receipt.dispatch.ownerId !== params.dispatchIdentity.ownerId ||
1039
- receipt.dispatch.epoch !== params.dispatchIdentity.epoch
1040
- )
1041
- return elevationFailure("grant_spent", "elevation grant was dispatched by another broker tenure");
1042
- return { ok: true, value: { grant, receipt } };
1043
- }
1044
- if (receipt.state === "uncertain")
1045
- return elevationFailure(
1046
- "terminal_uncertain",
1047
- "elevation grant dispatch outcome is unknown; retry requires a new grant",
1048
- );
1049
- if (grant.state !== "claimed")
1050
- return elevationFailure("grant_spent", "elevation grant is not in the claimed state");
1051
- const dispatchedGrant: ElevationGrantRecord = {
1052
- ...grant,
1053
- state: "dispatched",
1054
- outcome: params.outcome,
1055
- ts: now,
1056
- };
1057
- const dispatchedReceipt = dispatchReceipt(
1058
- receipt,
1059
- {
1060
- ownerId: params.dispatchIdentity.ownerId,
1061
- epoch: params.dispatchIdentity.epoch,
1062
- dispatchedAt: now,
1063
- },
1064
- params.outcome,
1065
- );
1066
- await this.#appendReceipt(dispatchedReceipt);
1067
- await this.#appendGrant(dispatchedGrant);
1068
- await this.#appendAudit({
1069
- ts: now,
1070
- elevationRequestId: grant.elevationRequestId,
1071
- issueIndex: grant.issueIndex,
1072
- event: {
1073
- kind: "dispatched",
1074
- dispatch: {
1075
- ownerId: params.dispatchIdentity.ownerId,
1076
- epoch: params.dispatchIdentity.epoch,
1077
- dispatchedAt: now,
1078
- },
1079
- outcome:
1080
- params.outcome.status === "ok"
1081
- ? { status: "ok", dispatchedAt: params.outcome.dispatchedAt }
1082
- : {
1083
- status: "failed",
1084
- code: params.outcome.status === "failed" ? params.outcome.code : "dispatch_failed",
1085
- message: params.outcome.message,
1086
- dispatchedAt: params.outcome.dispatchedAt,
1087
- },
1088
- },
1089
- });
1090
- return { ok: true, value: { grant: dispatchedGrant, receipt: dispatchedReceipt } };
1091
- });
1092
- }
1093
-
1094
- /** Pure read without reconciliation writes; used before actions. */
1095
- async get(elevationRequestId: string): Promise<ElevationResult<ElevationReadValue>> {
1096
- if (!isElevationRequestId(elevationRequestId))
1097
- return elevationFailure("invalid_input", "elevationRequestId must be a UUID");
1098
- return this.#mutate(async () => {
1099
- const grant = this.#byRequestId.get(elevationRequestId);
1100
- if (!grant) return elevationFailure("not_found", "elevation request was not found");
1101
- return { ok: true, value: { grant, receipt: this.#byReceiptRequestId.get(elevationRequestId) } };
1102
- });
1103
- }
1104
-
1105
- /** Truthful read: reconciles expiry and crash-state claims before returning. */
1106
- async resolve(
1107
- elevationRequestId: string,
1108
- claimIdentity: ElevationClaimIdentity,
1109
- ): Promise<ElevationResult<ElevationReadValue>> {
1110
- if (!isElevationRequestId(elevationRequestId))
1111
- return elevationFailure("invalid_input", "elevationRequestId must be a UUID");
1112
- if (!isElevationClaimIdentity(claimIdentity))
1113
- return elevationFailure("invalid_input", "claimIdentity is malformed");
1114
- return this.#mutate(async () => {
1115
- const grant = this.#byRequestId.get(elevationRequestId);
1116
- if (!grant) return elevationFailure("not_found", "elevation request was not found");
1117
- const receipt = this.#byReceiptRequestId.get(elevationRequestId);
1118
- await this.#reconcile(grant, receipt, claimIdentity, this.#now());
1119
- const current = this.#byRequestId.get(elevationRequestId) ?? grant;
1120
- return { ok: true, value: { grant: current, receipt: this.#byReceiptRequestId.get(elevationRequestId) } };
1121
- });
1122
- }
1123
-
1124
- /** Truthful listing: reconciles every grant before returning. */
1125
- async list(claimIdentity: ElevationClaimIdentity): Promise<ElevationResult<ElevationListValue>> {
1126
- if (!isElevationClaimIdentity(claimIdentity))
1127
- return elevationFailure("invalid_input", "claimIdentity is malformed");
1128
- return this.#mutate(async () => {
1129
- const now = this.#now();
1130
- const items: ElevationReadValue[] = [];
1131
- for (const grant of this.#byRequestId.values()) {
1132
- const receipt = this.#byReceiptRequestId.get(grant.elevationRequestId);
1133
- await this.#reconcile(grant, receipt, claimIdentity, now);
1134
- const current = this.#byRequestId.get(grant.elevationRequestId) ?? grant;
1135
- items.push({ grant: current, receipt: this.#byReceiptRequestId.get(grant.elevationRequestId) });
1136
- }
1137
- return { ok: true, value: { grants: items } };
1138
- });
1139
- }
1140
- }
1141
-
1142
- export { isElevationGrantRecord, isElevationOperation };