@vellumai/assistant 0.6.1 → 0.6.3

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 (463) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docker-entrypoint.sh +12 -2
  4. package/docs/architecture/memory.md +1 -1
  5. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  6. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  7. package/openapi.yaml +184 -69
  8. package/package.json +41 -41
  9. package/scripts/generate-openapi.ts +1 -2
  10. package/src/__tests__/acp-session.test.ts +43 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  12. package/src/__tests__/app-executors.test.ts +1 -0
  13. package/src/__tests__/app-source-watcher.test.ts +37 -11
  14. package/src/__tests__/approval-routes-http.test.ts +178 -1
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  17. package/src/__tests__/browser-manager.test.ts +40 -27
  18. package/src/__tests__/catalog-files.test.ts +862 -0
  19. package/src/__tests__/channel-approvals.test.ts +53 -0
  20. package/src/__tests__/checker.test.ts +104 -170
  21. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  22. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  23. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  24. package/src/__tests__/config-schema.test.ts +125 -48
  25. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  26. package/src/__tests__/context-overflow-approval.test.ts +21 -6
  27. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  28. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  29. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  30. package/src/__tests__/conversation-attachments.test.ts +80 -4
  31. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  32. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  33. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  34. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  35. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  36. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  37. package/src/__tests__/conversation-queue.test.ts +45 -2
  38. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  39. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  40. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  41. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  42. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  43. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  44. package/src/__tests__/conversation-store.test.ts +195 -0
  45. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  46. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
  47. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  48. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  49. package/src/__tests__/credential-vault.test.ts +152 -13
  50. package/src/__tests__/credentials-cli.test.ts +2 -2
  51. package/src/__tests__/date-context.test.ts +4 -4
  52. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  53. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  54. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  55. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  56. package/src/__tests__/gemini-provider.test.ts +2 -2
  57. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  58. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  59. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  60. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  61. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  62. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  63. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  64. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  65. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  66. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  67. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  68. package/src/__tests__/host-browser-routes.test.ts +198 -0
  69. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  70. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  71. package/src/__tests__/host-file-proxy.test.ts +185 -1
  72. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  73. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  74. package/src/__tests__/host-shell-tool.test.ts +1 -11
  75. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  76. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  77. package/src/__tests__/inline-command-runner.test.ts +7 -5
  78. package/src/__tests__/integration-status.test.ts +6 -7
  79. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  80. package/src/__tests__/log-export-workspace.test.ts +190 -0
  81. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  82. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  83. package/src/__tests__/mcp-health-check.test.ts +10 -3
  84. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  85. package/src/__tests__/migration-export-http.test.ts +61 -2
  86. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  87. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  88. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  89. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  90. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  91. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  92. package/src/__tests__/oauth-cli.test.ts +707 -60
  93. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  94. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  95. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  96. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  97. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  98. package/src/__tests__/oauth-store.test.ts +1386 -182
  99. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  100. package/src/__tests__/onboarding-template-contract.test.ts +74 -55
  101. package/src/__tests__/openai-provider.test.ts +2 -2
  102. package/src/__tests__/outlook-categories.test.ts +1 -1
  103. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  104. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  105. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  106. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  107. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  108. package/src/__tests__/outlook-trash.test.ts +1 -1
  109. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  110. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  111. package/src/__tests__/permission-mode.test.ts +28 -56
  112. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  113. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  114. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  115. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  116. package/src/__tests__/require-fresh-approval.test.ts +40 -3
  117. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  118. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  119. package/src/__tests__/schedule-routes.test.ts +162 -0
  120. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  121. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  123. package/src/__tests__/set-permission-mode.test.ts +13 -250
  124. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  125. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  126. package/src/__tests__/slack-channel-config.test.ts +12 -15
  127. package/src/__tests__/subagent-detail.test.ts +44 -2
  128. package/src/__tests__/subagent-disposal.test.ts +1 -0
  129. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  130. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  131. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  132. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  134. package/src/__tests__/subagent-tools.test.ts +1 -0
  135. package/src/__tests__/subagent-types.test.ts +1 -0
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  137. package/src/__tests__/system-prompt.test.ts +72 -1
  138. package/src/__tests__/task-scheduler.test.ts +32 -6
  139. package/src/__tests__/telegram-config.test.ts +10 -13
  140. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  141. package/src/__tests__/terminal-tools.test.ts +11 -5
  142. package/src/__tests__/test-preload.ts +14 -0
  143. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  144. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  145. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  146. package/src/__tests__/tool-executor.test.ts +0 -1
  147. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  148. package/src/__tests__/top-level-renderer.test.ts +73 -1
  149. package/src/__tests__/transport-hints-queue.test.ts +62 -0
  150. package/src/__tests__/trust-store.test.ts +4 -4
  151. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  152. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  153. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  154. package/src/__tests__/workspace-policy.test.ts +2 -7
  155. package/src/acp/client-handler.ts +30 -4
  156. package/src/agent/loop.ts +12 -35
  157. package/src/approvals/guardian-request-resolvers.ts +21 -15
  158. package/src/browser-session/__tests__/manager.test.ts +297 -0
  159. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  160. package/src/browser-session/backends/extension.ts +26 -0
  161. package/src/browser-session/backends/local.ts +24 -0
  162. package/src/browser-session/events.ts +164 -0
  163. package/src/browser-session/index.ts +27 -0
  164. package/src/browser-session/manager.ts +159 -0
  165. package/src/browser-session/types.ts +28 -0
  166. package/src/channels/__tests__/types.test.ts +134 -0
  167. package/src/channels/types.ts +55 -0
  168. package/src/cli/__tests__/run-assistant-command.ts +34 -7
  169. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  170. package/src/cli/commands/browser-relay.ts +339 -409
  171. package/src/cli/commands/credentials.ts +3 -3
  172. package/src/cli/commands/default-action.ts +68 -1
  173. package/src/cli/commands/email.ts +18 -13
  174. package/src/cli/commands/mcp.ts +16 -4
  175. package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
  176. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  177. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  178. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  179. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  180. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  181. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  182. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  183. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  184. package/src/cli/commands/oauth/apps.ts +7 -4
  185. package/src/cli/commands/oauth/connect.ts +16 -2
  186. package/src/cli/commands/oauth/disconnect.ts +1 -1
  187. package/src/cli/commands/oauth/providers.ts +200 -36
  188. package/src/cli/commands/oauth/shared.ts +5 -5
  189. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  190. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  191. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  192. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  193. package/src/cli/commands/platform/index.ts +107 -10
  194. package/src/cli/commands/usage.ts +10 -9
  195. package/src/cli/lib/daemon-credential-client.ts +4 -0
  196. package/src/cli/program.ts +10 -3
  197. package/src/config/assistant-feature-flags.ts +59 -55
  198. package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
  199. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  200. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  201. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  202. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  203. package/src/config/bundled-skills/document/SKILL.md +4 -0
  204. package/src/config/bundled-skills/gmail/SKILL.md +12 -7
  205. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  206. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  207. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  208. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  209. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  210. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  211. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  212. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  213. package/src/config/env-registry.ts +14 -0
  214. package/src/config/env.ts +21 -0
  215. package/src/config/feature-flag-registry.json +46 -7
  216. package/src/config/loader.ts +56 -1
  217. package/src/config/sanitize-for-transfer.ts +47 -0
  218. package/src/config/schema.ts +46 -5
  219. package/src/config/schemas/host-browser.ts +66 -0
  220. package/src/config/schemas/memory-lifecycle.ts +1 -1
  221. package/src/config/schemas/memory-retrieval.ts +103 -0
  222. package/src/config/schemas/security.ts +0 -6
  223. package/src/config/schemas/services.ts +16 -0
  224. package/src/config/types.ts +0 -1
  225. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  226. package/src/context/window-manager.ts +19 -1
  227. package/src/credential-execution/approval-bridge.ts +49 -16
  228. package/src/credential-execution/managed-catalog.ts +3 -7
  229. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  230. package/src/daemon/app-source-watcher.ts +35 -0
  231. package/src/daemon/config-watcher.ts +6 -2
  232. package/src/daemon/context-overflow-approval.ts +5 -1
  233. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  234. package/src/daemon/conversation-agent-loop.ts +74 -19
  235. package/src/daemon/conversation-attachments.ts +40 -1
  236. package/src/daemon/conversation-messaging.ts +3 -0
  237. package/src/daemon/conversation-process.ts +66 -3
  238. package/src/daemon/conversation-queue-manager.ts +8 -0
  239. package/src/daemon/conversation-runtime-assembly.ts +159 -20
  240. package/src/daemon/conversation-surfaces.ts +78 -12
  241. package/src/daemon/conversation-tool-setup.ts +74 -11
  242. package/src/daemon/conversation-workspace.ts +12 -0
  243. package/src/daemon/conversation.ts +227 -11
  244. package/src/daemon/date-context.ts +10 -10
  245. package/src/daemon/first-greeting.ts +3 -2
  246. package/src/daemon/handlers/conversations.ts +9 -139
  247. package/src/daemon/handlers/shared.ts +65 -0
  248. package/src/daemon/handlers/skills.ts +232 -37
  249. package/src/daemon/host-bash-proxy.ts +48 -13
  250. package/src/daemon/host-browser-proxy.ts +191 -0
  251. package/src/daemon/host-cu-proxy.ts +36 -11
  252. package/src/daemon/host-file-proxy.ts +57 -9
  253. package/src/daemon/lifecycle.ts +86 -12
  254. package/src/daemon/message-protocol.ts +7 -0
  255. package/src/daemon/message-types/conversations.ts +59 -13
  256. package/src/daemon/message-types/host-browser.ts +100 -0
  257. package/src/daemon/message-types/messages.ts +5 -6
  258. package/src/daemon/message-types/notifications.ts +12 -0
  259. package/src/daemon/message-types/settings.ts +12 -0
  260. package/src/daemon/message-types/skills.ts +10 -0
  261. package/src/daemon/message-types/subagents.ts +2 -0
  262. package/src/daemon/server.ts +112 -35
  263. package/src/daemon/tool-side-effects.ts +6 -0
  264. package/src/daemon/transport-hints.ts +14 -0
  265. package/src/inbound/platform-callback-registration.ts +18 -17
  266. package/src/index.ts +1 -1
  267. package/src/mcp/client.ts +59 -24
  268. package/src/memory/app-store.ts +31 -1
  269. package/src/memory/conversation-crud.ts +38 -10
  270. package/src/memory/conversation-directories.ts +39 -0
  271. package/src/memory/conversation-group-migration.ts +65 -5
  272. package/src/memory/conversation-starters-cadence.ts +76 -0
  273. package/src/memory/conversation-title-service.ts +5 -2
  274. package/src/memory/db-init.ts +12 -0
  275. package/src/memory/embedding-backend.test.ts +75 -0
  276. package/src/memory/embedding-backend.ts +131 -5
  277. package/src/memory/embedding-gemini.test.ts +54 -0
  278. package/src/memory/embedding-gemini.ts +20 -9
  279. package/src/memory/embedding-local.ts +177 -18
  280. package/src/memory/graph/capability-seed.ts +3 -5
  281. package/src/memory/graph/consolidation.ts +10 -23
  282. package/src/memory/graph/extraction-job.ts +15 -0
  283. package/src/memory/graph/retriever.ts +40 -22
  284. package/src/memory/graph/store.test.ts +7 -3
  285. package/src/memory/graph/store.ts +47 -12
  286. package/src/memory/group-crud.ts +25 -9
  287. package/src/memory/llm-usage-store.ts +45 -4
  288. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  289. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  290. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  291. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  292. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  293. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  294. package/src/memory/migrations/index.ts +6 -0
  295. package/src/memory/migrations/registry.ts +8 -0
  296. package/src/memory/schema/conversations.ts +1 -0
  297. package/src/memory/schema/oauth.ts +18 -13
  298. package/src/messaging/provider.ts +1 -1
  299. package/src/notifications/broadcaster.ts +6 -0
  300. package/src/notifications/conversation-pairing.ts +12 -4
  301. package/src/notifications/emit-signal.ts +14 -0
  302. package/src/notifications/signal.ts +11 -0
  303. package/src/oauth/AGENTS.md +76 -0
  304. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  305. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  306. package/src/oauth/byo-connection.test.ts +8 -8
  307. package/src/oauth/byo-connection.ts +7 -7
  308. package/src/oauth/connect-orchestrator.ts +23 -21
  309. package/src/oauth/connect-types.ts +3 -3
  310. package/src/oauth/connection-resolver.test.ts +17 -4
  311. package/src/oauth/connection-resolver.ts +16 -16
  312. package/src/oauth/connection.ts +1 -1
  313. package/src/oauth/manual-token-connection.ts +13 -13
  314. package/src/oauth/oauth-store.ts +214 -100
  315. package/src/oauth/platform-connection.test.ts +5 -5
  316. package/src/oauth/platform-connection.ts +4 -4
  317. package/src/oauth/provider-serializer.ts +31 -5
  318. package/src/oauth/revoke.ts +76 -0
  319. package/src/oauth/seed-providers.ts +127 -87
  320. package/src/oauth/token-persistence.ts +1 -1
  321. package/src/permissions/checker.ts +3 -3
  322. package/src/permissions/defaults.ts +7 -8
  323. package/src/permissions/permission-mode.ts +4 -11
  324. package/src/permissions/prompter.ts +13 -3
  325. package/src/permissions/v2-consent-policy.ts +87 -0
  326. package/src/platform/client.ts +1 -1
  327. package/src/prompts/system-prompt.ts +18 -21
  328. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  329. package/src/prompts/templates/BOOTSTRAP.md +59 -96
  330. package/src/prompts/templates/SOUL.md +11 -11
  331. package/src/providers/anthropic/client.ts +1 -0
  332. package/src/providers/types.ts +1 -1
  333. package/src/runtime/AGENTS.md +23 -0
  334. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  335. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  336. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  337. package/src/runtime/assistant-event-hub.ts +24 -2
  338. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  339. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  341. package/src/runtime/auth/middleware.ts +98 -0
  342. package/src/runtime/auth/route-policy.ts +6 -7
  343. package/src/runtime/auth/token-service.ts +8 -0
  344. package/src/runtime/capability-tokens.ts +414 -0
  345. package/src/runtime/channel-approvals.ts +18 -5
  346. package/src/runtime/chrome-extension-registry.ts +332 -0
  347. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  348. package/src/runtime/guardian-decision-types.ts +7 -0
  349. package/src/runtime/http-server.ts +425 -70
  350. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  351. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  352. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  353. package/src/runtime/migrations/migration-transport.ts +6 -0
  354. package/src/runtime/migrations/migration-wizard.ts +22 -2
  355. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  356. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  357. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  358. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  359. package/src/runtime/pending-interactions.ts +29 -13
  360. package/src/runtime/routes/approval-routes.ts +90 -16
  361. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  362. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  363. package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
  364. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  365. package/src/runtime/routes/conversation-routes.ts +308 -28
  366. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  367. package/src/runtime/routes/group-routes.ts +22 -8
  368. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  369. package/src/runtime/routes/host-browser-routes.ts +279 -0
  370. package/src/runtime/routes/host-file-routes.ts +9 -1
  371. package/src/runtime/routes/identity-routes.ts +259 -16
  372. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  373. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  374. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  375. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  376. package/src/runtime/routes/log-export-routes.ts +60 -25
  377. package/src/runtime/routes/memory-item-routes.ts +1 -7
  378. package/src/runtime/routes/migration-routes.ts +87 -2
  379. package/src/runtime/routes/oauth-apps.ts +15 -17
  380. package/src/runtime/routes/oauth-providers.ts +4 -0
  381. package/src/runtime/routes/schedule-routes.ts +24 -11
  382. package/src/runtime/routes/settings-routes.ts +9 -97
  383. package/src/runtime/routes/skills-routes.ts +52 -2
  384. package/src/runtime/routes/subagents-routes.ts +14 -10
  385. package/src/runtime/routes/usage-routes.ts +8 -7
  386. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  387. package/src/runtime/routes/workspace-routes.ts +8 -1
  388. package/src/runtime/routes/workspace-utils.ts +2 -0
  389. package/src/schedule/scheduler.ts +7 -5
  390. package/src/security/ces-credential-client.ts +20 -0
  391. package/src/security/ces-rpc-credential-backend.ts +17 -0
  392. package/src/security/credential-backend.ts +5 -0
  393. package/src/security/oauth2.ts +42 -25
  394. package/src/security/secure-keys.ts +118 -25
  395. package/src/security/token-manager.ts +23 -10
  396. package/src/skills/catalog-files.ts +492 -0
  397. package/src/skills/inline-command-runner.ts +12 -14
  398. package/src/subagent/manager.ts +131 -26
  399. package/src/subagent/types.ts +19 -0
  400. package/src/tools/apps/executors.ts +11 -2
  401. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  402. package/src/tools/browser/auth-detector.ts +43 -12
  403. package/src/tools/browser/browser-execution.ts +645 -340
  404. package/src/tools/browser/browser-manager.ts +36 -12
  405. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  406. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  407. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  408. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  409. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  410. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  411. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  412. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  413. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  414. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  415. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  416. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  417. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  418. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  419. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  420. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  421. package/src/tools/browser/cdp-client/errors.ts +34 -0
  422. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  423. package/src/tools/browser/cdp-client/factory.ts +204 -0
  424. package/src/tools/browser/cdp-client/index.ts +14 -0
  425. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  426. package/src/tools/browser/cdp-client/types.ts +52 -0
  427. package/src/tools/filesystem/edit.ts +1 -1
  428. package/src/tools/filesystem/list.ts +1 -1
  429. package/src/tools/filesystem/read.ts +1 -1
  430. package/src/tools/filesystem/write.ts +2 -1
  431. package/src/tools/host-filesystem/edit.ts +1 -1
  432. package/src/tools/host-filesystem/read.ts +12 -15
  433. package/src/tools/host-filesystem/write.ts +1 -1
  434. package/src/tools/host-terminal/host-shell.ts +21 -16
  435. package/src/tools/permission-checker.ts +77 -100
  436. package/src/tools/registry.ts +0 -2
  437. package/src/tools/secret-detection-handler.ts +34 -1
  438. package/src/tools/shared/filesystem/image-read.ts +61 -40
  439. package/src/tools/skills/sandbox-runner.ts +3 -6
  440. package/src/tools/subagent/spawn.ts +47 -3
  441. package/src/tools/subagent/status.ts +2 -0
  442. package/src/tools/system/register.ts +2 -16
  443. package/src/tools/terminal/safe-env.ts +7 -0
  444. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  445. package/src/tools/terminal/sandbox.ts +4 -1
  446. package/src/tools/terminal/shell.ts +24 -21
  447. package/src/tools/tool-approval-handler.ts +48 -2
  448. package/src/tools/types.ts +2 -3
  449. package/src/util/platform.ts +14 -19
  450. package/src/watcher/provider-types.ts +1 -1
  451. package/src/workspace/migrations/029-seed-pkb.ts +1 -0
  452. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  453. package/src/workspace/migrations/registry.ts +2 -0
  454. package/src/workspace/top-level-renderer.ts +19 -1
  455. package/src/__tests__/chrome-cdp.test.ts +0 -419
  456. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  457. package/src/__tests__/permission-mode-store.test.ts +0 -277
  458. package/src/browser-extension-relay/protocol.ts +0 -63
  459. package/src/browser-extension-relay/server.ts +0 -203
  460. package/src/config/schemas/sandbox.ts +0 -14
  461. package/src/permissions/permission-mode-store.ts +0 -180
  462. package/src/tools/browser/chrome-cdp.ts +0 -239
  463. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -21,6 +21,11 @@ import { invalidateConfigCache } from "../../config/loader.js";
