@vellumai/assistant 0.5.9 → 0.5.11

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 (278) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +48 -48
  3. package/Dockerfile +2 -0
  4. package/README.md +1 -1
  5. package/docs/architecture/integrations.md +6 -13
  6. package/docs/architecture/memory.md +7 -12
  7. package/docs/architecture/security.md +5 -5
  8. package/docs/credential-execution-service.md +9 -9
  9. package/docs/skills.md +1 -1
  10. package/node_modules/@vellumai/credential-storage/src/index.ts +2 -2
  11. package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
  12. package/openapi.yaml +7130 -0
  13. package/package.json +2 -1
  14. package/scripts/generate-openapi.ts +562 -0
  15. package/src/__tests__/acp-session.test.ts +239 -44
  16. package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
  17. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
  19. package/src/__tests__/browser-skill-endstate.test.ts +1 -1
  20. package/src/__tests__/btw-routes.test.ts +8 -0
  21. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
  22. package/src/__tests__/channel-approvals.test.ts +7 -7
  23. package/src/__tests__/channel-readiness-service.test.ts +41 -0
  24. package/src/__tests__/config-schema.test.ts +10 -2
  25. package/src/__tests__/context-memory-e2e.test.ts +2 -6
  26. package/src/__tests__/conversation-skill-tools.test.ts +1 -3
  27. package/src/__tests__/conversation-title-service.test.ts +2 -15
  28. package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
  29. package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
  30. package/src/__tests__/credential-security-e2e.test.ts +4 -4
  31. package/src/__tests__/credential-security-invariants.test.ts +3 -3
  32. package/src/__tests__/credentials-cli.test.ts +3 -3
  33. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
  34. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  35. package/src/__tests__/heartbeat-service.test.ts +35 -0
  36. package/src/__tests__/host-shell-tool.test.ts +1 -1
  37. package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
  38. package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
  39. package/src/__tests__/log-export-workspace.test.ts +1 -1
  40. package/src/__tests__/mcp-client-auth.test.ts +1 -1
  41. package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
  42. package/src/__tests__/memory-recall-log-store.test.ts +182 -0
  43. package/src/__tests__/memory-recall-quality.test.ts +6 -8
  44. package/src/__tests__/memory-regressions.test.ts +53 -42
  45. package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
  46. package/src/__tests__/messaging-skill-split.test.ts +2 -17
  47. package/src/__tests__/oauth-cli.test.ts +98 -551
  48. package/src/__tests__/platform-callback-registration.test.ts +119 -0
  49. package/src/__tests__/secret-ingress-channel.test.ts +261 -0
  50. package/src/__tests__/secret-ingress-cli.test.ts +201 -0
  51. package/src/__tests__/secret-ingress-http.test.ts +312 -0
  52. package/src/__tests__/secret-ingress.test.ts +283 -0
  53. package/src/__tests__/secret-onetime-send.test.ts +4 -4
  54. package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
  55. package/src/__tests__/skill-feature-flags.test.ts +11 -19
  56. package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
  57. package/src/__tests__/skill-load-inline-command.test.ts +3 -3
  58. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  59. package/src/__tests__/skill-memory.test.ts +2 -4
  60. package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
  61. package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
  62. package/src/__tests__/skills.test.ts +16 -2
  63. package/src/__tests__/slack-channel-config.test.ts +1 -1
  64. package/src/__tests__/slack-skill.test.ts +5 -69
  65. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
  66. package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +5 -238
  67. package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +5 -206
  68. package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
  69. package/src/__tests__/workspace-migrations-runner.test.ts +15 -7
  70. package/src/acp/client-handler.ts +113 -31
  71. package/src/acp/session-manager.ts +29 -27
  72. package/src/approvals/guardian-request-resolvers.ts +1 -1
  73. package/src/cli/AGENTS.md +73 -0
  74. package/src/cli/commands/autonomy.ts +3 -5
  75. package/src/cli/commands/credential-execution.ts +1 -2
  76. package/src/cli/commands/credentials.ts +4 -4
  77. package/src/cli/commands/memory.ts +2 -3
  78. package/src/cli/commands/oauth/__tests__/connect.test.ts +785 -0
  79. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +760 -0
  80. package/src/cli/commands/oauth/__tests__/mode.test.ts +672 -0
  81. package/src/cli/commands/oauth/__tests__/ping.test.ts +690 -0
  82. package/src/cli/commands/oauth/__tests__/status.test.ts +579 -0
  83. package/src/cli/commands/oauth/__tests__/token.test.ts +467 -0
  84. package/src/cli/commands/oauth/apps.ts +29 -11
  85. package/src/cli/commands/oauth/connect.ts +373 -0
  86. package/src/cli/commands/oauth/connections.ts +14 -493
  87. package/src/cli/commands/oauth/disconnect.ts +333 -0
  88. package/src/cli/commands/oauth/index.ts +62 -10
  89. package/src/cli/commands/oauth/mode.ts +263 -0
  90. package/src/cli/commands/oauth/ping.ts +222 -0
  91. package/src/cli/commands/oauth/providers.ts +30 -3
  92. package/src/cli/commands/oauth/request.ts +576 -0
  93. package/src/cli/commands/oauth/shared.ts +132 -0
  94. package/src/cli/commands/oauth/status.ts +202 -0
  95. package/src/cli/commands/oauth/token.ts +159 -0
  96. package/src/cli/commands/platform.ts +20 -14
  97. package/src/cli.ts +82 -17
  98. package/src/config/assistant-feature-flags.ts +74 -11
  99. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
  100. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
  101. package/src/config/bundled-skills/messaging/SKILL.md +13 -36
  102. package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
  103. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  104. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  105. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  106. package/src/config/bundled-skills/settings/SKILL.md +5 -3
  107. package/src/config/bundled-skills/settings/TOOLS.json +17 -0
  108. package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
  109. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
  110. package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
  111. package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
  112. package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
  113. package/src/config/bundled-skills/slack/SKILL.md +58 -44
  114. package/src/config/bundled-tool-registry.ts +2 -19
  115. package/src/config/env.ts +5 -1
  116. package/src/config/feature-flag-registry.json +57 -41
  117. package/src/config/loader.ts +4 -0
  118. package/src/config/schemas/platform.ts +0 -8
  119. package/src/config/schemas/security.ts +9 -1
  120. package/src/config/schemas/services.ts +1 -1
  121. package/src/config/skill-state.ts +1 -3
  122. package/src/config/skills.ts +2 -4
  123. package/src/credential-execution/feature-gates.ts +9 -16
  124. package/src/credential-execution/process-manager.ts +12 -0
  125. package/src/daemon/config-watcher.ts +4 -0
  126. package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
  127. package/src/daemon/conversation-agent-loop.ts +49 -2
  128. package/src/daemon/conversation-memory.ts +0 -1
  129. package/src/daemon/handlers/config-slack-channel.ts +43 -1
  130. package/src/daemon/handlers/conversations.ts +41 -33
  131. package/src/daemon/lifecycle.ts +28 -5
  132. package/src/daemon/message-types/acp.ts +0 -15
  133. package/src/daemon/message-types/memory.ts +0 -1
  134. package/src/daemon/message-types/messages.ts +9 -1
  135. package/src/daemon/message-types/schedules.ts +9 -0
  136. package/src/daemon/server.ts +19 -7
  137. package/src/email/feature-gate.ts +3 -3
  138. package/src/heartbeat/heartbeat-service.ts +48 -0
  139. package/src/inbound/platform-callback-registration.ts +61 -7
  140. package/src/mcp/mcp-oauth-provider.ts +3 -3
  141. package/src/memory/app-store.ts +3 -3
  142. package/src/memory/conversation-crud.ts +124 -0
  143. package/src/memory/conversation-title-service.ts +7 -17
  144. package/src/memory/db-init.ts +8 -0
  145. package/src/memory/embedding-local.ts +47 -2
  146. package/src/memory/indexer.ts +13 -10
  147. package/src/memory/items-extractor.ts +12 -4
  148. package/src/memory/job-utils.ts +5 -0
  149. package/src/memory/jobs-store.ts +10 -2
  150. package/src/memory/journal-memory.ts +6 -2
  151. package/src/memory/llm-request-log-store.ts +88 -21
  152. package/src/memory/memory-recall-log-store.ts +128 -0
  153. package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
  154. package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
  155. package/src/memory/migrations/index.ts +2 -0
  156. package/src/memory/migrations/validate-migration-state.ts +14 -1
  157. package/src/memory/retriever.test.ts +4 -5
  158. package/src/memory/schema/infrastructure.ts +31 -0
  159. package/src/memory/schema/oauth.ts +3 -0
  160. package/src/messaging/providers/telegram-bot/adapter.ts +1 -1
  161. package/src/oauth/connect-orchestrator.ts +54 -0
  162. package/src/oauth/manual-token-connection.ts +5 -5
  163. package/src/oauth/oauth-store.ts +26 -5
  164. package/src/oauth/seed-providers.ts +10 -1
  165. package/src/permissions/checker.ts +2 -2
  166. package/src/permissions/trust-client.ts +2 -2
  167. package/src/platform/client.ts +2 -2
  168. package/src/prompts/journal-context.ts +6 -1
  169. package/src/providers/anthropic/client.ts +143 -1
  170. package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
  171. package/src/runtime/auth/route-policy.ts +0 -1
  172. package/src/runtime/btw-sidechain.ts +7 -1
  173. package/src/runtime/channel-approvals.ts +2 -2
  174. package/src/runtime/channel-readiness-service.ts +30 -7
  175. package/src/runtime/http-router.ts +31 -0
  176. package/src/runtime/http-server.ts +21 -4
  177. package/src/runtime/http-types.ts +2 -0
  178. package/src/runtime/pending-interactions.ts +21 -3
  179. package/src/runtime/routes/acp-routes.ts +46 -28
  180. package/src/runtime/routes/app-management-routes.ts +123 -0
  181. package/src/runtime/routes/app-routes.ts +31 -0
  182. package/src/runtime/routes/approval-routes.ts +108 -3
  183. package/src/runtime/routes/attachment-routes.ts +45 -0
  184. package/src/runtime/routes/avatar-routes.ts +16 -0
  185. package/src/runtime/routes/brain-graph-routes.ts +18 -0
  186. package/src/runtime/routes/btw-routes.ts +20 -0
  187. package/src/runtime/routes/call-routes.ts +81 -0
  188. package/src/runtime/routes/channel-readiness-routes.ts +48 -7
  189. package/src/runtime/routes/channel-routes.ts +18 -0
  190. package/src/runtime/routes/channel-verification-routes.ts +49 -1
  191. package/src/runtime/routes/contact-routes.ts +77 -0
  192. package/src/runtime/routes/conversation-attention-routes.ts +37 -0
  193. package/src/runtime/routes/conversation-management-routes.ts +94 -0
  194. package/src/runtime/routes/conversation-query-routes.ts +78 -0
  195. package/src/runtime/routes/conversation-routes.ts +115 -38
  196. package/src/runtime/routes/conversation-starter-routes.ts +29 -0
  197. package/src/runtime/routes/debug-routes.ts +23 -0
  198. package/src/runtime/routes/diagnostics-routes.ts +30 -0
  199. package/src/runtime/routes/documents-routes.ts +42 -0
  200. package/src/runtime/routes/events-routes.ts +10 -0
  201. package/src/runtime/routes/global-search-routes.ts +35 -0
  202. package/src/runtime/routes/guardian-action-routes.ts +47 -2
  203. package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
  204. package/src/runtime/routes/heartbeat-routes.ts +278 -0
  205. package/src/runtime/routes/host-bash-routes.ts +16 -1
  206. package/src/runtime/routes/host-cu-routes.ts +23 -1
  207. package/src/runtime/routes/host-file-routes.ts +18 -1
  208. package/src/runtime/routes/identity-routes.ts +35 -0
  209. package/src/runtime/routes/inbound-message-handler.ts +46 -25
  210. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
  211. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
  212. package/src/runtime/routes/integrations/twilio.ts +32 -22
  213. package/src/runtime/routes/invite-routes.ts +83 -0
  214. package/src/runtime/routes/log-export-routes.ts +14 -0
  215. package/src/runtime/routes/memory-item-routes.ts +99 -1
  216. package/src/runtime/routes/migration-rollback-routes.ts +25 -0
  217. package/src/runtime/routes/migration-routes.ts +40 -0
  218. package/src/runtime/routes/notification-routes.ts +20 -0
  219. package/src/runtime/routes/oauth-apps.ts +11 -3
  220. package/src/runtime/routes/pairing-routes.ts +15 -0
  221. package/src/runtime/routes/recording-routes.ts +72 -0
  222. package/src/runtime/routes/schedule-routes.ts +77 -5
  223. package/src/runtime/routes/secret-routes.ts +63 -1
  224. package/src/runtime/routes/settings-routes.ts +91 -1
  225. package/src/runtime/routes/skills-routes.ts +98 -16
  226. package/src/runtime/routes/subagents-routes.ts +38 -3
  227. package/src/runtime/routes/surface-action-routes.ts +66 -24
  228. package/src/runtime/routes/surface-content-routes.ts +20 -0
  229. package/src/runtime/routes/telemetry-routes.ts +12 -0
  230. package/src/runtime/routes/trace-event-routes.ts +25 -0
  231. package/src/runtime/routes/trust-rules-routes.ts +46 -0
  232. package/src/runtime/routes/tts-routes.ts +15 -4
  233. package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
  234. package/src/runtime/routes/usage-routes.ts +59 -0
  235. package/src/runtime/routes/watch-routes.ts +28 -0
  236. package/src/runtime/routes/work-items-routes.ts +59 -0
  237. package/src/runtime/routes/workspace-commit-routes.ts +12 -0
  238. package/src/runtime/routes/workspace-routes.ts +102 -0
  239. package/src/schedule/scheduler.ts +7 -1
  240. package/src/security/AGENTS.md +7 -0
  241. package/src/security/credential-backend.ts +1 -1
  242. package/src/security/encrypted-store.ts +3 -3
  243. package/src/security/oauth2.ts +55 -0
  244. package/src/security/secret-ingress.ts +174 -0
  245. package/src/security/secret-patterns.ts +133 -0
  246. package/src/security/secret-scanner.ts +28 -117
  247. package/src/signals/confirm.ts +12 -8
  248. package/src/signals/user-message.ts +18 -3
  249. package/src/skills/skill-memory.ts +1 -2
  250. package/src/tasks/task-runner.ts +7 -1
  251. package/src/tools/credentials/broker.ts +1 -1
  252. package/src/tools/credentials/metadata-store.ts +1 -1
  253. package/src/tools/credentials/vault.ts +2 -3
  254. package/src/tools/memory/definitions.ts +1 -1
  255. package/src/tools/memory/handlers.test.ts +2 -4
  256. package/src/tools/skills/load.ts +1 -1
  257. package/src/tools/terminal/safe-env.ts +7 -0
  258. package/src/tools/tool-manifest.ts +1 -1
  259. package/src/util/log-redact.ts +9 -34
  260. package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +13 -148
  261. package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +7 -145
  262. package/src/workspace/migrations/AGENTS.md +11 -0
  263. package/src/workspace/migrations/runner.ts +16 -6
  264. package/src/workspace/migrations/types.ts +7 -0
  265. package/docs/architecture/keychain-broker.md +0 -69
  266. package/src/__tests__/keychain-broker-client.test.ts +0 -800
  267. package/src/cli/commands/oauth/platform.ts +0 -525
  268. package/src/config/bundled-skills/slack/TOOLS.json +0 -272
  269. package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
  270. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
  271. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
  272. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
  273. package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
  274. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
  275. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
  276. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
  277. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
  278. package/src/security/keychain-broker-client.ts +0 -446
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { and, asc, count, desc, eq, inArray, like, ne, or } from "drizzle-orm";
12
12
  import { v4 as uuid } from "uuid";
