@vellumai/assistant 0.7.0 → 0.7.2

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 (989) hide show
  1. package/ARCHITECTURE.md +38 -56
  2. package/Dockerfile +2 -0
  3. package/README.md +3 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +88 -142
  5. package/bun.lock +29 -26
  6. package/docs/architecture/security.md +38 -16
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +2 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
  19. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -5
  20. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
  21. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
  22. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
  23. package/node_modules/@vellumai/slack-text/bun.lock +24 -0
  24. package/node_modules/@vellumai/slack-text/package.json +18 -0
  25. package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
  26. package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
  27. package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
  28. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  29. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  30. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  31. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  32. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  33. package/openapi.yaml +869 -129
  34. package/package.json +8 -3
  35. package/scripts/generate-openapi.ts +16 -111
  36. package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
  37. package/src/__tests__/anthropic-provider.test.ts +56 -13
  38. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  39. package/src/__tests__/app-bundler.test.ts +170 -1
  40. package/src/__tests__/app-control-flow.test.ts +374 -0
  41. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  42. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  43. package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
  44. package/src/__tests__/app-conversation-ids.test.ts +151 -0
  45. package/src/__tests__/app-executors.test.ts +30 -43
  46. package/src/__tests__/approval-cascade.test.ts +0 -15
  47. package/src/__tests__/approval-routes-http.test.ts +29 -23
  48. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  49. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  50. package/src/__tests__/assistant-event-hub.test.ts +235 -79
  51. package/src/__tests__/assistant-event.test.ts +10 -5
  52. package/src/__tests__/assistant-events-sse-hardening.test.ts +44 -17
  53. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -36
  54. package/src/__tests__/background-shell-host-bash.test.ts +46 -56
  55. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  56. package/src/__tests__/btw-routes.test.ts +13 -4
  57. package/src/__tests__/call-controller.test.ts +50 -2
  58. package/src/__tests__/call-domain.test.ts +0 -2
  59. package/src/__tests__/call-routes-http.test.ts +0 -2
  60. package/src/__tests__/call-site-routing-provider.test.ts +193 -0
  61. package/src/__tests__/channel-approval-routes.test.ts +10 -296
  62. package/src/__tests__/channel-approvals.test.ts +25 -17
  63. package/src/__tests__/channel-guardian.test.ts +100 -146
  64. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  65. package/src/__tests__/checker.test.ts +23 -38
  66. package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
  67. package/src/__tests__/compaction-events.test.ts +2 -0
  68. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  69. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  70. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  71. package/src/__tests__/config-schema.test.ts +6 -48
  72. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  73. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  74. package/src/__tests__/config-watcher.test.ts +14 -2
  75. package/src/__tests__/connection-policy.test.ts +1 -52
  76. package/src/__tests__/contacts-write.test.ts +2 -64
  77. package/src/__tests__/context-image-dimensions.test.ts +1 -1
  78. package/src/__tests__/context-search-memory-source.test.ts +120 -1
  79. package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
  80. package/src/__tests__/context-search-pkb-source.test.ts +49 -0
  81. package/src/__tests__/context-search-workspace-source.test.ts +9 -22
  82. package/src/__tests__/context-window-manager.test.ts +46 -0
  83. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  84. package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
  85. package/src/__tests__/conversation-agent-loop.test.ts +980 -13
  86. package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
  87. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  88. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  89. package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
  90. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
  91. package/src/__tests__/conversation-history-web-search.test.ts +4 -3
  92. package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
  93. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  94. package/src/__tests__/conversation-lifecycle.test.ts +40 -4
  95. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  96. package/src/__tests__/conversation-process-callsite.test.ts +79 -2
  97. package/src/__tests__/conversation-queue.test.ts +3 -8
  98. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -161
  99. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -104
  100. package/src/__tests__/conversation-routes-slash-commands.test.ts +76 -66
  101. package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
  102. package/src/__tests__/conversation-slash-commands.test.ts +24 -8
  103. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  104. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  105. package/src/__tests__/conversation-starter-routes.test.ts +79 -2
  106. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  107. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  108. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
  109. package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
  110. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
  111. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
  112. package/src/__tests__/conversation-usage.test.ts +253 -3
  113. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  114. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  115. package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
  116. package/src/__tests__/credential-health-service.test.ts +68 -0
  117. package/src/__tests__/credential-security-e2e.test.ts +4 -3
  118. package/src/__tests__/credential-security-invariants.test.ts +1 -5
  119. package/src/__tests__/credential-token-resolver.test.ts +180 -0
  120. package/src/__tests__/credentials-cli.test.ts +5 -12
  121. package/src/__tests__/cu-unified-flow.test.ts +206 -27
  122. package/src/__tests__/daemon-assistant-events.test.ts +34 -21
  123. package/src/__tests__/daemon-credential-client.test.ts +102 -17
  124. package/src/__tests__/db-connection-isolation.test.ts +125 -0
  125. package/src/__tests__/db-migration-rollback.test.ts +101 -0
  126. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  127. package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
  128. package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
  129. package/src/__tests__/document-conversations.test.ts +332 -0
  130. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  131. package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
  132. package/src/__tests__/emit-event-signal.test.ts +4 -6
  133. package/src/__tests__/events-client-registration.test.ts +193 -49
  134. package/src/__tests__/filing-service.test.ts +58 -7
  135. package/src/__tests__/first-greeting.test.ts +156 -150
  136. package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
  137. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  138. package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
  139. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  140. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  141. package/src/__tests__/guardian-grant-minting.test.ts +7 -2
  142. package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
  143. package/src/__tests__/guardian-routing-state.test.ts +1 -1
  144. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  145. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +30 -11
  146. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -84
  147. package/src/__tests__/headless-browser-mode.test.ts +4 -9
  148. package/src/__tests__/headless-browser-navigate.test.ts +21 -20
  149. package/src/__tests__/heartbeat-service.test.ts +1007 -8
  150. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  151. package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
  152. package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
  153. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  154. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  155. package/src/__tests__/host-bash-proxy.test.ts +270 -147
  156. package/src/__tests__/host-bash-routes.test.ts +294 -0
  157. package/src/__tests__/host-browser-proxy.test.ts +126 -198
  158. package/src/__tests__/host-browser-routes.test.ts +50 -54
  159. package/src/__tests__/host-cu-proxy.test.ts +78 -144
  160. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  161. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  162. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  163. package/src/__tests__/host-file-proxy.test.ts +62 -122
  164. package/src/__tests__/host-file-read-tool.test.ts +59 -21
  165. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  166. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  167. package/src/__tests__/host-proxy-base.test.ts +312 -0
  168. package/src/__tests__/host-shell-tool.test.ts +53 -70
  169. package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
  170. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  171. package/src/__tests__/host-transfer-proxy.test.ts +145 -56
  172. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  173. package/src/__tests__/http-user-message-parity.test.ts +1 -6
  174. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  175. package/src/__tests__/identity-routes.test.ts +103 -1
  176. package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
  177. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  178. package/src/__tests__/injector-chain.test.ts +10 -5
  179. package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
  180. package/src/__tests__/inline-command-runner.test.ts +0 -67
  181. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -13
  182. package/src/__tests__/install-skill-routing.test.ts +1 -13
  183. package/src/__tests__/integration-status.test.ts +85 -5
  184. package/src/__tests__/intent-routing.test.ts +0 -1
  185. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  186. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  187. package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
  188. package/src/__tests__/llm-catalog-parity.test.ts +90 -0
  189. package/src/__tests__/llm-context-resolution.test.ts +180 -0
  190. package/src/__tests__/llm-resolver.test.ts +80 -12
  191. package/src/__tests__/llm-usage-store.test.ts +269 -4
  192. package/src/__tests__/log-export-routes.test.ts +89 -0
  193. package/src/__tests__/managed-profile-guard.test.ts +225 -0
  194. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -11
  195. package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
  196. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  197. package/src/__tests__/mcp-cli.test.ts +338 -2
  198. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  199. package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
  200. package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
  201. package/src/__tests__/migration-export-http.test.ts +33 -26
  202. package/src/__tests__/migration-export-streaming.test.ts +18 -10
  203. package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
  204. package/src/__tests__/migration-import-commit-http.test.ts +172 -21
  205. package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
  206. package/src/__tests__/migration-import-from-url.test.ts +20 -6
  207. package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
  208. package/src/__tests__/migration-parity-persistence.test.ts +62 -25
  209. package/src/__tests__/migration-transport.test.ts +115 -23
  210. package/src/__tests__/migration-validate-http.test.ts +105 -80
  211. package/src/__tests__/migration-wizard.test.ts +133 -27
  212. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  213. package/src/__tests__/non-member-access-request.test.ts +1 -1
  214. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  215. package/src/__tests__/oauth-cli.test.ts +0 -2
  216. package/src/__tests__/oauth-store.test.ts +19 -0
  217. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  218. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  219. package/src/__tests__/platform-bash-auto-approve.test.ts +26 -21
  220. package/src/__tests__/prechat-onboarding-contract.test.ts +34 -8
  221. package/src/__tests__/pricing.test.ts +68 -4
  222. package/src/__tests__/process-message-background-slack.test.ts +333 -0
  223. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  224. package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
  225. package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
  226. package/src/__tests__/provider-usage-tracking.test.ts +208 -0
  227. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  228. package/src/__tests__/reaction-persistence.test.ts +9 -6
  229. package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
  230. package/src/__tests__/recording-handler.test.ts +64 -81
  231. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
  232. package/src/__tests__/relay-server.test.ts +18 -13
  233. package/src/__tests__/require-fresh-approval.test.ts +13 -23
  234. package/src/__tests__/retry-backoff.test.ts +87 -0
  235. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  236. package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
  237. package/src/__tests__/runtime-events-sse.test.ts +13 -18
  238. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  239. package/src/__tests__/schedule-retry.test.ts +715 -0
  240. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  241. package/src/__tests__/search-skills-unified.test.ts +9 -15
  242. package/src/__tests__/secret-ingress-cli.test.ts +2 -5
  243. package/src/__tests__/secret-ingress-http.test.ts +1 -4
  244. package/src/__tests__/secret-onetime-send.test.ts +4 -2
  245. package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
  246. package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
  247. package/src/__tests__/secret-response-routing.test.ts +29 -15
  248. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
  249. package/src/__tests__/secret-scanner.test.ts +2 -545
  250. package/src/__tests__/send-endpoint-busy.test.ts +12 -24
  251. package/src/__tests__/settings-routes.test.ts +1 -1
  252. package/src/__tests__/shell-credential-ref.test.ts +0 -8
  253. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -57
  254. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  255. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  256. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  257. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  258. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  259. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -12
  260. package/src/__tests__/skill-tool-factory.test.ts +97 -0
  261. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
  262. package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
  263. package/src/__tests__/slack-channel-config.test.ts +9 -14
  264. package/src/__tests__/slack-inbound-verification.test.ts +1 -62
  265. package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
  266. package/src/__tests__/subagent-manager-notify.test.ts +70 -70
  267. package/src/__tests__/subagent-notify-parent.test.ts +80 -83
  268. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  269. package/src/__tests__/system-prompt.test.ts +115 -14
  270. package/src/__tests__/telegram-config.test.ts +0 -1
  271. package/src/__tests__/terminal-tools.test.ts +0 -89
  272. package/src/__tests__/test-preload.ts +8 -0
  273. package/src/__tests__/thread-backfill.test.ts +945 -31
  274. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  275. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  276. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
  277. package/src/__tests__/tool-execute-pipeline.test.ts +0 -7
  278. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -17
  279. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
  280. package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -8
  281. package/src/__tests__/tool-executor.test.ts +12 -20
  282. package/src/__tests__/tool-metrics-listener.test.ts +0 -35
  283. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  284. package/src/__tests__/tool-trace-listener.test.ts +0 -17
  285. package/src/__tests__/transfer-progress-screen.test.ts +63 -26
  286. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
  287. package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
  288. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  289. package/src/__tests__/tts-catalog-parity.test.ts +16 -5
  290. package/src/__tests__/twilio-config.test.ts +3 -16
  291. package/src/__tests__/twilio-routes.test.ts +3 -5
  292. package/src/__tests__/twilio-validation.test.ts +93 -0
  293. package/src/__tests__/usage-attribution.test.ts +247 -0
  294. package/src/__tests__/usage-cli.test.ts +143 -0
  295. package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
  296. package/src/__tests__/usage-routes.test.ts +150 -0
  297. package/src/__tests__/validation-results-screen.test.ts +39 -16
  298. package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
  299. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +47 -138
  300. package/src/__tests__/verification-control-plane-policy.test.ts +6 -11
  301. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  302. package/src/__tests__/voice-session-bridge.test.ts +5 -5
  303. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  304. package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
  305. package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
  306. package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
  307. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  308. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  309. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
  310. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  311. package/src/acp/index.ts +0 -15
  312. package/src/acp/session-manager.ts +37 -34
  313. package/src/agent/loop.ts +16 -1
  314. package/src/approvals/AGENTS.md +4 -0
  315. package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
  316. package/src/approvals/guardian-request-resolvers.ts +10 -2
  317. package/src/backup/__tests__/paths.test.ts +0 -22
  318. package/src/backup/__tests__/restore.test.ts +94 -177
  319. package/src/backup/paths.ts +2 -15
  320. package/src/backup/restore.ts +107 -231
  321. package/src/browser-session/events.ts +0 -9
  322. package/src/bundler/app-bundler.ts +51 -3
  323. package/src/calls/call-store.ts +1 -34
  324. package/src/calls/guardian-question-copy.ts +0 -108
  325. package/src/calls/relay-server.ts +4 -68
  326. package/src/calls/twilio-config.ts +2 -17
  327. package/src/calls/twilio-rest.ts +31 -141
  328. package/src/calls/twilio-routes.ts +12 -13
  329. package/src/calls/voice-session-bridge.ts +7 -38
  330. package/src/channels/types.ts +8 -42
  331. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  332. package/src/cli/commands/__tests__/cache.test.ts +152 -5
  333. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  334. package/src/cli/commands/__tests__/memory-v2.test.ts +18 -28
  335. package/src/cli/commands/__tests__/trust.test.ts +21 -387
  336. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  337. package/src/cli/commands/backup.ts +6 -331
  338. package/src/cli/commands/cache-fs.ts +8 -0
  339. package/src/cli/commands/cache.ts +153 -82
  340. package/src/cli/commands/clients.ts +64 -7
  341. package/src/cli/commands/completions.ts +3 -3
  342. package/src/cli/commands/contacts.ts +304 -76
  343. package/src/cli/commands/conversations.ts +2 -5
  344. package/src/cli/commands/credentials.ts +15 -7
  345. package/src/cli/commands/domain.ts +66 -15
  346. package/src/cli/commands/gateway.ts +183 -0
  347. package/src/cli/commands/keys.ts +13 -7
  348. package/src/cli/commands/mcp.ts +116 -156
  349. package/src/cli/commands/memory-v2.ts +320 -53
  350. package/src/cli/commands/oauth/shared.ts +2 -29
  351. package/src/cli/commands/pending.ts +102 -0
  352. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  353. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  354. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  355. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  356. package/src/cli/commands/platform/disconnect.ts +5 -4
  357. package/src/cli/commands/platform/index.ts +0 -18
  358. package/src/cli/commands/skills.ts +77 -35
  359. package/src/cli/commands/trust.ts +70 -430
  360. package/src/cli/commands/usage.ts +25 -16
  361. package/src/cli/lib/daemon-credential-client.ts +115 -19
  362. package/src/cli/program.ts +4 -0
  363. package/src/cli.ts +0 -21
  364. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  365. package/src/config/assistant-feature-flags.ts +67 -10
  366. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  367. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  368. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  369. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  370. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  371. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  372. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  373. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  374. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  375. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  376. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  377. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  378. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  379. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  380. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  381. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  382. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  383. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  384. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  385. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  386. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  387. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  388. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  389. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  390. package/src/config/bundled-skills/messaging/TOOLS.json +14 -44
  391. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  392. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  393. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  394. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  395. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  396. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  397. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  398. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  399. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  400. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  401. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  402. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  403. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  404. package/src/config/bundled-tool-registry.ts +21 -0
  405. package/src/config/env-registry.ts +12 -4
  406. package/src/config/env.ts +22 -26
  407. package/src/config/feature-flag-registry.json +40 -152
  408. package/src/config/llm-callsite-catalog.ts +12 -0
  409. package/src/config/llm-context-resolution.ts +80 -0
  410. package/src/config/llm-resolver.ts +58 -22
  411. package/src/config/loader.ts +76 -102
  412. package/src/config/sanitize-for-transfer.ts +2 -0
  413. package/src/config/schema.ts +2 -158
  414. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  415. package/src/config/schemas/__tests__/memory-v2.test.ts +8 -4
  416. package/src/config/schemas/call-site-catalog.ts +271 -0
  417. package/src/config/schemas/calls.ts +5 -14
  418. package/src/config/schemas/heartbeat.ts +63 -0
  419. package/src/config/schemas/inference.ts +1 -1
  420. package/src/config/schemas/ingress.ts +11 -7
  421. package/src/config/schemas/llm.ts +34 -11
  422. package/src/config/schemas/memory-lifecycle.ts +77 -24
  423. package/src/config/schemas/memory-retrieval.ts +2 -2
  424. package/src/config/schemas/memory-v2.ts +57 -4
  425. package/src/config/schemas/platform.ts +6 -0
  426. package/src/config/schemas/security.ts +1 -42
  427. package/src/config/schemas/services.ts +7 -21
  428. package/src/config/schemas/skills.ts +5 -11
  429. package/src/config/schemas/tts.ts +1 -1
  430. package/src/config/seed-inference-profiles.ts +117 -0
  431. package/src/config/skills.ts +0 -90
  432. package/src/config/types.ts +3 -6
  433. package/src/contacts/contact-store.ts +0 -47
  434. package/src/contacts/contacts-write.ts +1 -132
  435. package/src/context/window-manager.ts +43 -5
  436. package/src/credential-execution/feature-gates.ts +10 -10
  437. package/src/credential-execution/process-manager.ts +46 -51
  438. package/src/credential-health/credential-health-service.ts +21 -16
  439. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
  440. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  441. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
  442. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  443. package/src/daemon/config-watcher.ts +4 -3
  444. package/src/daemon/connection-policy.ts +1 -26
  445. package/src/daemon/conversation-agent-loop-handlers.ts +74 -7
  446. package/src/daemon/conversation-agent-loop.ts +309 -64
  447. package/src/daemon/conversation-history.ts +8 -8
  448. package/src/daemon/conversation-launch.ts +20 -135
  449. package/src/daemon/conversation-lifecycle.ts +8 -1
  450. package/src/daemon/conversation-messaging.ts +1 -0
  451. package/src/daemon/conversation-process.ts +97 -172
  452. package/src/daemon/conversation-runtime-assembly.ts +219 -76
  453. package/src/daemon/conversation-slash.ts +47 -5
  454. package/src/daemon/conversation-store.ts +7 -31
  455. package/src/daemon/conversation-surfaces.ts +144 -29
  456. package/src/daemon/conversation-tool-setup.ts +18 -87
  457. package/src/daemon/conversation-usage.ts +36 -0
  458. package/src/daemon/conversation.ts +134 -231
  459. package/src/daemon/daemon-control.ts +3 -71
  460. package/src/daemon/daemon-skill-host.ts +8 -11
  461. package/src/daemon/dictation-profile-store.ts +2 -26
  462. package/src/daemon/doordash-steps.ts +1 -1
  463. package/src/daemon/first-greeting.ts +44 -156
  464. package/src/daemon/handlers/config-channels.ts +12 -12
  465. package/src/daemon/handlers/config-ingress.ts +4 -165
  466. package/src/daemon/handlers/config-model.ts +1 -1
  467. package/src/daemon/handlers/config-voice.ts +0 -42
  468. package/src/daemon/handlers/conversations.ts +11 -190
  469. package/src/daemon/handlers/recording.ts +26 -158
  470. package/src/daemon/handlers/shared.ts +27 -72
  471. package/src/daemon/handlers/skills.ts +42 -93
  472. package/src/daemon/host-app-control-proxy.ts +293 -0
  473. package/src/daemon/host-bash-proxy.ts +124 -92
  474. package/src/daemon/host-browser-proxy.ts +111 -88
  475. package/src/daemon/host-cu-proxy.ts +100 -104
  476. package/src/daemon/host-file-proxy.ts +136 -91
  477. package/src/daemon/host-proxy-base.ts +294 -0
  478. package/src/daemon/host-proxy-preactivation.ts +82 -0
  479. package/src/daemon/host-transfer-proxy.ts +303 -147
  480. package/src/daemon/lifecycle.ts +164 -132
  481. package/src/daemon/message-protocol.ts +3 -8
  482. package/src/daemon/message-types/contacts.ts +23 -1
  483. package/src/daemon/message-types/conversations.ts +18 -8
  484. package/src/daemon/message-types/host-app-control.ts +150 -0
  485. package/src/daemon/message-types/host-bash.ts +5 -0
  486. package/src/daemon/message-types/host-cu.ts +3 -0
  487. package/src/daemon/message-types/host-file.ts +5 -0
  488. package/src/daemon/message-types/host-transfer.ts +4 -0
  489. package/src/daemon/message-types/messages.ts +10 -9
  490. package/src/daemon/message-types/schedules.ts +8 -3
  491. package/src/daemon/message-types/skills.ts +2 -2
  492. package/src/daemon/message-types/workspace.ts +1 -1
  493. package/src/daemon/process-message.ts +119 -239
  494. package/src/daemon/server.ts +13 -462
  495. package/src/daemon/shutdown-handlers.ts +2 -5
  496. package/src/daemon/tool-setup-types.ts +51 -0
  497. package/src/daemon/tool-side-effects.ts +126 -108
  498. package/src/daemon/trust-context.ts +13 -0
  499. package/src/daemon/wake-target-adapter.ts +4 -9
  500. package/src/events/domain-events.ts +0 -8
  501. package/src/events/tool-audit-listener.ts +5 -2
  502. package/src/events/tool-domain-event-publisher.ts +0 -10
  503. package/src/events/tool-metrics-listener.ts +0 -17
  504. package/src/events/tool-trace-listener.ts +0 -14
  505. package/src/filing/filing-service.ts +13 -1
  506. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +21 -9
  507. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  508. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  509. package/src/heartbeat/heartbeat-service.ts +303 -54
  510. package/src/home/__tests__/feed-writer.test.ts +0 -4
  511. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  512. package/src/home/__tests__/relationship-state-writer.test.ts +41 -9
  513. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  514. package/src/home/feed-writer.ts +1 -2
  515. package/src/home/post-connect-feed.ts +68 -0
  516. package/src/home/relationship-state-writer.ts +33 -95
  517. package/src/home/suggested-prompts.ts +46 -10
  518. package/src/inbound/public-ingress-urls.ts +32 -34
  519. package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
  520. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  521. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
  522. package/src/ipc/assistant-server.ts +17 -11
  523. package/src/ipc/cli-client.ts +32 -1
  524. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
  525. package/src/ipc/routes/route-adapter.ts +1 -1
  526. package/src/ipc/routes/trust-rules.test.ts +0 -95
  527. package/src/ipc/skill-ipc-types.ts +41 -0
  528. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
  529. package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
  530. package/src/ipc/skill-routes/events.ts +12 -23
  531. package/src/ipc/skill-routes/identity.ts +4 -17
  532. package/src/ipc/skill-routes/index.ts +1 -1
  533. package/src/ipc/skill-server.ts +6 -39
  534. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
  535. package/src/live-voice/live-voice-metrics.ts +10 -10
  536. package/src/live-voice/protocol.ts +4 -13
  537. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  538. package/src/mcp/manager.ts +0 -5
  539. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  540. package/src/mcp/mcp-auth-state.ts +133 -0
  541. package/src/mcp/mcp-oauth-provider.ts +19 -0
  542. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
  543. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  544. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
  545. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  546. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  547. package/src/memory/anisotropy.test.ts +247 -0
  548. package/src/memory/anisotropy.ts +443 -0
  549. package/src/memory/app-git-service.ts +0 -32
  550. package/src/memory/app-store.ts +154 -0
  551. package/src/memory/attachments-store.ts +6 -0
  552. package/src/memory/auto-analysis-constants.ts +17 -0
  553. package/src/memory/auto-analysis-guard.ts +5 -15
  554. package/src/memory/canonical-guardian-store.ts +7 -7
  555. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  556. package/src/memory/context-search/agent-protocol.ts +6 -6
  557. package/src/memory/context-search/agent-runner.ts +32 -7
  558. package/src/memory/context-search/sources/memory-v2.ts +590 -0
  559. package/src/memory/context-search/sources/memory.ts +5 -0
  560. package/src/memory/context-search/sources/pkb.ts +10 -1
  561. package/src/memory/context-search/sources/workspace.ts +3 -2
  562. package/src/memory/conversation-crud.ts +30 -5
  563. package/src/memory/conversation-disk-view.ts +1 -5
  564. package/src/memory/conversation-key-store.ts +2 -15
  565. package/src/memory/conversation-starter-checkpoints.ts +63 -0
  566. package/src/memory/db-connection.ts +62 -0
  567. package/src/memory/db-init.ts +18 -0
  568. package/src/memory/embedding-backend.ts +12 -42
  569. package/src/memory/embedding-gemini.ts +0 -2
  570. package/src/memory/embedding-local.ts +6 -6
  571. package/src/memory/embedding-ollama.ts +6 -6
  572. package/src/memory/embedding-openai.ts +6 -6
  573. package/src/memory/embedding-types.ts +21 -0
  574. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -8
  575. package/src/memory/graph/conversation-graph-memory.ts +35 -36
  576. package/src/memory/graph/graph-search.ts +8 -0
  577. package/src/memory/graph/injection.test.ts +2 -2
  578. package/src/memory/graph/injection.ts +1 -1
  579. package/src/memory/graph/retriever.ts +28 -0
  580. package/src/memory/graph/tools.ts +1 -1
  581. package/src/memory/guardian-action-store.ts +0 -83
  582. package/src/memory/guardian-approvals.ts +0 -48
  583. package/src/memory/indexer.ts +1 -15
  584. package/src/memory/job-handlers/conversation-starters.ts +36 -53
  585. package/src/memory/job-utils.ts +0 -6
  586. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  587. package/src/memory/jobs/embed-concept-page.ts +28 -2
  588. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  589. package/src/memory/jobs-store.ts +66 -23
  590. package/src/memory/jobs-worker.ts +114 -79
  591. package/src/memory/llm-request-log-store.ts +0 -41
  592. package/src/memory/llm-usage-store.ts +129 -43
  593. package/src/memory/memory-v2-activation-log-store.ts +115 -0
  594. package/src/memory/migrations/233-document-conversations.ts +54 -0
  595. package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
  596. package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
  597. package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
  598. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
  599. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  600. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  601. package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
  602. package/src/memory/migrations/index.ts +19 -0
  603. package/src/memory/migrations/registry.ts +32 -0
  604. package/src/memory/pkb/pkb-search.ts +7 -0
  605. package/src/memory/qdrant-client.ts +50 -20
  606. package/src/memory/raw-query.ts +2 -68
  607. package/src/memory/schema/conversations.ts +7 -0
  608. package/src/memory/schema/infrastructure.ts +40 -0
  609. package/src/memory/search/semantic.ts +12 -16
  610. package/src/memory/sparse-tokenize.ts +49 -0
  611. package/src/memory/tool-usage-store.ts +2 -0
  612. package/src/memory/usage-buckets.ts +40 -1
  613. package/src/memory/usage-grouped-buckets.ts +127 -0
  614. package/src/memory/v2/__tests__/activation.test.ts +361 -180
  615. package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
  616. package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
  617. package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
  618. package/src/memory/v2/__tests__/injection.test.ts +424 -33
  619. package/src/memory/v2/__tests__/migration.test.ts +64 -36
  620. package/src/memory/v2/__tests__/page-store.test.ts +191 -8
  621. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
  622. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  623. package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
  624. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  625. package/src/memory/v2/__tests__/static-context.test.ts +152 -0
  626. package/src/memory/v2/activation.ts +215 -163
  627. package/src/memory/v2/backfill-jobs.ts +15 -100
  628. package/src/memory/v2/consolidation-job.ts +17 -17
  629. package/src/memory/v2/constants.ts +7 -0
  630. package/src/memory/v2/edge-index.ts +191 -0
  631. package/src/memory/v2/injection.ts +241 -84
  632. package/src/memory/v2/migration.ts +57 -64
  633. package/src/memory/v2/now-text.ts +2 -3
  634. package/src/memory/v2/page-store.ts +168 -31
  635. package/src/memory/v2/prompts/consolidation.ts +385 -88
  636. package/src/memory/v2/prompts/sweep.ts +3 -3
  637. package/src/memory/v2/qdrant.ts +99 -1
  638. package/src/memory/v2/sim.ts +126 -16
  639. package/src/memory/v2/skill-qdrant.ts +12 -3
  640. package/src/memory/v2/skill-store.ts +71 -8
  641. package/src/memory/v2/sparse-bm25.ts +245 -0
  642. package/src/memory/v2/static-context.ts +63 -0
  643. package/src/memory/v2/types.ts +10 -20
  644. package/src/memory/validation.ts +0 -11
  645. package/src/messaging/draft-store.ts +0 -6
  646. package/src/messaging/provider-types.ts +8 -0
  647. package/src/messaging/provider.ts +7 -0
  648. package/src/messaging/providers/gmail/client.ts +1 -121
  649. package/src/messaging/providers/gmail/types.ts +0 -49
  650. package/src/messaging/providers/outlook/client.ts +0 -73
  651. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
  652. package/src/messaging/providers/slack/adapter.ts +123 -52
  653. package/src/messaging/providers/slack/backfill.test.ts +95 -6
  654. package/src/messaging/providers/slack/backfill.ts +89 -11
  655. package/src/messaging/providers/slack/client.ts +10 -124
  656. package/src/messaging/providers/slack/message-metadata.ts +12 -2
  657. package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
  658. package/src/messaging/providers/slack/render-transcript.ts +126 -25
  659. package/src/messaging/providers/slack/types.ts +1 -32
  660. package/src/notifications/README.md +10 -10
  661. package/src/notifications/broadcaster.ts +1 -1
  662. package/src/notifications/guardian-question-mode.ts +5 -5
  663. package/src/oauth/connect-orchestrator.ts +4 -0
  664. package/src/oauth/connection-resolver.test.ts +8 -0
  665. package/src/oauth/connection-resolver.ts +8 -16
  666. package/src/oauth/credential-token-resolver.ts +95 -0
  667. package/src/oauth/manual-token-connection.ts +26 -34
  668. package/src/oauth/oauth-store.ts +6 -4
  669. package/src/outbound-proxy/certs.ts +0 -7
  670. package/src/outbound-proxy/index.ts +1 -59
  671. package/src/outbound-proxy/logging.ts +1 -1
  672. package/src/outbound-proxy/policy.ts +6 -5
  673. package/src/outbound-proxy/router.ts +2 -1
  674. package/src/permissions/approval-policy.test.ts +6 -275
  675. package/src/permissions/approval-policy.ts +0 -51
  676. package/src/permissions/approval-provenance.test.ts +184 -0
  677. package/src/permissions/approval-provenance.ts +70 -0
  678. package/src/permissions/checker.test.ts +0 -1
  679. package/src/permissions/checker.ts +7 -18
  680. package/src/permissions/gateway-threshold-reader.ts +6 -1
  681. package/src/permissions/prompter.ts +43 -3
  682. package/src/permissions/secret-prompter.ts +25 -48
  683. package/src/permissions/types.ts +33 -0
  684. package/src/permissions/workspace-policy.ts +0 -5
  685. package/src/platform/sync-identity.ts +0 -8
  686. package/src/plugins/defaults/injectors.ts +69 -2
  687. package/src/plugins/defaults/overflow-reduce.ts +3 -2
  688. package/src/plugins/types.ts +8 -0
  689. package/src/prompts/bootstrap-cleanup.ts +27 -0
  690. package/src/prompts/system-prompt.ts +37 -88
  691. package/src/prompts/templates/BOOTSTRAP.md +52 -6
  692. package/src/prompts/templates/SOUL.md +13 -1
  693. package/src/prompts/update-bulletin-job.ts +2 -0
  694. package/src/providers/__tests__/retry-callsite.test.ts +138 -1
  695. package/src/providers/anthropic/client.ts +72 -33
  696. package/src/providers/call-site-routing.ts +42 -3
  697. package/src/providers/gemini/client.ts +18 -2
  698. package/src/providers/managed-proxy/context.ts +0 -5
  699. package/src/providers/model-catalog.ts +105 -19
  700. package/src/providers/openai/chat-completions-provider.ts +6 -0
  701. package/src/providers/openai/responses-provider.ts +7 -1
  702. package/src/providers/provider-send-message.ts +45 -2
  703. package/src/providers/ratelimit.ts +7 -2
  704. package/src/providers/registry.ts +14 -9
  705. package/src/providers/retry.ts +96 -8
  706. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  707. package/src/providers/types.ts +13 -0
  708. package/src/providers/usage-tracking.ts +96 -0
  709. package/src/runtime/AGENTS.md +10 -6
  710. package/src/runtime/__tests__/agent-wake.test.ts +89 -0
  711. package/src/runtime/agent-wake.ts +39 -2
  712. package/src/runtime/assistant-event-hub.ts +570 -52
  713. package/src/runtime/assistant-event.ts +2 -6
  714. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  715. package/src/runtime/auth/context.ts +0 -9
  716. package/src/runtime/auth/middleware.ts +1 -97
  717. package/src/runtime/auth/route-policy.ts +30 -9
  718. package/src/runtime/auth/token-service.ts +0 -11
  719. package/src/runtime/btw-sidechain.ts +2 -3
  720. package/src/runtime/channel-approvals.ts +6 -2
  721. package/src/runtime/channel-invite-transport.ts +2 -48
  722. package/src/runtime/channel-invite-transports/email.ts +1 -1
  723. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  724. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  725. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  726. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  727. package/src/runtime/channel-invite-types.ts +54 -0
  728. package/src/runtime/channel-readiness-service.ts +32 -13
  729. package/src/runtime/channel-verification-service.ts +3 -5
  730. package/src/runtime/http-errors.ts +0 -34
  731. package/src/runtime/http-router.ts +6 -3
  732. package/src/runtime/http-server.ts +16 -402
  733. package/src/runtime/http-types.ts +5 -5
  734. package/src/runtime/interactive-ui.ts +0 -1
  735. package/src/runtime/middleware/auth.ts +0 -20
  736. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
  737. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
  738. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
  739. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
  740. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  741. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  742. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  743. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
  744. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
  745. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +296 -80
  746. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
  747. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  748. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  749. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  750. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  751. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  752. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
  753. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +421 -0
  754. package/src/runtime/migrations/migration-transport.ts +49 -16
  755. package/src/runtime/migrations/migration-wizard.ts +2 -2
  756. package/src/runtime/migrations/origin-mode.ts +40 -0
  757. package/src/runtime/migrations/vbundle-builder.ts +457 -136
  758. package/src/runtime/migrations/vbundle-import-analyzer.ts +13 -11
  759. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  760. package/src/runtime/migrations/vbundle-importer.ts +251 -74
  761. package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
  762. package/src/runtime/migrations/vbundle-streaming-importer.ts +329 -38
  763. package/src/runtime/migrations/vbundle-streaming-validator.ts +203 -28
  764. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  765. package/src/runtime/migrations/vbundle-validator.ts +328 -41
  766. package/src/runtime/pending-interactions.ts +48 -13
  767. package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
  768. package/src/runtime/routes/__tests__/backup-routes.test.ts +49 -168
  769. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +333 -0
  770. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  771. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  772. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
  773. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
  774. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
  775. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
  776. package/src/runtime/routes/acp-routes.test.ts +0 -3
  777. package/src/runtime/routes/acp-routes.ts +3 -7
  778. package/src/runtime/routes/app-management-routes.ts +18 -9
  779. package/src/runtime/routes/approval-interception-types.ts +13 -0
  780. package/src/runtime/routes/approval-routes.ts +55 -14
  781. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  782. package/src/runtime/routes/avatar-routes.ts +3 -5
  783. package/src/runtime/routes/backup-routes.ts +15 -38
  784. package/src/runtime/routes/browser-routes.ts +1 -15
  785. package/src/runtime/routes/btw-routes.ts +14 -37
  786. package/src/runtime/routes/channel-guardian-routes.ts +1 -5
  787. package/src/runtime/routes/channel-readiness-routes.ts +3 -7
  788. package/src/runtime/routes/channel-route-shared.ts +2 -28
  789. package/src/runtime/routes/client-routes.ts +46 -12
  790. package/src/runtime/routes/consolidation-routes.ts +115 -0
  791. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  792. package/src/runtime/routes/conversation-list-routes.ts +12 -29
  793. package/src/runtime/routes/conversation-management-routes.ts +14 -51
  794. package/src/runtime/routes/conversation-query-routes.ts +156 -9
  795. package/src/runtime/routes/conversation-routes.ts +72 -539
  796. package/src/runtime/routes/conversation-starter-routes.ts +19 -40
  797. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  798. package/src/runtime/routes/documents-routes.ts +83 -18
  799. package/src/runtime/routes/errors.ts +19 -4
  800. package/src/runtime/routes/events-routes.ts +68 -94
  801. package/src/runtime/routes/filing-routes.ts +18 -1
  802. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  803. package/src/runtime/routes/guardian-action-routes.ts +4 -9
  804. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  805. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  806. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  807. package/src/runtime/routes/host-bash-routes.ts +37 -6
  808. package/src/runtime/routes/host-browser-routes.ts +96 -25
  809. package/src/runtime/routes/host-cu-routes.ts +48 -13
  810. package/src/runtime/routes/host-file-routes.ts +35 -11
  811. package/src/runtime/routes/host-transfer-routes.ts +73 -37
  812. package/src/runtime/routes/http-adapter.ts +1 -0
  813. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  814. package/src/runtime/routes/identity-routes.ts +93 -49
  815. package/src/runtime/routes/inbound-message-handler.ts +581 -146
  816. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -95
  817. package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
  818. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  819. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  820. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  821. package/src/runtime/routes/index.ts +12 -0
  822. package/src/runtime/routes/integrations/slack/channel.ts +0 -24
  823. package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
  824. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  825. package/src/runtime/routes/memory-item-routes.ts +10 -12
  826. package/src/runtime/routes/memory-v2-routes.ts +451 -16
  827. package/src/runtime/routes/migration-routes.ts +284 -31
  828. package/src/runtime/routes/playground/guard.ts +1 -1
  829. package/src/runtime/routes/playground/index.ts +0 -2
  830. package/src/runtime/routes/recording-routes.ts +4 -24
  831. package/src/runtime/routes/rename-conversation-routes.ts +2 -6
  832. package/src/runtime/routes/schedule-routes.ts +10 -6
  833. package/src/runtime/routes/secret-routes.ts +87 -18
  834. package/src/runtime/routes/settings-routes.ts +29 -28
  835. package/src/runtime/routes/skills-routes.ts +12 -31
  836. package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
  837. package/src/runtime/routes/task-routes.ts +6 -6
  838. package/src/runtime/routes/trust-rules-routes.ts +3 -94
  839. package/src/runtime/routes/types.ts +4 -4
  840. package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
  841. package/src/runtime/routes/usage-routes.ts +87 -10
  842. package/src/runtime/routes/user-routes.ts +17 -31
  843. package/src/runtime/routes/work-items-routes.ts +1 -4
  844. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
  845. package/src/runtime/services/analyze-conversation.ts +7 -17
  846. package/src/runtime/services/conversation-serializer.ts +2 -4
  847. package/src/runtime/verification-outbound-actions.ts +1 -1
  848. package/src/runtime/verification-rate-limiter.ts +1 -1
  849. package/src/runtime/verification-templates.ts +4 -7
  850. package/src/schedule/integration-status.ts +66 -2
  851. package/src/schedule/recurrence-engine.ts +4 -1
  852. package/src/schedule/retry-backoff.ts +18 -0
  853. package/src/schedule/retry-policy.ts +82 -0
  854. package/src/schedule/schedule-recovery.ts +64 -0
  855. package/src/schedule/schedule-store.ts +106 -18
  856. package/src/schedule/scheduler-types.ts +25 -0
  857. package/src/schedule/scheduler.ts +63 -38
  858. package/src/security/oauth-callback-registry.ts +8 -0
  859. package/src/security/secret-scanner.ts +14 -547
  860. package/src/security/secure-keys.ts +31 -11
  861. package/src/security/token-manager.ts +7 -3
  862. package/src/sequence/analytics.ts +5 -5
  863. package/src/sequence/engine.ts +1 -1
  864. package/src/signals/cancel.ts +16 -25
  865. package/src/signals/conversation-undo.ts +2 -27
  866. package/src/signals/emit-event.ts +1 -2
  867. package/src/signals/user-message.ts +108 -22
  868. package/src/skills/catalog-files.ts +2 -8
  869. package/src/skills/catalog-install.ts +1 -0
  870. package/src/skills/clawhub.ts +2 -2
  871. package/src/skills/include-graph.ts +5 -5
  872. package/src/skills/inline-command-runner.ts +1 -7
  873. package/src/skills/remote-skill-policy.ts +5 -5
  874. package/src/skills/skill-file-provider.ts +1 -1
  875. package/src/skills/skill-file-types.ts +13 -0
  876. package/src/skills/skillssh-audit-types.ts +28 -0
  877. package/src/skills/skillssh-registry.ts +8 -21
  878. package/src/subagent/manager.ts +67 -84
  879. package/src/tasks/task-store.ts +1 -28
  880. package/src/telemetry/types.ts +8 -0
  881. package/src/telemetry/usage-telemetry-reporter.test.ts +59 -15
  882. package/src/telemetry/usage-telemetry-reporter.ts +4 -5
  883. package/src/tools/acp/spawn.test.ts +1 -2
  884. package/src/tools/acp/steer.test.ts +1 -2
  885. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  886. package/src/tools/apps/executors.ts +56 -69
  887. package/src/tools/browser/__tests__/browser-status.test.ts +55 -135
  888. package/src/tools/browser/browser-execution.ts +31 -147
  889. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +145 -70
  890. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  891. package/src/tools/browser/cdp-client/factory.ts +62 -91
  892. package/src/tools/browser/cdp-client/index.ts +1 -27
  893. package/src/tools/computer-use/definitions.ts +42 -20
  894. package/src/tools/executor.ts +46 -31
  895. package/src/tools/host-filesystem/edit.ts +29 -2
  896. package/src/tools/host-filesystem/read.ts +29 -2
  897. package/src/tools/host-filesystem/transfer.test.ts +45 -42
  898. package/src/tools/host-filesystem/transfer.ts +35 -4
  899. package/src/tools/host-filesystem/write.ts +29 -2
  900. package/src/tools/host-terminal/host-shell.ts +62 -3
  901. package/src/tools/network/script-proxy/index.ts +1 -10
  902. package/src/tools/permission-checker.ts +66 -1
  903. package/src/tools/schedule/create.ts +6 -0
  904. package/src/tools/schedule/list.ts +2 -0
  905. package/src/tools/schedule/update.ts +10 -0
  906. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  907. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  908. package/src/tools/skills/load.ts +0 -32
  909. package/src/tools/skills/sandbox-runner.ts +1 -6
  910. package/src/tools/skills/skill-tool-factory.ts +32 -0
  911. package/src/tools/terminal/safe-env.ts +1 -0
  912. package/src/tools/terminal/shell.ts +2 -78
  913. package/src/tools/tool-approval-handler.ts +1 -5
  914. package/src/tools/types.ts +16 -39
  915. package/src/tts/__tests__/provider-catalog.test.ts +2 -2
  916. package/src/tts/provider-catalog.ts +1 -1
  917. package/src/usage/actors.ts +2 -1
  918. package/src/usage/attribution.ts +185 -0
  919. package/src/usage/pricing.ts +166 -0
  920. package/src/usage/types.ts +14 -0
  921. package/src/util/json.ts +13 -0
  922. package/src/util/logger.ts +3 -3
  923. package/src/util/pricing.ts +50 -3
  924. package/src/work-items/work-item-runner.ts +15 -42
  925. package/src/workspace/hatched-date.ts +86 -0
  926. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  927. package/src/workspace/migrations/006-services-config.ts +8 -5
  928. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  929. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  930. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  931. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  932. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  933. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  934. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +6 -4
  935. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
  936. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  937. package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
  938. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +54 -0
  939. package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
  940. package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
  941. package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
  942. package/src/workspace/migrations/AGENTS.md +1 -1
  943. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  944. package/src/workspace/migrations/registry.ts +8 -0
  945. package/src/workspace/migrations/utils.ts +21 -0
  946. package/src/workspace/provider-commit-message-generator.ts +3 -3
  947. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -904
  948. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -296
  949. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -431
  950. package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
  951. package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
  952. package/src/__tests__/secret-detection-handler.test.ts +0 -67
  953. package/src/__tests__/secret-scanner-executor.test.ts +0 -450
  954. package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
  955. package/src/__tests__/terminal-sandbox.test.ts +0 -374
  956. package/src/__tests__/tool-notification-listener.test.ts +0 -65
  957. package/src/__tests__/twilio-rest.test.ts +0 -34
  958. package/src/backup/__tests__/backup-key.test.ts +0 -152
  959. package/src/backup/__tests__/backup-worker.test.ts +0 -754
  960. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  961. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  962. package/src/backup/backup-key.ts +0 -137
  963. package/src/backup/backup-worker.ts +0 -438
  964. package/src/backup/offsite-writer.ts +0 -222
  965. package/src/backup/stream-crypt.ts +0 -263
  966. package/src/context/__tests__/microcompact.test.ts +0 -805
  967. package/src/context/microcompact.ts +0 -443
  968. package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
  969. package/src/daemon/message-types/pairing.ts +0 -58
  970. package/src/events/tool-notification-listener.ts +0 -17
  971. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
  972. package/src/memory/v2/__tests__/edges.test.ts +0 -435
  973. package/src/memory/v2/edges.ts +0 -217
  974. package/src/outbound-proxy/config.ts +0 -94
  975. package/src/outbound-proxy/health.ts +0 -62
  976. package/src/outbound-proxy/types.ts +0 -150
  977. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
  978. package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
  979. package/src/runtime/__tests__/client-registry.test.ts +0 -271
  980. package/src/runtime/capability-tokens.ts +0 -190
  981. package/src/runtime/chrome-extension-registry.ts +0 -368
  982. package/src/runtime/client-registry.ts +0 -254
  983. package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
  984. package/src/signals/mcp-reload.ts +0 -18
  985. package/src/tools/secret-detection-handler.ts +0 -269
  986. package/src/tools/terminal/backends/native.ts +0 -327
  987. package/src/tools/terminal/backends/types.ts +0 -37
  988. package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
  989. package/src/tools/terminal/sandbox.ts +0 -40