21
21
  import { getDb, resetDb } from "../../memory/db-connection.js";
22
22
  import { validateMigrationState } from "../../memory/migrations/validate-migration-state.js";
23
23
  import { clearCache as clearTrustCache } from "../../permissions/trust-store.js";
24
+ import {
25
+ bulkSetSecureKeysAsync,
26
+ getSecureKeyResultAsync,
27
+ listSecureKeysAsync,
28
+ } from "../../security/secure-keys.js";
24
29
  import { getLogger } from "../../util/logger.js";
25
30
  import {
26
31
  getDbPath,
@@ -34,7 +39,10 @@ import {
34
39
  analyzeImport,
35
40
  DefaultPathResolver,
36
41
  } from "../migrations/vbundle-import-analyzer.js";
37
- import { commitImport } from "../migrations/vbundle-importer.js";
42
+ import {
43
+ commitImport,
44
+ extractCredentialsFromBundle,
45
+ } from "../migrations/vbundle-importer.js";
38
46
  import { validateVBundle } from "../migrations/vbundle-validator.js";
39
47
 
40
48
  const log = getLogger("migration-routes");
@@ -146,6 +154,27 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
146
154
  let cleanup: (() => Promise<void>) | undefined;
147
155
 
148
156
  try {
157
+ // Read all stored credentials to include in the export bundle
158
+ const credentialList = await listSecureKeysAsync();
159
+ const credentials: Array<{ account: string; value: string }> = [];
160
+ if (credentialList.unreachable) {
161
+ log.warn(
162
+ "Credential store is unreachable — export will not include credentials",
163
+ );
164
+ } else {
165
+ for (const account of credentialList.accounts) {
166
+ const result = await getSecureKeyResultAsync(account);
167
+ if (result.unreachable) {
168
+ log.warn(
169
+ { account },
170
+ "Credential store unreachable when reading credential — skipping",
171
+ );
172
+ } else if (result.value != null) {
173
+ credentials.push({ account, value: result.value });
174
+ }
175
+ }
176
+ }
177
+
149
178
  const result = await streamExportVBundle({
150
179
  // hooksDir is intentionally omitted — hooks now live under workspace/hooks/
151
180
  // and are included in the workspace walk. Passing hooksDir separately would
@@ -153,6 +182,7 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
153
182
  workspaceDir: getWorkspaceDir(),
154
183
  source: "runtime-export",
155
184
  description,
185
+ credentials,
156
186
  checkpoint: () => {
157
187
  const dbPath = getDbPath();
158
188
  try {
@@ -196,6 +226,7 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
196
226
  "Content-Length": String(size),
197
227
  "X-Vbundle-Schema-Version": manifest.schema_version,
198
228
  "X-Vbundle-Manifest-Sha256": manifest.manifest_sha256,
229
+ "X-Vbundle-Credentials-Included": String(credentials.length),
199
230
  },
200
231
  });
201
232
  } catch (err) {
@@ -444,6 +475,57 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
444
475
  );
445
476
  }
446
477
 
478
+ // Import credentials from the bundle into CES (non-blocking — failures
479
+ // are logged as warnings but do not fail the overall import).
480
+ let credentialsImported:
481
+ | {
482
+ total: number;
483
+ succeeded: number;
484
+ failed: number;
485
+ failedAccounts: string[];
486
+ }
487
+ | undefined;
488
+
489
+ if (validation.entries) {
490
+ const bundleCredentials = extractCredentialsFromBundle(
491
+ validation.entries,
492
+ validation.manifest!,
493
+ );
494
+ if (bundleCredentials.length > 0) {
495
+ try {
496
+ const credResults = await bulkSetSecureKeysAsync(bundleCredentials);
497
+ const failedResults = credResults.filter((r) => !r.ok);
498
+ if (failedResults.length > 0) {
499
+ log.warn(
500
+ { failed: failedResults.map((f) => f.account) },
501
+ "Some credentials failed to import",
502
+ );
503
+ }
504
+ log.info(
505
+ { total: bundleCredentials.length, failed: failedResults.length },
506
+ "Credential import complete",
507
+ );
508
+ const succeeded = bundleCredentials.length - failedResults.length;
509
+ credentialsImported = {
510
+ total: bundleCredentials.length,
511
+ succeeded,
512
+ failed: failedResults.length,
513
+ failedAccounts: failedResults.map((f) => f.account),
514
+ };
515
+ if (failedResults.length > 0) {
516
+ result.report.warnings.push(
517
+ `Imported ${succeeded} credential(s), ${failedResults.length} failed`,
518
+ );
519
+ }
520
+ } catch (err) {
521
+ log.warn({ err }, "Credential import failed entirely");
522
+ result.report.warnings.push(
523
+ `Credential import failed: ${err instanceof Error ? err.message : String(err)}`,
524
+ );
525
+ }
526
+ }
527
+ }
528
+
447
529
  // Invalidate in-process caches so imported settings.json and trust.json take effect
448
530
  invalidateConfigCache();
449
531
  clearTrustCache();
@@ -463,7 +545,10 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
463
545
  // Don't fail the import if validation itself errors
464
546
  }