13
+ import { z } from "zod";
13
14
 
14
15
  import { getConfig } from "../../config/loader.js";
15
16
  import { getDb } from "../../memory/db.js";
@@ -597,7 +598,11 @@ export async function handleUpdateMemoryItem(
597
598
  // If sourceType was set (either directly or via mapping), also write verificationState
598
599
  if (body.sourceType !== undefined && body.verificationState === undefined) {
599
600
  set.verificationState =
600
- body.sourceType === "tool" ? "user_confirmed" : "assistant_inferred";
601
+ body.sourceType === "tool"
602
+ ? "user_confirmed"
603
+ : existing.verificationState === "user_reported"
604
+ ? "user_reported"
605
+ : "assistant_inferred";
601
606
  }
602
607
 
603
608
  // If subject, statement, or kind changed, recompute fingerprint
@@ -717,29 +722,122 @@ export function memoryItemRouteDefinitions(): RouteDefinition[] {
717
722
  {
718
723
  endpoint: "memory-items",
719
724
  method: "GET",
725
+ summary: "List memory items",
726
+ description:
727
+ "Return memory items with filtering, search, sorting, and pagination.",
728
+ tags: ["memory"],
729
+ queryParams: [
730
+ {
731
+ name: "kind",
732
+ schema: { type: "string" },
733
+ description: "Filter by kind",
734
+ },
735
+ {
736
+ name: "status",
737
+ schema: { type: "string" },
738
+ description: "Filter by status (default active)",
739
+ },
740
+ {
741
+ name: "search",
742
+ schema: { type: "string" },
743
+ description: "Full-text search query",
744
+ },
745
+ {
746
+ name: "sort",
747
+ schema: { type: "string" },
748
+ description: "Sort field (default lastSeenAt)",
749
+ },
750
+ {
751
+ name: "order",
752
+ schema: { type: "string" },
753
+ description: "asc or desc (default desc)",
754
+ },
755
+ {
756
+ name: "limit",
757
+ schema: { type: "integer" },
758
+ description: "Max results (default 100)",
759
+ },
760
+ {
761
+ name: "offset",
762
+ schema: { type: "integer" },
763
+ description: "Pagination offset",
764
+ },
765
+ ],
766
+ responseBody: z.object({
767
+ items: z.array(z.unknown()).describe("Memory item objects"),
768
+ total: z.number(),
769
+ }),
720
770
  handler: (ctx) => handleListMemoryItems(ctx.url),
721
771
  },
722
772
  {
723
773
  endpoint: "memory-items/:id",
724
774
  method: "GET",
725
775
  policyKey: "memory-items",
776
+ summary: "Get a memory item",
777
+ description:
778
+ "Return a single memory item by ID with supersession metadata.",
779
+ tags: ["memory"],
780
+ responseBody: z.object({
781
+ item: z
782
+ .object({})
783
+ .passthrough()
784
+ .describe("Memory item with scopeLabel and supersession info"),
785
+ }),
726
786
  handler: (ctx) => handleGetMemoryItem(ctx),
727
787
  },
728
788
  {
729
789
  endpoint: "memory-items",
730
790
  method: "POST",
791
+ summary: "Create a memory item",
792
+ description: "Create a new memory item and enqueue embedding.",
793
+ tags: ["memory"],
794
+ requestBody: z.object({
795
+ kind: z
796
+ .string()
797
+ .describe("Memory kind (identity, preference, project, etc.)"),
798
+ subject: z.string().describe("Subject line"),
799
+ statement: z.string().describe("Statement content"),
800
+ importance: z
801
+ .number()
802
+ .describe("Importance score (default 0.8)")
803
+ .optional(),
804
+ }),
805
+ responseBody: z.object({
806
+ item: z.object({}).passthrough().describe("Created memory item"),
807
+ }),
731
808
  handler: (ctx) => handleCreateMemoryItem(ctx),
732
809
  },
733
810
  {
734
811
  endpoint: "memory-items/:id",
735
812
  method: "PATCH",
736
813
  policyKey: "memory-items",
814
+ summary: "Update a memory item",
815
+ description: "Partially update fields on an existing memory item.",
816
+ tags: ["memory"],
817
+ requestBody: z.object({
818
+ subject: z.string(),
819
+ statement: z.string(),
820
+ kind: z.string(),
821
+ status: z.string(),
822
+ importance: z.number(),
823
+ sourceType: z.string(),
824
+ verificationState: z.string(),
825
+ }),
826
+ responseBody: z.object({
827
+ item: z.object({}).passthrough().describe("Updated memory item"),
828
+ }),
737
829
  handler: (ctx) => handleUpdateMemoryItem(ctx),
738
830
  },
739
831
  {
740
832
  endpoint: "memory-items/:id",
741
833
  method: "DELETE",
742
834
  policyKey: "memory-items",
835
+ summary: "Delete a memory item",
836
+ description: "Delete a memory item and its embeddings.",
837
+ tags: ["memory"],
838
+ responseBody: z.object({
839
+ ok: z.boolean(),
840
+ }),
743
841
  handler: (ctx) => handleDeleteMemoryItem(ctx),
744
842
  },
745
843
  ];
@@ -7,6 +7,8 @@
7
7
  * the same pattern as other gateway-forwarded control-plane endpoints.
8
8
  */
9
9
 
10
+ import { z } from "zod";
11
+
10
12
  import { getDb } from "../../memory/db-connection.js";
11
13
  import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
12
14
  import { rollbackMemoryMigration } from "../../memory/migrations/validate-migration-state.js";
@@ -25,6 +27,29 @@ export function migrationRollbackRouteDefinitions(): RouteDefinition[] {
25
27
  {
26
28
  endpoint: "admin/rollback-migrations",
27
29
  method: "POST",
30
+ summary: "Rollback migrations",
31
+ description:
32
+ "Roll back DB and/or workspace migrations to a specified target version. Restricted to gateway service principals.",
33
+ tags: ["admin"],
34
+ requestBody: z.object({
35
+ targetDbVersion: z
36
+ .number()
37
+ .int()
38
+ .describe("Target DB migration version"),
39
+ targetWorkspaceMigrationId: z
40
+ .string()
41
+ .describe("Target workspace migration ID"),
42
+ rollbackToRegistryCeiling: z
43
+ .boolean()
44
+ .describe("Auto-determine targets from daemon registry ceilings"),
45
+ }),
46
+ responseBody: z.object({
47
+ ok: z.boolean(),
48
+ rolledBack: z
49
+ .object({})
50
+ .passthrough()
51
+ .describe("Lists of rolled-back DB and workspace migrations"),
52
+ }),
28
53
  handler: async ({ req }) => {
29
54
  let body: unknown;
30
55
  try {
@@ -14,6 +14,8 @@
14
14
  import { join } from "node:path";
15
15
  import { Database } from "bun:sqlite";
16
16
 
17
+ import { z } from "zod";
18
+
17
19
  import { invalidateConfigCache } from "../../config/loader.js";
18
20
  import { getDb, resetDb } from "../../memory/db-connection.js";
19
21
  import { validateMigrationState } from "../../memory/migrations/validate-migration-state.js";
@@ -474,21 +476,59 @@ export function migrationRouteDefinitions(): RouteDefinition[] {
474
476
  {
475
477
  endpoint: "migrations/validate",
476
478
  method: "POST",
479
+ summary: "Validate a .vbundle archive",
480
+ description:
481
+ "Upload a .vbundle archive for validation. Accepts raw binary or multipart form data.",
482
+ tags: ["migrations"],
483
+ responseBody: z.object({
484
+ is_valid: z.boolean(),
485
+ errors: z.array(z.unknown()),
486
+ manifest: z.object({}).passthrough(),
487
+ }),
477
488
  handler: async ({ req }) => handleMigrationValidate(req),
478
489
  },
479
490
  {
480
491
  endpoint: "migrations/export",
481
492
  method: "POST",
493
+ summary: "Export a .vbundle archive",
494
+ description:
495
+ "Generate and download a .vbundle archive of the assistant's data. Optional JSON body for metadata.",
496
+ tags: ["migrations"],
497
+ requestBody: z.object({
498
+ description: z.string().describe("Human-readable export description"),
499
+ }),
482
500
  handler: async ({ req }) => handleMigrationExport(req),
483
501
  },
484
502
  {
485
503
  endpoint: "migrations/import-preflight",
486
504
  method: "POST",
505
+ summary: "Dry-run import analysis",
506
+ description:
507
+ "Validate a .vbundle archive and return a report of what would change on import without modifying data.",
508
+ tags: ["migrations"],
509
+ responseBody: z.object({
510
+ can_import: z.boolean(),
511
+ summary: z.object({}).passthrough(),
512
+ files: z.array(z.unknown()),
513
+ conflicts: z.array(z.unknown()),
514
+ manifest: z.object({}).passthrough(),
515
+ }),
487
516
  handler: async ({ req }) => handleMigrationImportPreflight(req),
488
517
  },
489
518
  {
490
519
  endpoint: "migrations/import",
491
520
  method: "POST",
521
+ summary: "Import a .vbundle archive",
522
+ description:
523
+ "Commit a .vbundle archive import to disk — destructive. Backs up existing files before overwriting.",
524
+ tags: ["migrations"],
525
+ responseBody: z.object({
526
+ success: z.boolean(),
527
+ summary: z.object({}).passthrough(),
528
+ files: z.array(z.unknown()),
529
+ manifest: z.object({}).passthrough(),
530
+ warnings: z.array(z.unknown()),
531
+ }),
492
532
  handler: async ({ req }) => handleMigrationImport(req),
493
533
  },
494
534
  ];
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { eq } from "drizzle-orm";
9
+ import { z } from "zod";
9
10
 
10
11
  import { getDb } from "../../memory/db.js";
11
12
  import { notificationDeliveries } from "../../memory/schema.js";
@@ -19,6 +20,25 @@ export function notificationRouteDefinitions(): RouteDefinition[] {
19
20
  endpoint: "notification-intent-result",
20
21
  method: "POST",
21
22
  policyKey: "notification-intent-result",
23
+ summary: "Report notification delivery result",
24
+ description:
25
+ "Client acknowledgment for local notification delivery outcome.",
26
+ tags: ["notifications"],
27
+ requestBody: z.object({
28
+ deliveryId: z.string().describe("Notification delivery ID"),
29
+ success: z.boolean().describe("Whether delivery succeeded").optional(),
30
+ errorMessage: z
31
+ .string()
32
+ .describe("Error message if delivery failed")
33
+ .optional(),
34
+ errorCode: z
35
+ .string()
36
+ .describe("Error code if delivery failed")
37
+ .optional(),
38
+ }),
39
+ responseBody: z.object({
40
+ ok: z.boolean(),
41
+ }),
22
42
  handler: async ({ req }) => {
23
43
  const body = (await req.json()) as {
24
44
  deliveryId?: string;
@@ -21,9 +21,13 @@ import {
21
21
  import { httpError } from "../http-errors.js";
22
22
  import type { RouteDefinition } from "../http-router.js";
23
23
 
24
- function parseGrantedScopes(grantedScopes: string | string[] | null | undefined): string[] {
24
+ function parseGrantedScopes(
25
+ grantedScopes: string | string[] | null | undefined,
26
+ ): string[] {
25
27
  if (Array.isArray(grantedScopes)) {
26
- return grantedScopes.filter((scope): scope is string => typeof scope === "string");
28
+ return grantedScopes.filter(
29
+ (scope): scope is string => typeof scope === "string",
30
+ );
27
31
  }
28
32
 
29
33
  if (typeof grantedScopes !== "string" || grantedScopes.trim() === "") {
@@ -159,7 +163,11 @@ export function oauthAppsRouteDefinitions(): RouteDefinition[] {
159
163
  handler: async ({ params }) => {
160
164
  const app = getApp(params.id);
161
165
  if (!app) {
162
- return httpError("NOT_FOUND", `OAuth app not found: ${params.id}`, 404);
166
+ return httpError(
167
+ "NOT_FOUND",
168
+ `OAuth app not found: ${params.id}`,
169
+ 404,
170
+ );
163
171
  }
164
172
 
165
173
  // Disconnect all connections for this app first to clean up tokens.
@@ -2,6 +2,8 @@
2
2
  * Pairing HTTP route handlers for device pairing flow.
3
3
  */
4
4
 
5
+ import { z } from "zod";
6
+
5
7
  import {
6
8
  hashDeviceId,
7
9
  isDeviceApproved,
@@ -417,6 +419,19 @@ export function pairingRouteDefinitions(deps: {
417
419
  {
418
420
  endpoint: "pairing/register",
419
421
  method: "POST",
422
+ summary: "Register pairing request",
423
+ description:
424
+ "Pre-register a pairing request when the QR code is displayed.",
425
+ tags: ["pairing"],
426
+ requestBody: z.object({
427
+ pairingRequestId: z.string(),
428
+ pairingSecret: z.string(),
429
+ gatewayUrl: z.string(),
430
+ localLanUrl: z.string().optional(),
431
+ }),
432
+ responseBody: z.object({
433
+ ok: z.boolean(),
434
+ }),
420
435
  handler: async ({ req }) =>
421
436
  handlePairingRegister(req, deps.getPairingContext()),
422
437
  },
@@ -9,6 +9,8 @@
9
9
  * require `settings.read`.
10
10
  */
11
11
 
12
+ import { z } from "zod";
13
+
12
14
  import {
13
15
  getActiveRestartToken,
14
16
  handleRecordingPause,
@@ -298,36 +300,106 @@ export function recordingRouteDefinitions(deps: {
298
300
  endpoint: "recordings/start",
299
301
  method: "POST",
300
302
  policyKey: "recordings/start",
303
+ summary: "Start recording",
304
+ description: "Start a screen recording for a conversation.",
305
+ tags: ["recordings"],
306
+ requestBody: z.object({
307
+ conversationId: z.string(),
308
+ options: z
309
+ .object({})
310
+ .passthrough()
311
+ .describe("Recording options")
312
+ .optional(),
313
+ }),
314
+ responseBody: z.object({
315
+ recordingId: z.string(),
316
+ }),
301
317
  handler: async ({ req }) => handleStartRecording(req, getDeps()),
302
318
  },
303
319
  {
304
320
  endpoint: "recordings/stop",
305
321
  method: "POST",
306
322
  policyKey: "recordings/stop",
323
+ summary: "Stop recording",
324
+ description: "Stop the active screen recording.",
325
+ tags: ["recordings"],
326
+ requestBody: z.object({
327
+ conversationId: z.string(),
328
+ }),
329
+ responseBody: z.object({
330
+ recordingId: z.string(),
331
+ stopped: z.boolean(),
332
+ }),
307
333
  handler: async ({ req }) => handleStopRecording(req, getDeps()),
308
334
  },
309
335
  {
310
336
  endpoint: "recordings/pause",
311
337
  method: "POST",
312
338
  policyKey: "recordings/pause",
339
+ summary: "Pause recording",
340
+ description: "Pause the active screen recording.",
341
+ tags: ["recordings"],
342
+ requestBody: z.object({
343
+ conversationId: z.string(),
344
+ }),
345
+ responseBody: z.object({
346
+ recordingId: z.string(),
347
+ paused: z.boolean(),
348
+ }),
313
349
  handler: async ({ req }) => handlePauseRecording(req, getDeps()),
314
350
  },
315
351
  {
316
352
  endpoint: "recordings/resume",
317
353
  method: "POST",
318
354
  policyKey: "recordings/resume",
355
+ summary: "Resume recording",
356
+ description: "Resume a paused screen recording.",
357
+ tags: ["recordings"],
358
+ requestBody: z.object({
359
+ conversationId: z.string(),
360
+ }),
361
+ responseBody: z.object({
362
+ recordingId: z.string(),
363
+ resumed: z.boolean(),
364
+ }),
319
365
  handler: async ({ req }) => handleResumeRecording(req, getDeps()),
320
366
  },
321
367
  {
322
368
  endpoint: "recordings/status",
323
369
  method: "GET",
324
370
  policyKey: "recordings/status",
371
+ summary: "Get recording status",
372
+ description: "Return the current recording state.",
373
+ tags: ["recordings"],
374
+ responseBody: z.object({
375
+ idle: z.boolean(),
376
+ restartInProgress: z.boolean(),
377
+ }),
325
378
  handler: () => handleGetRecordingStatus(),
326
379
  },
327
380
  {
328
381
  endpoint: "recordings/status",
329
382
  method: "POST",
330
383
  policyKey: "recordings/status:POST",
384
+ summary: "Post recording status",
385
+ description: "Recording lifecycle callback from the client.",
386
+ tags: ["recordings"],
387
+ requestBody: z.object({
388
+ conversationId: z.string(),
389
+ status: z
390
+ .string()
391
+ .describe(
392
+ "started, stopped, failed, restart_cancelled, paused, resumed",
393
+ ),
394
+ filePath: z.string().optional(),
395
+ durationMs: z.number().optional(),
396
+ error: z.string().optional(),
397
+ attachToConversationId: z.string().optional(),
398
+ operationToken: z.string().optional(),
399
+ }),
400
+ responseBody: z.object({
401
+ ok: z.boolean(),
402
+ }),
331
403
  handler: async ({ req }) => handlePostRecordingStatus(req, getDeps()),
332
404
  },
333
405
  ];
@@ -4,6 +4,8 @@
4
4
  * HTTP route handlers for schedule management.
5
5
  */
6
6
 
7
+ import { z } from "zod";
8
+
7
9
  import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
8
10
  import {
9
11
  cancelSchedule,
@@ -112,11 +114,27 @@ function handleUpdateSchedule(
112
114
  ): Response {
113
115
  const updates: Record<string, unknown> = {};
114
116
 
115
- if ("mode" in body && !VALID_MODES.includes(body.mode as (typeof VALID_MODES)[number])) {
116
- return httpError("BAD_REQUEST", `Invalid mode: must be one of ${VALID_MODES.join(", ")}`, 400);
117
+ if (
118
+ "mode" in body &&
119
+ !VALID_MODES.includes(body.mode as (typeof VALID_MODES)[number])
120
+ ) {
121
+ return httpError(
122
+ "BAD_REQUEST",
123
+ `Invalid mode: must be one of ${VALID_MODES.join(", ")}`,
124
+ 400,
125
+ );
117
126
  }
118
- if ("routingIntent" in body && !VALID_ROUTING_INTENTS.includes(body.routingIntent as (typeof VALID_ROUTING_INTENTS)[number])) {
119
- return httpError("BAD_REQUEST", `Invalid routingIntent: must be one of ${VALID_ROUTING_INTENTS.join(", ")}`, 400);
127
+ if (
128
+ "routingIntent" in body &&
129
+ !VALID_ROUTING_INTENTS.includes(
130
+ body.routingIntent as (typeof VALID_ROUTING_INTENTS)[number],
131
+ )
132
+ ) {
133
+ return httpError(
134
+ "BAD_REQUEST",
135
+ `Invalid routingIntent: must be one of ${VALID_ROUTING_INTENTS.join(", ")}`,
136
+ 400,
137
+ );
120
138
  }
121
139
 
122
140
  for (const key of [
@@ -276,12 +294,27 @@ export function scheduleRouteDefinitions(deps: {
276
294
  endpoint: "schedules",
277
295
  method: "GET",
278
296
  policyKey: "schedules",
297
+ summary: "List schedules",
298
+ description: "Return all scheduled jobs.",
299
+ tags: ["schedules"],
300
+ responseBody: z.object({
301
+ schedules: z.array(z.unknown()).describe("Schedule objects"),
302
+ }),
279
303
  handler: () => handleListSchedules(),
280
304
  },
281
305
  {
282
306
  endpoint: "schedules/:id/toggle",
283
307
  method: "POST",
284
308
  policyKey: "schedules/toggle",
309
+ summary: "Toggle schedule",
310
+ description: "Enable or disable a schedule.",
311
+ tags: ["schedules"],
312
+ requestBody: z.object({
313
+ enabled: z.boolean().describe("New enabled state"),
314
+ }),
315
+ responseBody: z.object({
316
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
317
+ }),
285
318
  handler: async ({ req, params }) => {
286
319
  const body = (await req.json()) as { enabled?: boolean };
287
320
  if (body.enabled === undefined) {
@@ -294,16 +327,43 @@ export function scheduleRouteDefinitions(deps: {
294
327
  endpoint: "schedules/:id",
295
328
  method: "DELETE",
296
329
  policyKey: "schedules",
330
+ summary: "Delete schedule",
331
+ description: "Remove a schedule by ID.",
332
+ tags: ["schedules"],
333
+ responseBody: z.object({
334
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
335
+ }),
297
336
  handler: ({ params }) => handleDeleteSchedule(params.id),
298
337
  },
299
338
  {
300
339
  endpoint: "schedules/:id",
301
340
  method: "PATCH",
302
341
  policyKey: "schedules",
342
+ summary: "Update schedule",
343
+ description: "Partially update fields on a schedule.",
344
+ tags: ["schedules"],
345
+ requestBody: z.object({
346
+ name: z.string(),
347
+ expression: z.string(),
348
+ timezone: z.string(),
349
+ message: z.string(),
350
+ mode: z.string().describe("notify or execute"),
351
+ routingIntent: z
352
+ .string()
353
+ .describe("single_channel, multi_channel, or all_channels"),
354
+ quiet: z.boolean(),
355
+ }),
356
+ responseBody: z.object({
357
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
358
+ }),
303
359
  handler: async ({ req, params }) => {
304
360
  const body: unknown = await req.json();
305
361
  if (typeof body !== "object" || !body || Array.isArray(body)) {
306
- return httpError("BAD_REQUEST", "Request body must be a JSON object", 400);
362
+ return httpError(
363
+ "BAD_REQUEST",
364
+ "Request body must be a JSON object",
365
+ 400,
366
+ );
307
367
  }
308
368
  return handleUpdateSchedule(params.id, body as Record<string, unknown>);
309
369
  },
@@ -312,6 +372,12 @@ export function scheduleRouteDefinitions(deps: {
312
372
  endpoint: "schedules/:id/run",
313
373
  method: "POST",
314
374
  policyKey: "schedules/run",
375
+ summary: "Run schedule now",
376
+ description: "Trigger an immediate execution of a schedule.",
377
+ tags: ["schedules"],
378
+ responseBody: z.object({
379
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
380
+ }),
315
381
  handler: async ({ params }) =>
316
382
  handleRunScheduleNow(params.id, deps.sendMessageDeps),
317
383
  },
@@ -319,6 +385,12 @@ export function scheduleRouteDefinitions(deps: {
319
385
  endpoint: "schedules/:id/cancel",
320
386
  method: "POST",
321
387
  policyKey: "schedules/cancel",
388
+ summary: "Cancel schedule",
389
+ description: "Cancel a pending schedule.",
390
+ tags: ["schedules"],
391
+ responseBody: z.object({
392
+ schedules: z.array(z.unknown()).describe("Updated schedule list"),
393
+ }),
322
394
  handler: ({ params }) => handleCancelSchedule(params.id),
323
395
  },
324
396
  ];