@@ -6,7 +6,6 @@
6
6
  * - workspace/: the entire ~/.vellum/workspace/ directory tree (DB, config,
7
7
  * skills, prompts, attachments, etc.) — excluding large/regenerable
8
8
  * dirs (embedding-models/, data/qdrant/)
9
- * - trust/trust.json: trust rules (optional)
10
9
  */
11
10
 
12
11
  import { createHash, randomUUID } from "node:crypto";
@@ -20,15 +19,18 @@ import {
20
19
  readdirSync,
21
20
  readFileSync,
22
21
  readSync,
22
+ realpathSync,
23
23
  } from "node:fs";
24
24
  import { stat, unlink } from "node:fs/promises";
25
25
  import { tmpdir } from "node:os";
26
- import { join, relative } from "node:path";
26
+ import { dirname, join, relative, resolve, sep } from "node:path";
27
27
  import { Readable } from "node:stream";
28
28
  import { pipeline } from "node:stream/promises";
29
29
  import { createGzip, gzipSync } from "node:zlib";
30
30
 
31
31
  import { sanitizeConfigForTransfer } from "../../config/sanitize-for-transfer.js";
32
+ import { getLogger } from "../../util/logger.js";
33
+ import type { VBundleOriginMode } from "./origin-mode.js";
32
34
  import type {
33
35
  ManifestFileEntryType,
34
36
  ManifestType,
@@ -41,17 +43,54 @@ import type {
41
43
  export interface VBundleFileEntry {
42
44
  path: string;
43
45
  data: Uint8Array;
46
+ /** When set, `data` is ignored: the entry is emitted as a tar typeflag-2 (symlink) record with empty body, and `linkTarget` is the symlink target encoded relative to the symlink's own directory inside the archive. */
47
+ linkTarget?: string;
48
+ }
49
+
50
+ /** v1 manifest `assistant` block. */
51
+ export interface VBundleAssistantInfo {
52
+ id: string;
53
+ name: string;
54
+ runtime_version: string;
55
+ }
56
+
57
+ /** v1 manifest `origin` block. */
58
+ export interface VBundleOriginInfo {
59
+ mode: VBundleOriginMode;
60
+ platform_version?: string;
61
+ hostname?: string;
62
+ }
63
+
64
+ /** v1 manifest `compatibility` block. */
65
+ export interface VBundleCompatibility {
66
+ min_runtime_version: string;
67
+ max_runtime_version: string | null;
68
+ }
69
+
70
+ /** v1 manifest `export_options` block. */
71
+ export interface VBundleExportOptions {
72
+ include_logs: boolean;
73
+ include_browser_state: boolean;
74
+ include_memory_vectors: boolean;
44
75
  }
45
76
 
46
77
  export interface BuildVBundleOptions {
47
78
  /** Files to include in the archive. Must include data/db/assistant.db. */
48
79
  files: VBundleFileEntry[];
49
- /** Schema version for the manifest. Defaults to "1.0". */
50
- schemaVersion?: string;
51
- /** Source identifier (e.g. "runtime-export"). */
52
- source?: string;
53
- /** Human-readable description. */
54
- description?: string;
80
+ /** Identity of the assistant that produced this bundle. */
81
+ assistant: VBundleAssistantInfo;
82
+ /** Where this bundle was produced. */
83
+ origin: VBundleOriginInfo;
84
+ /** Runtime-version compatibility window for importers. */
85
+ compatibility: VBundleCompatibility;
86
+ /** Which optional bundle contents this export carries. */
87
+ exportOptions: VBundleExportOptions;
88
+ /**
89
+ * Whether secrets were stripped from the bundle before archiving.
90
+ * Required at the type level — defaulting silently is exactly how the
91
+ * prior schema mismatch went unnoticed.
92
+ */
93
+ secretsRedacted: boolean;
55
94
  }
56
95
 
57
96
  export interface BuildVBundleResult {
@@ -74,13 +113,24 @@ interface InMemoryEntry {
74
113
  size: number;
75
114
  }
76
115
 
77
- /** Union of disk-backed and in-memory tar stream entries. */
78
- type TarStreamEntry = FileMetadata | InMemoryEntry;
116
+ /** Symlink entry emitted as a tar typeflag-2 record with empty body. */
117
+ interface SymlinkMetadata {
118
+ archivePath: string;
119
+ linkTarget: string;
120
+ size: 0;
121
+ }
122
+
123
+ /** Union of disk-backed, in-memory, and symlink tar stream entries. */
124
+ type TarStreamEntry = FileMetadata | InMemoryEntry | SymlinkMetadata;
79
125
 
80
126
  function isInMemoryEntry(entry: TarStreamEntry): entry is InMemoryEntry {
81
127
  return "data" in entry;
82
128
  }
83
129
 
130
+ function isSymlinkEntry(entry: TarStreamEntry): entry is SymlinkMetadata {
131
+ return "linkTarget" in entry;
132
+ }
133
+
84
134
  // ---------------------------------------------------------------------------
85
135
  // Hash helpers
86
136
  // ---------------------------------------------------------------------------
@@ -199,7 +249,11 @@ function createPaxPathEntry(name: string): Uint8Array {
199
249
  return result;
200
250
  }
201
251
 
202
- function createTarEntry(name: string, data: Uint8Array): Uint8Array {
252
+ function createTarEntry(
253
+ name: string,
254
+ data: Uint8Array,
255
+ linkTarget?: string,
256
+ ): Uint8Array {
203
257
  const encoder = new TextEncoder();
204
258
  const nameBytes = encoder.encode(name);
205
259
 
@@ -208,6 +262,14 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
208
262
  const needsPax = nameBytes.length > 100;
209
263
  const paxEntry = needsPax ? createPaxPathEntry(name) : null;
210
264
 
265
+ const isSymlink = linkTarget !== undefined;
266
+ const linkTargetBytes = isSymlink ? encoder.encode(linkTarget) : null;
267
+ if (linkTargetBytes && linkTargetBytes.length > 100) {
268
+ throw new Error(
269
+ `Symlink target "${linkTarget}" is ${linkTargetBytes.length} bytes, exceeding the ustar linkname-field 100-byte limit. The walker should guard against this case before calling createTarEntry.`,
270
+ );
271
+ }
272
+
211
273
  const header = new Uint8Array(BLOCK_SIZE);
212
274
 
213
275
  // File name (0-99) — truncated if >100 bytes; PAX header carries the full name
@@ -222,14 +284,19 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
222
284
  // Group ID (116-123)
223
285
  writeOctal(header, 116, 8, 0);
224
286
 
225
- // File size (124-135)
226
- writeOctal(header, 124, 12, data.length);
287
+ // File size (124-135) — symlink entries always carry size 0
288
+ writeOctal(header, 124, 12, isSymlink ? 0 : data.length);
227
289
 
228
290
  // Modification time (136-147)
229
291
  writeOctal(header, 136, 12, Math.floor(Date.now() / 1000));
230
292
 
231
- // Type flag (156): regular file
232
- header[156] = "0".charCodeAt(0);
293
+ // Type flag (156): regular file ("0") or symlink ("2")
294
+ header[156] = (isSymlink ? "2" : "0").charCodeAt(0);
295
+
296
+ // Linkname (157-256) — only set for symlinks; null-padded by default
297
+ if (linkTargetBytes) {
298
+ header.set(linkTargetBytes, 157);
299
+ }
233
300
 
234
301
  // USTAR magic (257-262)
235
302
  const magic = encoder.encode("ustar\0");
@@ -239,16 +306,22 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
239
306
  header[263] = "0".charCodeAt(0);
240
307
  header[264] = "0".charCodeAt(0);
241
308
 
242
- // Compute and write checksum (148-155)
309
+ // Compute and write checksum (148-155) — must be last so the linkname
310
+ // (and every other field) contributes to the sum.
243
311
  const checksum = computeHeaderChecksum(header);
244
312
  writeOctal(header, 148, 7, checksum);
245
313
  header[155] = 0x20; // trailing space
246
314
 
247
- // Combine header + padded data
248
- const paddedData = padToBlock(data);
249
- const fileEntry = new Uint8Array(header.length + paddedData.length);
250
- fileEntry.set(header, 0);
251
- fileEntry.set(paddedData, header.length);
315
+ // Symlink entries are header-only no body, no padding.
316
+ const fileEntry = isSymlink
317
+ ? header
318
+ : (() => {
319
+ const paddedData = padToBlock(data);
320
+ const combined = new Uint8Array(header.length + paddedData.length);
321
+ combined.set(header, 0);
322
+ combined.set(paddedData, header.length);
323
+ return combined;
324
+ })();
252
325
 
253
326
  if (paxEntry) {
254
327
  const result = new Uint8Array(paxEntry.length + fileEntry.length);
@@ -261,11 +334,11 @@ function createTarEntry(name: string, data: Uint8Array): Uint8Array {
261
334
  }
262
335
 
263
336
  function createTarArchive(
264
- entries: Array<{ name: string; data: Uint8Array }>,
337
+ entries: Array<{ name: string; data: Uint8Array; linkTarget?: string }>,
265
338
  ): Uint8Array {
266
339
  const parts: Uint8Array[] = [];
267
340
  for (const entry of entries) {
268
- parts.push(createTarEntry(entry.name, entry.data));
341
+ parts.push(createTarEntry(entry.name, entry.data, entry.linkTarget));
269
342
  }
270
343
  // End-of-archive: two zero blocks
271
344
  parts.push(new Uint8Array(BLOCK_SIZE * 2));
@@ -284,50 +357,98 @@ function createTarArchive(
284
357
  // Core builder
285
358
  // ---------------------------------------------------------------------------
286
359
 
360
+ /**
361
+ * Build the v1 manifest object and its serialized JSON bytes for a vbundle.
362
+ *
363
+ * Shared by the buffered (`buildVBundle`) and streaming
364
+ * (`streamExportVBundle`) emit sites so the manifest shape and self-checksum
365
+ * computation live in exactly one place.
366
+ *
367
+ * The checksum is computed over the canonicalized manifest with the
368
+ * `checksum` field set to the empty string (per the schema spec) — both
369
+ * producers and the validator agree on this exact wire shape.
370
+ */
371
+ function buildManifestObject(input: {
372
+ contents: ManifestFileEntryType[];
373
+ assistant: VBundleAssistantInfo;
374
+ origin: VBundleOriginInfo;
375
+ compatibility: VBundleCompatibility;
376
+ exportOptions: VBundleExportOptions;
377
+ secretsRedacted: boolean;
378
+ now: Date;
379
+ }): { manifest: ManifestType; manifestData: Uint8Array } {
380
+ const manifestWithEmptyChecksum = {
381
+ schema_version: 1 as const,
382
+ bundle_id: randomUUID(),
383
+ created_at: input.now.toISOString(),
384
+ assistant: input.assistant,
385
+ origin: input.origin,
386
+ compatibility: input.compatibility,
387
+ contents: input.contents,
388
+ checksum: "",
389
+ secrets_redacted: input.secretsRedacted,
390
+ export_options: input.exportOptions,
391
+ };
392
+ const checksum = sha256Hex(canonicalizeJson(manifestWithEmptyChecksum));
393
+ const manifest: ManifestType = { ...manifestWithEmptyChecksum, checksum };
394
+ const manifestData = new TextEncoder().encode(JSON.stringify(manifest));
395
+ return { manifest, manifestData };
396
+ }
397
+
287
398
  /**
288
399
  * Build a .vbundle archive from the given files and metadata.
289
400
  *
290
401
  * Generates a valid manifest with SHA-256 checksums for all files and
291
- * a self-referencing manifest_sha256 checksum. The archive is returned
402
+ * a self-referencing `checksum`. The archive is returned
292
403
  * as gzip-compressed tar bytes.
293
404
  */
294
405
  export function buildVBundle(options: BuildVBundleOptions): BuildVBundleResult {
295
406
  const {
296
407
  files,
297
- schemaVersion = "1.0",
298
- source = "runtime-export",
299
- description = "Runtime export bundle",
408
+ assistant,
409
+ origin,
410
+ compatibility,
411
+ exportOptions,
412
+ secretsRedacted,
300
413
  } = options;
301
414
 
302
- // Build file entries for the manifest
303
- const fileEntries: ManifestFileEntryType[] = files.map((f) => ({
304
- path: f.path,
305
- sha256: sha256Hex(f.data),
306
- size: f.data.length,
307
- }));
308
-
309
- // Build manifest without the self-checksum
310
- const manifestWithoutChecksum = {
311
- schema_version: schemaVersion,
312
- created_at: new Date().toISOString(),
313
- source,
314
- description,
315
- files: fileEntries,
316
- };
317
-
318
- // Compute the manifest self-checksum
319
- const manifestSha256 = sha256Hex(canonicalizeJson(manifestWithoutChecksum));
320
- const manifest: ManifestType = {
321
- ...manifestWithoutChecksum,
322
- manifest_sha256: manifestSha256,
323
- };
415
+ // Build file entries for the manifest. Symlink entries hash the link target
416
+ // string (not the empty data buffer) and declare size_bytes: 0.
417
+ const fileEntries: ManifestFileEntryType[] = files.map((f) =>
418
+ f.linkTarget !== undefined
419
+ ? {
420
+ path: f.path,
421
+ sha256: sha256Hex(f.linkTarget),
422
+ size_bytes: 0,
423
+ link_target: f.linkTarget,
424
+ }
425
+ : {
426
+ path: f.path,
427
+ sha256: sha256Hex(f.data),
428
+ size_bytes: f.data.length,
429
+ },
430
+ );
324
431
 
325
- const manifestData = new TextEncoder().encode(JSON.stringify(manifest));
432
+ const { manifest, manifestData } = buildManifestObject({
433
+ contents: fileEntries,
434
+ assistant,
435
+ origin,
436
+ compatibility,
437
+ exportOptions,
438
+ secretsRedacted,
439
+ now: new Date(),
440
+ });
326
441
 
327
- // Build tar entries: manifest first, then all files
442
+ // Build tar entries: manifest first, then all files. Symlink entries forward
443
+ // `linkTarget` so createTarEntry emits a typeflag-2 header; `data` is unused
444
+ // in that branch but must still be a valid Uint8Array.
328
445
  const tarEntries = [
329
446
  { name: "manifest.json", data: manifestData },
330
- ...files.map((f) => ({ name: f.path, data: f.data })),
447
+ ...files.map((f) =>
448
+ f.linkTarget !== undefined
449
+ ? { name: f.path, data: new Uint8Array(0), linkTarget: f.linkTarget }
450
+ : { name: f.path, data: f.data },
451
+ ),
331
452
  ];
332
453
 
333
454
  const tar = createTarArchive(tarEntries);
@@ -343,33 +464,143 @@ export function buildVBundle(options: BuildVBundleOptions): BuildVBundleResult {
343
464
  interface WalkDirectoryOptions {
344
465
  /** Include binary files (files containing null bytes). Default: false. */
345
466
  includeBinary?: boolean;
346
- /** Directory names to skip (matched against immediate child name). */
467
+ /** Directory names to skip (matched against relative path from walk root). */
347
468
  skipDirs?: string[];
469
+ /** File names to skip (matched against the entry basename). */
470
+ skipFiles?: string[];
348
471
  }
349
472
 
350
473
  /**
351
- * Recursively walk a directory and return all non-symlink files as
352
- * VBundleFileEntry objects with paths prefixed by `archivePrefix`.
474
+ * Resolve and classify a symlink encountered during a walk.
475
+ *
476
+ * Returns one of:
477
+ * { kind: "class1", linkTarget } — emit as a tar typeflag-2 entry whose
478
+ * `linkname` field holds `linkTarget` (the symlink target encoded as a
479
+ * POSIX path relative to the symlink's own directory).
480
+ * { kind: "drop", reason } — drop the symlink. Reasons cover broken
481
+ * links, targets outside the workspace (class 2), targets inside a
482
+ * skipped directory (class 3), directory targets (out of scope), and
483
+ * link targets whose UTF-8 encoding exceeds the 100-byte ustar
484
+ * `linkname` field limit.
485
+ */
486
+ type SymlinkClassification =
487
+ | { kind: "class1"; linkTarget: string }
488
+ | { kind: "drop"; reason: string };
489
+
490
+ function classifySymlink(args: {
491
+ fullPath: string;
492
+ walkRoot: string;
493
+ skipDirs: readonly string[];
494
+ }): SymlinkClassification {
495
+ const { fullPath, walkRoot, skipDirs } = args;
496
+
497
+ let absoluteTarget: string;
498
+ try {
499
+ absoluteTarget = realpathSync(fullPath);
500
+ } catch {
501
+ return { kind: "drop", reason: "broken symlink (realpath failed)" };
502
+ }
503
+
504
+ let targetStat;
505
+ try {
506
+ targetStat = lstatSync(absoluteTarget);
507
+ } catch {
508
+ return { kind: "drop", reason: "broken symlink (target stat failed)" };
509
+ }
510
+ if (!targetStat.isFile()) {
511
+ return { kind: "drop", reason: "target is not a regular file" };
512
+ }
513
+
514
+ let dirAbs: string;
515
+ try {
516
+ dirAbs = realpathSync(walkRoot);
517
+ } catch {
518
+ dirAbs = resolve(walkRoot);
519
+ }
520
+ const targetAbs = resolve(absoluteTarget);
521
+ const insideWorkspace =
522
+ targetAbs === dirAbs || targetAbs.startsWith(dirAbs + sep);
523
+ if (!insideWorkspace) {
524
+ return { kind: "drop", reason: "target outside workspace" };
525
+ }
526
+
527
+ const targetRelToWorkspace = relative(dirAbs, targetAbs);
528
+ if (
529
+ skipDirs.some(
530
+ (s) =>
531
+ targetRelToWorkspace === s || targetRelToWorkspace.startsWith(s + "/"),
532
+ )
533
+ ) {
534
+ return { kind: "drop", reason: "target inside skipDir" };
535
+ }
536
+
537
+ // Canonicalize the symlink's parent directory so the relative linkTarget
538
+ // computation lines up with `absoluteTarget` (which is canonical from
539
+ // realpathSync). On macOS, walking through /var/folders/... and resolving
540
+ // the target through /private/var/folders/... would otherwise produce a
541
+ // long ../../../private/... path that exceeds the 100-byte ustar limit.
542
+ let parentAbs: string;
543
+ try {
544
+ parentAbs = realpathSync(dirname(fullPath));
545
+ } catch {
546
+ parentAbs = resolve(dirname(fullPath));
547
+ }
548
+ const linkTarget = relative(parentAbs, absoluteTarget);
549
+ if (new TextEncoder().encode(linkTarget).length > 100) {
550
+ return {
551
+ kind: "drop",
552
+ reason: "encoded link target exceeds 100-byte ustar limit",
553
+ };
554
+ }
555
+
556
+ return { kind: "class1", linkTarget };
557
+ }
558
+
559
+ /**
560
+ * Recursively walk a directory and return all regular files (and bundleable
561
+ * symlinks) as VBundleFileEntry objects with paths prefixed by
562
+ * `archivePrefix`. Symlinks that resolve to a regular file inside the walk
563
+ * root and outside any skipDir are emitted as typeflag-2 entries (data
564
+ * empty, `linkTarget` populated). All other symlinks (broken, directory
565
+ * target, target outside workspace, target inside skipDir, encoded
566
+ * linkTarget over 100 bytes) are reported via the returned `droppedSymlinks`
567
+ * array as workspace-relative paths of the symlink itself.
353
568
  *
354
569
  * By default, binary files (detected via null-byte heuristic in the first
355
570
  * 8 KB) are skipped. Pass `includeBinary: true` to include them.
356
571
  */
357
- function walkDirectory(
572
+ export function walkDirectory(
358
573
  dir: string,
359
574
  archivePrefix: string,
360
575
  options: WalkDirectoryOptions = {},
361
- ): VBundleFileEntry[] {
362
- const { includeBinary = false, skipDirs = [] } = options;
576
+ ): { files: VBundleFileEntry[]; droppedSymlinks: string[] } {
577
+ const { includeBinary = false, skipDirs = [], skipFiles = [] } = options;
363
578
  const entries: VBundleFileEntry[] = [];
579
+ const droppedSymlinks: string[] = [];
364
580
 
365
581
  function walk(currentDir: string): void {
366
582
  const dirEntries = readdirSync(currentDir, { withFileTypes: true });
367
583
  for (const entry of dirEntries) {
368
584
  const fullPath = join(currentDir, entry.name);
369
585
 
370
- // Skip symlinks
371
586
  const stat = lstatSync(fullPath);
372
- if (stat.isSymbolicLink()) continue;
587
+ if (stat.isSymbolicLink()) {
588
+ const classification = classifySymlink({
589
+ fullPath,
590
+ walkRoot: dir,
591
+ skipDirs,
592
+ });
593
+ if (classification.kind === "class1") {
594
+ entries.push({
595
+ path: `${archivePrefix}/${relative(dir, fullPath)}`,
596
+ data: new Uint8Array(0),
597
+ linkTarget: classification.linkTarget,
598
+ });
599
+ } else {
600
+ droppedSymlinks.push(relative(dir, fullPath));
601
+ }
602
+ continue;
603
+ }
373
604
 
374
605
  if (stat.isDirectory()) {
375
606
  // Check skip list against the relative path from the walk root
@@ -379,6 +610,9 @@ function walkDirectory(
379
610
  }
380
611
  walk(fullPath);
381
612
  } else if (stat.isFile()) {
613
+ // Skip files by basename (e.g. backup key)
614
+ if (skipFiles.includes(entry.name)) continue;
615
+
382
616
  // Skip SQLite auxiliary files — these are ephemeral and race-prone
383
617
  // with the live DB connection. The WAL is checkpointed before the
384
618
  // walk, so the main .db file has all committed rows.
@@ -415,7 +649,7 @@ function walkDirectory(
415
649
  }
416
650
 
417
651
  walk(dir);
418
- return entries;
652
+ return { files: entries, droppedSymlinks };
419
653
  }
420
654
 
421
655
  // ---------------------------------------------------------------------------
@@ -423,12 +657,16 @@ function walkDirectory(
423
657
  // ---------------------------------------------------------------------------
424
658
 
425
659
  export interface BuildExportVBundleOptions {
426
- /** Source identifier. Defaults to "runtime-export". */
427
- source?: string;
428
- /** Human-readable description. */
429
- description?: string;
430
- /** Absolute path to trust.json. If provided and the file exists, it is included in the archive. */
431
- trustPath?: string;
660
+ /** Identity of the assistant that produced this bundle. */
661
+ assistant: VBundleAssistantInfo;
662
+ /** Where this bundle was produced. */
663
+ origin: VBundleOriginInfo;
664
+ /** Runtime-version compatibility window for importers. */
665
+ compatibility: VBundleCompatibility;
666
+ /** Which optional bundle contents this export carries. */
667
+ exportOptions: VBundleExportOptions;
668
+ /** Whether secrets were stripped from the bundle before archiving. */
669
+ secretsRedacted: boolean;
432
670
  /**
433
671
  * Absolute path to the workspace directory (~/.vellum/workspace/).
434
672
  * When provided and exists, the entire directory tree is walked and
@@ -455,7 +693,7 @@ export interface BuildExportVBundleOptions {
455
693
  * Walks the entire workspace directory (~/.vellum/workspace/) and includes
456
694
  * all files in the archive, skipping only large/regenerable directories
457
695
  * (embedding-models/, data/qdrant/). Binary files (SQLite DB, attachments)
458
- * are included. Trust rules are handled separately.
696
+ * are included.
459
697
  *
460
698
  * The WAL is checkpointed before the walk so the exported DB file contains
461
699
  * all committed rows.
@@ -464,10 +702,12 @@ export function buildExportVBundle(
464
702
  options: BuildExportVBundleOptions,
465
703
  ): BuildVBundleResult {
466
704
  const {
467
- source,
468
- description,
705
+ assistant,
706
+ origin,
707
+ compatibility,
708
+ exportOptions,
709
+ secretsRedacted,
469
710
  checkpoint,
470
- trustPath,
471
711
  workspaceDir,
472
712
  credentials,
473
713
  } = options;
@@ -488,12 +728,22 @@ export function buildExportVBundle(
488
728
  existsSync(workspaceDir) &&
489
729
  lstatSync(workspaceDir).isDirectory()
490
730
  ) {
491
- files.push(
492
- ...walkDirectory(workspaceDir, "workspace", {
731
+ const { files: walkedFiles, droppedSymlinks } = walkDirectory(
732
+ workspaceDir,
733
+ "workspace",
734
+ {
493
735
  includeBinary: true,
494
736
  skipDirs: ["embedding-models", "data/qdrant", "signals", "deprecated"],
495
- }),
737
+ skipFiles: [".backup.key"],
738
+ },
496
739
  );
740
+ files.push(...walkedFiles);
741
+ if (droppedSymlinks.length > 0) {
742
+ getLogger("vbundle-builder").warn(
743
+ { count: droppedSymlinks.length, paths: droppedSymlinks },
744
+ `Dropped ${droppedSymlinks.length} symlinks pointing outside workspace or into skipped directories`,
745
+ );
746
+ }
497
747
  }
498
748
 
499
749
  // Sanitize workspace/config.json to strip environment-specific fields
@@ -504,12 +754,6 @@ export function buildExportVBundle(
504
754
  configEntry.data = new TextEncoder().encode(sanitized);
505
755
  }
506
756
 
507
- // Include trust rules if the file exists.
508
- if (trustPath && existsSync(trustPath)) {
509
- const trustData = new Uint8Array(readFileSync(trustPath));
510
- files.push({ path: "trust/trust.json", data: trustData });
511
- }
512
-
513
757
  // Include credential entries if provided
514
758
  if (credentials?.length) {
515
759
  for (const { account, value } of credentials) {
@@ -520,8 +764,11 @@ export function buildExportVBundle(
520
764
 
521
765
  return buildVBundle({
522
766
  files,
523
- source: source ?? "runtime-export",
524
- description: description ?? "Runtime export bundle",
767
+ assistant,
768
+ origin,
769
+ compatibility,
770
+ exportOptions,
771
+ secretsRedacted,
525
772
  });
526
773
  }
527
774
 
@@ -531,26 +778,48 @@ export function buildExportVBundle(
531
778
 
532
779
  /**
533
780
  * Walk a directory tree and collect file metadata (paths + sizes) without
534
- * reading file contents into memory. Uses the same filtering logic as
535
- * `walkDirectory` (symlink skip, SQLite auxiliary skip, binary detection,
536
- * skip dirs).
781
+ * reading file contents into memory. Mirrors `walkDirectory`'s filtering
782
+ * logic (SQLite auxiliary skip, binary detection, skipDirs) and symlink
783
+ * classification — bundleable symlinks are emitted as `SymlinkMetadata`
784
+ * entries; non-bundleable symlinks are reported via `droppedSymlinks`.
537
785
  */
538
- function walkDirectoryForMetadata(
786
+ export function walkDirectoryForMetadata(
539
787
  dir: string,
540
788
  archivePrefix: string,
541
789
  options: WalkDirectoryOptions = {},
542
- ): FileMetadata[] {
543
- const { includeBinary = false, skipDirs = [] } = options;
790
+ ): {
791
+ files: FileMetadata[];
792
+ symlinks: SymlinkMetadata[];
793
+ droppedSymlinks: string[];
794
+ } {
795
+ const { includeBinary = false, skipDirs = [], skipFiles = [] } = options;
544
796
  const entries: FileMetadata[] = [];
797
+ const symlinks: SymlinkMetadata[] = [];
798
+ const droppedSymlinks: string[] = [];
545
799
 
546
800
  function walk(currentDir: string): void {
547
801
  const dirEntries = readdirSync(currentDir, { withFileTypes: true });
548
802
  for (const entry of dirEntries) {
549
803
  const fullPath = join(currentDir, entry.name);
550
804
 
551
- // Skip symlinks
552
805
  const fileStat = lstatSync(fullPath);
553
- if (fileStat.isSymbolicLink()) continue;
806
+ if (fileStat.isSymbolicLink()) {
807
+ const classification = classifySymlink({
808
+ fullPath,
809
+ walkRoot: dir,
810
+ skipDirs,
811
+ });
812
+ if (classification.kind === "class1") {
813
+ symlinks.push({
814
+ archivePath: `${archivePrefix}/${relative(dir, fullPath)}`,
815
+ linkTarget: classification.linkTarget,
816
+ size: 0,
817
+ });
818
+ } else {
819
+ droppedSymlinks.push(relative(dir, fullPath));
820
+ }
821
+ continue;
822
+ }
554
823
 
555
824
  if (fileStat.isDirectory()) {
556
825
  // Check skip list against the relative path from the walk root
@@ -560,6 +829,9 @@ function walkDirectoryForMetadata(
560
829
  }
561
830
  walk(fullPath);
562
831
  } else if (fileStat.isFile()) {
832
+ // Skip files by basename (e.g. backup key)
833
+ if (skipFiles.includes(entry.name)) continue;
834
+
563
835
  // Skip SQLite auxiliary files — these are ephemeral and race-prone
564
836
  if (
565
837
  entry.name.endsWith(".db-wal") ||
@@ -603,7 +875,7 @@ function walkDirectoryForMetadata(
603
875
  }
604
876
 
605
877
  walk(dir);
606
- return entries;
878
+ return { files: entries, symlinks, droppedSymlinks };
607
879
  }
608
880
 
609
881
  /**
@@ -629,8 +901,18 @@ async function computeFileSha256(
629
901
  /**
630
902
  * Create just the 512-byte tar header block for a regular file entry.
631
903
  * Extracted from `createTarEntry` logic — does NOT include data or padding.
904
+ *
905
+ * When `linkTarget` is provided, the header is emitted as a tar typeflag-2
906
+ * (symlink) record: typeflag is "2", the link target is written into the
907
+ * `linkname` field (header[157..256], 100-byte limit), and `size` is forced
908
+ * to 0 in the header field. Caller is responsible for not yielding any body
909
+ * or padding bytes for symlink entries.
632
910
  */
633
- function createTarHeaderBlock(name: string, size: number): Uint8Array {
911
+ function createTarHeaderBlock(
912
+ name: string,
913
+ size: number,
914
+ linkTarget?: string,
915
+ ): Uint8Array {
634
916
  const encoder = new TextEncoder();
635
917
  const nameBytes = encoder.encode(name);
636
918
 
@@ -648,14 +930,26 @@ function createTarHeaderBlock(name: string, size: number): Uint8Array {
648
930
  // Group ID (116-123)
649
931
  writeOctal(header, 116, 8, 0);
650
932
 
651
- // File size (124-135)
652
- writeOctal(header, 124, 12, size);
933
+ // File size (124-135) — symlink entries always declare size=0
934
+ writeOctal(header, 124, 12, linkTarget !== undefined ? 0 : size);
653
935
 
654
936
  // Modification time (136-147)
655
937
  writeOctal(header, 136, 12, Math.floor(Date.now() / 1000));
656
938
 
657
- // Type flag (156): regular file
658
- header[156] = "0".charCodeAt(0);
939
+ // Type flag (156): regular file ("0") or symlink ("2")
940
+ header[156] =
941
+ linkTarget !== undefined ? "2".charCodeAt(0) : "0".charCodeAt(0);
942
+
943
+ // Linkname (157-256, 100 bytes) — only set for symlink entries
944
+ if (linkTarget !== undefined) {
945
+ const linkBytes = encoder.encode(linkTarget);
946
+ if (linkBytes.length > 100) {
947
+ throw new Error(
948
+ `symlink target exceeds 100-byte ustar linkname limit (${linkBytes.length} bytes): ${linkTarget}`,
949
+ );
950
+ }
951
+ header.set(linkBytes, 157);
952
+ }
659
953
 
660
954
  // USTAR magic (257-262)
661
955
  const magic = encoder.encode("ustar\0");
@@ -665,7 +959,8 @@ function createTarHeaderBlock(name: string, size: number): Uint8Array {
665
959
  header[263] = "0".charCodeAt(0);
666
960
  header[264] = "0".charCodeAt(0);
667
961
 
668
- // Compute and write checksum (148-155)
962
+ // Compute and write checksum (148-155). Must run AFTER linkname is set
963
+ // so the checksum covers the symlink target bytes.
669
964
  const checksum = computeHeaderChecksum(header);
670
965
  writeOctal(header, 148, 7, checksum);
671
966
  header[155] = 0x20; // trailing space
@@ -677,13 +972,21 @@ function createTarHeaderBlock(name: string, size: number): Uint8Array {
677
972
  * If name exceeds 100 bytes, returns the PAX extended header entry
678
973
  * concatenated with the regular header block. Otherwise returns just
679
974
  * the header block.
975
+ *
976
+ * `linkTarget` is forwarded to `createTarHeaderBlock` so symlink entries
977
+ * still get a PAX path header for long names while emitting a typeflag-2
978
+ * ustar block.
680
979
  */
681
- function createPaxAndHeaderBlocks(name: string, size: number): Uint8Array {
980
+ function createPaxAndHeaderBlocks(
981
+ name: string,
982
+ size: number,
983
+ linkTarget?: string,
984
+ ): Uint8Array {
682
985
  const encoder = new TextEncoder();
683
986
  const nameBytes = encoder.encode(name);
684
987
  const needsPax = nameBytes.length > 100;
685
988
 
686
- const header = createTarHeaderBlock(name, size);
989
+ const header = createTarHeaderBlock(name, size, linkTarget);
687
990
 
688
991
  if (needsPax) {
689
992
  const paxEntry = createPaxPathEntry(name);
@@ -721,7 +1024,15 @@ async function* generateTarStream(
721
1024
 
722
1025
  // File entries
723
1026
  for (const file of files) {
724
- const entrySize = isInMemoryEntry(file) ? file.size : file.size;
1027
+ if (isSymlinkEntry(file)) {
1028
+ // Symlink entry: typeflag-2 header carries the linkname; no body, no
1029
+ // padding. Skip the entrySize/body/padding logic entirely so the
1030
+ // surrounding stream stays block-aligned.
1031
+ yield createPaxAndHeaderBlocks(file.archivePath, 0, file.linkTarget);
1032
+ continue;
1033
+ }
1034
+
1035
+ const entrySize = file.size;
725
1036
  yield createPaxAndHeaderBlocks(file.archivePath, entrySize);
726
1037
 
727
1038
  if (isInMemoryEntry(file)) {
@@ -799,10 +1110,12 @@ export async function streamExportVBundle(
799
1110
  options: BuildExportVBundleOptions,
800
1111
  ): Promise<StreamExportVBundleResult> {
801
1112
  const {
802
- source,
803
- description,
1113
+ assistant,
1114
+ origin,
1115
+ compatibility,
1116
+ exportOptions,
1117
+ secretsRedacted,
804
1118
  checkpoint,
805
- trustPath,
806
1119
  workspaceDir,
807
1120
  credentials,
808
1121
  } = options;
@@ -813,6 +1126,7 @@ export async function streamExportVBundle(
813
1126
  }
814
1127
 
815
1128
  const allFileMetadata: FileMetadata[] = [];
1129
+ const symlinkEntries: SymlinkMetadata[] = [];
816
1130
 
817
1131
  // Walk the entire workspace directory, including binary files
818
1132
  if (
@@ -820,23 +1134,22 @@ export async function streamExportVBundle(
820
1134
  existsSync(workspaceDir) &&
821
1135
  lstatSync(workspaceDir).isDirectory()
822
1136
  ) {
823
- allFileMetadata.push(
824
- ...walkDirectoryForMetadata(workspaceDir, "workspace", {
825
- includeBinary: true,
826
- skipDirs: ["embedding-models", "data/qdrant", "signals", "deprecated"],
827
- }),
828
- );
829
- }
830
-
831
- // Include trust rules if the file exists
832
- if (trustPath && existsSync(trustPath)) {
833
- const trustStat = lstatSync(trustPath);
834
- if (trustStat.isFile()) {
835
- allFileMetadata.push({
836
- archivePath: "trust/trust.json",
837
- diskPath: trustPath,
838
- size: trustStat.size,
839
- });
1137
+ const {
1138
+ files: walkedFiles,
1139
+ symlinks: walkedSymlinks,
1140
+ droppedSymlinks,
1141
+ } = walkDirectoryForMetadata(workspaceDir, "workspace", {
1142
+ includeBinary: true,
1143
+ skipDirs: ["embedding-models", "data/qdrant", "signals", "deprecated"],
1144
+ skipFiles: [".backup.key"],
1145
+ });
1146
+ allFileMetadata.push(...walkedFiles);
1147
+ symlinkEntries.push(...walkedSymlinks);
1148
+ if (droppedSymlinks.length > 0) {
1149
+ getLogger("vbundle-builder").warn(
1150
+ { count: droppedSymlinks.length, paths: droppedSymlinks },
1151
+ `Dropped ${droppedSymlinks.length} symlinks pointing outside workspace or into skipped directories`,
1152
+ );
840
1153
  }
841
1154
  }
842
1155
 
@@ -886,7 +1199,7 @@ export async function streamExportVBundle(
886
1199
  fileEntries.push({
887
1200
  path: file.archivePath,
888
1201
  sha256,
889
- size: file.size,
1202
+ size_bytes: file.size,
890
1203
  });
891
1204
  }
892
1205
 
@@ -896,25 +1209,32 @@ export async function streamExportVBundle(
896
1209
  fileEntries.push({
897
1210
  path: entry.archivePath,
898
1211
  sha256,
899
- size: entry.size,
1212
+ size_bytes: entry.size,
900
1213
  });
901
1214
  }
902
1215
 
903
- const manifestWithoutChecksum = {
904
- schema_version: "1.0",
905
- created_at: new Date().toISOString(),
906
- source: source ?? "runtime-export",
907
- description: description ?? "Runtime export bundle",
908
- files: fileEntries,
909
- };
910
-
911
- const manifestSha256 = sha256Hex(canonicalizeJson(manifestWithoutChecksum));
912
- const manifest: ManifestType = {
913
- ...manifestWithoutChecksum,
914
- manifest_sha256: manifestSha256,
915
- };
1216
+ // Add symlink entries to the manifest. The sha256 is computed over the
1217
+ // link target string (UTF-8 encoded) so the streaming validator can
1218
+ // verify the manifest declared the same target the tar header carries.
1219
+ // size_bytes is always 0 for symlink entries.
1220
+ for (const entry of symlinkEntries) {
1221
+ fileEntries.push({
1222
+ path: entry.archivePath,
1223
+ sha256: sha256Hex(entry.linkTarget),
1224
+ size_bytes: 0,
1225
+ link_target: entry.linkTarget,
1226
+ });
1227
+ }
916
1228
 
917
- const manifestData = new TextEncoder().encode(JSON.stringify(manifest));
1229
+ const { manifest, manifestData } = buildManifestObject({
1230
+ contents: fileEntries,
1231
+ assistant,
1232
+ origin,
1233
+ compatibility,
1234
+ exportOptions,
1235
+ secretsRedacted,
1236
+ now: new Date(),
1237
+ });
918
1238
 
919
1239
  // ------------------------------------------------------------------
920
1240
  // Pass 2: Stream tar through gzip into a temp file
@@ -926,6 +1246,7 @@ export async function streamExportVBundle(
926
1246
  ...allFileMetadata,
927
1247
  ...sanitizedConfigEntries,
928
1248
  ...inMemoryEntries,
1249
+ ...symlinkEntries,
929
1250
  ];
930
1251
  const tarGenerator = generateTarStream(manifestData, allEntries);
931
1252
  const tarReadable = Readable.from(tarGenerator);