@vellumai/assistant 0.4.26 → 0.4.30

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 (1360) hide show
  1. package/.env.example +2 -2
  2. package/AGENTS.md +5 -0
  3. package/ARCHITECTURE.md +207 -105
  4. package/Dockerfile +1 -1
  5. package/README.md +111 -113
  6. package/bun.lock +0 -3
  7. package/docs/architecture/integrations.md +0 -1
  8. package/docs/architecture/memory.md +100 -63
  9. package/docs/error-handling.md +71 -0
  10. package/docs/runbook-trusted-contacts.md +89 -52
  11. package/docs/trusted-contact-access.md +48 -46
  12. package/package.json +3 -3
  13. package/scripts/compare-benchmarks.sh +12 -5
  14. package/scripts/ipc/check-swift-decoder-drift.ts +5 -3
  15. package/scripts/test.sh +89 -5
  16. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +50 -37
  17. package/src/__tests__/access-request-decision.test.ts +0 -1
  18. package/src/__tests__/account-registry.test.ts +1 -1
  19. package/src/__tests__/actor-token-service.test.ts +40 -26
  20. package/src/__tests__/agent-loop-thinking.test.ts +29 -13
  21. package/src/__tests__/agent-loop.test.ts +2 -1
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -1
  23. package/src/__tests__/app-executors.test.ts +7 -17
  24. package/src/__tests__/approval-routes-http.test.ts +2 -2
  25. package/src/__tests__/asset-materialize-tool.test.ts +7 -7
  26. package/src/__tests__/asset-search-tool.test.ts +7 -7
  27. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -10
  28. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  29. package/src/__tests__/browser-skill-endstate.test.ts +10 -1
  30. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +218 -0
  31. package/src/__tests__/call-controller.test.ts +99 -69
  32. package/src/__tests__/call-start-guardian-guard.test.ts +1 -1
  33. package/src/__tests__/channel-approval-routes.test.ts +157 -114
  34. package/src/__tests__/channel-approval.test.ts +8 -0
  35. package/src/__tests__/channel-approvals.test.ts +39 -1
  36. package/src/__tests__/channel-guardian.test.ts +176 -275
  37. package/src/__tests__/channel-readiness-service.test.ts +6 -2
  38. package/src/__tests__/channel-reply-delivery.test.ts +33 -2
  39. package/src/__tests__/channel-retry-sweep.test.ts +14 -14
  40. package/src/__tests__/checker.test.ts +12 -31
  41. package/src/__tests__/claude-code-tool-profiles.test.ts +1 -1
  42. package/src/__tests__/commit-message-enrichment-service.test.ts +71 -59
  43. package/src/__tests__/compaction.benchmark.test.ts +6 -2
  44. package/src/__tests__/computer-use-tools.test.ts +1 -1
  45. package/src/__tests__/config-schema.test.ts +66 -7
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -29
  47. package/src/__tests__/contacts-tools.test.ts +63 -2
  48. package/src/__tests__/context-overflow-approval.test.ts +141 -0
  49. package/src/__tests__/context-overflow-policy.test.ts +171 -0
  50. package/src/__tests__/context-overflow-reducer.test.ts +533 -0
  51. package/src/__tests__/context-window-manager.test.ts +97 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +38 -46
  53. package/src/__tests__/conversation-pairing.test.ts +2 -2
  54. package/src/__tests__/conversation-routes-guardian-reply.test.ts +214 -10
  55. package/src/__tests__/conversation-routes.test.ts +4 -7
  56. package/src/__tests__/credential-broker-browser-fill.test.ts +13 -2
  57. package/src/__tests__/credential-security-e2e.test.ts +1 -1
  58. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  59. package/src/__tests__/credential-vault-unit.test.ts +1 -1
  60. package/src/__tests__/credential-vault.test.ts +11 -8
  61. package/src/__tests__/daemon-lifecycle.test.ts +2 -2
  62. package/src/__tests__/daemon-server-session-init.test.ts +6 -6
  63. package/src/__tests__/delete-managed-skill-tool.test.ts +1 -1
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -2
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +9 -0
  66. package/src/__tests__/emit-signal-routing-intent.test.ts +4 -0
  67. package/src/__tests__/encrypted-store.test.ts +10 -7
  68. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  69. package/src/__tests__/file-edit-tool.test.ts +1 -1
  70. package/src/__tests__/file-read-tool.test.ts +1 -1
  71. package/src/__tests__/file-write-tool.test.ts +1 -1
  72. package/src/__tests__/fixtures/credential-security-fixtures.ts +87 -64
  73. package/src/__tests__/fixtures/media-reuse-fixtures.ts +37 -31
  74. package/src/__tests__/fixtures/mock-signup-server.ts +171 -115
  75. package/src/__tests__/fixtures/proxy-fixtures.ts +39 -39
  76. package/src/__tests__/followup-tools.test.ts +1 -1
  77. package/src/__tests__/gateway-only-guard.test.ts +4 -0
  78. package/src/__tests__/gemini-image-service.test.ts +2 -2
  79. package/src/__tests__/guardian-actions-endpoint.test.ts +543 -1
  80. package/src/__tests__/guardian-control-plane-policy.test.ts +15 -15
  81. package/src/__tests__/guardian-dispatch.test.ts +79 -1
  82. package/src/__tests__/guardian-grant-minting.test.ts +20 -20
  83. package/src/__tests__/guardian-outbound-http.test.ts +1 -2
  84. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -41
  85. package/src/__tests__/guardian-routing-invariants.test.ts +36 -16
  86. package/src/__tests__/guardian-routing-state.test.ts +36 -52
  87. package/src/__tests__/guardian-verification-intent-routing.test.ts +4 -6
  88. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +6 -8
  89. package/src/__tests__/handle-user-message-secret-resume.test.ts +39 -1
  90. package/src/__tests__/handlers-cu-observation-blob.test.ts +21 -10
  91. package/src/__tests__/handlers-telegram-config.test.ts +14 -14
  92. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +23 -2
  93. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  94. package/src/__tests__/headless-browser-navigate.test.ts +1 -1
  95. package/src/__tests__/headless-browser-read-tools.test.ts +1 -1
  96. package/src/__tests__/headless-browser-snapshot.test.ts +1 -1
  97. package/src/__tests__/heartbeat-service.test.ts +45 -2
  98. package/src/__tests__/host-file-edit-tool.test.ts +1 -1
  99. package/src/__tests__/host-file-read-tool.test.ts +1 -1
  100. package/src/__tests__/host-file-write-tool.test.ts +1 -1
  101. package/src/__tests__/host-shell-tool.test.ts +1 -1
  102. package/src/__tests__/inbound-invite-redemption.test.ts +17 -19
  103. package/src/__tests__/ingress-reconcile.test.ts +2 -2
  104. package/src/__tests__/integrations-cli.test.ts +232 -0
  105. package/src/__tests__/intent-routing.test.ts +7 -5
  106. package/src/__tests__/invite-redemption-service.test.ts +5 -4
  107. package/src/__tests__/{ingress-routes-http.test.ts → invite-routes-http.test.ts} +42 -321
  108. package/src/__tests__/ipc-snapshot.test.ts +32 -31
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  110. package/src/__tests__/mcp-cli.test.ts +136 -57
  111. package/src/__tests__/mcp-client-auth.test.ts +95 -0
  112. package/src/__tests__/media-generate-image.test.ts +2 -2
  113. package/src/__tests__/media-reuse-story.e2e.test.ts +8 -8
  114. package/src/__tests__/memory-regressions.test.ts +6 -6
  115. package/src/__tests__/messaging-send-tool.test.ts +1 -1
  116. package/src/__tests__/migration-cross-version-compatibility.test.ts +1855 -0
  117. package/src/__tests__/migration-export-http.test.ts +540 -0
  118. package/src/__tests__/migration-import-commit-http.test.ts +823 -0
  119. package/src/__tests__/migration-import-preflight-http.test.ts +755 -0
  120. package/src/__tests__/migration-parity-persistence.test.ts +1854 -0
  121. package/src/__tests__/migration-transport.test.ts +904 -0
  122. package/src/__tests__/migration-validate-http.test.ts +698 -0
  123. package/src/__tests__/migration-wizard.test.ts +1289 -0
  124. package/src/__tests__/nl-approval-parser.test.ts +305 -0
  125. package/src/__tests__/non-member-access-request.test.ts +17 -17
  126. package/src/__tests__/notification-decision-strategy.test.ts +110 -2
  127. package/src/__tests__/notification-deep-link.test.ts +18 -0
  128. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  129. package/src/__tests__/oauth-provider-profiles.test.ts +34 -0
  130. package/src/__tests__/oauth2-gateway-transport.test.ts +1 -1
  131. package/src/__tests__/playbook-execution.test.ts +1 -1
  132. package/src/__tests__/playbook-tools.test.ts +1 -1
  133. package/src/__tests__/provider-error-scenarios.test.ts +68 -0
  134. package/src/__tests__/provider-streaming.benchmark.test.ts +3 -1
  135. package/src/__tests__/proxy-approval-callback.test.ts +1 -1
  136. package/src/__tests__/qdrant-manager.test.ts +40 -11
  137. package/src/__tests__/rebind-secrets-screen.test.ts +839 -0
  138. package/src/__tests__/recording-handler.test.ts +2 -2
  139. package/src/__tests__/recording-intent-handler.test.ts +3 -3
  140. package/src/__tests__/recording-state-machine.test.ts +2 -2
  141. package/src/__tests__/relay-server.test.ts +507 -228
  142. package/src/__tests__/reminder-store.test.ts +8 -0
  143. package/src/__tests__/reminder.test.ts +8 -0
  144. package/src/__tests__/{resolve-guardian-trust-class.test.ts → resolve-trust-class.test.ts} +11 -17
  145. package/src/__tests__/retry-after-extraction.test.ts +111 -0
  146. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  147. package/src/__tests__/schedule-tools.test.ts +1 -1
  148. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  149. package/src/__tests__/script-proxy-connect-tunnel.test.ts +2 -3
  150. package/src/__tests__/script-proxy-decision-trace.test.ts +2 -2
  151. package/src/__tests__/script-proxy-http-forwarder.test.ts +1 -1
  152. package/src/__tests__/script-proxy-injection-runtime.test.ts +5 -5
  153. package/src/__tests__/script-proxy-mitm-handler.test.ts +4 -4
  154. package/src/__tests__/script-proxy-policy-runtime.test.ts +2 -2
  155. package/src/__tests__/script-proxy-policy.test.ts +2 -2
  156. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +127 -0
  157. package/src/__tests__/script-proxy-session-manager.test.ts +4 -7
  158. package/src/__tests__/script-proxy-session-runtime.test.ts +1 -6
  159. package/src/__tests__/secret-onetime-send.test.ts +4 -4
  160. package/src/__tests__/secret-scanner-executor.test.ts +2 -2
  161. package/src/__tests__/send-endpoint-busy.test.ts +11 -9
  162. package/src/__tests__/send-notification-tool.test.ts +2 -2
  163. package/src/__tests__/session-abort-tool-results.test.ts +17 -2
  164. package/src/__tests__/session-agent-loop.test.ts +456 -35
  165. package/src/__tests__/session-confirmation-signals.test.ts +3 -2
  166. package/src/__tests__/session-conflict-gate.test.ts +20 -3
  167. package/src/__tests__/session-init.benchmark.test.ts +2 -2
  168. package/src/__tests__/session-load-history-repair.test.ts +7 -7
  169. package/src/__tests__/session-media-retry.test.ts +147 -0
  170. package/src/__tests__/session-pre-run-repair.test.ts +17 -2
  171. package/src/__tests__/session-profile-injection.test.ts +20 -3
  172. package/src/__tests__/session-provider-retry-repair.test.ts +86 -6
  173. package/src/__tests__/session-queue.test.ts +33 -18
  174. package/src/__tests__/session-runtime-assembly.test.ts +147 -1
  175. package/src/__tests__/session-runtime-workspace.test.ts +40 -0
  176. package/src/__tests__/session-slash-known.test.ts +21 -3
  177. package/src/__tests__/session-slash-queue.test.ts +17 -2
  178. package/src/__tests__/session-slash-unknown.test.ts +17 -2
  179. package/src/__tests__/session-surfaces-deselection.test.ts +208 -0
  180. package/src/__tests__/session-workspace-cache-state.test.ts +2 -2
  181. package/src/__tests__/session-workspace-injection.test.ts +17 -2
  182. package/src/__tests__/session-workspace-tool-tracking.test.ts +17 -2
  183. package/src/__tests__/shell-credential-ref.test.ts +1 -1
  184. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  185. package/src/__tests__/skill-feature-flags-integration.test.ts +9 -5
  186. package/src/__tests__/skill-feature-flags.test.ts +18 -12
  187. package/src/__tests__/skill-load-feature-flag.test.ts +5 -4
  188. package/src/__tests__/skill-load-tool.test.ts +1 -1
  189. package/src/__tests__/skill-script-runner-host.test.ts +1 -1
  190. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -1
  191. package/src/__tests__/skill-script-runner.test.ts +1 -1
  192. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  193. package/src/__tests__/slack-block-formatting.test.ts +100 -0
  194. package/src/__tests__/slack-inbound-verification.test.ts +346 -0
  195. package/src/__tests__/slack-reaction-approvals.test.ts +77 -0
  196. package/src/__tests__/slack-skill.test.ts +4 -2
  197. package/src/__tests__/starter-task-flow.test.ts +0 -1
  198. package/src/__tests__/subagent-tools.test.ts +3 -3
  199. package/src/__tests__/swarm-recursion.test.ts +1 -1
  200. package/src/__tests__/swarm-session-integration.test.ts +1 -1
  201. package/src/__tests__/swarm-tool.test.ts +1 -1
  202. package/src/__tests__/task-management-tools.test.ts +1 -1
  203. package/src/__tests__/task-tools.test.ts +1 -1
  204. package/src/__tests__/terminal-tools.test.ts +1 -1
  205. package/src/__tests__/test-support/browser-skill-harness.ts +39 -27
  206. package/src/__tests__/test-support/computer-use-skill-harness.ts +14 -14
  207. package/src/__tests__/tool-approval-handler.test.ts +15 -15
  208. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  209. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  210. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  211. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  212. package/src/__tests__/tool-executor.test.ts +23 -182
  213. package/src/__tests__/tool-grant-request-escalation.test.ts +11 -11
  214. package/src/__tests__/tool-permission-simulate-handler.test.ts +4 -4
  215. package/src/__tests__/transfer-progress-screen.test.ts +1180 -0
  216. package/src/__tests__/trust-context-guards.test.ts +25 -29
  217. package/src/__tests__/trusted-contact-approval-notifier.test.ts +23 -21
  218. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +37 -40
  219. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +29 -25
  220. package/src/__tests__/trusted-contact-multichannel.test.ts +25 -24
  221. package/src/__tests__/trusted-contact-verification.test.ts +64 -76
  222. package/src/__tests__/turn-commit.test.ts +18 -18
  223. package/src/__tests__/twilio-provider.test.ts +7 -7
  224. package/src/__tests__/validation-results-screen.test.ts +1107 -0
  225. package/src/__tests__/view-image-tool.test.ts +1 -1
  226. package/src/__tests__/voice-invite-redemption.test.ts +4 -3
  227. package/src/__tests__/voice-scoped-grant-consumer.test.ts +12 -12
  228. package/src/__tests__/voice-session-bridge.test.ts +24 -24
  229. package/src/agent/attachments.ts +3 -1
  230. package/src/agent/loop.ts +13 -13
  231. package/src/agent/message-types.ts +13 -7
  232. package/src/amazon/cart.ts +59 -32
  233. package/src/amazon/checkout.ts +25 -14
  234. package/src/amazon/client.ts +61 -58
  235. package/src/amazon/product-details.ts +3 -3
  236. package/src/amazon/request-extractor.ts +46 -31
  237. package/src/amazon/search.ts +6 -4
  238. package/src/amazon/session.ts +33 -24
  239. package/src/approvals/AGENTS.md +26 -0
  240. package/src/approvals/approval-primitive.ts +87 -64
  241. package/src/approvals/guardian-decision-primitive.ts +172 -81
  242. package/src/approvals/guardian-request-resolvers.ts +262 -155
  243. package/src/autonomy/autonomy-resolver.ts +7 -5
  244. package/src/autonomy/autonomy-store.ts +34 -19
  245. package/src/autonomy/disposition-mapper.ts +5 -5
  246. package/src/autonomy/index.ts +6 -6
  247. package/src/autonomy/types.ts +7 -3
  248. package/src/browser-extension-relay/client.ts +50 -19
  249. package/src/browser-extension-relay/protocol.ts +11 -11
  250. package/src/browser-extension-relay/server.ts +45 -20
  251. package/src/bundler/app-bundler.ts +75 -50
  252. package/src/bundler/bundle-scanner.ts +145 -41
  253. package/src/bundler/bundle-signer.ts +16 -14
  254. package/src/bundler/signature-verifier.ts +36 -33
  255. package/src/calls/call-constants.ts +10 -3
  256. package/src/calls/call-controller.ts +473 -214
  257. package/src/calls/call-conversation-messages.ts +25 -15
  258. package/src/calls/call-domain.ts +401 -148
  259. package/src/calls/call-pointer-message-composer.ts +26 -21
  260. package/src/calls/call-pointer-messages.ts +52 -28
  261. package/src/calls/call-recovery.ts +53 -37
  262. package/src/calls/call-state-machine.ts +37 -7
  263. package/src/calls/call-state.ts +35 -13
  264. package/src/calls/call-store.ts +165 -77
  265. package/src/calls/elevenlabs-client.ts +39 -20
  266. package/src/calls/guardian-action-sweep.ts +42 -24
  267. package/src/calls/guardian-dispatch.ts +79 -56
  268. package/src/calls/guardian-question-copy.ts +28 -23
  269. package/src/calls/relay-server.ts +1149 -532
  270. package/src/calls/speaker-identification.ts +21 -15
  271. package/src/calls/twilio-config.ts +34 -17
  272. package/src/calls/twilio-provider.ts +108 -55
  273. package/src/calls/twilio-rest.ts +212 -100
  274. package/src/calls/twilio-routes.ts +165 -92
  275. package/src/calls/types.ts +55 -7
  276. package/src/calls/voice-quality.ts +6 -4
  277. package/src/calls/voice-session-bridge.ts +181 -133
  278. package/src/channels/config.ts +18 -14
  279. package/src/channels/types.ts +38 -10
  280. package/src/cli/amazon.ts +333 -227
  281. package/src/cli/config-commands.ts +236 -146
  282. package/src/cli/core-commands.ts +403 -329
  283. package/src/cli/email-guardrails.ts +38 -19
  284. package/src/cli/email.ts +207 -153
  285. package/src/cli/influencer.ts +58 -56
  286. package/src/cli/integrations.ts +306 -0
  287. package/src/cli/ipc-client.ts +24 -19
  288. package/src/cli/map.ts +176 -129
  289. package/src/cli/mcp.ts +260 -152
  290. package/src/cli/sequence.ts +165 -107
  291. package/src/cli/twitter.ts +302 -218
  292. package/src/cli.ts +418 -279
  293. package/src/commands/cc-command-registry.ts +52 -27
  294. package/src/config/agent-schema.ts +217 -134
  295. package/src/config/assistant-feature-flags.ts +23 -18
  296. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +19 -0
  297. package/src/config/bundled-skills/app-builder/SKILL.md +193 -1500
  298. package/src/config/bundled-skills/app-builder/TOOLS.json +70 -18
  299. package/src/config/bundled-skills/app-builder/tools/app-create.ts +7 -4
  300. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +6 -3
  301. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +7 -4
  302. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +6 -3
  303. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +6 -3
  304. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +7 -4
  305. package/src/config/bundled-skills/app-builder/tools/app-list.ts +6 -3
  306. package/src/config/bundled-skills/app-builder/tools/app-query.ts +6 -3
  307. package/src/config/bundled-skills/app-builder/tools/app-update.ts +6 -3
  308. package/src/config/bundled-skills/browser/TOOLS.json +59 -2
  309. package/src/config/bundled-skills/browser/tools/browser-click.ts +5 -2
  310. package/src/config/bundled-skills/browser/tools/browser-close.ts +5 -2
  311. package/src/config/bundled-skills/browser/tools/browser-extract.ts +5 -2
  312. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +5 -2
  313. package/src/config/bundled-skills/browser/tools/browser-hover.ts +5 -2
  314. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +5 -2
  315. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +5 -2
  316. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +5 -2
  317. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +5 -2
  318. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +5 -2
  319. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +5 -2
  320. package/src/config/bundled-skills/browser/tools/browser-type.ts +5 -2
  321. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +13 -6
  322. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +5 -2
  323. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +4 -0
  324. package/src/config/bundled-skills/claude-code/TOOLS.json +4 -0
  325. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +5 -2
  326. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  327. package/src/config/bundled-skills/computer-use/TOOLS.json +50 -2
  328. package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +6 -3
  329. package/src/config/bundled-skills/computer-use/tools/computer-use-done.ts +6 -3
  330. package/src/config/bundled-skills/computer-use/tools/computer-use-double-click.ts +10 -3
  331. package/src/config/bundled-skills/computer-use/tools/computer-use-drag.ts +6 -3
  332. package/src/config/bundled-skills/computer-use/tools/computer-use-key.ts +6 -3
  333. package/src/config/bundled-skills/computer-use/tools/computer-use-open-app.ts +6 -3
  334. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +10 -3
  335. package/src/config/bundled-skills/computer-use/tools/computer-use-respond.ts +6 -3
  336. package/src/config/bundled-skills/computer-use/tools/computer-use-right-click.ts +10 -3
  337. package/src/config/bundled-skills/computer-use/tools/computer-use-run-applescript.ts +10 -3
  338. package/src/config/bundled-skills/computer-use/tools/computer-use-scroll.ts +6 -3
  339. package/src/config/bundled-skills/computer-use/tools/computer-use-type-text.ts +6 -3
  340. package/src/config/bundled-skills/computer-use/tools/computer-use-wait.ts +6 -3
  341. package/src/config/bundled-skills/configure-settings/SKILL.md +28 -14
  342. package/src/config/bundled-skills/contacts/SKILL.md +453 -15
  343. package/src/config/bundled-skills/contacts/TOOLS.json +22 -2
  344. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +79 -20
  345. package/src/config/bundled-skills/contacts/tools/contact-search.ts +55 -18
  346. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +64 -19
  347. package/src/config/bundled-skills/document/TOOLS.json +8 -0
  348. package/src/config/bundled-skills/document/tools/document-create.ts +5 -2
  349. package/src/config/bundled-skills/document/tools/document-update.ts +5 -2
  350. package/src/config/bundled-skills/doordash/doordash-cli.ts +17 -7
  351. package/src/config/bundled-skills/email-setup/SKILL.md +12 -9
  352. package/src/config/bundled-skills/followups/TOOLS.json +12 -0
  353. package/src/config/bundled-skills/followups/tools/followup-create.ts +5 -2
  354. package/src/config/bundled-skills/followups/tools/followup-list.ts +5 -2
  355. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +5 -2
  356. package/src/config/bundled-skills/google-calendar/TOOLS.json +124 -26
  357. package/src/config/bundled-skills/google-calendar/calendar-client.ts +44 -32
  358. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +11 -5
  359. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +13 -7
  360. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +11 -5
  361. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +13 -7
  362. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +28 -12
  363. package/src/config/bundled-skills/google-calendar/tools/shared.ts +6 -4
  364. package/src/config/bundled-skills/google-calendar/types.ts +3 -3
  365. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +88 -33
  366. package/src/config/bundled-skills/image-studio/TOOLS.json +12 -2
  367. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +48 -25
  368. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +13 -3
  369. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +60 -35
  370. package/src/config/bundled-skills/mcp-setup/SKILL.md +75 -0
  371. package/src/config/bundled-skills/media-processing/SKILL.md +55 -15
  372. package/src/config/bundled-skills/media-processing/TOOLS.json +48 -2
  373. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +12 -10
  374. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +34 -19
  375. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +82 -66
  376. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +148 -0
  377. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +1 -1
  378. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +8 -3
  379. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +117 -53
  380. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +273 -0
  381. package/src/config/bundled-skills/media-processing/services/preprocess.ts +185 -97
  382. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +32 -27
  383. package/src/config/bundled-skills/media-processing/services/reduce.ts +101 -24
  384. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +121 -55
  385. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +58 -24
  386. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +198 -92
  387. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +98 -70
  388. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +59 -19
  389. package/src/config/bundled-skills/media-processing/tools/media-status.ts +26 -10
  390. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +29 -14
  391. package/src/config/bundled-skills/messaging/SKILL.md +7 -5
  392. package/src/config/bundled-skills/messaging/TOOLS.json +232 -186
  393. package/src/config/bundled-skills/messaging/tools/gmail-archive-by-query.ts +31 -13
  394. package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +16 -10
  395. package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +18 -9
  396. package/src/config/bundled-skills/messaging/tools/gmail-download-attachment.ts +23 -16
  397. package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +28 -12
  398. package/src/config/bundled-skills/messaging/tools/gmail-filters.ts +41 -21
  399. package/src/config/bundled-skills/messaging/tools/gmail-follow-up.ts +44 -23
  400. package/src/config/bundled-skills/messaging/tools/gmail-forward.ts +73 -33
  401. package/src/config/bundled-skills/messaging/tools/gmail-label.ts +15 -9
  402. package/src/config/bundled-skills/messaging/tools/gmail-list-attachments.ts +22 -14
  403. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +99 -50
  404. package/src/config/bundled-skills/messaging/tools/gmail-send-draft.ts +14 -8
  405. package/src/config/bundled-skills/messaging/tools/gmail-send-with-attachments.ts +63 -44
  406. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +90 -46
  407. package/src/config/bundled-skills/messaging/tools/gmail-summarize-thread.ts +43 -22
  408. package/src/config/bundled-skills/messaging/tools/gmail-trash.ts +15 -9
  409. package/src/config/bundled-skills/messaging/tools/gmail-triage.ts +51 -22
  410. package/src/config/bundled-skills/messaging/tools/gmail-unsubscribe.ts +62 -26
  411. package/src/config/bundled-skills/messaging/tools/gmail-vacation.ts +34 -19
  412. package/src/config/bundled-skills/messaging/tools/google-contacts.ts +32 -16
  413. package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +10 -4
  414. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +91 -47
  415. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +21 -9
  416. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +9 -3
  417. package/src/config/bundled-skills/messaging/tools/messaging-draft.ts +30 -17
  418. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +10 -4
  419. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +14 -6
  420. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +16 -5
  421. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +63 -36
  422. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +10 -4
  423. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +30 -12
  424. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +48 -29
  425. package/src/config/bundled-skills/messaging/tools/scan-result-store.ts +20 -6
  426. package/src/config/bundled-skills/messaging/tools/send-notification.ts +1 -1
  427. package/src/config/bundled-skills/messaging/tools/sequence-analytics.ts +59 -22
  428. package/src/config/bundled-skills/messaging/tools/sequence-cancel.ts +13 -7
  429. package/src/config/bundled-skills/messaging/tools/sequence-create.ts +27 -12
  430. package/src/config/bundled-skills/messaging/tools/sequence-delete.ts +14 -6
  431. package/src/config/bundled-skills/messaging/tools/sequence-enroll.ts +30 -11
  432. package/src/config/bundled-skills/messaging/tools/sequence-enrollment-list.ts +16 -8
  433. package/src/config/bundled-skills/messaging/tools/sequence-get.ts +31 -13
  434. package/src/config/bundled-skills/messaging/tools/sequence-import.ts +38 -22
  435. package/src/config/bundled-skills/messaging/tools/sequence-list.ts +16 -7
  436. package/src/config/bundled-skills/messaging/tools/sequence-pause.ts +29 -10
  437. package/src/config/bundled-skills/messaging/tools/sequence-resume.ts +16 -8
  438. package/src/config/bundled-skills/messaging/tools/sequence-update.ts +35 -16
  439. package/src/config/bundled-skills/messaging/tools/shared.ts +26 -12
  440. package/src/config/bundled-skills/notifications/SKILL.md +3 -2
  441. package/src/config/bundled-skills/notifications/TOOLS.json +7 -13
  442. package/src/config/bundled-skills/notifications/tools/send-notification.ts +69 -34
  443. package/src/config/bundled-skills/notifications/tools/shared.ts +1 -1
  444. package/src/config/bundled-skills/phone-calls/SKILL.md +46 -48
  445. package/src/config/bundled-skills/phone-calls/TOOLS.json +13 -1
  446. package/src/config/bundled-skills/phone-calls/tools/call-end.ts +1 -1
  447. package/src/config/bundled-skills/phone-calls/tools/call-start.ts +1 -1
  448. package/src/config/bundled-skills/phone-calls/tools/call-status.ts +1 -1
  449. package/src/config/bundled-skills/playbooks/TOOLS.json +16 -0
  450. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +91 -51
  451. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +30 -16
  452. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +66 -27
  453. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +89 -42
  454. package/src/config/bundled-skills/public-ingress/SKILL.md +26 -19
  455. package/src/config/bundled-skills/reminder/TOOLS.json +15 -2
  456. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +5 -2
  457. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +5 -2
  458. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +5 -2
  459. package/src/config/bundled-skills/schedule/SKILL.md +33 -15
  460. package/src/config/bundled-skills/schedule/TOOLS.json +17 -1
  461. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +5 -2
  462. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +5 -2
  463. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +5 -2
  464. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +5 -2
  465. package/src/config/bundled-skills/screen-recording/SKILL.md +11 -3
  466. package/src/config/bundled-skills/self-upgrade/SKILL.md +9 -8
  467. package/src/config/bundled-skills/slack/SKILL.md +30 -1
  468. package/src/config/bundled-skills/slack/TOOLS.json +122 -17
  469. package/src/config/bundled-skills/slack/tools/shared.ts +7 -5
  470. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +11 -5
  471. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +11 -5
  472. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +146 -0
  473. package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +46 -16
  474. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +11 -5
  475. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +28 -0
  476. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +12 -6
  477. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +120 -0
  478. package/src/config/bundled-skills/slack-app-setup/SKILL.md +200 -0
  479. package/src/config/bundled-skills/sms-setup/SKILL.md +5 -8
  480. package/src/config/bundled-skills/subagent/TOOLS.json +22 -2
  481. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +5 -2
  482. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +5 -2
  483. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +5 -2
  484. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +5 -2
  485. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +5 -2
  486. package/src/config/bundled-skills/tasks/TOOLS.json +86 -14
  487. package/src/config/bundled-skills/tasks/tools/task-delete.ts +5 -2
  488. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +5 -2
  489. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +5 -2
  490. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +5 -2
  491. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +5 -2
  492. package/src/config/bundled-skills/tasks/tools/task-list.ts +5 -2
  493. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +5 -2
  494. package/src/config/bundled-skills/tasks/tools/task-run.ts +5 -2
  495. package/src/config/bundled-skills/tasks/tools/task-save.ts +5 -2
  496. package/src/config/bundled-skills/telegram-setup/SKILL.md +7 -8
  497. package/src/config/bundled-skills/transcribe/TOOLS.json +4 -0
  498. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +232 -127
  499. package/src/config/bundled-skills/twilio-setup/SKILL.md +7 -12
  500. package/src/config/bundled-skills/twitter/SKILL.md +19 -2
  501. package/src/config/bundled-skills/voice-setup/SKILL.md +5 -5
  502. package/src/config/bundled-skills/watcher/TOOLS.json +20 -0
  503. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +5 -2
  504. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +5 -2
  505. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +5 -2
  506. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +5 -2
  507. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +5 -2
  508. package/src/config/bundled-skills/weather/TOOLS.json +4 -0
  509. package/src/config/bundled-skills/weather/tools/get-weather.ts +5 -2
  510. package/src/config/bundled-tool-registry.ts +2 -0
  511. package/src/config/calls-schema.ts +108 -63
  512. package/src/config/channel-permission-profiles.ts +155 -0
  513. package/src/config/computer-use-prompt.ts +7 -7
  514. package/src/config/core-schema.ts +239 -155
  515. package/src/config/defaults.ts +2 -2
  516. package/src/config/elevenlabs-schema.ts +15 -15
  517. package/src/config/env-registry.ts +33 -33
  518. package/src/config/env.ts +4 -1
  519. package/src/config/feature-flag-registry.json +31 -7
  520. package/src/config/loader.ts +118 -58
  521. package/src/config/mcp-schema.ts +29 -15
  522. package/src/config/memory-schema.ts +434 -229
  523. package/src/config/notifications-schema.ts +4 -4
  524. package/src/config/sandbox-schema.ts +2 -2
  525. package/src/config/schema.ts +12 -2
  526. package/src/config/skill-state.ts +27 -15
  527. package/src/config/skills-schema.ts +72 -23
  528. package/src/config/skills.ts +303 -143
  529. package/src/config/system-prompt.ts +25 -6
  530. package/src/config/types.ts +1 -1
  531. package/src/config/update-bulletin-format.ts +3 -3
  532. package/src/config/update-bulletin-state.ts +15 -6
  533. package/src/config/update-bulletin-template-path.ts +8 -4
  534. package/src/config/update-bulletin.ts +33 -14
  535. package/src/config/user-reference.ts +8 -8
  536. package/src/contacts/contact-events.ts +21 -0
  537. package/src/contacts/contact-store.ts +813 -100
  538. package/src/contacts/contacts-write.ts +287 -0
  539. package/src/contacts/index.ts +13 -4
  540. package/src/contacts/startup-migration.ts +21 -0
  541. package/src/contacts/types.ts +73 -2
  542. package/src/context/token-estimator.ts +54 -31
  543. package/src/context/tool-result-truncation.ts +41 -7
  544. package/src/context/window-manager.ts +225 -120
  545. package/src/daemon/approval-generators.ts +83 -55
  546. package/src/daemon/approved-devices-store.ts +33 -20
  547. package/src/daemon/assistant-attachments.ts +157 -101
  548. package/src/daemon/auth-manager.ts +17 -15
  549. package/src/daemon/classifier.ts +117 -46
  550. package/src/daemon/computer-use-session.ts +316 -187
  551. package/src/daemon/config-watcher.ts +91 -44
  552. package/src/daemon/connection-policy.ts +18 -10
  553. package/src/daemon/context-overflow-approval.ts +48 -0
  554. package/src/daemon/context-overflow-policy.ts +50 -0
  555. package/src/daemon/context-overflow-reducer.ts +300 -0
  556. package/src/daemon/daemon-control.ts +79 -51
  557. package/src/daemon/date-context.ts +119 -69
  558. package/src/daemon/dictation-profile-store.ts +94 -48
  559. package/src/daemon/dictation-text-processing.ts +33 -12
  560. package/src/daemon/doordash-steps.ts +92 -49
  561. package/src/daemon/guardian-action-generators.ts +62 -46
  562. package/src/daemon/guardian-verification-intent.ts +35 -19
  563. package/src/daemon/handlers/apps.ts +258 -113
  564. package/src/daemon/handlers/avatar.ts +20 -15
  565. package/src/daemon/handlers/computer-use.ts +82 -39
  566. package/src/daemon/handlers/config-channels.ts +146 -69
  567. package/src/daemon/handlers/config-heartbeat.ts +114 -59
  568. package/src/daemon/handlers/config-inbox.ts +213 -160
  569. package/src/daemon/handlers/config-ingress.ts +127 -55
  570. package/src/daemon/handlers/config-integrations.ts +145 -88
  571. package/src/daemon/handlers/config-model.ts +58 -22
  572. package/src/daemon/handlers/config-platform.ts +40 -16
  573. package/src/daemon/handlers/config-scheduling.ts +109 -48
  574. package/src/daemon/handlers/config-slack-channel.ts +67 -35
  575. package/src/daemon/handlers/config-slack.ts +21 -20
  576. package/src/daemon/handlers/config-telegram.ts +100 -70
  577. package/src/daemon/handlers/config-tools.ts +103 -55
  578. package/src/daemon/handlers/config-trust.ts +50 -20
  579. package/src/daemon/handlers/config.ts +72 -24
  580. package/src/daemon/handlers/contacts.ts +163 -0
  581. package/src/daemon/handlers/diagnostics.ts +90 -48
  582. package/src/daemon/handlers/documents.ts +74 -46
  583. package/src/daemon/handlers/guardian-actions.ts +57 -77
  584. package/src/daemon/handlers/home-base.ts +19 -16
  585. package/src/daemon/handlers/identity.ts +65 -45
  586. package/src/daemon/handlers/index.ts +78 -54
  587. package/src/daemon/handlers/misc.ts +664 -234
  588. package/src/daemon/handlers/navigate-settings.ts +14 -11
  589. package/src/daemon/handlers/oauth-connect.ts +48 -35
  590. package/src/daemon/handlers/open-bundle-handler.ts +31 -24
  591. package/src/daemon/handlers/pairing.ts +51 -25
  592. package/src/daemon/handlers/publish.ts +55 -33
  593. package/src/daemon/handlers/recording.ts +378 -162
  594. package/src/daemon/handlers/sessions.ts +922 -423
  595. package/src/daemon/handlers/shared.ts +202 -117
  596. package/src/daemon/handlers/signing.ts +25 -6
  597. package/src/daemon/handlers/subagents.ts +117 -56
  598. package/src/daemon/handlers/twitter-auth.ts +70 -49
  599. package/src/daemon/handlers/work-items.ts +264 -112
  600. package/src/daemon/handlers/workspace-files.ts +27 -20
  601. package/src/daemon/handlers.ts +2 -2
  602. package/src/daemon/history-repair.ts +16 -15
  603. package/src/daemon/identity-helpers.ts +4 -4
  604. package/src/daemon/install-cli-launchers.ts +33 -22
  605. package/src/daemon/ipc-blob-store.ts +38 -24
  606. package/src/daemon/ipc-contract/apps.ts +61 -50
  607. package/src/daemon/ipc-contract/computer-use.ts +47 -37
  608. package/src/daemon/ipc-contract/contacts.ts +69 -0
  609. package/src/daemon/ipc-contract/diagnostics.ts +14 -14
  610. package/src/daemon/ipc-contract/documents.ts +8 -8
  611. package/src/daemon/ipc-contract/guardian-actions.ts +4 -4
  612. package/src/daemon/ipc-contract/inbox.ts +12 -71
  613. package/src/daemon/ipc-contract/integrations.ts +57 -44
  614. package/src/daemon/ipc-contract/memory.ts +3 -5
  615. package/src/daemon/ipc-contract/messages.ts +95 -69
  616. package/src/daemon/ipc-contract/notifications.ts +10 -6
  617. package/src/daemon/ipc-contract/pairing.ts +8 -8
  618. package/src/daemon/ipc-contract/schedules.ts +20 -20
  619. package/src/daemon/ipc-contract/sessions.ts +89 -57
  620. package/src/daemon/ipc-contract/settings.ts +12 -7
  621. package/src/daemon/ipc-contract/shared.ts +9 -7
  622. package/src/daemon/ipc-contract/skills.ts +46 -26
  623. package/src/daemon/ipc-contract/subagents.ts +9 -9
  624. package/src/daemon/ipc-contract/surfaces.ts +0 -1
  625. package/src/daemon/ipc-contract/trust.ts +11 -11
  626. package/src/daemon/ipc-contract/work-items.ts +33 -28
  627. package/src/daemon/ipc-contract/workspace.ts +28 -21
  628. package/src/daemon/ipc-contract-inventory.json +10 -4
  629. package/src/daemon/ipc-contract-inventory.ts +29 -26
  630. package/src/daemon/ipc-contract.ts +111 -44
  631. package/src/daemon/ipc-handler.ts +27 -19
  632. package/src/daemon/ipc-protocol.ts +22 -12
  633. package/src/daemon/ipc-validate.ts +91 -46
  634. package/src/daemon/lifecycle.ts +39 -3
  635. package/src/daemon/main.ts +10 -8
  636. package/src/daemon/media-visibility-policy.ts +3 -1
  637. package/src/daemon/pairing-store.ts +72 -40
  638. package/src/daemon/providers-setup.ts +35 -25
  639. package/src/daemon/recording-executor.ts +37 -30
  640. package/src/daemon/recording-intent-fallback.ts +58 -28
  641. package/src/daemon/recording-intent.ts +71 -61
  642. package/src/daemon/ride-shotgun-handler.ts +201 -121
  643. package/src/daemon/seed-files.ts +28 -17
  644. package/src/daemon/server.ts +23 -14
  645. package/src/daemon/session-agent-loop-handlers.ts +270 -135
  646. package/src/daemon/session-agent-loop.ts +796 -253
  647. package/src/daemon/session-attachments.ts +109 -40
  648. package/src/daemon/session-conflict-gate.ts +72 -28
  649. package/src/daemon/session-dynamic-profile.ts +36 -22
  650. package/src/daemon/session-error.ts +68 -45
  651. package/src/daemon/session-evictor.ts +17 -10
  652. package/src/daemon/session-history.ts +201 -89
  653. package/src/daemon/session-lifecycle.ts +80 -44
  654. package/src/daemon/session-media-retry.ts +104 -42
  655. package/src/daemon/session-memory.ts +77 -55
  656. package/src/daemon/session-messaging.ts +261 -111
  657. package/src/daemon/session-notifiers.ts +57 -45
  658. package/src/daemon/session-process.ts +370 -154
  659. package/src/daemon/session-queue-manager.ts +30 -13
  660. package/src/daemon/session-runtime-assembly.ts +61 -15
  661. package/src/daemon/session-skill-tools.ts +84 -36
  662. package/src/daemon/session-slash.ts +178 -113
  663. package/src/daemon/session-surfaces.ts +498 -212
  664. package/src/daemon/session-tool-setup.ts +24 -16
  665. package/src/daemon/session-usage.ts +26 -13
  666. package/src/daemon/session-workspace.ts +7 -4
  667. package/src/daemon/session.ts +18 -19
  668. package/src/daemon/shutdown-handlers.ts +36 -33
  669. package/src/daemon/tls-certs.ts +90 -57
  670. package/src/daemon/tool-side-effects.ts +97 -65
  671. package/src/daemon/trace-emitter.ts +8 -7
  672. package/src/daemon/video-thumbnail.ts +55 -25
  673. package/src/daemon/watch-handler.ts +164 -86
  674. package/src/email/provider.ts +1 -1
  675. package/src/email/providers/agentmail.ts +87 -45
  676. package/src/email/providers/index.ts +19 -14
  677. package/src/email/service.ts +52 -24
  678. package/src/email/types.ts +2 -2
  679. package/src/errors.ts +1 -1
  680. package/src/events/bus.ts +30 -10
  681. package/src/events/domain-events.ts +20 -13
  682. package/src/events/index.ts +6 -6
  683. package/src/events/tool-audit-listener.ts +34 -20
  684. package/src/events/tool-domain-event-publisher.ts +22 -20
  685. package/src/events/tool-metrics-listener.ts +26 -21
  686. package/src/events/tool-notification-listener.ts +5 -5
  687. package/src/events/tool-profiling-listener.ts +33 -23
  688. package/src/events/tool-trace-listener.ts +70 -46
  689. package/src/export/formatter.ts +38 -32
  690. package/src/followups/followup-store.ts +43 -36
  691. package/src/followups/index.ts +2 -2
  692. package/src/followups/types.ts +1 -1
  693. package/src/gallery/default-gallery.ts +37 -34
  694. package/src/gallery/gallery-manifest.ts +9 -9
  695. package/src/heartbeat/heartbeat-service.ts +59 -37
  696. package/src/home-base/app-link-store.ts +14 -12
  697. package/src/home-base/bootstrap.ts +14 -8
  698. package/src/home-base/prebuilt/seed.ts +34 -26
  699. package/src/home-base/prebuilt-home-base-updater.ts +14 -8
  700. package/src/hooks/cli.ts +56 -43
  701. package/src/hooks/config.ts +27 -14
  702. package/src/hooks/discovery.ts +53 -33
  703. package/src/hooks/manager.ts +50 -26
  704. package/src/hooks/runner.ts +35 -29
  705. package/src/hooks/templates.ts +38 -15
  706. package/src/hooks/types.ts +13 -13
  707. package/src/inbound/platform-callback-registration.ts +21 -15
  708. package/src/inbound/public-ingress-urls.ts +9 -6
  709. package/src/index.ts +20 -19
  710. package/src/influencer/client.ts +261 -117
  711. package/src/instrument.ts +3 -1
  712. package/src/logfire.ts +64 -39
  713. package/src/mcp/client.ts +107 -55
  714. package/src/mcp/manager.ts +45 -18
  715. package/src/mcp/mcp-oauth-provider.ts +114 -62
  716. package/src/media/gemini-image-service.ts +75 -23
  717. package/src/memory/account-store.ts +16 -9
  718. package/src/memory/admin.ts +87 -57
  719. package/src/memory/app-git-service.ts +77 -47
  720. package/src/memory/app-store.ts +148 -78
  721. package/src/memory/attachments-store.ts +123 -53
  722. package/src/memory/canonical-guardian-store.ts +190 -48
  723. package/src/memory/channel-delivery-store.ts +5 -5
  724. package/src/memory/channel-guardian-store.ts +31 -16
  725. package/src/memory/checkpoints.ts +14 -7
  726. package/src/memory/clarification-resolver.ts +219 -104
  727. package/src/memory/conflict-intent.ts +74 -23
  728. package/src/memory/conflict-policy.ts +20 -7
  729. package/src/memory/conflict-store.ts +144 -94
  730. package/src/memory/contradiction-checker.ts +257 -132
  731. package/src/memory/conversation-attention-store.ts +74 -32
  732. package/src/memory/conversation-bootstrap.ts +28 -0
  733. package/src/memory/conversation-crud.ts +12 -5
  734. package/src/memory/conversation-display-order-migration.ts +7 -7
  735. package/src/memory/conversation-key-store.ts +18 -13
  736. package/src/memory/conversation-queries.ts +130 -52
  737. package/src/memory/conversation-store.ts +43 -26
  738. package/src/memory/conversation-title-service.ts +89 -66
  739. package/src/memory/db-init.ts +94 -2
  740. package/src/memory/db.ts +10 -3
  741. package/src/memory/delivery-channels.ts +12 -6
  742. package/src/memory/delivery-crud.ts +26 -12
  743. package/src/memory/delivery-status.ts +19 -16
  744. package/src/memory/embedding-backend.ts +205 -77
  745. package/src/memory/embedding-gemini.ts +23 -10
  746. package/src/memory/embedding-local.ts +89 -44
  747. package/src/memory/embedding-ollama.ts +25 -13
  748. package/src/memory/embedding-openai.ts +20 -11
  749. package/src/memory/embedding-runtime-manager.ts +163 -90
  750. package/src/memory/entity-extractor.ts +185 -123
  751. package/src/memory/external-conversation-store.ts +30 -12
  752. package/src/memory/fingerprint.ts +2 -2
  753. package/src/memory/fts-reconciler.ts +57 -28
  754. package/src/memory/guardian-action-store.ts +162 -100
  755. package/src/memory/guardian-approvals.ts +63 -129
  756. package/src/memory/guardian-rate-limits.ts +20 -9
  757. package/src/memory/guardian-verification.ts +82 -35
  758. package/src/memory/indexer.ts +96 -55
  759. package/src/memory/{ingress-invite-store.ts → invite-store.ts} +28 -169
  760. package/src/memory/items-extractor.ts +313 -157
  761. package/src/memory/job-handlers/backfill.ts +116 -63
  762. package/src/memory/job-handlers/cleanup.ts +64 -41
  763. package/src/memory/job-handlers/conflict.ts +90 -49
  764. package/src/memory/job-handlers/embedding.ts +32 -17
  765. package/src/memory/job-handlers/extraction.ts +58 -33
  766. package/src/memory/job-handlers/index-maintenance.ts +31 -17
  767. package/src/memory/job-handlers/media-processing.ts +65 -24
  768. package/src/memory/job-handlers/summarization.ts +186 -128
  769. package/src/memory/job-utils.ts +100 -57
  770. package/src/memory/jobs-store.ts +235 -142
  771. package/src/memory/jobs-worker.ts +167 -83
  772. package/src/memory/llm-request-log-store.ts +13 -11
  773. package/src/memory/llm-usage-store.ts +35 -26
  774. package/src/memory/media-store.ts +151 -44
  775. package/src/memory/message-content.ts +28 -18
  776. package/src/memory/migrations/001-job-deferrals.ts +11 -5
  777. package/src/memory/migrations/002-tool-invocations-fk.ts +14 -6
  778. package/src/memory/migrations/003-memory-fts-backfill.ts +11 -5
  779. package/src/memory/migrations/004-entity-relation-dedup.ts +17 -11
  780. package/src/memory/migrations/005-fingerprint-scope-unique.ts +36 -21
  781. package/src/memory/migrations/006-scope-salted-fingerprints.ts +35 -20
  782. package/src/memory/migrations/007-assistant-id-to-self.ts +40 -27
  783. package/src/memory/migrations/008-remove-assistant-id-columns.ts +58 -36
  784. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +36 -22
  785. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +21 -11
  786. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +30 -15
  787. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +4 -2
  788. package/src/memory/migrations/013-guardian-action-tables.ts +29 -11
  789. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +35 -21
  790. package/src/memory/migrations/015-drop-active-search-index.ts +17 -11
  791. package/src/memory/migrations/016-memory-segments-indexes.ts +7 -3
  792. package/src/memory/migrations/017-memory-items-indexes.ts +4 -2
  793. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -5
  794. package/src/memory/migrations/019-notification-tables-schema-migration.ts +34 -20
  795. package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +87 -53
  796. package/src/memory/migrations/021-conversation-status-indexes.ts +7 -3
  797. package/src/memory/migrations/022-add-origin-interface.ts +4 -2
  798. package/src/memory/migrations/023-memory-item-sources-indexes.ts +4 -2
  799. package/src/memory/migrations/024-embedding-vector-blob.ts +34 -18
  800. package/src/memory/migrations/025-messages-fts-backfill.ts +11 -5
  801. package/src/memory/migrations/026-guardian-verification-sessions.ts +80 -14
  802. package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +42 -26
  803. package/src/memory/migrations/027-notification-delivery-pairing-columns.ts +22 -8
  804. package/src/memory/migrations/027a-guardian-bootstrap-token.ts +11 -3
  805. package/src/memory/migrations/028-call-session-mode.ts +13 -3
  806. package/src/memory/migrations/028-notification-delivery-client-ack.ts +22 -8
  807. package/src/memory/migrations/029-channel-inbound-delivered-segments.ts +7 -3
  808. package/src/memory/migrations/030-guardian-action-followup.ts +46 -8
  809. package/src/memory/migrations/030-guardian-verification-purpose.ts +4 -2
  810. package/src/memory/migrations/031-conversations-thread-type-index.ts +4 -2
  811. package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +4 -2
  812. package/src/memory/migrations/032-notification-delivery-thread-decision.ts +22 -8
  813. package/src/memory/migrations/033-scoped-approval-grants.ts +1 -1
  814. package/src/memory/migrations/034-guardian-action-tool-metadata.ts +15 -3
  815. package/src/memory/migrations/035-guardian-action-supersession.ts +15 -3
  816. package/src/memory/migrations/036-normalize-phone-identities.ts +101 -87
  817. package/src/memory/migrations/037-voice-invite-columns.ts +22 -4
  818. package/src/memory/migrations/038-actor-token-records.ts +5 -9
  819. package/src/memory/migrations/039-actor-refresh-token-records.ts +7 -13
  820. package/src/memory/migrations/100-core-tables.ts +1 -1
  821. package/src/memory/migrations/101-watchers-and-logs.ts +1 -1
  822. package/src/memory/migrations/103-complex-migrations.ts +9 -9
  823. package/src/memory/migrations/104-core-indexes.ts +188 -64
  824. package/src/memory/migrations/105-contacts-and-triage.ts +28 -10
  825. package/src/memory/migrations/106-call-sessions.ts +58 -16
  826. package/src/memory/migrations/107-followups.ts +16 -6
  827. package/src/memory/migrations/108-tasks-and-work-items.ts +43 -11
  828. package/src/memory/migrations/109-external-conversation-bindings.ts +11 -5
  829. package/src/memory/migrations/110-channel-guardian.ts +48 -10
  830. package/src/memory/migrations/111-media-assets.ts +52 -18
  831. package/src/memory/migrations/112-assistant-inbox.ts +32 -12
  832. package/src/memory/migrations/113-late-migrations.ts +12 -12
  833. package/src/memory/migrations/114-notifications.ts +28 -12
  834. package/src/memory/migrations/115-sequences.ts +10 -4
  835. package/src/memory/migrations/116-messages-fts.ts +1 -1
  836. package/src/memory/migrations/117-conversation-attention.ts +16 -6
  837. package/src/memory/migrations/118-reminder-routing-intent.ts +7 -3
  838. package/src/memory/migrations/119-schema-indexes-and-columns.ts +35 -15
  839. package/src/memory/migrations/120-fk-cascade-rebuilds.ts +36 -17
  840. package/src/memory/migrations/121-canonical-guardian-requests.ts +25 -9
  841. package/src/memory/migrations/122-canonical-guardian-requester-chat-id.ts +11 -3
  842. package/src/memory/migrations/123-canonical-guardian-deliveries-destination-index.ts +4 -2
  843. package/src/memory/migrations/124-voice-invite-display-metadata.ts +15 -3
  844. package/src/memory/migrations/125-guardian-principal-id-columns.ts +22 -4
  845. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +174 -126
  846. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +58 -42
  847. package/src/memory/migrations/128-contacts-role-principal.ts +26 -0
  848. package/src/memory/migrations/129-contact-channels-access-fields.ts +105 -0
  849. package/src/memory/migrations/130-contact-channels-type-ext-chat-id-index.ts +15 -0
  850. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +134 -0
  851. package/src/memory/migrations/132-contacts-assistant-id.ts +21 -0
  852. package/src/memory/migrations/133-assistant-contact-metadata.ts +21 -0
  853. package/src/memory/migrations/index.ts +83 -73
  854. package/src/memory/migrations/registry.ts +53 -37
  855. package/src/memory/migrations/validate-migration-state.ts +73 -46
  856. package/src/memory/profile-compiler.ts +58 -24
  857. package/src/memory/published-pages-store.ts +12 -16
  858. package/src/memory/qdrant-circuit-breaker.ts +28 -20
  859. package/src/memory/qdrant-client.ts +99 -63
  860. package/src/memory/qdrant-manager.ts +89 -57
  861. package/src/memory/query-builder.ts +9 -7
  862. package/src/memory/raw-query.ts +63 -14
  863. package/src/memory/recall-cache.ts +15 -8
  864. package/src/memory/retrieval-budget.ts +0 -1
  865. package/src/memory/retriever.ts +385 -192
  866. package/src/memory/schema-migration.ts +1 -1
  867. package/src/memory/schema.ts +56 -56
  868. package/src/memory/scoped-approval-grants.ts +99 -45
  869. package/src/memory/search/entity.ts +102 -40
  870. package/src/memory/search/formatting.ts +70 -52
  871. package/src/memory/search/lexical.ts +82 -43
  872. package/src/memory/search/ranking.ts +103 -39
  873. package/src/memory/search/semantic.ts +59 -35
  874. package/src/memory/search/types.ts +8 -8
  875. package/src/memory/segmenter.ts +20 -12
  876. package/src/memory/shared-app-links-store.ts +21 -16
  877. package/src/memory/slack-thread-store.ts +187 -0
  878. package/src/memory/task-memory-cleanup.ts +18 -8
  879. package/src/memory/tool-usage-store.ts +27 -19
  880. package/src/memory/validation.ts +4 -2
  881. package/src/messaging/activity-analyzer.ts +7 -7
  882. package/src/messaging/draft-store.ts +13 -10
  883. package/src/messaging/email-classifier.ts +73 -37
  884. package/src/messaging/index.ts +3 -3
  885. package/src/messaging/outreach-classifier.ts +76 -38
  886. package/src/messaging/provider-types.ts +2 -4
  887. package/src/messaging/provider.ts +37 -8
  888. package/src/messaging/providers/gmail/adapter.ts +183 -66
  889. package/src/messaging/providers/gmail/client.ts +3 -1
  890. package/src/messaging/providers/gmail/mime-builder.ts +21 -19
  891. package/src/messaging/providers/gmail/people-client.ts +22 -9
  892. package/src/messaging/providers/gmail/types.ts +6 -6
  893. package/src/messaging/providers/slack/adapter.ts +93 -43
  894. package/src/messaging/providers/slack/client.ts +165 -48
  895. package/src/messaging/providers/slack/types.ts +10 -0
  896. package/src/messaging/providers/sms/adapter.ts +76 -40
  897. package/src/messaging/providers/sms/client.ts +4 -4
  898. package/src/messaging/providers/telegram-bot/adapter.ts +52 -30
  899. package/src/messaging/providers/telegram-bot/client.ts +7 -7
  900. package/src/messaging/providers/whatsapp/adapter.ts +58 -31
  901. package/src/messaging/providers/whatsapp/client.ts +4 -4
  902. package/src/messaging/registry.ts +9 -5
  903. package/src/messaging/style-analyzer.ts +69 -39
  904. package/src/messaging/thread-summarizer.ts +101 -53
  905. package/src/messaging/triage-engine.ts +111 -82
  906. package/src/messaging/types.ts +10 -10
  907. package/src/migrations/config-merge.ts +18 -10
  908. package/src/migrations/data-layout.ts +35 -22
  909. package/src/migrations/data-merge.ts +17 -7
  910. package/src/migrations/hooks-merge.ts +43 -16
  911. package/src/migrations/index.ts +6 -6
  912. package/src/migrations/log.ts +9 -5
  913. package/src/migrations/skills-merge.ts +17 -7
  914. package/src/migrations/workspace-layout.ts +39 -25
  915. package/src/notifications/AGENTS.md +5 -0
  916. package/src/notifications/adapters/macos.ts +21 -14
  917. package/src/notifications/adapters/slack.ts +90 -0
  918. package/src/notifications/adapters/sms.ts +28 -15
  919. package/src/notifications/adapters/telegram.ts +24 -15
  920. package/src/notifications/broadcaster.ts +108 -52
  921. package/src/notifications/conversation-pairing.ts +64 -29
  922. package/src/notifications/copy-composer.ts +165 -95
  923. package/src/notifications/decision-engine.ts +353 -147
  924. package/src/notifications/decisions-store.ts +26 -10
  925. package/src/notifications/deliveries-store.ts +23 -13
  926. package/src/notifications/destination-resolver.ts +83 -24
  927. package/src/notifications/deterministic-checks.ts +78 -27
  928. package/src/notifications/emit-signal.ts +95 -41
  929. package/src/notifications/events-store.ts +13 -7
  930. package/src/notifications/guardian-question-mode.ts +125 -75
  931. package/src/notifications/preference-extractor.ts +85 -53
  932. package/src/notifications/preference-summary.ts +31 -18
  933. package/src/notifications/preferences-store.ts +29 -18
  934. package/src/notifications/runtime-dispatch.ts +22 -12
  935. package/src/notifications/signal.ts +4 -4
  936. package/src/notifications/thread-candidates.ts +59 -23
  937. package/src/notifications/thread-seed-composer.ts +45 -27
  938. package/src/notifications/types.ts +19 -10
  939. package/src/oauth/connect-orchestrator.ts +105 -54
  940. package/src/oauth/connect-types.ts +3 -3
  941. package/src/oauth/provider-profiles.ts +102 -59
  942. package/src/oauth/scope-policy.ts +5 -2
  943. package/src/oauth/token-persistence.ts +58 -24
  944. package/src/outbound-proxy/certs.ts +284 -0
  945. package/src/outbound-proxy/config.ts +94 -0
  946. package/src/outbound-proxy/connect-tunnel.ts +84 -0
  947. package/src/outbound-proxy/health.ts +62 -0
  948. package/src/outbound-proxy/host-pattern-match.ts +67 -0
  949. package/src/outbound-proxy/http-forwarder.ts +162 -0
  950. package/src/outbound-proxy/index.ts +80 -0
  951. package/src/outbound-proxy/logging.ts +193 -0
  952. package/src/outbound-proxy/mitm-handler.ts +292 -0
  953. package/src/outbound-proxy/policy.ts +172 -0
  954. package/src/outbound-proxy/router.ts +64 -0
  955. package/src/outbound-proxy/server.ts +145 -0
  956. package/src/outbound-proxy/types.ts +150 -0
  957. package/src/permissions/checker.ts +481 -189
  958. package/src/permissions/defaults.ts +135 -108
  959. package/src/permissions/prompter.ts +53 -27
  960. package/src/permissions/secret-prompter.ts +21 -15
  961. package/src/permissions/shell-identity.ts +47 -16
  962. package/src/permissions/trust-store.ts +185 -73
  963. package/src/permissions/types.ts +22 -12
  964. package/src/permissions/workspace-policy.ts +47 -38
  965. package/src/playbooks/index.ts +10 -2
  966. package/src/playbooks/playbook-compiler.ts +30 -24
  967. package/src/playbooks/types.ts +11 -8
  968. package/src/providers/anthropic/client.ts +328 -168
  969. package/src/providers/failover.ts +57 -22
  970. package/src/providers/fireworks/client.ts +9 -5
  971. package/src/providers/gemini/client.ts +61 -39
  972. package/src/providers/model-intents.ts +40 -33
  973. package/src/providers/ollama/client.ts +7 -7
  974. package/src/providers/openai/client.ts +109 -68
  975. package/src/providers/openrouter/client.ts +9 -5
  976. package/src/providers/provider-send-message.ts +59 -27
  977. package/src/providers/ratelimit.ts +25 -8
  978. package/src/providers/registry.ts +86 -38
  979. package/src/providers/retry.ts +93 -37
  980. package/src/providers/stream-timeout.ts +5 -3
  981. package/src/providers/types.ts +7 -6
  982. package/src/runtime/AGENTS.md +42 -0
  983. package/src/runtime/access-request-helper.ts +118 -68
  984. package/src/runtime/actor-refresh-token-store.ts +21 -16
  985. package/src/runtime/actor-token-store.ts +25 -18
  986. package/src/runtime/actor-trust-resolver.ts +191 -80
  987. package/src/runtime/approval-conversation-turn.ts +39 -26
  988. package/src/runtime/approval-message-composer.ts +116 -84
  989. package/src/runtime/assistant-event-hub.ts +25 -6
  990. package/src/runtime/assistant-event.ts +4 -4
  991. package/src/runtime/assistant-scope.ts +1 -1
  992. package/src/runtime/auth/__tests__/guard-tests.test.ts +36 -14
  993. package/src/runtime/auth/context.ts +8 -7
  994. package/src/runtime/auth/credential-service.ts +60 -38
  995. package/src/runtime/auth/external-assistant-id.ts +16 -8
  996. package/src/runtime/auth/index.ts +23 -16
  997. package/src/runtime/auth/require-bound-guardian.ts +44 -0
  998. package/src/runtime/auth/route-policy.ts +166 -104
  999. package/src/runtime/auth/scopes.ts +22 -29
  1000. package/src/runtime/auth/subject.ts +19 -13
  1001. package/src/runtime/auth/token-service.ts +3 -3
  1002. package/src/runtime/auth/types.ts +23 -23
  1003. package/src/runtime/channel-approval-parser.ts +37 -14
  1004. package/src/runtime/channel-approval-types.ts +30 -4
  1005. package/src/runtime/channel-approvals.ts +49 -23
  1006. package/src/runtime/channel-guardian-service.ts +144 -103
  1007. package/src/runtime/channel-invite-transport.ts +5 -3
  1008. package/src/runtime/channel-invite-transports/telegram.ts +16 -10
  1009. package/src/runtime/channel-invite-transports/voice.ts +7 -7
  1010. package/src/runtime/channel-readiness-service.ts +139 -90
  1011. package/src/runtime/channel-readiness-types.ts +4 -2
  1012. package/src/runtime/channel-reply-delivery.ts +83 -14
  1013. package/src/runtime/channel-retry-sweep.ts +111 -62
  1014. package/src/runtime/confirmation-request-guardian-bridge.ts +73 -54
  1015. package/src/runtime/gateway-client.ts +122 -55
  1016. package/src/runtime/gateway-internal-client.ts +86 -0
  1017. package/src/runtime/guardian-action-conversation-turn.ts +34 -18
  1018. package/src/runtime/guardian-action-followup-executor.ts +115 -45
  1019. package/src/runtime/guardian-action-grant-minter.ts +40 -24
  1020. package/src/runtime/guardian-action-message-composer.ts +105 -84
  1021. package/src/runtime/guardian-action-service.ts +127 -0
  1022. package/src/runtime/guardian-decision-types.ts +28 -13
  1023. package/src/runtime/guardian-outbound-actions.ts +9 -0
  1024. package/src/runtime/guardian-reply-router.ts +274 -145
  1025. package/src/runtime/guardian-vellum-migration.ts +38 -24
  1026. package/src/runtime/guardian-verification-templates.ts +24 -12
  1027. package/src/runtime/http-router.ts +175 -0
  1028. package/src/runtime/http-server.ts +913 -680
  1029. package/src/runtime/http-types.ts +2 -2
  1030. package/src/runtime/invite-redemption-service.ts +211 -134
  1031. package/src/runtime/invite-redemption-templates.ts +18 -11
  1032. package/src/runtime/{ingress-service.ts → invite-service.ts} +92 -151
  1033. package/src/runtime/local-actor-identity.ts +73 -55
  1034. package/src/runtime/middleware/auth.ts +25 -14
  1035. package/src/runtime/middleware/error-handler.ts +15 -11
  1036. package/src/runtime/middleware/rate-limiter.ts +23 -17
  1037. package/src/runtime/middleware/request-logger.ts +4 -4
  1038. package/src/runtime/middleware/twilio-validation.ts +29 -20
  1039. package/src/runtime/migrations/migration-transport.ts +575 -0
  1040. package/src/runtime/migrations/migration-wizard.ts +715 -0
  1041. package/src/runtime/migrations/rebind-secrets-screen.ts +351 -0
  1042. package/src/runtime/migrations/transfer-progress-screen.ts +321 -0
  1043. package/src/runtime/migrations/validation-results-screen.ts +467 -0
  1044. package/src/runtime/migrations/vbundle-builder.ts +295 -0
  1045. package/src/runtime/migrations/vbundle-import-analyzer.ts +212 -0
  1046. package/src/runtime/migrations/vbundle-importer.ts +339 -0
  1047. package/src/runtime/migrations/vbundle-validator.ts +356 -0
  1048. package/src/runtime/nl-approval-parser.ts +138 -0
  1049. package/src/runtime/pending-interactions.ts +16 -7
  1050. package/src/runtime/routes/access-request-decision.ts +73 -52
  1051. package/src/runtime/routes/app-routes.ts +56 -38
  1052. package/src/runtime/routes/approval-routes.ts +144 -92
  1053. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +930 -0
  1054. package/src/runtime/routes/approval-strategies/guardian-legacy-fallback-strategy.ts +82 -0
  1055. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +151 -0
  1056. package/src/runtime/routes/attachment-routes.ts +59 -48
  1057. package/src/runtime/routes/brain-graph-routes.ts +85 -69
  1058. package/src/runtime/routes/call-routes.ts +79 -38
  1059. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +10 -10
  1060. package/src/runtime/routes/channel-delivery-routes.ts +19 -14
  1061. package/src/runtime/routes/channel-guardian-routes.ts +3 -3
  1062. package/src/runtime/routes/channel-inbound-routes.ts +2 -2
  1063. package/src/runtime/routes/channel-readiness-routes.ts +12 -6
  1064. package/src/runtime/routes/channel-route-shared.ts +67 -25
  1065. package/src/runtime/routes/channel-routes.ts +4 -6
  1066. package/src/runtime/routes/contact-routes.ts +374 -17
  1067. package/src/runtime/routes/conversation-attention-routes.ts +57 -28
  1068. package/src/runtime/routes/conversation-routes.ts +321 -174
  1069. package/src/runtime/routes/debug-routes.ts +14 -10
  1070. package/src/runtime/routes/events-routes.ts +90 -57
  1071. package/src/runtime/routes/global-search-routes.ts +266 -0
  1072. package/src/runtime/routes/guardian-action-routes.ts +112 -113
  1073. package/src/runtime/routes/guardian-approval-interception.ts +325 -874
  1074. package/src/runtime/routes/guardian-approval-prompt.ts +40 -24
  1075. package/src/runtime/routes/guardian-approval-reply-helpers.ts +135 -0
  1076. package/src/runtime/routes/guardian-bootstrap-routes.ts +55 -36
  1077. package/src/runtime/routes/guardian-expiry-sweep.ts +63 -37
  1078. package/src/runtime/routes/guardian-refresh-routes.ts +40 -19
  1079. package/src/runtime/routes/identity-routes.ts +71 -42
  1080. package/src/runtime/routes/inbound-conversation.ts +17 -11
  1081. package/src/runtime/routes/inbound-message-handler.ts +305 -1459
  1082. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +880 -0
  1083. package/src/runtime/routes/inbound-stages/background-dispatch.ts +600 -0
  1084. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +214 -0
  1085. package/src/runtime/routes/inbound-stages/edit-intercept.ts +116 -0
  1086. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +167 -0
  1087. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +185 -0
  1088. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +132 -0
  1089. package/src/runtime/routes/inbound-stages/verification-intercept.ts +340 -0
  1090. package/src/runtime/routes/integration-routes.ts +60 -21
  1091. package/src/runtime/routes/invite-routes.ts +140 -0
  1092. package/src/runtime/routes/migration-routes.ts +434 -0
  1093. package/src/runtime/routes/pairing-routes.ts +157 -79
  1094. package/src/runtime/routes/secret-routes.ts +6 -2
  1095. package/src/runtime/routes/twilio-routes.ts +443 -249
  1096. package/src/runtime/slack-block-formatting.ts +176 -0
  1097. package/src/runtime/tool-grant-request-helper.ts +36 -27
  1098. package/src/runtime/{guardian-context-resolver.ts → trust-context-resolver.ts} +29 -41
  1099. package/src/schedule/integration-status.ts +44 -9
  1100. package/src/schedule/recurrence-engine.ts +47 -24
  1101. package/src/schedule/recurrence-types.ts +12 -7
  1102. package/src/schedule/schedule-store.ts +166 -83
  1103. package/src/schedule/scheduler.ts +37 -24
  1104. package/src/security/encrypted-store.ts +68 -38
  1105. package/src/security/keychain.ts +183 -120
  1106. package/src/security/oauth-callback-registry.ts +3 -3
  1107. package/src/security/oauth2.ts +226 -138
  1108. package/src/security/redaction.ts +24 -24
  1109. package/src/security/secret-allowlist.ts +46 -21
  1110. package/src/security/secret-ingress.ts +15 -7
  1111. package/src/security/secret-scanner.ts +193 -104
  1112. package/src/security/secure-keys.ts +9 -3
  1113. package/src/security/token-manager.ts +99 -40
  1114. package/src/security/tool-approval-digest.ts +3 -3
  1115. package/src/sequence/analytics.ts +52 -27
  1116. package/src/sequence/engine.ts +135 -72
  1117. package/src/sequence/guardrails.ts +32 -20
  1118. package/src/sequence/importer.ts +75 -37
  1119. package/src/sequence/reply-matcher.ts +36 -18
  1120. package/src/sequence/store.ts +137 -75
  1121. package/src/sequence/types.ts +30 -16
  1122. package/src/services/published-app-updater.ts +26 -16
  1123. package/src/services/vercel-deploy.ts +19 -15
  1124. package/src/skills/active-skill-tools.ts +3 -3
  1125. package/src/skills/clawhub.ts +178 -90
  1126. package/src/skills/include-graph.ts +24 -17
  1127. package/src/skills/managed-store.ts +89 -42
  1128. package/src/skills/path-classifier.ts +10 -10
  1129. package/src/skills/remote-skill-policy.ts +31 -22
  1130. package/src/skills/slash-commands.ts +36 -30
  1131. package/src/skills/tool-manifest.ts +60 -31
  1132. package/src/skills/version-hash.ts +25 -15
  1133. package/src/slack/slack-webhook.ts +19 -15
  1134. package/src/subagent/index.ts +4 -8
  1135. package/src/subagent/manager.ts +119 -69
  1136. package/src/subagent/types.ts +9 -12
  1137. package/src/swarm/backend-claude-code.ts +124 -45
  1138. package/src/swarm/checkpoint.ts +36 -16
  1139. package/src/swarm/graph-utils.ts +1 -3
  1140. package/src/swarm/index.ts +38 -19
  1141. package/src/swarm/limits.ts +13 -4
  1142. package/src/swarm/orchestrator.ts +108 -57
  1143. package/src/swarm/plan-validator.ts +23 -17
  1144. package/src/swarm/router-planner.ts +51 -22
  1145. package/src/swarm/router-prompts.ts +4 -1
  1146. package/src/swarm/synthesizer.ts +26 -18
  1147. package/src/swarm/types.ts +14 -4
  1148. package/src/swarm/worker-backend.ts +36 -26
  1149. package/src/swarm/worker-prompts.ts +13 -9
  1150. package/src/swarm/worker-runner.ts +40 -34
  1151. package/src/tasks/candidate-store.ts +14 -6
  1152. package/src/tasks/ephemeral-permissions.ts +9 -5
  1153. package/src/tasks/task-compiler.ts +41 -38
  1154. package/src/tasks/task-runner.ts +54 -26
  1155. package/src/tasks/task-scheduler.ts +1 -1
  1156. package/src/tasks/task-store.ts +20 -7
  1157. package/src/tasks/tool-sanitizer.ts +3 -3
  1158. package/src/tools/apps/definitions.ts +23 -15
  1159. package/src/tools/apps/executors.ts +122 -40
  1160. package/src/tools/apps/open-proxy.ts +5 -5
  1161. package/src/tools/apps/registry.ts +2 -2
  1162. package/src/tools/assets/materialize.ts +59 -41
  1163. package/src/tools/assets/search.ts +86 -48
  1164. package/src/tools/browser/api-map.ts +52 -36
  1165. package/src/tools/browser/auth-cache.ts +21 -18
  1166. package/src/tools/browser/auth-detector.ts +43 -28
  1167. package/src/tools/browser/auto-navigate.ts +149 -68
  1168. package/src/tools/browser/browser-execution.ts +9 -3
  1169. package/src/tools/browser/headless-browser.ts +287 -150
  1170. package/src/tools/browser/jit-auth.ts +37 -21
  1171. package/src/tools/browser/network-recorder.ts +138 -56
  1172. package/src/tools/browser/recording-store.ts +22 -15
  1173. package/src/tools/browser/runtime-check.ts +8 -5
  1174. package/src/tools/browser/x-auto-navigate.ts +88 -47
  1175. package/src/tools/calls/call-end.ts +10 -7
  1176. package/src/tools/calls/call-start.ts +30 -20
  1177. package/src/tools/calls/call-status.ts +8 -5
  1178. package/src/tools/claude-code/claude-code.ts +301 -165
  1179. package/src/tools/computer-use/definitions.ts +175 -130
  1180. package/src/tools/computer-use/registry.ts +2 -2
  1181. package/src/tools/computer-use/request-computer-control.ts +21 -13
  1182. package/src/tools/computer-use/skill-proxy-bridge.ts +1 -1
  1183. package/src/tools/credentials/account-registry.ts +52 -35
  1184. package/src/tools/credentials/broker-types.ts +1 -1
  1185. package/src/tools/credentials/broker.ts +97 -55
  1186. package/src/tools/credentials/domain-policy.ts +5 -2
  1187. package/src/tools/credentials/host-pattern-match.ts +15 -8
  1188. package/src/tools/credentials/metadata-store.ts +93 -43
  1189. package/src/tools/credentials/policy-types.ts +5 -2
  1190. package/src/tools/credentials/policy-validate.ts +21 -14
  1191. package/src/tools/credentials/post-connect-hooks.ts +18 -7
  1192. package/src/tools/credentials/resolve.ts +11 -10
  1193. package/src/tools/credentials/selection.ts +30 -25
  1194. package/src/tools/credentials/tool-policy.ts +5 -2
  1195. package/src/tools/credentials/vault.ts +538 -185
  1196. package/src/tools/document/document-tool.ts +23 -17
  1197. package/src/tools/document/editor-template.ts +12 -7
  1198. package/src/tools/execution-target.ts +13 -10
  1199. package/src/tools/execution-timeout.ts +6 -5
  1200. package/src/tools/executor.ts +141 -74
  1201. package/src/tools/filesystem/edit.ts +82 -45
  1202. package/src/tools/filesystem/fuzzy-match.ts +70 -32
  1203. package/src/tools/filesystem/read.ts +46 -28
  1204. package/src/tools/filesystem/view-image.ts +86 -42
  1205. package/src/tools/filesystem/write.ts +53 -32
  1206. package/src/tools/followups/followup_create.ts +43 -17
  1207. package/src/tools/followups/followup_list.ts +28 -13
  1208. package/src/tools/followups/followup_resolve.ts +9 -6
  1209. package/src/tools/guardian-control-plane-policy.ts +15 -14
  1210. package/src/tools/host-filesystem/edit.ts +77 -42
  1211. package/src/tools/host-filesystem/read.ts +52 -33
  1212. package/src/tools/host-filesystem/write.ts +50 -29
  1213. package/src/tools/host-terminal/host-shell.ts +97 -61
  1214. package/src/tools/mcp/mcp-tool-factory.ts +21 -14
  1215. package/src/tools/memory/definitions.ts +60 -28
  1216. package/src/tools/memory/handlers.ts +149 -77
  1217. package/src/tools/memory/register.ts +39 -16
  1218. package/src/tools/network/__tests__/web-search.test.ts +236 -177
  1219. package/src/tools/network/domain-normalize.ts +13 -9
  1220. package/src/tools/network/script-proxy/__tests__/logging.test.ts +193 -123
  1221. package/src/tools/network/script-proxy/__tests__/policy.test.ts +225 -127
  1222. package/src/tools/network/script-proxy/index.ts +1 -17
  1223. package/src/tools/network/script-proxy/session-manager.ts +178 -86
  1224. package/src/tools/network/url-safety.ts +56 -34
  1225. package/src/tools/network/web-fetch.ts +273 -155
  1226. package/src/tools/network/web-search.ts +166 -81
  1227. package/src/tools/permission-checker.ts +24 -25
  1228. package/src/tools/policy-context.ts +8 -5
  1229. package/src/tools/registry.ts +73 -46
  1230. package/src/tools/reminder/reminder-store.ts +65 -44
  1231. package/src/tools/reminder/reminder.ts +76 -35
  1232. package/src/tools/schedule/create.ts +44 -21
  1233. package/src/tools/schedule/delete.ts +8 -5
  1234. package/src/tools/schedule/list.ts +39 -19
  1235. package/src/tools/schedule/update.ts +49 -26
  1236. package/src/tools/secret-detection-handler.ts +130 -49
  1237. package/src/tools/sensitive-output-placeholders.ts +15 -8
  1238. package/src/tools/shared/filesystem/edit-engine.ts +45 -14
  1239. package/src/tools/shared/filesystem/errors.ts +18 -18
  1240. package/src/tools/shared/filesystem/file-ops-service.ts +59 -32
  1241. package/src/tools/shared/filesystem/format-diff.ts +21 -11
  1242. package/src/tools/shared/filesystem/path-policy.ts +17 -13
  1243. package/src/tools/shared/filesystem/size-guard.ts +8 -4
  1244. package/src/tools/shared/filesystem/types.ts +2 -2
  1245. package/src/tools/shared/shell-output.ts +4 -3
  1246. package/src/tools/side-effects.ts +36 -28
  1247. package/src/tools/skills/delete-managed.ts +30 -17
  1248. package/src/tools/skills/load.ts +88 -46
  1249. package/src/tools/skills/sandbox-runner.ts +62 -46
  1250. package/src/tools/skills/scaffold-managed.ts +98 -48
  1251. package/src/tools/skills/script-contract.ts +5 -2
  1252. package/src/tools/skills/skill-script-runner.ts +29 -13
  1253. package/src/tools/skills/skill-tool-factory.ts +20 -10
  1254. package/src/tools/subagent/abort.ts +10 -4
  1255. package/src/tools/subagent/message.ts +14 -8
  1256. package/src/tools/subagent/read.ts +20 -11
  1257. package/src/tools/subagent/spawn.ts +14 -6
  1258. package/src/tools/subagent/status.ts +7 -4
  1259. package/src/tools/swarm/delegate.ts +75 -49
  1260. package/src/tools/system/avatar-generator.ts +46 -33
  1261. package/src/tools/system/navigate-settings.ts +29 -19
  1262. package/src/tools/system/open-system-settings.ts +30 -20
  1263. package/src/tools/system/request-permission.ts +59 -44
  1264. package/src/tools/system/version.ts +27 -16
  1265. package/src/tools/system/voice-config.ts +116 -53
  1266. package/src/tools/tasks/index.ts +8 -8
  1267. package/src/tools/tasks/task-delete.ts +61 -22
  1268. package/src/tools/tasks/task-list.ts +23 -11
  1269. package/src/tools/tasks/task-run.ts +41 -16
  1270. package/src/tools/tasks/task-save.ts +27 -10
  1271. package/src/tools/tasks/work-item-enqueue.ts +114 -48
  1272. package/src/tools/tasks/work-item-list.ts +20 -10
  1273. package/src/tools/tasks/work-item-remove.ts +49 -15
  1274. package/src/tools/tasks/work-item-run.ts +34 -13
  1275. package/src/tools/tasks/work-item-update.ts +84 -31
  1276. package/src/tools/terminal/backends/native.ts +64 -35
  1277. package/src/tools/terminal/backends/types.ts +6 -2
  1278. package/src/tools/terminal/parser.ts +200 -125
  1279. package/src/tools/terminal/safe-env.ts +27 -21
  1280. package/src/tools/terminal/sandbox-diagnostics.ts +31 -13
  1281. package/src/tools/terminal/sandbox.ts +10 -6
  1282. package/src/tools/terminal/shell.ts +134 -68
  1283. package/src/tools/tool-approval-handler.ts +239 -140
  1284. package/src/tools/types.ts +79 -22
  1285. package/src/tools/ui-surface/definitions.ts +124 -89
  1286. package/src/tools/ui-surface/registry.ts +2 -2
  1287. package/src/tools/watch/screen-watch.ts +50 -32
  1288. package/src/tools/watch/watch-state.ts +41 -15
  1289. package/src/tools/watcher/create.ts +37 -15
  1290. package/src/tools/watcher/delete.ts +9 -6
  1291. package/src/tools/watcher/digest.ts +10 -6
  1292. package/src/tools/watcher/list.ts +37 -14
  1293. package/src/tools/watcher/update.ts +33 -18
  1294. package/src/tools/weather/service.ts +331 -174
  1295. package/src/twitter/client.ts +261 -138
  1296. package/src/twitter/oauth-client.ts +17 -13
  1297. package/src/twitter/router.ts +51 -23
  1298. package/src/twitter/session.ts +27 -18
  1299. package/src/types/qrcode.d.ts +6 -3
  1300. package/src/usage/actors.ts +16 -16
  1301. package/src/usage/types.ts +3 -3
  1302. package/src/util/bundled-asset.ts +10 -6
  1303. package/src/util/canonicalize-identity.ts +11 -4
  1304. package/src/util/clipboard.ts +7 -7
  1305. package/src/util/content-id.ts +3 -3
  1306. package/src/util/debounce.ts +3 -2
  1307. package/src/util/diff.ts +55 -33
  1308. package/src/util/errors.ts +31 -27
  1309. package/src/util/fs.ts +8 -2
  1310. package/src/util/log-redact.ts +12 -12
  1311. package/src/util/logger.ts +112 -51
  1312. package/src/util/network-info.ts +13 -5
  1313. package/src/util/object.ts +4 -2
  1314. package/src/util/phone.ts +4 -4
  1315. package/src/util/platform.ts +80 -58
  1316. package/src/util/pricing.ts +49 -31
  1317. package/src/util/retry.ts +39 -7
  1318. package/src/util/row-mapper.ts +7 -4
  1319. package/src/util/silently.ts +7 -4
  1320. package/src/util/spawn.ts +48 -0
  1321. package/src/util/spinner.ts +9 -7
  1322. package/src/util/time.ts +16 -3
  1323. package/src/util/truncate.ts +1 -1
  1324. package/src/util/voice-code.ts +6 -4
  1325. package/src/util/xml.ts +5 -1
  1326. package/src/version.ts +12 -8
  1327. package/src/watcher/engine.ts +71 -44
  1328. package/src/watcher/provider-registry.ts +1 -1
  1329. package/src/watcher/providers/github.ts +40 -23
  1330. package/src/watcher/providers/gmail.ts +59 -38
  1331. package/src/watcher/providers/google-calendar.ts +62 -48
  1332. package/src/watcher/providers/linear.ts +219 -150
  1333. package/src/watcher/providers/slack.ts +125 -29
  1334. package/src/watcher/watcher-store.ts +75 -55
  1335. package/src/work-items/work-item-runner.ts +62 -29
  1336. package/src/work-items/work-item-store.ts +137 -47
  1337. package/src/workspace/commit-message-enrichment-service.ts +65 -25
  1338. package/src/workspace/commit-message-provider.ts +14 -12
  1339. package/src/workspace/git-service.ts +355 -239
  1340. package/src/workspace/heartbeat-service.ts +74 -37
  1341. package/src/workspace/provider-commit-message-generator.ts +95 -70
  1342. package/src/workspace/top-level-renderer.ts +10 -8
  1343. package/src/workspace/top-level-scanner.ts +9 -3
  1344. package/src/workspace/turn-commit.ts +63 -36
  1345. package/src/__tests__/ingress-member-store.test.ts +0 -294
  1346. package/src/__tests__/script-proxy-router.test.ts +0 -215
  1347. package/src/config/bundled-skills/trusted-contacts/SKILL.md +0 -372
  1348. package/src/memory/guardian-bindings.ts +0 -158
  1349. package/src/memory/ingress-member-store.ts +0 -352
  1350. package/src/runtime/routes/ingress-routes.ts +0 -229
  1351. package/src/tools/network/script-proxy/__tests__/router.test.ts +0 -77
  1352. package/src/tools/network/script-proxy/certs.ts +0 -7
  1353. package/src/tools/network/script-proxy/connect-tunnel.ts +0 -1
  1354. package/src/tools/network/script-proxy/http-forwarder.ts +0 -2
  1355. package/src/tools/network/script-proxy/logging.ts +0 -12
  1356. package/src/tools/network/script-proxy/mitm-handler.ts +0 -2
  1357. package/src/tools/network/script-proxy/policy.ts +0 -4
  1358. package/src/tools/network/script-proxy/router.ts +0 -2
  1359. package/src/tools/network/script-proxy/server.ts +0 -5
  1360. package/src/tools/network/script-proxy/types.ts +0 -19