465
547
 
466
- return Response.json(result.report);
548
+ return Response.json({
549
+ ...result.report,
550
+ ...(credentialsImported ? { credentialsImported } : {}),
551
+ });
467
552
  } catch (err) {
468
553
  log.error({ err }, "Unexpected error during import commit");
469
554
  return httpError(
@@ -60,8 +60,8 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
60
60
  endpoint: "oauth/apps",
61
61
  method: "GET",
62
62
  handler: ({ url }) => {
63
- const providerKey = url.searchParams.get("provider_key");
64
- if (!providerKey) {
63
+ const provider = url.searchParams.get("provider_key");
64
+ if (!provider) {
65
65
  return httpError(
66
66
  "BAD_REQUEST",
67
67
  "provider_key query parameter is required",
@@ -70,20 +70,18 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
70
70
  }
71
71
 
72
72
  const allApps = listApps();
73
- const filtered = allApps.filter(
74
- (row) => row.providerKey === providerKey,
75
- );
73
+ const filtered = allApps.filter((row) => row.provider === provider);
76
74
 
77
- const providerRow = getProvider(providerKey);
78
- const provider = providerRow
75
+ const providerRow = getProvider(provider);
76
+ const providerSummary = providerRow
79
77
  ? serializeProviderSummary(providerRow)
80
78
  : null;
81
79
 
82
80
  return Response.json({
83
- provider,
81
+ provider: providerSummary,
84
82
  apps: filtered.map((row) => ({
85
83
  id: row.id,
86
- provider_key: row.providerKey,
84
+ provider_key: row.provider,
87
85
  client_id: row.clientId,
88
86
  created_at: row.createdAt,
89
87
  updated_at: row.updatedAt,
@@ -138,7 +136,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
138
136
  {
139
137
  app: {
140
138
  id: app.id,
141
- provider_key: app.providerKey,
139
+ provider_key: app.provider,
142
140
  client_id: app.clientId,
143
141
  created_at: app.createdAt,
144
142
  updated_at: app.updatedAt,
@@ -165,9 +163,9 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
165
163
  }
166
164
 
167
165
  // Disconnect all connections for this app first to clean up tokens.
168
- const connections = listConnections(app.providerKey, app.clientId);
166
+ const connections = listConnections(app.provider, app.clientId);
169
167
  for (const conn of connections) {
170
- await disconnectOAuthProvider(app.providerKey, app.clientId, conn.id);
168
+ await disconnectOAuthProvider(app.provider, app.clientId, conn.id);
171
169
  }
172
170
 
173
171
  await deleteApp(params.id);
@@ -190,12 +188,12 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
190
188
  );
191
189
  }
192
190
 
193
- const connections = listConnections(app.providerKey, app.clientId);
191
+ const connections = listConnections(app.provider, app.clientId);
194
192
 
195
193
  return Response.json({
196
194
  connections: connections.map((row) => ({
197
195
  id: row.id,
198
- provider_key: row.providerKey,
196
+ provider_key: row.provider,
199
197
  account_info: row.accountInfo,
200
198
  granted_scopes: parseGrantedScopes(row.grantedScopes),
201
199
  status: row.status,
@@ -223,7 +221,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
223
221
  }
224
222
 
225
223
  const result = await disconnectOAuthProvider(
226
- conn.providerKey,
224
+ conn.provider,
227
225
  undefined,
228
226
  conn.id,
229
227
  );
@@ -282,7 +280,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
282
280
  const clientSecret = await getAppClientSecret(app);
283
281
 
284
282
  const result = await orchestrateOAuthConnect({
285
- service: app.providerKey,
283
+ service: app.provider,
286
284
  clientId: app.clientId,
287
285
  clientSecret,
288
286
  requestedScopes: body.scopes,
@@ -292,7 +290,7 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
292
290
 
293
291
  if (result.success && result.deferred) {
294
292
  return Response.json({
295
- auth_url: result.authUrl,
293
+ auth_url: result.authorizeUrl,
296
294
  state: result.state,
297
295
  });
298
296
  }
@@ -44,6 +44,10 @@ export function oauthProvidersRouteDefinitions(): RouteDefinition[] {
44
44
  },
45
45
 
46
46
  // GET /v1/oauth/providers/:providerKey — Get a single provider.
47
+ // The path parameter name `providerKey` is preserved for backward
48
+ // compatibility with the published OpenAPI spec (operationId
49
+ // `oauth_providers_by_providerKey_get`). The actual URL the client hits
50
+ // (`/v1/oauth/providers/google`) is unchanged either way.
47
51
  {
48
52
  endpoint: "oauth/providers/:providerKey",
49
53
  method: "GET",
@@ -25,6 +25,10 @@ import type { RouteDefinition } from "../http-router.js";
25
25
  import type { SendMessageDeps } from "../http-types.js";
26
26
 
27
27
  const log = getLogger("schedule-routes");
28
+ const SCHEDULE_GUARDIAN_TRUST_CONTEXT = {
29
+ sourceChannel: "vellum",
30
+ trustClass: "guardian",
31
+ } as const;
28
32
 
29
33
  // ---------------------------------------------------------------------------
30
34
  // Handlers
@@ -202,17 +206,23 @@ async function handleRunScheduleNow(
202
206
  );
203
207
  }
204
208
  const conversation =
205
- await sendMessageDeps.getOrCreateConversation(conversationId);
209
+ await sendMessageDeps.getOrCreateConversation(conversationId, {
210
+ trustContext: SCHEDULE_GUARDIAN_TRUST_CONTEXT,
211
+ });
206
212
  conversation.taskRunId = taskRunId;
207
- await conversation.processMessage(
208
- message,
209
- [],
210
- () => {}, // no event callback for HTTP mode
211
- undefined,
212
- undefined,
213
- undefined,
214
- { isInteractive: false },
215
- );
213
+ try {
214
+ await conversation.processMessage(
215
+ message,
216
+ [],
217
+ () => {}, // no event callback for HTTP mode
218
+ undefined,
219
+ undefined,
220
+ undefined,
221
+ { isInteractive: false },
222
+ );
223
+ } finally {
224
+ conversation.taskRunId = undefined;
225
+ }
216
226
  },
217
227
  );
218
228
 
@@ -276,7 +286,10 @@ async function handleRunScheduleNow(
276
286
  throw new Error("sendMessageDeps not available for schedule execution");
277
287
  }
278
288
  const activeConversation =
279
- await sendMessageDeps.getOrCreateConversation(conversationId);
289
+ await sendMessageDeps.getOrCreateConversation(conversationId, {
290
+ trustContext: SCHEDULE_GUARDIAN_TRUST_CONTEXT,
291
+ });
292
+ activeConversation.taskRunId = undefined;
280
293
  await activeConversation.processMessage(
281
294
  schedule.message,
282
295
  [],
@@ -12,16 +12,11 @@ import { join } from "node:path";
12
12
 
13
13
  import { z } from "zod";
14
14
 
15
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
16
15
  import {
17
16
  getPlatformBaseUrl,
18
17
  setIngressPublicBaseUrl,
19
18
  } from "../../config/env.js";
20
- import {
21
- getConfig,
22
- loadRawConfig,
23
- saveRawConfig,
24
- } from "../../config/loader.js";
19
+ import { loadRawConfig, saveRawConfig } from "../../config/loader.js";
25
20
  import { loadSkillCatalog } from "../../config/skills.js";
26
21
  import {
27
22
  computeGatewayTarget,
@@ -41,12 +36,6 @@ import {
41
36
  generateAllowlistOptions,
42
37
  generateScopeOptions,
43
38
  } from "../../permissions/checker.js";
44
- import {
45
- getMode,
46
- onModeChanged,
47
- setAskBeforeActing,
48
- setHostAccess,
49
- } from "../../permissions/permission-mode-store.js";
50
39
  import { getSecureKeyAsync } from "../../security/secure-keys.js";
51
40
  import { parseToolManifestFile } from "../../skills/tool-manifest.js";
52
41
  import {
@@ -72,24 +61,6 @@ import { resolveWorkspacePath } from "./workspace-utils.js";
72
61
 
73
62
  const log = getLogger("settings-routes");
74
63
 
75
- // ---------------------------------------------------------------------------
76
- // Permission mode SSE broadcast
77
- // ---------------------------------------------------------------------------
78
-
79
- onModeChanged((mode) => {
80
- assistantEventHub
81
- .publish(
82
- buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
83
- type: "permission_mode_update",
84
- askBeforeActing: mode.askBeforeActing,
85
- hostAccess: mode.hostAccess,
86
- }),
87
- )
88
- .catch((err) => {
89
- log.warn({ err }, "Failed to publish permission_mode_update event");
90
- });
91
- });
92
-
93
64
  // ---------------------------------------------------------------------------
94
65
  // Voice config
95
66
  // ---------------------------------------------------------------------------
@@ -240,7 +211,7 @@ async function handleOAuthConnectStart(body: {
240
211
  try {
241
212
  // For HTTP, we cannot send `open_url` mid-request. The auth URL is
242
213
  // returned to the client to open.
243
- let authUrl: string | undefined;
214
+ let authorizeUrl: string | undefined;
244
215
 
245
216
  const result = await orchestrateOAuthConnect({
246
217
  service,
@@ -250,7 +221,7 @@ async function handleOAuthConnectStart(body: {
250
221
  callbackTransport: "loopback",
251
222
  isInteractive: true,
252
223
  openUrl: (url: string) => {
253
- authUrl = url;
224
+ authorizeUrl = url;
254
225
  },
255
226
  onDeferredComplete: (deferredResult) => {
256
227
  // Prefer accountInfo from oauth-store when available.
@@ -309,7 +280,9 @@ async function handleOAuthConnectStart(body: {
309
280
  return Response.json({
310
281
  ok: true,
311
282
  deferred: true,
312
- authUrl: result.authUrl,
283
+ // Wire key stays `authUrl` for backward compatibility with existing
284
+ // clients; the internal field on `result` is `authorizeUrl`.
285
+ authUrl: result.authorizeUrl,
313
286
  });
314
287
  }
315
288
 
@@ -326,7 +299,9 @@ async function handleOAuthConnectStart(body: {
326
299
  ok: true,
327
300
  grantedScopes: result.grantedScopes,
328
301
  accountInfo: responseAccountInfo,
329
- ...(authUrl ? { authUrl } : {}),
302
+ // Wire key stays `authUrl` for backward compatibility with existing
303
+ // clients; the local variable was renamed to `authorizeUrl`.
304
+ ...(authorizeUrl ? { authUrl: authorizeUrl } : {}),
330
305
  });
331
306
  } catch (err) {
332
307
  const message = err instanceof Error ? err.message : String(err);
@@ -921,68 +896,5 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
921
896
  }
922
897
  },
923
898
  },
924
-
925
- // Permission mode (GET always available; PUT gated on feature flag)
926
- {
927
- endpoint: "permission-mode",
928
- method: "GET",
929
- policyKey: "permission-mode:GET",
930
- summary: "Get permission mode",
931
- description:
932
- "Return the current two-axis permission mode (askBeforeActing, hostAccess).",
933
- tags: ["settings"],
934
- responseBody: z.object({
935
- askBeforeActing: z.boolean(),
936
- hostAccess: z.boolean(),
937
- }),
938
- handler: () => {
939
- const mode = getMode();
940
- return Response.json({
941
- askBeforeActing: mode.askBeforeActing,
942
- hostAccess: mode.hostAccess,
943
- });
944
- },
945
- },
946
- {
947
- endpoint: "permission-mode",
948
- method: "PUT",
949
- policyKey: "permission-mode",
950
- summary: "Update permission mode",
951
- description:
952
- "Update the two-axis permission mode. Requires the permission-controls-v2 feature flag.",
953
- tags: ["settings"],
954
- requestBody: z.object({
955
- askBeforeActing: z.boolean().optional(),
956
- hostAccess: z.boolean().optional(),
957
- }),
958
- responseBody: z.object({
959
- askBeforeActing: z.boolean(),
960
- hostAccess: z.boolean(),
961
- }),
962
- handler: async ({ req }) => {
963
- const config = getConfig();
964
- if (!isAssistantFeatureFlagEnabled("permission-controls-v2", config)) {
965
- return httpError("NOT_FOUND", "Not found", 404);
966
- }
967
-
968
- const body = (await req.json()) as {
969
- askBeforeActing?: boolean;
970
- hostAccess?: boolean;
971
- };
972
-
973
- if (typeof body.askBeforeActing === "boolean") {
974
- setAskBeforeActing(body.askBeforeActing);
975
- }
976
- if (typeof body.hostAccess === "boolean") {
977
- setHostAccess(body.hostAccess);
978
- }
979
-
980
- const mode = getMode();
981
- return Response.json({
982
- askBeforeActing: mode.askBeforeActing,
983
- hostAccess: mode.hostAccess,
984
- });
985
- },
986
- },
987
899
  ];
988
900
  }
@@ -19,6 +19,7 @@ import {
19
19
  draftSkill,
20
20
  enableSkill,
21
21
  getSkill,
22
+ getSkillFileContent,
22
23
  getSkillFiles,
23
24
  inspectSkill,
24
25
  installSkill,
@@ -151,6 +152,55 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
151
152
  },
152
153
  },
153
154
 
155
+ // The router uses strict anchored-regex matching, so this route is never
156
+ // ambiguous with skills/:id/files.
157
+ {
158
+ endpoint: "skills/:id/files/content",
159
+ method: "GET",
160
+ policyKey: "skills",
161
+ summary: "Get skill file content",
162
+ description:
163
+ "Return the content of a single file belonging to an installed or catalog skill.",
164
+ tags: ["skills"],
165
+ queryParams: [
166
+ {
167
+ name: "path",
168
+ schema: { type: "string" },
169
+ required: true,
170
+ description: "Relative path of the file within the skill directory",
171
+ },
172
+ ],
173
+ responseBody: z.object({
174
+ path: z.string(),
175
+ name: z.string(),
176
+ size: z.number().int(),
177
+ mimeType: z.string(),
178
+ isBinary: z.boolean(),
179
+ content: z.string().nullable(),
180
+ }),
181
+ handler: async ({ params, url }) => {
182
+ const path = url.searchParams.get("path");
183
+ if (!path) {
184
+ return httpError(
185
+ "BAD_REQUEST",
186
+ "path query parameter is required",
187
+ 400,
188
+ );
189
+ }
190
+ const result = await getSkillFileContent(params.id, path, ctx());
191
+ if ("error" in result) {
192
+ if (result.status === 400) {
193
+ return httpError("BAD_REQUEST", result.error, 400);
194
+ }
195
+ if (result.status === 404) {
196
+ return httpError("NOT_FOUND", result.error, 404);
197
+ }
198
+ return httpError("INTERNAL_ERROR", result.error, 500);
199
+ }
200
+ return Response.json(result);
201
+ },
202
+ },
203
+
154
204
  {
155
205
  endpoint: "skills/:id/files",
156
206
  method: "GET",
@@ -158,8 +208,8 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
158
208
  summary: "Get skill files",
159
209
  description: "Return skill metadata and directory contents.",
160
210
  tags: ["skills"],
161
- handler: ({ params }) => {
162
- const result = getSkillFiles(params.id, ctx());
211
+ handler: async ({ params }) => {
212
+ const result = await getSkillFiles(params.id, ctx());
163
213
  if ("error" in result) {
164
214
  if (result.status === 404) {
165
215
  return httpError("NOT_FOUND", result.error, 404);
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { z } from "zod";
9
9
 
10
- import { getMessages } from "../../memory/conversation-crud.js";
10
+ import { getMessages, type MessageRow } from "../../memory/conversation-crud.js";
11
11
  import { getSubagentManager } from "../../subagent/index.js";
12
12
  import { getLogger } from "../../util/logger.js";
13
13
  import { httpError } from "../http-errors.js";
@@ -31,16 +31,25 @@ export interface SubagentDetailResult {
31
31
  content: string;
32
32
  toolName?: string;
33
33
  isError?: boolean;
34
+ messageId?: string;
34
35
  }>;
35
36
  }
36
37
 
38
+ const FORK_DIRECTIVE_RE =
39
+ /^⎯⎯⎯ FORK TASK ⎯⎯⎯\n[\s\S]*?Complete this task directly and return only your findings:\n\n([\s\S]*?)\n⎯⎯⎯+$/;
40
+
41
+ function stripForkDirectiveFraming(text: string): string {
42
+ const match = FORK_DIRECTIVE_RE.exec(text);
43
+ return match ? match[1] : text;
44
+ }
45
+
37
46
  /**
38
47
  * Parse raw message rows into subagent detail events. Extracted as a pure
39
48
  * function so it can be unit-tested without a database.
40
49
  */
41
50
  export function parseSubagentMessages(
42
51
  subagentId: string,
43
- messages: Array<{ role: string; content: string }>,
52
+ messages: MessageRow[],
44
53
  ): SubagentDetailResult {
45
54
  // Extract objective from the first user message
46
55
  let objective: string | undefined;
@@ -53,7 +62,7 @@ export function parseSubagentMessages(
53
62
  (b: Record<string, unknown>) => isRecord(b) && b.type === "text",
54
63
  );
55
64
  if (textBlock && typeof textBlock.text === "string") {
56
- objective = textBlock.text;
65
+ objective = stripForkDirectiveFraming(textBlock.text);
57
66
  }
58
67
  }
59
68
  } catch {
@@ -62,12 +71,7 @@ export function parseSubagentMessages(
62
71
  }
63
72
 
64
73
  // Extract events from both assistant and user messages.
65
- const events: Array<{
66
- type: string;
67
- content: string;
68
- toolName?: string;
69
- isError?: boolean;
70
- }> = [];
74
+ const events: SubagentDetailResult["events"] = [];
71
75
  const pendingTools = new Map<string, string>();
72
76
  for (const m of messages) {
73
77
  if (m.role !== "assistant" && m.role !== "user") continue;
@@ -86,7 +90,7 @@ export function parseSubagentMessages(
86
90
  block.type === "text" &&
87
91
  typeof block.text === "string"
88
92
  ) {
89
- events.push({ type: "text", content: block.text });
93
+ events.push({ type: "text", content: block.text, messageId: m.id });
90
94
  } else if (block.type === "tool_use") {
91
95
  const name = typeof block.name === "string" ? block.name : "unknown";
92
96
  const input = isRecord(block.input)
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * GET /v1/usage/totals?from=&to= — aggregate totals for a time range
5
5
  * GET /v1/usage/daily?from=&to= — per-day buckets for a time range
6
- * GET /v1/usage/breakdown?from=&to=&groupBy= — grouped breakdown (actor, provider, model)
6
+ * GET /v1/usage/breakdown?from=&to=&groupBy= — grouped breakdown (actor, provider, model, or conversation)
7
7
  */
8
8
 
9
9
  import { z } from "zod";
@@ -17,7 +17,7 @@ import {
17
17
  import { httpError } from "../http-errors.js";
18
18
  import type { RouteDefinition } from "../http-router.js";
19
19
 
20
- const VALID_GROUP_BY = new Set(["actor", "provider", "model"]);
20
+ const VALID_GROUP_BY = new Set(["actor", "provider", "model", "conversation"]);
21
21
 
22
22
  /**
23
23
  * Parse and validate the `from` and `to` epoch-millis query parameters.
@@ -137,7 +137,7 @@ export function usageRouteDefinitions(): RouteDefinition[] {
137
137
  method: "GET",
138
138
  summary: "Get usage breakdown",
139
139
  description:
140
- "Return grouped usage breakdown (by actor, provider, or model).",
140
+ "Return grouped usage breakdown (by actor, provider, model, or conversation).",
141
141
  tags: ["usage"],
142
142
  queryParams: [
143
143
  {
@@ -153,7 +153,8 @@ export function usageRouteDefinitions(): RouteDefinition[] {
153
153
  {
154
154
  name: "groupBy",
155
155
  schema: { type: "string" },
156
- description: "Group by: actor, provider, or model (required)",
156
+ description:
157
+ "Group by: actor, provider, model, or conversation (required)",
157
158
  },
158
159
  ],
159
160
  responseBody: z.object({
@@ -167,21 +168,21 @@ export function usageRouteDefinitions(): RouteDefinition[] {
167
168
  if (!groupBy) {
168
169
  return httpError(
169
170
  "BAD_REQUEST",
170
- 'Missing required query parameter: "groupBy" (one of: actor, provider, model)',
171
+ 'Missing required query parameter: "groupBy" (one of: actor, provider, model, conversation)',
171
172
  400,
172
173
  );
173
174
  }
174
175
  if (!VALID_GROUP_BY.has(groupBy)) {
175
176
  return httpError(
176
177
  "BAD_REQUEST",
177
- `Invalid "groupBy" value: "${groupBy}". Must be one of: actor, provider, model`,
178
+ `Invalid "groupBy" value: "${groupBy}". Must be one of: actor, provider, model, conversation`,
178
179
  400,
179
180
  );
180
181
  }
181
182
 
182
183
  const breakdown = getUsageGroupBreakdown(
183
184
  range,
184
- groupBy as "actor" | "provider" | "model",
185
+ groupBy as "actor" | "provider" | "model" | "conversation",
185
186
  );
186
187
  return Response.json({ breakdown });
187
188
  },