@@ -0,0 +1,1854 @@
1
+ /**
2
+ * Tests for macOS/iOS parity and persistence/resume behavior.
3
+ *
4
+ * Covers:
5
+ * - Cross-platform parity: shared modules produce identical results regardless
6
+ * of platform context. Serialized state is portable between macOS and iOS.
7
+ * - Persistence/resume at every wizard step: serialize, deserialize, resume,
8
+ * verify correct screen states.
9
+ * - Interrupted flows: crash during transfer, partial completion, loading states.
10
+ * - Full end-to-end flows: managed-to-self-hosted and self-hosted-to-managed
11
+ * through all screen view models.
12
+ * - Edge cases: timeouts during polling, stale/expired states, concurrent wizard
13
+ * instances, state corruption recovery, bundle data loss after deserialization.
14
+ * - Cross-screen consistency: screen transitions maintain consistent state across
15
+ * validation -> transfer -> rebind screens.
16
+ */
17
+
18
+ import { describe, expect, test } from "bun:test";
19
+
20
+ import type {
21
+ ImportCommitResponse,
22
+ ImportPreflightResponse,
23
+ TransportConfig,
24
+ ValidateResponse,
25
+ } from "../runtime/migrations/migration-transport.js";
26
+ import type {
27
+ MigrationDirection,
28
+ MigrationWizardState,
29
+ StepExecutorOptions,
30
+ WizardStep,
31
+ } from "../runtime/migrations/migration-wizard.js";
32
+ import {
33
+ canRetryCurrentStep,
34
+ completeRebindSecrets,
35
+ createWizardState,
36
+ deserializeWizardState,
37
+ getCurrentStepIndex,
38
+ getStepOrder,
39
+ getTotalSteps,
40
+ goBackTo,
41
+ isResumable,
42
+ isStepAccessible,
43
+ isWizardComplete,
44
+ prepareForResume,
45
+ resetStepForRetry,
46
+ selectDirection,
47
+ serializeWizardState,
48
+ setBundleUploaded,
49
+ validateWizardTransition,
50
+ } from "../runtime/migrations/migration-wizard.js";
51
+ import type { RebindTaskCompletionState } from "../runtime/migrations/rebind-secrets-screen.js";
52
+ import {
53
+ areAllRequiredTasksComplete,
54
+ completeMigration,
55
+ createTaskCompletionState,
56
+ deriveRebindSecretsScreenState,
57
+ getTaskIds,
58
+ isRebindSecretsScreenAccessible,
59
+ markTaskComplete,
60
+ } from "../runtime/migrations/rebind-secrets-screen.js";
61
+ import {
62
+ deriveTransferScreenState,
63
+ executeTransferFlow,
64
+ isTransferScreenAccessible,
65
+ } from "../runtime/migrations/transfer-progress-screen.js";
66
+ import {
67
+ deriveValidationScreenState,
68
+ executeValidationFlow,
69
+ isValidationScreenAccessible,
70
+ } from "../runtime/migrations/validation-results-screen.js";
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Test helpers
74
+ // ---------------------------------------------------------------------------
75
+
76
+ function mockFetch(
77
+ status: number,
78
+ body: unknown,
79
+ headers?: Record<string, string>,
80
+ ): typeof fetch {
81
+ return (async () => {
82
+ const responseHeaders = new Headers(headers);
83
+ if (
84
+ typeof body === "object" &&
85
+ body !== undefined &&
86
+ !(body instanceof ArrayBuffer)
87
+ ) {
88
+ responseHeaders.set("Content-Type", "application/json");
89
+ return new Response(JSON.stringify(body), {
90
+ status,
91
+ headers: responseHeaders,
92
+ });
93
+ }
94
+ if (body instanceof ArrayBuffer) {
95
+ return new Response(body, { status, headers: responseHeaders });
96
+ }
97
+ return new Response(String(body), { status, headers: responseHeaders });
98
+ }) as unknown as typeof fetch;
99
+ }
100
+
101
+ function runtimeConfig(overrides?: Partial<TransportConfig>): TransportConfig {
102
+ return {
103
+ baseURL: "http://localhost:7821",
104
+ target: "runtime",
105
+ authHeader: "Bearer test-jwt",
106
+ fetchFn: mockFetch(200, {}),
107
+ ...overrides,
108
+ };
109
+ }
110
+
111
+ function managedConfig(overrides?: Partial<TransportConfig>): TransportConfig {
112
+ return {
113
+ baseURL: "https://platform.vellum.ai",
114
+ target: "managed",
115
+ authHeader: "session-token-abc",
116
+ fetchFn: mockFetch(200, {}),
117
+ ...overrides,
118
+ };
119
+ }
120
+
121
+ function makeExecutorOptions(
122
+ overrides?: Partial<StepExecutorOptions>,
123
+ ): StepExecutorOptions {
124
+ return {
125
+ sourceConfig: runtimeConfig(),
126
+ destConfig: runtimeConfig(),
127
+ bundleData: new ArrayBuffer(16),
128
+ ...overrides,
129
+ };
130
+ }
131
+
132
+ function makeValidateSuccess(): ValidateResponse {
133
+ return {
134
+ is_valid: true,
135
+ errors: [],
136
+ manifest: {
137
+ schema_version: "1.0",
138
+ created_at: "2025-01-01T00:00:00Z",
139
+ source: "test",
140
+ description: "Test bundle",
141
+ files: [
142
+ { path: "config.json", sha256: "abc123", size: 1024 },
143
+ { path: "skills/test.md", sha256: "def456", size: 2048 },
144
+ ],
145
+ manifest_sha256: "manifest-hash",
146
+ },
147
+ };
148
+ }
149
+
150
+ function makePreflightSuccess(): ImportPreflightResponse {
151
+ return {
152
+ can_import: true,
153
+ summary: {
154
+ total_files: 3,
155
+ files_to_create: 1,
156
+ files_to_overwrite: 1,
157
+ files_unchanged: 1,
158
+ files_to_skip: 0,
159
+ },
160
+ files: [
161
+ {
162
+ path: "config.json",
163
+ action: "overwrite",
164
+ bundle_size: 1024,
165
+ current_size: 800,
166
+ bundle_sha256: "abc123",
167
+ current_sha256: "old123",
168
+ },
169
+ {
170
+ path: "skills/new-skill.md",
171
+ action: "create",
172
+ bundle_size: 512,
173
+ current_size: null,
174
+ bundle_sha256: "ghi789",
175
+ current_sha256: null,
176
+ },
177
+ {
178
+ path: "playbooks/default.md",
179
+ action: "unchanged",
180
+ bundle_size: 2048,
181
+ current_size: 2048,
182
+ bundle_sha256: "mno345",
183
+ current_sha256: "mno345",
184
+ },
185
+ ],
186
+ conflicts: [],
187
+ manifest: {
188
+ schema_version: "1.0",
189
+ created_at: "2025-01-01T00:00:00Z",
190
+ files: [
191
+ { path: "config.json", sha256: "abc123", size: 1024 },
192
+ { path: "skills/new-skill.md", sha256: "ghi789", size: 512 },
193
+ ],
194
+ manifest_sha256: "manifest-hash",
195
+ },
196
+ };
197
+ }
198
+
199
+ function makeImportSuccess(): ImportCommitResponse {
200
+ return {
201
+ success: true,
202
+ summary: {
203
+ total_files: 3,
204
+ files_created: 1,
205
+ files_overwritten: 1,
206
+ files_skipped: 1,
207
+ backups_created: 1,
208
+ },
209
+ files: [
210
+ {
211
+ path: "config.json",
212
+ disk_path: "/data/config.json",
213
+ action: "overwritten",
214
+ size: 1024,
215
+ sha256: "abc123",
216
+ backup_path: "/data/config.json.bak",
217
+ },
218
+ ],
219
+ manifest: {
220
+ schema_version: "1.0",
221
+ created_at: "2025-01-01T00:00:00Z",
222
+ files: [{ path: "config.json", sha256: "abc123", size: 1024 }],
223
+ manifest_sha256: "manifest-hash",
224
+ },
225
+ warnings: ["Backup created for config.json"],
226
+ };
227
+ }
228
+
229
+ /** Advance state to a specific step for testing. */
230
+ function advanceTo(
231
+ step: WizardStep,
232
+ direction: MigrationDirection = "managed-to-self-hosted",
233
+ ): MigrationWizardState {
234
+ let state = createWizardState();
235
+ const stepOrder: WizardStep[] = [
236
+ "select-direction",
237
+ "upload-bundle",
238
+ "validate",
239
+ "preflight-review",
240
+ "transfer",
241
+ "rebind-secrets",
242
+ "complete",
243
+ ];
244
+ const targetIdx = stepOrder.indexOf(step);
245
+
246
+ if (targetIdx >= 1) {
247
+ state = selectDirection(state, direction);
248
+ }
249
+ if (targetIdx >= 2) {
250
+ state = setBundleUploaded(state);
251
+ }
252
+ if (targetIdx >= 3) {
253
+ state = {
254
+ ...state,
255
+ steps: { ...state.steps, validate: { status: "success" } },
256
+ validateResult: makeValidateSuccess(),
257
+ currentStep: "preflight-review",
258
+ };
259
+ }
260
+ if (targetIdx >= 4) {
261
+ state = {
262
+ ...state,
263
+ steps: { ...state.steps, "preflight-review": { status: "success" } },
264
+ preflightResult: makePreflightSuccess(),
265
+ currentStep: "transfer",
266
+ };
267
+ }
268
+ if (targetIdx >= 5) {
269
+ state = {
270
+ ...state,
271
+ steps: { ...state.steps, transfer: { status: "success" } },
272
+ importResult: makeImportSuccess(),
273
+ currentStep: "rebind-secrets",
274
+ };
275
+ }
276
+ if (targetIdx >= 6) {
277
+ state = completeRebindSecrets(state);
278
+ }
279
+
280
+ return state;
281
+ }
282
+
283
+ /** Mark all required tasks as complete. */
284
+ function completeOnlyRequired(
285
+ state: RebindTaskCompletionState,
286
+ ): RebindTaskCompletionState {
287
+ let current = state;
288
+ current = markTaskComplete(current, "re-enter-secrets");
289
+ current = markTaskComplete(current, "rebind-channels");
290
+ current = markTaskComplete(current, "reconfigure-auth");
291
+ return current;
292
+ }
293
+
294
+ /** Mark all tasks (including optional) as complete. */
295
+ function completeAllTasks(
296
+ state: RebindTaskCompletionState,
297
+ ): RebindTaskCompletionState {
298
+ let current = state;
299
+ for (const id of getTaskIds()) {
300
+ current = markTaskComplete(current, id);
301
+ }
302
+ return current;
303
+ }
304
+
305
+ // ===========================================================================
306
+ // 1. CROSS-PLATFORM PARITY
307
+ // ===========================================================================
308
+
309
+ describe("macOS + iOS parity — shared module determinism", () => {
310
+ test("wizard state machine produces identical results for both directions", () => {
311
+ // Both platforms use the same shared modules, so identical inputs must
312
+ // produce identical outputs regardless of which platform instantiates them.
313
+ const stateA = createWizardState();
314
+ const stateB = createWizardState();
315
+
316
+ // Compare structure (timestamps may differ slightly, so strip them)
317
+ const normalize = (s: MigrationWizardState) => ({
318
+ ...s,
319
+ createdAt: "FIXED",
320
+ updatedAt: "FIXED",
321
+ });
322
+
323
+ expect(normalize(stateA)).toEqual(normalize(stateB));
324
+
325
+ // Advance both through the same transitions
326
+ const advA = selectDirection(stateA, "managed-to-self-hosted");
327
+ const advB = selectDirection(stateB, "managed-to-self-hosted");
328
+
329
+ expect(normalize(advA).currentStep).toBe(normalize(advB).currentStep);
330
+ expect(normalize(advA).direction).toBe(normalize(advB).direction);
331
+ expect(normalize(advA).steps).toEqual(normalize(advB).steps);
332
+ });
333
+
334
+ test("serialized state is portable — serialize on 'macOS', deserialize on 'iOS'", () => {
335
+ // Simulate creating state on one platform and deserializing on another.
336
+ // The key invariant: the deserialized state must be structurally identical
337
+ // (except hasBundleData which is always false after deserialization).
338
+ const original = advanceTo("rebind-secrets", "managed-to-self-hosted");
339
+ const json = serializeWizardState(original);
340
+
341
+ // "Transfer" the JSON string to another platform
342
+ const restored = deserializeWizardState(json);
343
+ expect(restored).toBeDefined();
344
+
345
+ // All fields except hasBundleData and timestamps should match
346
+ expect(restored!.currentStep).toBe(original.currentStep);
347
+ expect(restored!.direction).toBe(original.direction);
348
+ expect(restored!.hasBundleData).toBe(false); // Always false after deserialization
349
+
350
+ // Step states should be identical
351
+ for (const step of getStepOrder(original)) {
352
+ expect(restored!.steps[step].status).toBe(original.steps[step].status);
353
+ }
354
+
355
+ // Results should be preserved
356
+ expect(restored!.validateResult).toEqual(original.validateResult);
357
+ expect(restored!.preflightResult).toEqual(original.preflightResult);
358
+ expect(restored!.importResult).toEqual(original.importResult);
359
+ });
360
+
361
+ test("state machine transitions produce identical step orders for both directions", () => {
362
+ const m2s = getStepOrder(
363
+ advanceTo("upload-bundle", "managed-to-self-hosted"),
364
+ );
365
+ const s2m = getStepOrder(
366
+ advanceTo("upload-bundle", "self-hosted-to-managed"),
367
+ );
368
+
369
+ // Both directions use the same step sequence
370
+ expect(m2s).toEqual(s2m);
371
+ expect(m2s).toEqual([
372
+ "select-direction",
373
+ "upload-bundle",
374
+ "validate",
375
+ "preflight-review",
376
+ "transfer",
377
+ "rebind-secrets",
378
+ "complete",
379
+ ]);
380
+ });
381
+
382
+ test("validation screen view model produces identical output for same wizard state", () => {
383
+ const stateM2S = advanceTo("transfer", "managed-to-self-hosted");
384
+ const stateS2M = advanceTo("transfer", "self-hosted-to-managed");
385
+
386
+ // Both should produce success screen states with identical structure
387
+ // (only difference is the direction field on wizard state)
388
+ const screenM2S = deriveValidationScreenState(stateM2S);
389
+ const screenS2M = deriveValidationScreenState(stateS2M);
390
+
391
+ expect(screenM2S.phase).toBe("success");
392
+ expect(screenS2M.phase).toBe("success");
393
+
394
+ if (screenM2S.phase === "success" && screenS2M.phase === "success") {
395
+ expect(screenM2S.validation).toEqual(screenS2M.validation);
396
+ expect(screenM2S.preflight).toEqual(screenS2M.preflight);
397
+ }
398
+ });
399
+
400
+ test("transfer screen view model produces identical output for same wizard state", () => {
401
+ const stateM2S = advanceTo("rebind-secrets", "managed-to-self-hosted");
402
+ const stateS2M = advanceTo("rebind-secrets", "self-hosted-to-managed");
403
+
404
+ const screenM2S = deriveTransferScreenState(stateM2S);
405
+ const screenS2M = deriveTransferScreenState(stateS2M);
406
+
407
+ expect(screenM2S.phase).toBe("success");
408
+ expect(screenS2M.phase).toBe("success");
409
+
410
+ if (screenM2S.phase === "success" && screenS2M.phase === "success") {
411
+ expect(screenM2S.importSummary).toEqual(screenS2M.importSummary);
412
+ }
413
+ });
414
+
415
+ test("rebind secrets screen produces identical output for same wizard state", () => {
416
+ const stateM2S = advanceTo("rebind-secrets", "managed-to-self-hosted");
417
+ const stateS2M = advanceTo("rebind-secrets", "self-hosted-to-managed");
418
+
419
+ const completion = createTaskCompletionState();
420
+ const screenM2S = deriveRebindSecretsScreenState(stateM2S, completion);
421
+ const screenS2M = deriveRebindSecretsScreenState(stateS2M, completion);
422
+
423
+ expect(screenM2S.phase).toBe("active");
424
+ expect(screenS2M.phase).toBe("active");
425
+
426
+ if (screenM2S.phase === "active" && screenS2M.phase === "active") {
427
+ expect(screenM2S.tasks).toEqual(screenS2M.tasks);
428
+ expect(screenM2S.requiredCount).toBe(screenS2M.requiredCount);
429
+ expect(screenM2S.totalCount).toBe(screenS2M.totalCount);
430
+ }
431
+ });
432
+
433
+ test("query helpers produce identical results for both directions", () => {
434
+ const stateM2S = advanceTo("transfer", "managed-to-self-hosted");
435
+ const stateS2M = advanceTo("transfer", "self-hosted-to-managed");
436
+
437
+ expect(getCurrentStepIndex(stateM2S)).toBe(getCurrentStepIndex(stateS2M));
438
+ expect(getTotalSteps()).toBe(7);
439
+ expect(isWizardComplete(stateM2S)).toBe(false);
440
+ expect(isWizardComplete(stateS2M)).toBe(false);
441
+
442
+ const completeM2S = advanceTo("complete", "managed-to-self-hosted");
443
+ const completeS2M = advanceTo("complete", "self-hosted-to-managed");
444
+ expect(isWizardComplete(completeM2S)).toBe(true);
445
+ expect(isWizardComplete(completeS2M)).toBe(true);
446
+ });
447
+
448
+ test("transition validation is symmetric across platform contexts", () => {
449
+ const state = advanceTo("validate");
450
+
451
+ // Forward transition validation
452
+ const forwardResult = validateWizardTransition(state, "preflight-review");
453
+ expect(forwardResult.valid).toBe(false); // validate step is idle, not success
454
+
455
+ // Same call with identical state produces identical result
456
+ const forwardResult2 = validateWizardTransition(state, "preflight-review");
457
+ expect(forwardResult2).toEqual(forwardResult);
458
+
459
+ // Back transition is always valid
460
+ const backResult = validateWizardTransition(state, "upload-bundle");
461
+ expect(backResult.valid).toBe(true);
462
+ });
463
+ });
464
+
465
+ // ===========================================================================
466
+ // 2. PERSISTENCE / RESUME BEHAVIOR
467
+ // ===========================================================================
468
+
469
+ describe("persistence/resume — serialize at each step", () => {
470
+ test("persist and resume at select-direction step", () => {
471
+ const state = createWizardState();
472
+ const json = serializeWizardState(state);
473
+ const restored = deserializeWizardState(json);
474
+ expect(restored).toBeDefined();
475
+ expect(restored!.currentStep).toBe("select-direction");
476
+ expect(isResumable(restored!)).toBe(false); // First step, nothing to resume
477
+ });
478
+
479
+ test("persist and resume at upload-bundle step", () => {
480
+ const state = advanceTo("upload-bundle");
481
+ const json = serializeWizardState(state);
482
+ const restored = deserializeWizardState(json);
483
+ expect(restored).toBeDefined();
484
+ expect(restored!.currentStep).toBe("upload-bundle");
485
+ expect(restored!.direction).toBe("managed-to-self-hosted");
486
+ expect(isResumable(restored!)).toBe(true);
487
+
488
+ const resumed = prepareForResume(restored!);
489
+ expect(resumed.currentStep).toBe("upload-bundle");
490
+ });
491
+
492
+ test("persist and resume at validate step — redirects to upload-bundle (no bundle data)", () => {
493
+ const state = advanceTo("validate");
494
+ const json = serializeWizardState(state);
495
+ const restored = deserializeWizardState(json);
496
+ expect(restored).toBeDefined();
497
+ expect(restored!.hasBundleData).toBe(false);
498
+
499
+ const resumed = prepareForResume(restored!);
500
+ // Validate step needs bundle data, so redirect to upload-bundle
501
+ expect(resumed.currentStep).toBe("upload-bundle");
502
+ });
503
+
504
+ test("persist and resume at preflight-review step — redirects to upload-bundle (no bundle data)", () => {
505
+ const state = advanceTo("preflight-review");
506
+ const json = serializeWizardState(state);
507
+ const restored = deserializeWizardState(json);
508
+ expect(restored).toBeDefined();
509
+
510
+ const resumed = prepareForResume(restored!);
511
+ expect(resumed.currentStep).toBe("upload-bundle");
512
+ });
513
+
514
+ test("persist and resume at transfer step — redirects to upload-bundle (no bundle data)", () => {
515
+ const state = advanceTo("transfer");
516
+ const json = serializeWizardState(state);
517
+ const restored = deserializeWizardState(json);
518
+ expect(restored).toBeDefined();
519
+
520
+ const resumed = prepareForResume(restored!);
521
+ expect(resumed.currentStep).toBe("upload-bundle");
522
+ });
523
+
524
+ test("persist and resume at rebind-secrets step — stays at rebind-secrets (no bundle needed)", () => {
525
+ const state = advanceTo("rebind-secrets");
526
+ const json = serializeWizardState(state);
527
+ const restored = deserializeWizardState(json);
528
+ expect(restored).toBeDefined();
529
+ expect(isResumable(restored!)).toBe(true);
530
+
531
+ const resumed = prepareForResume(restored!);
532
+ expect(resumed.currentStep).toBe("rebind-secrets");
533
+ });
534
+
535
+ test("persist and resume at complete step — not resumable", () => {
536
+ const state = advanceTo("complete");
537
+ const json = serializeWizardState(state);
538
+ const restored = deserializeWizardState(json);
539
+ expect(restored).toBeDefined();
540
+ expect(isResumable(restored!)).toBe(false);
541
+ });
542
+
543
+ test("persist and resume with loading status resets to idle", () => {
544
+ let state = advanceTo("validate");
545
+ state = {
546
+ ...state,
547
+ steps: { ...state.steps, validate: { status: "loading" } },
548
+ };
549
+
550
+ const json = serializeWizardState(state);
551
+ const restored = deserializeWizardState(json);
552
+ expect(restored).toBeDefined();
553
+
554
+ const resumed = prepareForResume(restored!);
555
+ // Loading was reset + redirected to upload-bundle because no bundle data
556
+ expect(resumed.currentStep).toBe("upload-bundle");
557
+ });
558
+
559
+ test("persist and resume with error status preserves error details", () => {
560
+ let state = advanceTo("validate");
561
+ state = {
562
+ ...state,
563
+ steps: {
564
+ ...state.steps,
565
+ validate: {
566
+ status: "error",
567
+ error: {
568
+ message: "validate: HTTP 500",
569
+ code: "HTTP_500",
570
+ retryable: true,
571
+ },
572
+ },
573
+ },
574
+ };
575
+
576
+ const json = serializeWizardState(state);
577
+ const restored = deserializeWizardState(json);
578
+ expect(restored).toBeDefined();
579
+ expect(restored!.steps.validate.status).toBe("error");
580
+ expect(restored!.steps.validate.error?.message).toBe("validate: HTTP 500");
581
+ expect(restored!.steps.validate.error?.retryable).toBe(true);
582
+ });
583
+
584
+ test("results survive round-trip serialization", () => {
585
+ let state = advanceTo("transfer");
586
+ state = {
587
+ ...state,
588
+ validateResult: makeValidateSuccess(),
589
+ preflightResult: makePreflightSuccess(),
590
+ };
591
+
592
+ const json = serializeWizardState(state);
593
+ const restored = deserializeWizardState(json);
594
+ expect(restored).toBeDefined();
595
+
596
+ // Validate result
597
+ expect(restored!.validateResult).toBeDefined();
598
+ expect(restored!.validateResult!.is_valid).toBe(true);
599
+ if (restored!.validateResult!.is_valid) {
600
+ expect(restored!.validateResult!.manifest.schema_version).toBe("1.0");
601
+ }
602
+
603
+ // Preflight result
604
+ expect(restored!.preflightResult).toBeDefined();
605
+ expect(restored!.preflightResult!.can_import).toBe(true);
606
+ if (restored!.preflightResult!.can_import) {
607
+ expect(restored!.preflightResult!.summary.total_files).toBe(3);
608
+ }
609
+ });
610
+
611
+ test("import result survives round-trip serialization", () => {
612
+ let state = advanceTo("rebind-secrets");
613
+ state = {
614
+ ...state,
615
+ importResult: makeImportSuccess(),
616
+ };
617
+
618
+ const json = serializeWizardState(state);
619
+ const restored = deserializeWizardState(json);
620
+ expect(restored).toBeDefined();
621
+
622
+ expect(restored!.importResult).toBeDefined();
623
+ expect(restored!.importResult!.success).toBe(true);
624
+ if (restored!.importResult!.success) {
625
+ expect(restored!.importResult!.summary.total_files).toBe(3);
626
+ expect(restored!.importResult!.warnings).toHaveLength(1);
627
+ }
628
+ });
629
+ });
630
+
631
+ // ===========================================================================
632
+ // 3. INTERRUPTED FLOWS
633
+ // ===========================================================================
634
+
635
+ describe("interrupted flow recovery", () => {
636
+ test("crash during transfer loading — resume resets to upload-bundle", () => {
637
+ let state = advanceTo("transfer");
638
+ state = {
639
+ ...state,
640
+ steps: { ...state.steps, transfer: { status: "loading" } },
641
+ exportResult: {
642
+ ok: true,
643
+ filename: "export.vbundle",
644
+ schemaVersion: "1.0",
645
+ manifestSha256: "abc",
646
+ },
647
+ };
648
+
649
+ const json = serializeWizardState(state);
650
+ const restored = deserializeWizardState(json);
651
+ expect(restored).toBeDefined();
652
+
653
+ const resumed = prepareForResume(restored!);
654
+ // Transfer needs bundle data; since hasBundleData=false after deserialization,
655
+ // redirect to upload-bundle
656
+ expect(resumed.currentStep).toBe("upload-bundle");
657
+ // Export result is preserved in the state even though we go back
658
+ // (the user will need to re-upload the bundle)
659
+ });
660
+
661
+ test("crash during validate loading — resume resets to upload-bundle", () => {
662
+ let state = advanceTo("validate");
663
+ state = {
664
+ ...state,
665
+ steps: { ...state.steps, validate: { status: "loading" } },
666
+ };
667
+
668
+ const json = serializeWizardState(state);
669
+ const restored = deserializeWizardState(json);
670
+ const resumed = prepareForResume(restored!);
671
+
672
+ expect(resumed.currentStep).toBe("upload-bundle");
673
+ expect(resumed.steps.validate.status).toBe("idle");
674
+ });
675
+
676
+ test("crash during preflight-review loading — resume resets to upload-bundle", () => {
677
+ let state = advanceTo("preflight-review");
678
+ state = {
679
+ ...state,
680
+ steps: { ...state.steps, "preflight-review": { status: "loading" } },
681
+ };
682
+
683
+ const json = serializeWizardState(state);
684
+ const restored = deserializeWizardState(json);
685
+ const resumed = prepareForResume(restored!);
686
+
687
+ expect(resumed.currentStep).toBe("upload-bundle");
688
+ expect(resumed.steps["preflight-review"].status).toBe("idle");
689
+ });
690
+
691
+ test("partial completion — validate done but preflight crashed", () => {
692
+ let state = advanceTo("preflight-review");
693
+ // Validation succeeded, but preflight was in progress when crash occurred
694
+ state = {
695
+ ...state,
696
+ validateResult: makeValidateSuccess(),
697
+ steps: {
698
+ ...state.steps,
699
+ validate: { status: "success" },
700
+ "preflight-review": { status: "loading" },
701
+ },
702
+ };
703
+
704
+ const json = serializeWizardState(state);
705
+ const restored = deserializeWizardState(json);
706
+ const resumed = prepareForResume(restored!);
707
+
708
+ // Goes to upload-bundle because no bundle data
709
+ expect(resumed.currentStep).toBe("upload-bundle");
710
+ // Results are cleared when rewinding to upload-bundle (no bundle data)
711
+ expect(resumed.validateResult).toBeUndefined();
712
+ });
713
+
714
+ test("partial completion — export done but import crashed", () => {
715
+ let state = advanceTo("transfer");
716
+ state = {
717
+ ...state,
718
+ steps: { ...state.steps, transfer: { status: "loading" } },
719
+ exportResult: {
720
+ ok: true,
721
+ filename: "export.vbundle",
722
+ schemaVersion: "1.0",
723
+ manifestSha256: "abc",
724
+ },
725
+ // No importResult — import had not started or crashed
726
+ };
727
+
728
+ const json = serializeWizardState(state);
729
+ const restored = deserializeWizardState(json);
730
+ const resumed = prepareForResume(restored!);
731
+
732
+ // Goes back to upload-bundle (no bundle data)
733
+ expect(resumed.currentStep).toBe("upload-bundle");
734
+ // Results are cleared when rewinding to upload-bundle (no bundle data)
735
+ expect(resumed.exportResult).toBeUndefined();
736
+ });
737
+
738
+ test("error state at transfer — user can retry after resume at rebind step doesn't apply", () => {
739
+ let state = advanceTo("transfer");
740
+ state = {
741
+ ...state,
742
+ steps: {
743
+ ...state.steps,
744
+ transfer: {
745
+ status: "error",
746
+ error: {
747
+ message: "import: HTTP 500",
748
+ code: "HTTP_500",
749
+ retryable: true,
750
+ },
751
+ },
752
+ },
753
+ };
754
+
755
+ const json = serializeWizardState(state);
756
+ const restored = deserializeWizardState(json);
757
+ expect(restored).toBeDefined();
758
+
759
+ // Without preparing for resume (raw deserialized state), error is preserved
760
+ expect(restored!.steps.transfer.status).toBe("error");
761
+ expect(canRetryCurrentStep(restored!)).toBe(true);
762
+ });
763
+ });
764
+
765
+ // ===========================================================================
766
+ // 4. FULL END-TO-END FLOWS
767
+ // ===========================================================================
768
+
769
+ describe("full end-to-end — managed-to-self-hosted migration", () => {
770
+ test("complete flow through all screen view models", async () => {
771
+ // Step 1: Create wizard and select direction
772
+ let state = createWizardState();
773
+ expect(deriveValidationScreenState(state).phase).toBe("disabled");
774
+ expect(deriveTransferScreenState(state).phase).toBe("disabled");
775
+ expect(
776
+ deriveRebindSecretsScreenState(state, createTaskCompletionState()).phase,
777
+ ).toBe("disabled");
778
+
779
+ state = selectDirection(state, "managed-to-self-hosted");
780
+ expect(state.currentStep).toBe("upload-bundle");
781
+ expect(state.direction).toBe("managed-to-self-hosted");
782
+
783
+ // Step 2: Upload bundle
784
+ state = setBundleUploaded(state);
785
+ expect(state.currentStep).toBe("validate");
786
+ expect(state.hasBundleData).toBe(true);
787
+
788
+ // Step 3: Validate + preflight
789
+ const validateSuccess = makeValidateSuccess();
790
+ const preflightSuccess = makePreflightSuccess();
791
+
792
+ let callCount = 0;
793
+ const sequentialFetch = (async () => {
794
+ callCount++;
795
+ if (callCount === 1) {
796
+ return new Response(JSON.stringify(validateSuccess), {
797
+ status: 200,
798
+ headers: { "Content-Type": "application/json" },
799
+ });
800
+ }
801
+ return new Response(JSON.stringify(preflightSuccess), {
802
+ status: 200,
803
+ headers: { "Content-Type": "application/json" },
804
+ });
805
+ }) as unknown as typeof fetch;
806
+
807
+ const validateOptions = makeExecutorOptions({
808
+ destConfig: runtimeConfig({ fetchFn: sequentialFetch }),
809
+ });
810
+
811
+ state = await executeValidationFlow(state, validateOptions);
812
+ expect(state.currentStep).toBe("transfer");
813
+
814
+ // Verify validation screen shows success
815
+ const valScreen = deriveValidationScreenState(state);
816
+ expect(valScreen.phase).toBe("success");
817
+ if (valScreen.phase === "success") {
818
+ expect(valScreen.validation.isValid).toBe(true);
819
+ expect(valScreen.preflight.summary.totalFiles).toBe(3);
820
+ }
821
+
822
+ // Step 4: Transfer (managed export via async polling + runtime import)
823
+ const archiveBytes = new ArrayBuffer(32);
824
+ const importResponse = makeImportSuccess();
825
+
826
+ let exportCallCount = 0;
827
+ const sourceFetch = (async (url: string | URL | Request) => {
828
+ const urlStr = String(url);
829
+ if (urlStr.endsWith("/export/")) {
830
+ // Managed export: initiate async job
831
+ return new Response(
832
+ JSON.stringify({ job_id: "exp-m2sh", status: "pending" }),
833
+ {
834
+ status: 200,
835
+ headers: { "Content-Type": "application/json" },
836
+ },
837
+ );
838
+ }
839
+ if (urlStr.includes("/export/") && urlStr.includes("/status/")) {
840
+ exportCallCount++;
841
+ if (exportCallCount === 1) {
842
+ // First poll: still in progress
843
+ return new Response(
844
+ JSON.stringify({
845
+ job_id: "exp-m2sh",
846
+ status: "in_progress",
847
+ progress: 50,
848
+ }),
849
+ {
850
+ status: 200,
851
+ headers: { "Content-Type": "application/json" },
852
+ },
853
+ );
854
+ }
855
+ // Second poll: complete with download URL
856
+ return new Response(
857
+ JSON.stringify({
858
+ job_id: "exp-m2sh",
859
+ status: "complete",
860
+ download_url: "https://platform.vellum.ai/downloads/exp-m2sh",
861
+ }),
862
+ {
863
+ status: 200,
864
+ headers: { "Content-Type": "application/json" },
865
+ },
866
+ );
867
+ }
868
+ if (urlStr.includes("/downloads/")) {
869
+ // Download the exported archive
870
+ return new Response(archiveBytes, {
871
+ status: 200,
872
+ headers: {
873
+ "Content-Disposition": 'attachment; filename="export.vbundle"',
874
+ },
875
+ });
876
+ }
877
+ return new Response("Not found", { status: 404 });
878
+ }) as unknown as typeof fetch;
879
+
880
+ const destFetch = (async () => {
881
+ return new Response(JSON.stringify(importResponse), {
882
+ status: 200,
883
+ headers: { "Content-Type": "application/json" },
884
+ });
885
+ }) as unknown as typeof fetch;
886
+
887
+ const transferOptions = makeExecutorOptions({
888
+ sourceConfig: managedConfig({ fetchFn: sourceFetch }),
889
+ destConfig: runtimeConfig({ fetchFn: destFetch }),
890
+ });
891
+
892
+ state = await executeTransferFlow(state, transferOptions);
893
+ expect(state.currentStep).toBe("rebind-secrets");
894
+
895
+ // Verify transfer screen shows success
896
+ const transferScreen = deriveTransferScreenState(state);
897
+ expect(transferScreen.phase).toBe("success");
898
+ if (transferScreen.phase === "success") {
899
+ expect(transferScreen.importSummary.totalFiles).toBe(3);
900
+ }
901
+
902
+ // Step 5: Rebind secrets
903
+ let completion = createTaskCompletionState();
904
+ let rebindScreen = deriveRebindSecretsScreenState(state, completion);
905
+ expect(rebindScreen.phase).toBe("active");
906
+ if (rebindScreen.phase === "active") {
907
+ expect(rebindScreen.allRequiredComplete).toBe(false);
908
+ }
909
+
910
+ // Complete required tasks one by one
911
+ completion = markTaskComplete(completion, "re-enter-secrets");
912
+ completion = markTaskComplete(completion, "rebind-channels");
913
+ completion = markTaskComplete(completion, "reconfigure-auth");
914
+
915
+ rebindScreen = deriveRebindSecretsScreenState(state, completion);
916
+ if (rebindScreen.phase === "active") {
917
+ expect(rebindScreen.allRequiredComplete).toBe(true);
918
+ }
919
+
920
+ // Step 6: Complete migration
921
+ state = completeMigration(state, completion);
922
+ expect(state.currentStep).toBe("complete");
923
+ expect(isWizardComplete(state)).toBe(true);
924
+
925
+ // All screens should still show their success states
926
+ expect(deriveValidationScreenState(state).phase).toBe("success");
927
+ expect(deriveTransferScreenState(state).phase).toBe("success");
928
+ expect(deriveRebindSecretsScreenState(state, completion).phase).toBe(
929
+ "complete",
930
+ );
931
+ });
932
+ });
933
+
934
+ describe("full end-to-end — self-hosted-to-managed migration", () => {
935
+ test("complete flow with managed export (async polling)", async () => {
936
+ // Step 1: Create wizard and select direction
937
+ let state = createWizardState();
938
+ state = selectDirection(state, "self-hosted-to-managed");
939
+ expect(state.direction).toBe("self-hosted-to-managed");
940
+
941
+ // Step 2: Upload bundle
942
+ state = setBundleUploaded(state);
943
+
944
+ // Step 3: Validate + preflight
945
+ const validateSuccess = makeValidateSuccess();
946
+ const preflightSuccess = makePreflightSuccess();
947
+
948
+ let valCallCount = 0;
949
+ const sequentialFetch = (async () => {
950
+ valCallCount++;
951
+ if (valCallCount === 1) {
952
+ return new Response(JSON.stringify(validateSuccess), {
953
+ status: 200,
954
+ headers: { "Content-Type": "application/json" },
955
+ });
956
+ }
957
+ return new Response(JSON.stringify(preflightSuccess), {
958
+ status: 200,
959
+ headers: { "Content-Type": "application/json" },
960
+ });
961
+ }) as unknown as typeof fetch;
962
+
963
+ state = await executeValidationFlow(
964
+ state,
965
+ makeExecutorOptions({
966
+ destConfig: managedConfig({ fetchFn: sequentialFetch }),
967
+ }),
968
+ );
969
+ expect(state.currentStep).toBe("transfer");
970
+
971
+ // Step 4: Transfer with managed source (async polling)
972
+ const importResponse = makeImportSuccess();
973
+
974
+ const sourceFetch = (async (url: string | URL | Request) => {
975
+ const urlStr = String(url);
976
+ if (urlStr.endsWith("/export")) {
977
+ // Runtime export returns binary directly
978
+ return new Response(new ArrayBuffer(32), {
979
+ status: 200,
980
+ headers: {
981
+ "Content-Disposition": 'attachment; filename="export.vbundle"',
982
+ "X-Vbundle-Schema-Version": "1.0",
983
+ "X-Vbundle-Manifest-Sha256": "abc",
984
+ },
985
+ });
986
+ }
987
+ return new Response("Not found", { status: 404 });
988
+ }) as unknown as typeof fetch;
989
+
990
+ const destFetch = (async () => {
991
+ return new Response(JSON.stringify(importResponse), {
992
+ status: 200,
993
+ headers: { "Content-Type": "application/json" },
994
+ });
995
+ }) as unknown as typeof fetch;
996
+
997
+ state = await executeTransferFlow(
998
+ state,
999
+ makeExecutorOptions({
1000
+ sourceConfig: runtimeConfig({ fetchFn: sourceFetch }),
1001
+ destConfig: managedConfig({ fetchFn: destFetch }),
1002
+ }),
1003
+ );
1004
+ expect(state.currentStep).toBe("rebind-secrets");
1005
+
1006
+ // Step 5-6: Rebind and complete
1007
+ const completion = completeOnlyRequired(createTaskCompletionState());
1008
+ state = completeMigration(state, completion);
1009
+ expect(isWizardComplete(state)).toBe(true);
1010
+ });
1011
+ });
1012
+
1013
+ // ===========================================================================
1014
+ // 5. EDGE CASES
1015
+ // ===========================================================================
1016
+
1017
+ describe("edge cases — import failures and transport errors", () => {
1018
+ test("transfer step handles import validation failure as retryable error", async () => {
1019
+ const destFetch = (async () => {
1020
+ return new Response(
1021
+ JSON.stringify({
1022
+ success: false,
1023
+ reason: "validation_failed",
1024
+ message: "Manifest SHA mismatch",
1025
+ }),
1026
+ {
1027
+ status: 200,
1028
+ headers: { "Content-Type": "application/json" },
1029
+ },
1030
+ );
1031
+ }) as unknown as typeof fetch;
1032
+
1033
+ const state = advanceTo("transfer");
1034
+ const options = makeExecutorOptions({
1035
+ destConfig: runtimeConfig({ fetchFn: destFetch }),
1036
+ });
1037
+
1038
+ const result = await executeTransferFlow(state, options);
1039
+ expect(result.steps.transfer.status).toBe("error");
1040
+ expect(result.steps.transfer.error?.message).toContain(
1041
+ "Manifest SHA mismatch",
1042
+ );
1043
+ expect(result.steps.transfer.error?.code).toBe("validation_failed");
1044
+
1045
+ const screen = deriveTransferScreenState(result);
1046
+ expect(screen.phase).toBe("error");
1047
+ if (screen.phase === "error") {
1048
+ expect(screen.failedPhase).toBe("import");
1049
+ expect(screen.canRetry).toBe(true);
1050
+ }
1051
+ });
1052
+
1053
+ test("transfer step handles import write failure", async () => {
1054
+ const destFetch = (async () => {
1055
+ return new Response(
1056
+ JSON.stringify({
1057
+ success: false,
1058
+ reason: "write_failed",
1059
+ message: "Disk full",
1060
+ }),
1061
+ {
1062
+ status: 200,
1063
+ headers: { "Content-Type": "application/json" },
1064
+ },
1065
+ );
1066
+ }) as unknown as typeof fetch;
1067
+
1068
+ const state = advanceTo("transfer");
1069
+ const options = makeExecutorOptions({
1070
+ destConfig: runtimeConfig({ fetchFn: destFetch }),
1071
+ });
1072
+
1073
+ const result = await executeTransferFlow(state, options);
1074
+ expect(result.steps.transfer.status).toBe("error");
1075
+ expect(result.steps.transfer.error?.code).toBe("write_failed");
1076
+ expect(result.steps.transfer.error?.retryable).toBe(true);
1077
+ });
1078
+
1079
+ test("transfer step handles import extraction failure", async () => {
1080
+ const destFetch = (async () => {
1081
+ return new Response(
1082
+ JSON.stringify({
1083
+ success: false,
1084
+ reason: "extraction_failed",
1085
+ message: "Corrupt archive",
1086
+ }),
1087
+ {
1088
+ status: 200,
1089
+ headers: { "Content-Type": "application/json" },
1090
+ },
1091
+ );
1092
+ }) as unknown as typeof fetch;
1093
+
1094
+ const state = advanceTo("transfer");
1095
+ const options = makeExecutorOptions({
1096
+ destConfig: runtimeConfig({ fetchFn: destFetch }),
1097
+ });
1098
+
1099
+ const result = await executeTransferFlow(state, options);
1100
+ expect(result.steps.transfer.status).toBe("error");
1101
+ expect(result.steps.transfer.error?.message).toContain("Corrupt archive");
1102
+ });
1103
+
1104
+ test("transfer step handles destination HTTP 503 as retryable transport error", async () => {
1105
+ const state = advanceTo("transfer");
1106
+ const options = makeExecutorOptions({
1107
+ destConfig: runtimeConfig({
1108
+ fetchFn: mockFetch(503, "Service Unavailable"),
1109
+ }),
1110
+ });
1111
+
1112
+ const result = await executeTransferFlow(state, options);
1113
+ expect(result.steps.transfer.status).toBe("error");
1114
+
1115
+ const screen = deriveTransferScreenState(result);
1116
+ expect(screen.phase).toBe("error");
1117
+ if (screen.phase === "error") {
1118
+ expect(screen.canRetry).toBe(true);
1119
+ expect(screen.failedPhase).toBe("import");
1120
+ }
1121
+ });
1122
+
1123
+ test("transfer step handles destination HTTP 429 rate limit as retryable", async () => {
1124
+ const archiveBytes = new ArrayBuffer(32);
1125
+ const sourceFetch = (async () => {
1126
+ return new Response(archiveBytes, {
1127
+ status: 200,
1128
+ headers: {
1129
+ "Content-Disposition": 'attachment; filename="export.vbundle"',
1130
+ "X-Vbundle-Schema-Version": "1.0",
1131
+ "X-Vbundle-Manifest-Sha256": "abc",
1132
+ },
1133
+ });
1134
+ }) as unknown as typeof fetch;
1135
+
1136
+ const state = advanceTo("transfer");
1137
+ const options = makeExecutorOptions({
1138
+ sourceConfig: runtimeConfig({ fetchFn: sourceFetch }),
1139
+ destConfig: runtimeConfig({
1140
+ fetchFn: mockFetch(429, "Too Many Requests"),
1141
+ }),
1142
+ });
1143
+
1144
+ const result = await executeTransferFlow(state, options);
1145
+ expect(result.steps.transfer.status).toBe("error");
1146
+ expect(result.steps.transfer.error?.retryable).toBe(true);
1147
+ expect(result.steps.transfer.error?.code).toBe("HTTP_429");
1148
+ });
1149
+ });
1150
+
1151
+ describe("edge cases — stale/expired states", () => {
1152
+ test("very old serialized state deserializes correctly", () => {
1153
+ // Simulate a state serialized months ago
1154
+ const oldState: MigrationWizardState = {
1155
+ currentStep: "rebind-secrets",
1156
+ direction: "managed-to-self-hosted",
1157
+ steps: {
1158
+ "select-direction": { status: "success" },
1159
+ "upload-bundle": { status: "success" },
1160
+ validate: { status: "success" },
1161
+ "preflight-review": { status: "success" },
1162
+ transfer: { status: "success" },
1163
+ "rebind-secrets": { status: "idle" },
1164
+ complete: { status: "idle" },
1165
+ },
1166
+ hasBundleData: true,
1167
+ createdAt: "2024-01-01T00:00:00Z", // Over a year old
1168
+ updatedAt: "2024-01-01T01:00:00Z",
1169
+ };
1170
+
1171
+ const json = JSON.stringify(oldState);
1172
+ const restored = deserializeWizardState(json);
1173
+ expect(restored).toBeDefined();
1174
+ expect(restored!.currentStep).toBe("rebind-secrets");
1175
+ expect(restored!.hasBundleData).toBe(false); // Always false after deserialization
1176
+
1177
+ // Resume should work even for stale state
1178
+ expect(isResumable(restored!)).toBe(true);
1179
+ const resumed = prepareForResume(restored!);
1180
+ expect(resumed.currentStep).toBe("rebind-secrets");
1181
+ });
1182
+
1183
+ test("state with expired timestamps still functions correctly", () => {
1184
+ const state = advanceTo("transfer");
1185
+ // Manually set old timestamps
1186
+ const oldState = {
1187
+ ...state,
1188
+ createdAt: "2023-06-15T00:00:00Z",
1189
+ updatedAt: "2023-06-15T01:00:00Z",
1190
+ };
1191
+
1192
+ const json = serializeWizardState(oldState);
1193
+ const restored = deserializeWizardState(json);
1194
+ expect(restored).toBeDefined();
1195
+ expect(restored!.createdAt).toBe("2023-06-15T00:00:00Z");
1196
+
1197
+ // The wizard should still work — transfer step is import-only
1198
+ const screen = deriveTransferScreenState(restored!);
1199
+ expect(screen.phase).toBe("importing");
1200
+ });
1201
+ });
1202
+
1203
+ describe("edge cases — concurrent wizard instances", () => {
1204
+ test("two independent wizard instances maintain separate state", () => {
1205
+ // Advance two wizards to different steps
1206
+ const w1Advanced = advanceTo("transfer", "managed-to-self-hosted");
1207
+ const w2Advanced = advanceTo("upload-bundle", "self-hosted-to-managed");
1208
+
1209
+ // States are completely independent
1210
+ expect(w1Advanced.currentStep).toBe("transfer");
1211
+ expect(w2Advanced.currentStep).toBe("upload-bundle");
1212
+ expect(w1Advanced.direction).toBe("managed-to-self-hosted");
1213
+ expect(w2Advanced.direction).toBe("self-hosted-to-managed");
1214
+
1215
+ // Serializing both produces independent JSON
1216
+ const json1 = serializeWizardState(w1Advanced);
1217
+ const json2 = serializeWizardState(w2Advanced);
1218
+ expect(json1).not.toBe(json2);
1219
+
1220
+ // Deserializing restores independent state
1221
+ const restored1 = deserializeWizardState(json1);
1222
+ const restored2 = deserializeWizardState(json2);
1223
+ expect(restored1!.currentStep).toBe("transfer");
1224
+ expect(restored2!.currentStep).toBe("upload-bundle");
1225
+ });
1226
+
1227
+ test("modifying one wizard does not affect a serialized snapshot of another", () => {
1228
+ let wizard = advanceTo("rebind-secrets");
1229
+ const snapshot = serializeWizardState(wizard);
1230
+
1231
+ // Advance the wizard to complete
1232
+ wizard = completeRebindSecrets(wizard);
1233
+ expect(wizard.currentStep).toBe("complete");
1234
+
1235
+ // The snapshot should still restore to rebind-secrets
1236
+ const restored = deserializeWizardState(snapshot);
1237
+ expect(restored!.currentStep).toBe("rebind-secrets");
1238
+ expect(isWizardComplete(restored!)).toBe(false);
1239
+ });
1240
+ });
1241
+
1242
+ describe("edge cases — state corruption recovery", () => {
1243
+ test("empty string returns undefined", () => {
1244
+ expect(deserializeWizardState("")).toBeUndefined();
1245
+ });
1246
+
1247
+ test("null returns undefined", () => {
1248
+ expect(deserializeWizardState("null")).toBeUndefined();
1249
+ });
1250
+
1251
+ test("invalid JSON returns undefined", () => {
1252
+ expect(deserializeWizardState("{invalid json}")).toBeUndefined();
1253
+ });
1254
+
1255
+ test("valid JSON but missing required fields returns undefined", () => {
1256
+ expect(deserializeWizardState('{"foo": "bar"}')).toBeUndefined();
1257
+ });
1258
+
1259
+ test("missing currentStep returns undefined", () => {
1260
+ const partial = {
1261
+ steps: {},
1262
+ createdAt: "2025-01-01T00:00:00Z",
1263
+ updatedAt: "2025-01-01T00:00:00Z",
1264
+ };
1265
+ expect(deserializeWizardState(JSON.stringify(partial))).toBeUndefined();
1266
+ });
1267
+
1268
+ test("unknown currentStep value returns undefined", () => {
1269
+ const invalid: Record<string, unknown> = {
1270
+ currentStep: "nonexistent-step",
1271
+ steps: {
1272
+ "select-direction": { status: "idle" },
1273
+ "upload-bundle": { status: "idle" },
1274
+ validate: { status: "idle" },
1275
+ "preflight-review": { status: "idle" },
1276
+ transfer: { status: "idle" },
1277
+ "rebind-secrets": { status: "idle" },
1278
+ complete: { status: "idle" },
1279
+ },
1280
+ hasBundleData: false,
1281
+ createdAt: "2025-01-01T00:00:00Z",
1282
+ updatedAt: "2025-01-01T00:00:00Z",
1283
+ };
1284
+ expect(deserializeWizardState(JSON.stringify(invalid))).toBeUndefined();
1285
+ });
1286
+
1287
+ test("missing step entries returns undefined", () => {
1288
+ const incomplete = {
1289
+ currentStep: "validate",
1290
+ steps: {
1291
+ "select-direction": { status: "success" },
1292
+ // Missing other steps
1293
+ },
1294
+ createdAt: "2025-01-01T00:00:00Z",
1295
+ updatedAt: "2025-01-01T00:00:00Z",
1296
+ };
1297
+ expect(deserializeWizardState(JSON.stringify(incomplete))).toBeUndefined();
1298
+ });
1299
+
1300
+ test("step with missing status returns undefined", () => {
1301
+ const badStatus = {
1302
+ currentStep: "validate",
1303
+ steps: {
1304
+ "select-direction": { status: "success" },
1305
+ "upload-bundle": { status: "success" },
1306
+ validate: {}, // Missing status
1307
+ "preflight-review": { status: "idle" },
1308
+ transfer: { status: "idle" },
1309
+ "rebind-secrets": { status: "idle" },
1310
+ complete: { status: "idle" },
1311
+ },
1312
+ createdAt: "2025-01-01T00:00:00Z",
1313
+ updatedAt: "2025-01-01T00:00:00Z",
1314
+ };
1315
+ expect(deserializeWizardState(JSON.stringify(badStatus))).toBeUndefined();
1316
+ });
1317
+
1318
+ test("corrupted JSON with partial data is safely rejected", () => {
1319
+ const truncated = '{"currentStep":"validate","steps":{"select-direction":{';
1320
+ expect(deserializeWizardState(truncated)).toBeUndefined();
1321
+ });
1322
+
1323
+ test("array instead of object returns undefined", () => {
1324
+ expect(deserializeWizardState("[1, 2, 3]")).toBeUndefined();
1325
+ });
1326
+
1327
+ test("number instead of object returns undefined", () => {
1328
+ expect(deserializeWizardState("42")).toBeUndefined();
1329
+ });
1330
+ });
1331
+
1332
+ describe("edge cases — bundle data loss after deserialization", () => {
1333
+ test("hasBundleData is always false after deserialization", () => {
1334
+ const state = advanceTo("validate");
1335
+ expect(state.hasBundleData).toBe(true);
1336
+
1337
+ const json = serializeWizardState(state);
1338
+ const restored = deserializeWizardState(json);
1339
+ expect(restored!.hasBundleData).toBe(false);
1340
+ });
1341
+
1342
+ test("all bundle-requiring steps redirect to upload-bundle on resume without bundle data", () => {
1343
+ const bundleRequiringSteps: WizardStep[] = [
1344
+ "validate",
1345
+ "preflight-review",
1346
+ "transfer",
1347
+ ];
1348
+
1349
+ for (const step of bundleRequiringSteps) {
1350
+ const state = advanceTo(step);
1351
+ const json = serializeWizardState(state);
1352
+ const restored = deserializeWizardState(json);
1353
+ const resumed = prepareForResume(restored!);
1354
+
1355
+ expect(resumed.currentStep).toBe("upload-bundle");
1356
+ }
1357
+ });
1358
+
1359
+ test("non-bundle-requiring steps stay in place on resume", () => {
1360
+ // rebind-secrets does not need bundle data
1361
+ const state = advanceTo("rebind-secrets");
1362
+ const json = serializeWizardState(state);
1363
+ const restored = deserializeWizardState(json);
1364
+ const resumed = prepareForResume(restored!);
1365
+
1366
+ expect(resumed.currentStep).toBe("rebind-secrets");
1367
+ });
1368
+
1369
+ test("direction survives but step results are cleared when bundle data is lost", () => {
1370
+ let state = advanceTo("transfer");
1371
+ state = {
1372
+ ...state,
1373
+ validateResult: makeValidateSuccess(),
1374
+ preflightResult: makePreflightSuccess(),
1375
+ };
1376
+
1377
+ const json = serializeWizardState(state);
1378
+ const restored = deserializeWizardState(json);
1379
+ const resumed = prepareForResume(restored!);
1380
+
1381
+ // Redirected to upload-bundle; direction is preserved but results are cleared
1382
+ expect(resumed.currentStep).toBe("upload-bundle");
1383
+ expect(resumed.direction).toBe("managed-to-self-hosted");
1384
+ expect(resumed.validateResult).toBeUndefined();
1385
+ expect(resumed.preflightResult).toBeUndefined();
1386
+ });
1387
+ });
1388
+
1389
+ // ===========================================================================
1390
+ // 6. CROSS-SCREEN CONSISTENCY
1391
+ // ===========================================================================
1392
+
1393
+ describe("cross-screen consistency — validation -> transfer -> rebind", () => {
1394
+ test("all screens report disabled before direction is selected", () => {
1395
+ const state = createWizardState();
1396
+ const completion = createTaskCompletionState();
1397
+
1398
+ expect(deriveValidationScreenState(state).phase).toBe("disabled");
1399
+ expect(deriveTransferScreenState(state).phase).toBe("disabled");
1400
+ expect(deriveRebindSecretsScreenState(state, completion).phase).toBe(
1401
+ "disabled",
1402
+ );
1403
+ });
1404
+
1405
+ test("only validation screen is accessible at validate step", () => {
1406
+ const state = advanceTo("validate");
1407
+
1408
+ expect(isValidationScreenAccessible(state)).toBe(true);
1409
+ expect(isTransferScreenAccessible(state)).toBe(false);
1410
+ expect(isRebindSecretsScreenAccessible(state)).toBe(false);
1411
+ });
1412
+
1413
+ test("validation and transfer screens accessible at transfer step", () => {
1414
+ const state = advanceTo("transfer");
1415
+
1416
+ expect(isValidationScreenAccessible(state)).toBe(true);
1417
+ expect(isTransferScreenAccessible(state)).toBe(true);
1418
+ expect(isRebindSecretsScreenAccessible(state)).toBe(false);
1419
+ });
1420
+
1421
+ test("all screens accessible at rebind-secrets step", () => {
1422
+ const state = advanceTo("rebind-secrets");
1423
+
1424
+ // Validation screen shows success from earlier steps
1425
+ expect(deriveValidationScreenState(state).phase).toBe("success");
1426
+ // Transfer screen shows success
1427
+ expect(deriveTransferScreenState(state).phase).toBe("success");
1428
+ // Rebind screen is active
1429
+ expect(isRebindSecretsScreenAccessible(state)).toBe(true);
1430
+ });
1431
+
1432
+ test("all screens show their success states at complete step", () => {
1433
+ const state = advanceTo("complete");
1434
+ const completion = completeAllTasks(createTaskCompletionState());
1435
+
1436
+ expect(deriveValidationScreenState(state).phase).toBe("success");
1437
+ expect(deriveTransferScreenState(state).phase).toBe("success");
1438
+ expect(deriveRebindSecretsScreenState(state, completion).phase).toBe(
1439
+ "complete",
1440
+ );
1441
+ });
1442
+
1443
+ test("going back from transfer resets transfer but preserves validation", () => {
1444
+ let state = advanceTo("transfer");
1445
+ state = {
1446
+ ...state,
1447
+ validateResult: makeValidateSuccess(),
1448
+ preflightResult: makePreflightSuccess(),
1449
+ };
1450
+
1451
+ // Go back to preflight-review
1452
+ const backState = goBackTo(state, "preflight-review");
1453
+
1454
+ // Validation screen should still show results
1455
+ const valScreen = deriveValidationScreenState(backState);
1456
+ expect(valScreen.phase).toBe("loading"); // preflight idle means "preparing"
1457
+
1458
+ // Transfer screen should be disabled
1459
+ expect(deriveTransferScreenState(backState).phase).toBe("disabled");
1460
+ });
1461
+
1462
+ test("going back from rebind-secrets resets rebind and transfer", () => {
1463
+ let state = advanceTo("rebind-secrets");
1464
+ state = {
1465
+ ...state,
1466
+ validateResult: makeValidateSuccess(),
1467
+ preflightResult: makePreflightSuccess(),
1468
+ importResult: makeImportSuccess(),
1469
+ };
1470
+
1471
+ // Go back to transfer
1472
+ const backState = goBackTo(state, "transfer");
1473
+
1474
+ expect(backState.currentStep).toBe("transfer");
1475
+ expect(backState.steps["rebind-secrets"].status).toBe("idle");
1476
+ expect(backState.steps.transfer.status).toBe("idle");
1477
+
1478
+ // Validation results should be preserved
1479
+ expect(backState.validateResult).toBeDefined();
1480
+ expect(backState.preflightResult).toBeDefined();
1481
+ // Transfer results should be cleared
1482
+ expect(backState.exportResult).toBeUndefined();
1483
+ expect(backState.importResult).toBeUndefined();
1484
+ });
1485
+
1486
+ test("screen states are consistent through full serialize/resume cycle at each step", () => {
1487
+ const steps: WizardStep[] = [
1488
+ "select-direction",
1489
+ "upload-bundle",
1490
+ "validate",
1491
+ "preflight-review",
1492
+ "transfer",
1493
+ "rebind-secrets",
1494
+ "complete",
1495
+ ];
1496
+
1497
+ for (const step of steps) {
1498
+ const state = advanceTo(step);
1499
+ const json = serializeWizardState(state);
1500
+ const restored = deserializeWizardState(json);
1501
+ expect(restored).toBeDefined();
1502
+
1503
+ // Screen derivation should not throw for any step
1504
+ const valScreen = deriveValidationScreenState(restored!);
1505
+ const transferScreen = deriveTransferScreenState(restored!);
1506
+ const rebindScreen = deriveRebindSecretsScreenState(
1507
+ restored!,
1508
+ createTaskCompletionState(),
1509
+ );
1510
+
1511
+ // All screens should return a valid phase
1512
+ expect(valScreen.phase).toBeDefined();
1513
+ expect(transferScreen.phase).toBeDefined();
1514
+ expect(rebindScreen.phase).toBeDefined();
1515
+ }
1516
+ });
1517
+
1518
+ test("validation screen success data is consistent with preflight results", () => {
1519
+ const state = advanceTo("transfer");
1520
+ const screen = deriveValidationScreenState(state);
1521
+
1522
+ if (screen.phase === "success") {
1523
+ const preflightResult = state.preflightResult;
1524
+ expect(preflightResult).toBeDefined();
1525
+
1526
+ if (preflightResult && preflightResult.can_import) {
1527
+ // Screen summary should match raw preflight data
1528
+ expect(screen.preflight.summary.totalFiles).toBe(
1529
+ preflightResult.summary.total_files,
1530
+ );
1531
+ expect(screen.preflight.summary.filesToCreate).toBe(
1532
+ preflightResult.summary.files_to_create,
1533
+ );
1534
+ expect(screen.preflight.summary.filesToOverwrite).toBe(
1535
+ preflightResult.summary.files_to_overwrite,
1536
+ );
1537
+ expect(screen.preflight.summary.filesUnchanged).toBe(
1538
+ preflightResult.summary.files_unchanged,
1539
+ );
1540
+
1541
+ // File count should match
1542
+ expect(screen.preflight.files.length).toBe(
1543
+ preflightResult.files.length,
1544
+ );
1545
+ }
1546
+ }
1547
+ });
1548
+
1549
+ test("transfer screen import summary is consistent with import result", () => {
1550
+ const state = advanceTo("rebind-secrets");
1551
+ const screen = deriveTransferScreenState(state);
1552
+
1553
+ if (screen.phase === "success") {
1554
+ const importResult = state.importResult;
1555
+ expect(importResult).toBeDefined();
1556
+
1557
+ if (importResult && importResult.success) {
1558
+ expect(screen.importSummary.totalFiles).toBe(
1559
+ importResult.summary.total_files,
1560
+ );
1561
+ expect(screen.importSummary.filesCreated).toBe(
1562
+ importResult.summary.files_created,
1563
+ );
1564
+ expect(screen.importSummary.filesOverwritten).toBe(
1565
+ importResult.summary.files_overwritten,
1566
+ );
1567
+ expect(screen.importSummary.filesSkipped).toBe(
1568
+ importResult.summary.files_skipped,
1569
+ );
1570
+ expect(screen.importSummary.backupsCreated).toBe(
1571
+ importResult.summary.backups_created,
1572
+ );
1573
+ }
1574
+ }
1575
+ });
1576
+
1577
+ test("rebind task count stays consistent throughout the wizard lifecycle", () => {
1578
+ const taskIds = getTaskIds();
1579
+ const expectedTaskCount = 4;
1580
+
1581
+ expect(taskIds.length).toBe(expectedTaskCount);
1582
+
1583
+ // At every step, if the screen is active, it should show the same tasks
1584
+ const state = advanceTo("rebind-secrets");
1585
+ const completion = createTaskCompletionState();
1586
+ const screen = deriveRebindSecretsScreenState(state, completion);
1587
+
1588
+ if (screen.phase === "active") {
1589
+ expect(screen.totalCount).toBe(expectedTaskCount);
1590
+ expect(screen.tasks.length).toBe(expectedTaskCount);
1591
+ // Task IDs match
1592
+ const screenTaskIds = screen.tasks.map((t) => t.id);
1593
+ expect(screenTaskIds).toEqual([...taskIds]);
1594
+ }
1595
+ });
1596
+ });
1597
+
1598
+ describe("cross-screen consistency — error propagation", () => {
1599
+ test("validation error does not leak into transfer or rebind screens", () => {
1600
+ let state = advanceTo("validate");
1601
+ state = {
1602
+ ...state,
1603
+ steps: {
1604
+ ...state.steps,
1605
+ validate: {
1606
+ status: "error",
1607
+ error: {
1608
+ message: "validate: HTTP 500",
1609
+ code: "HTTP_500",
1610
+ retryable: true,
1611
+ },
1612
+ },
1613
+ },
1614
+ };
1615
+
1616
+ // Validation screen shows the error
1617
+ const valScreen = deriveValidationScreenState(state);
1618
+ expect(valScreen.phase).toBe("transport-error");
1619
+
1620
+ // Transfer and rebind screens should be disabled, not errored
1621
+ expect(deriveTransferScreenState(state).phase).toBe("disabled");
1622
+ expect(
1623
+ deriveRebindSecretsScreenState(state, createTaskCompletionState()).phase,
1624
+ ).toBe("disabled");
1625
+ });
1626
+
1627
+ test("transfer error does not leak into validation or rebind screens", () => {
1628
+ let state = advanceTo("transfer");
1629
+ state = {
1630
+ ...state,
1631
+ steps: {
1632
+ ...state.steps,
1633
+ transfer: {
1634
+ status: "error",
1635
+ error: {
1636
+ message: "export: HTTP 500",
1637
+ code: "HTTP_500",
1638
+ retryable: true,
1639
+ },
1640
+ },
1641
+ },
1642
+ };
1643
+
1644
+ // Transfer screen shows the error
1645
+ const transferScreen = deriveTransferScreenState(state);
1646
+ expect(transferScreen.phase).toBe("error");
1647
+
1648
+ // Validation screen should still show success (validate and preflight passed)
1649
+ const valScreen = deriveValidationScreenState(state);
1650
+ expect(valScreen.phase).toBe("success");
1651
+
1652
+ // Rebind screen should be disabled
1653
+ expect(
1654
+ deriveRebindSecretsScreenState(state, createTaskCompletionState()).phase,
1655
+ ).toBe("disabled");
1656
+ });
1657
+ });
1658
+
1659
+ describe("cross-screen consistency — step accessibility invariants", () => {
1660
+ test("step accessibility is monotonically increasing through normal flow", () => {
1661
+ const steps: WizardStep[] = [
1662
+ "select-direction",
1663
+ "upload-bundle",
1664
+ "validate",
1665
+ "preflight-review",
1666
+ "transfer",
1667
+ "rebind-secrets",
1668
+ ];
1669
+
1670
+ // At each step, all previous steps should be accessible (can go back)
1671
+ for (let i = 0; i < steps.length; i++) {
1672
+ const state = advanceTo(steps[i]);
1673
+ for (let j = 0; j < i; j++) {
1674
+ expect(isStepAccessible(state, steps[j])).toBe(true);
1675
+ }
1676
+ }
1677
+ });
1678
+
1679
+ test("complete step is terminal — no transitions allowed", () => {
1680
+ const state = advanceTo("complete");
1681
+ const steps: WizardStep[] = [
1682
+ "select-direction",
1683
+ "upload-bundle",
1684
+ "validate",
1685
+ "preflight-review",
1686
+ "transfer",
1687
+ "rebind-secrets",
1688
+ ];
1689
+
1690
+ for (const step of steps) {
1691
+ const result = validateWizardTransition(state, step);
1692
+ expect(result.valid).toBe(false);
1693
+ expect(result.reason).toContain("terminal");
1694
+ }
1695
+ });
1696
+ });
1697
+
1698
+ // ===========================================================================
1699
+ // 7. ADDITIONAL PERSISTENCE EDGE CASES
1700
+ // ===========================================================================
1701
+
1702
+ describe("persistence — task completion state portability", () => {
1703
+ test("task completion state serializes and deserializes independently", () => {
1704
+ let completion = createTaskCompletionState();
1705
+ completion = markTaskComplete(completion, "re-enter-secrets");
1706
+ completion = markTaskComplete(completion, "rebind-channels");
1707
+
1708
+ const json = JSON.stringify(completion);
1709
+ const restored = JSON.parse(json) as RebindTaskCompletionState;
1710
+
1711
+ expect(restored["re-enter-secrets"]).toBe(true);
1712
+ expect(restored["rebind-channels"]).toBe(true);
1713
+ expect(restored["reconfigure-auth"]).toBe(false);
1714
+ expect(restored["verify-webhooks"]).toBe(false);
1715
+
1716
+ // areAllRequiredTasksComplete should work on deserialized state
1717
+ expect(areAllRequiredTasksComplete(restored)).toBe(false);
1718
+ });
1719
+
1720
+ test("wizard state + task completion state can be stored and restored together", () => {
1721
+ const wizardState = advanceTo("rebind-secrets");
1722
+ let completion = createTaskCompletionState();
1723
+ completion = completeOnlyRequired(completion);
1724
+
1725
+ // Simulate persisting both as a combined payload
1726
+ const combined = JSON.stringify({
1727
+ wizard: JSON.parse(serializeWizardState(wizardState)),
1728
+ tasks: completion,
1729
+ });
1730
+
1731
+ const parsed = JSON.parse(combined);
1732
+ const restoredWizard = deserializeWizardState(
1733
+ JSON.stringify(parsed.wizard),
1734
+ );
1735
+ const restoredTasks = parsed.tasks as RebindTaskCompletionState;
1736
+
1737
+ expect(restoredWizard).toBeDefined();
1738
+ expect(restoredWizard!.currentStep).toBe("rebind-secrets");
1739
+ expect(areAllRequiredTasksComplete(restoredTasks)).toBe(true);
1740
+
1741
+ // Derive screen from restored state
1742
+ const screen = deriveRebindSecretsScreenState(
1743
+ restoredWizard!,
1744
+ restoredTasks,
1745
+ );
1746
+ expect(screen.phase).toBe("active");
1747
+ if (screen.phase === "active") {
1748
+ expect(screen.allRequiredComplete).toBe(true);
1749
+ }
1750
+ });
1751
+ });
1752
+
1753
+ describe("persistence — double serialization idempotency", () => {
1754
+ test("serializing twice produces identical output", () => {
1755
+ const state = advanceTo("transfer");
1756
+ const json1 = serializeWizardState(state);
1757
+ const restored = deserializeWizardState(json1);
1758
+ const json2 = serializeWizardState(restored!);
1759
+
1760
+ // hasBundleData changes from true to false on deserialization,
1761
+ // so the second serialization will differ from the first in that field.
1762
+ const parsed1 = JSON.parse(json1);
1763
+ const parsed2 = JSON.parse(json2);
1764
+ parsed1.hasBundleData = false; // Normalize for comparison
1765
+ expect(parsed2).toEqual(parsed1);
1766
+ });
1767
+
1768
+ test("prepareForResume is idempotent for non-loading, non-bundle steps", () => {
1769
+ const state = advanceTo("rebind-secrets");
1770
+ const json = serializeWizardState(state);
1771
+ const restored = deserializeWizardState(json)!;
1772
+
1773
+ const resumed1 = prepareForResume(restored);
1774
+ const resumed2 = prepareForResume(resumed1);
1775
+
1776
+ // Both should be at rebind-secrets with idle status
1777
+ expect(resumed1.currentStep).toBe(resumed2.currentStep);
1778
+ expect(resumed1.steps["rebind-secrets"].status).toBe(
1779
+ resumed2.steps["rebind-secrets"].status,
1780
+ );
1781
+ });
1782
+ });
1783
+
1784
+ // ===========================================================================
1785
+ // 8. RETRY + BACK NAVIGATION AFTER PERSISTENCE
1786
+ // ===========================================================================
1787
+
1788
+ describe("retry and back navigation after persistence round-trip", () => {
1789
+ test("can retry after restoring error state from persistence", () => {
1790
+ let state = advanceTo("validate");
1791
+ state = {
1792
+ ...state,
1793
+ steps: {
1794
+ ...state.steps,
1795
+ validate: {
1796
+ status: "error",
1797
+ error: {
1798
+ message: "validate: HTTP 500",
1799
+ code: "HTTP_500",
1800
+ retryable: true,
1801
+ },
1802
+ },
1803
+ },
1804
+ };
1805
+
1806
+ const json = serializeWizardState(state);
1807
+ const restored = deserializeWizardState(json)!;
1808
+
1809
+ // Should be able to retry from restored state
1810
+ expect(canRetryCurrentStep(restored)).toBe(true);
1811
+
1812
+ const reset = resetStepForRetry(restored);
1813
+ expect(reset.steps.validate.status).toBe("idle");
1814
+ expect(reset.currentStep).toBe("validate");
1815
+ });
1816
+
1817
+ test("can navigate back after restoring state from persistence", () => {
1818
+ const state = advanceTo("transfer");
1819
+ const json = serializeWizardState(state);
1820
+ const restored = deserializeWizardState(json)!;
1821
+
1822
+ // Should be able to go back
1823
+ const backState = goBackTo(restored, "validate");
1824
+ expect(backState.currentStep).toBe("validate");
1825
+ expect(backState.steps.validate.status).toBe("idle");
1826
+ expect(backState.steps["preflight-review"].status).toBe("idle");
1827
+ expect(backState.steps.transfer.status).toBe("idle");
1828
+ });
1829
+
1830
+ test("non-retryable error is preserved through persistence round-trip", () => {
1831
+ let state = advanceTo("validate");
1832
+ state = {
1833
+ ...state,
1834
+ steps: {
1835
+ ...state.steps,
1836
+ validate: {
1837
+ status: "error",
1838
+ error: {
1839
+ message: "Fatal: corrupted bundle",
1840
+ retryable: false,
1841
+ },
1842
+ },
1843
+ },
1844
+ };
1845
+
1846
+ const json = serializeWizardState(state);
1847
+ const restored = deserializeWizardState(json)!;
1848
+
1849
+ expect(canRetryCurrentStep(restored)).toBe(false);
1850
+ expect(() => resetStepForRetry(restored)).toThrow(
1851
+ "not in a retryable error state",
1852
+ );
1853
+ });
1854
+ });