@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
@@ -18,6 +18,8 @@ import { stat } from "node:fs/promises";
18
18
  import { homedir } from "node:os";
19
19
  import { dirname, join } from "node:path";
20
20
 
21
+ import { z } from "zod";
22
+
21
23
  import { packageApp } from "../../bundler/app-bundler.js";
22
24
  import { compileApp } from "../../bundler/app-compiler.js";
23
25
  import { scanBundle } from "../../bundler/bundle-scanner.js";
@@ -378,6 +380,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
378
380
  endpoint: "apps",
379
381
  method: "GET",
380
382
  policyKey: "apps",
383
+ summary: "List apps",
384
+ description: "Return all locally installed apps.",
385
+ tags: ["apps"],
386
+ responseBody: z.object({
387
+ apps: z.array(z.unknown()).describe("Array of app summary objects"),
388
+ }),
381
389
  handler: () => {
382
390
  try {
383
391
  const apps = listAppsFiltered();
@@ -400,6 +408,13 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
400
408
  endpoint: "apps/open-bundle",
401
409
  method: "POST",
402
410
  policyKey: "apps/open-bundle",
411
+ summary: "Open a .vbundle file",
412
+ description:
413
+ "Scan and validate a .vbundle file from disk and return its manifest.",
414
+ tags: ["apps"],
415
+ requestBody: z.object({
416
+ filePath: z.string().describe("Absolute path to the .vbundle file"),
417
+ }),
403
418
  handler: async ({ req }) => {
404
419
  try {
405
420
  const body = (await req.json()) as { filePath?: string };
@@ -429,6 +444,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
429
444
  endpoint: "apps/shared",
430
445
  method: "GET",
431
446
  policyKey: "apps/shared-list",
447
+ summary: "List shared apps",
448
+ description: "Return all apps available via cloud share links.",
449
+ tags: ["apps"],
450
+ responseBody: z.object({
451
+ apps: z.array(z.unknown()).describe("Array of shared app objects"),
452
+ }),
432
453
  handler: () => {
433
454
  try {
434
455
  const apps = listSharedApps();
@@ -451,6 +472,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
451
472
  endpoint: "apps/fork",
452
473
  method: "POST",
453
474
  policyKey: "apps/fork",
475
+ summary: "Fork a shared app",
476
+ description: "Create a local copy of a shared app by its UUID.",
477
+ tags: ["apps"],
478
+ requestBody: z.object({
479
+ uuid: z.string().describe("UUID of the shared app to fork"),
480
+ }),
454
481
  handler: async ({ req }) => {
455
482
  try {
456
483
  const body = (await req.json()) as { uuid?: string };
@@ -479,6 +506,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
479
506
  endpoint: "apps/gallery/install",
480
507
  method: "POST",
481
508
  policyKey: "apps/gallery/install",
509
+ summary: "Install a gallery app",
510
+ description: "Install an app from the built-in gallery by its ID.",
511
+ tags: ["apps"],
512
+ requestBody: z.object({
513
+ galleryAppId: z.string(),
514
+ }),
482
515
  handler: async ({ req }) => {
483
516
  try {
484
517
  const body = (await req.json()) as { galleryAppId?: string };
@@ -505,6 +538,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
505
538
  endpoint: "apps/gallery",
506
539
  method: "GET",
507
540
  policyKey: "apps/gallery",
541
+ summary: "List gallery apps",
542
+ description: "Return the built-in app gallery catalog.",
543
+ tags: ["apps"],
544
+ responseBody: z.object({
545
+ gallery: z.array(z.unknown()).describe("Gallery app entries"),
546
+ }),
508
547
  handler: () => {
509
548
  return Response.json({ gallery: defaultGallery });
510
549
  },
@@ -516,6 +555,19 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
516
555
  endpoint: "apps/sign-bundle",
517
556
  method: "POST",
518
557
  policyKey: "apps/sign-bundle",
558
+ summary: "Sign an app bundle",
559
+ description:
560
+ "Return a signing payload or complete the signing step when signature fields are provided.",
561
+ tags: ["apps"],
562
+ requestBody: z.object({
563
+ payload: z.string().describe("Canonical JSON payload to sign"),
564
+ signature: z
565
+ .string()
566
+ .describe("Ed25519 signature (optional, completes signing)")
567
+ .optional(),
568
+ keyId: z.string().optional(),
569
+ publicKey: z.string().optional(),
570
+ }),
519
571
  handler: async ({ req }) => {
520
572
  try {
521
573
  const body = (await req.json()) as {
@@ -576,6 +628,10 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
576
628
  endpoint: "apps/signing-identity",
577
629
  method: "GET",
578
630
  policyKey: "apps/signing-identity",
631
+ summary: "Get signing identity",
632
+ description:
633
+ "Return signing identity info. Signing is managed client-side over HTTP.",
634
+ tags: ["apps"],
579
635
  handler: () => {
580
636
  // Signing identity is a client-side concept. Over HTTP, the
581
637
  // client already holds its own keys. Return a placeholder
@@ -597,6 +653,9 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
597
653
  endpoint: "apps/:id/data",
598
654
  method: "GET",
599
655
  policyKey: "apps/data",
656
+ summary: "Query app data",
657
+ description: "Read records from an app's local data store.",
658
+ tags: ["apps"],
600
659
  handler: ({ params, url }) => {
601
660
  try {
602
661
  const method = url.searchParams.get("method") ?? "query";
@@ -627,6 +686,15 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
627
686
  endpoint: "apps/:id/data",
628
687
  method: "POST",
629
688
  policyKey: "apps/data",
689
+ summary: "Mutate app data",
690
+ description:
691
+ "Create, update, or delete records in an app's local data store.",
692
+ tags: ["apps"],
693
+ requestBody: z.object({
694
+ method: z.string().describe("'create', 'update', or 'delete'"),
695
+ recordId: z.string(),
696
+ data: z.object({}).passthrough(),
697
+ }),
630
698
  handler: async ({ params, req }) => {
631
699
  try {
632
700
  const body = (await req.json()) as {
@@ -662,6 +730,16 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
662
730
  endpoint: "apps/:id/open",
663
731
  method: "POST",
664
732
  policyKey: "apps/open",
733
+ summary: "Open an app",
734
+ description:
735
+ "Compile (if needed) and return the app's HTML for rendering.",
736
+ tags: ["apps"],
737
+ responseBody: z.object({
738
+ appId: z.string(),
739
+ dirName: z.string(),
740
+ name: z.string(),
741
+ html: z.string(),
742
+ }),
665
743
  handler: async ({ params }) => {
666
744
  try {
667
745
  const appId = params.id;
@@ -719,6 +797,9 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
719
797
  endpoint: "apps/:id/delete",
720
798
  method: "POST",
721
799
  policyKey: "apps/delete",
800
+ summary: "Delete an app",
801
+ description: "Permanently remove an app and its data.",
802
+ tags: ["apps"],
722
803
  handler: ({ params }) => {
723
804
  try {
724
805
  deleteApp(params.id);
@@ -736,6 +817,9 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
736
817
  endpoint: "apps/:id/preview",
737
818
  method: "GET",
738
819
  policyKey: "apps/preview",
820
+ summary: "Get app preview",
821
+ description: "Return the preview image or HTML for an app.",
822
+ tags: ["apps"],
739
823
  handler: ({ params }) => {
740
824
  try {
741
825
  const preview = getAppPreview(params.id);
@@ -759,6 +843,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
759
843
  endpoint: "apps/:id/preview",
760
844
  method: "PUT",
761
845
  policyKey: "apps/preview",
846
+ summary: "Update app preview",
847
+ description: "Set a new preview image or HTML for an app.",
848
+ tags: ["apps"],
849
+ requestBody: z.object({
850
+ preview: z.string().describe("Base64-encoded image or HTML string"),
851
+ }),
762
852
  handler: async ({ params, req }) => {
763
853
  try {
764
854
  const body = (await req.json()) as { preview?: string };
@@ -785,6 +875,13 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
785
875
  endpoint: "apps/:id/history",
786
876
  method: "GET",
787
877
  policyKey: "apps/history",
878
+ summary: "Get app version history",
879
+ description: "Return the git commit history of an app.",
880
+ tags: ["apps"],
881
+ responseBody: z.object({
882
+ appId: z.string(),
883
+ versions: z.array(z.unknown()),
884
+ }),
788
885
  handler: async ({ params, url }) => {
789
886
  try {
790
887
  const limit = url.searchParams.get("limit")
@@ -808,6 +905,9 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
808
905
  endpoint: "apps/:id/diff",
809
906
  method: "GET",
810
907
  policyKey: "apps/diff",
908
+ summary: "Get app diff",
909
+ description: "Return a git diff between two commits for an app.",
910
+ tags: ["apps"],
811
911
  handler: async ({ params, url }) => {
812
912
  try {
813
913
  const fromCommit = url.searchParams.get("fromCommit");
@@ -837,6 +937,12 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
837
937
  endpoint: "apps/:id/restore",
838
938
  method: "POST",
839
939
  policyKey: "apps/restore",
940
+ summary: "Restore app version",
941
+ description: "Restore an app to a previous git commit.",
942
+ tags: ["apps"],
943
+ requestBody: z.object({
944
+ commitHash: z.string(),
945
+ }),
840
946
  handler: async ({ params, req }) => {
841
947
  try {
842
948
  const body = (await req.json()) as { commitHash?: string };
@@ -862,6 +968,15 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
862
968
  endpoint: "apps/:id/bundle",
863
969
  method: "POST",
864
970
  policyKey: "apps/bundle",
971
+ summary: "Bundle an app",
972
+ description: "Package an app into a distributable .vbundle archive.",
973
+ tags: ["apps"],
974
+ responseBody: z.object({
975
+ type: z.string(),
976
+ bundlePath: z.string(),
977
+ iconImageBase64: z.string(),
978
+ manifest: z.object({}).passthrough(),
979
+ }),
865
980
  handler: async ({ params }) => {
866
981
  try {
867
982
  const result = await packageApp(params.id);
@@ -889,6 +1004,14 @@ export function appManagementRouteDefinitions(): RouteDefinition[] {
889
1004
  endpoint: "apps/:id/share-cloud",
890
1005
  method: "POST",
891
1006
  policyKey: "apps/share-cloud",
1007
+ summary: "Share app to cloud",
1008
+ description: "Package and upload an app to the cloud share service.",
1009
+ tags: ["apps"],
1010
+ responseBody: z.object({
1011
+ success: z.boolean(),
1012
+ shareToken: z.string(),
1013
+ shareUrl: z.string(),
1014
+ }),
892
1015
  handler: async ({ params }) => {
893
1016
  try {
894
1017
  // Package without signing callback (HTTP clients handle signing
@@ -6,6 +6,7 @@ import { existsSync, readFileSync } from "node:fs";
6
6
  import { extname, join } from "node:path";
7
7
 
8
8
  import JSZip from "jszip";
9
+ import { z } from "zod";
9
10
 
10
11
  import type { AppManifest } from "../../bundler/manifest.js";
11
12
  import {
@@ -336,30 +337,60 @@ export function appRouteDefinitions(): RouteDefinition[] {
336
337
  endpoint: "apps/:appId/dist/:filename",
337
338
  method: "GET",
338
339
  policyKey: "apps/dist",
340
+ summary: "Serve app dist file",
341
+ description:
342
+ "Serve a static asset from an app's compiled dist/ directory.",
343
+ tags: ["apps"],
339
344
  handler: ({ params }) =>
340
345
  handleServeDistFile(params.appId, params.filename),
341
346
  },
342
347
  {
343
348
  endpoint: "apps/share",
344
349
  method: "POST",
350
+ summary: "Share an app",
351
+ description: "Upload a zip app bundle and create a shareable link.",
352
+ tags: ["apps"],
353
+ responseBody: z.object({
354
+ shareToken: z.string(),
355
+ shareUrl: z.string(),
356
+ bundleSizeBytes: z.number(),
357
+ }),
345
358
  handler: async ({ req }) => handleShareApp(req),
346
359
  },
347
360
  {
348
361
  endpoint: "apps/shared/:token/metadata",
349
362
  method: "GET",
350
363
  policyKey: "apps/shared/metadata",
364
+ summary: "Get shared app metadata",
365
+ description: "Return metadata for a shared app bundle.",
366
+ tags: ["apps"],
367
+ responseBody: z.object({
368
+ name: z.string(),
369
+ description: z.string(),
370
+ icon: z.string(),
371
+ bundleSizeBytes: z.number(),
372
+ }),
351
373
  handler: ({ params }) => handleGetSharedAppMetadata(params.token),
352
374
  },
353
375
  {
354
376
  endpoint: "apps/shared/:token",
355
377
  method: "GET",
356
378
  policyKey: "apps/shared",
379
+ summary: "Download shared app",
380
+ description: "Download a shared app bundle as a zip file.",
381
+ tags: ["apps"],
357
382
  handler: ({ params }) => handleDownloadSharedApp(params.token),
358
383
  },
359
384
  {
360
385
  endpoint: "apps/shared/:token",
361
386
  method: "DELETE",
362
387
  policyKey: "apps/shared",
388
+ summary: "Delete shared app",
389
+ description: "Remove a shared app link.",
390
+ tags: ["apps"],
391
+ responseBody: z.object({
392
+ success: z.boolean(),
393
+ }),
363
394
  handler: ({ params }) => handleDeleteSharedApp(params.token),
364
395
  },
365
396
  ];
@@ -8,6 +8,8 @@
8
8
  * header. Guardian decisions additionally verify that the actor is the
9
9
  * bound guardian.
10
10
  */
11
+ import { z } from "zod";
12
+
11
13
  import { getConversationByKey } from "../../memory/conversation-key-store.js";
12
14
  import { addRule } from "../../permissions/trust-store.js";
13
15
  import type { UserDecision } from "../../permissions/types.js";
@@ -69,6 +71,10 @@ export async function handleConfirm(
69
71
  // pending interaction — the client can retry with corrected values.
70
72
  const peeked = pendingInteractions.get(requestId);
71
73
  if (!peeked) {
74
+ log.warn(
75
+ { requestId, decision },
76
+ "Confirmation POST for unknown requestId (already consumed or never registered)",
77
+ );
72
78
  return httpError(
73
79
  "NOT_FOUND",
74
80
  "No pending interaction found for this requestId",
@@ -129,7 +135,23 @@ export async function handleConfirm(
129
135
  // Validation passed — consume the pending interaction.
130
136
  const interaction = pendingInteractions.resolve(requestId)!;
131
137
 
132
- interaction.conversation.handleConfirmationResponse(
138
+ log.info(
139
+ {
140
+ requestId,
141
+ decision,
142
+ toolName: interaction.confirmationDetails?.toolName,
143
+ conversationId: interaction.conversationId,
144
+ },
145
+ "Confirmation resolved via HTTP",
146
+ );
147
+
148
+ // ACP permissions: resolve directly without a Conversation object.
149
+ if (interaction.directResolve) {
150
+ interaction.directResolve(decision as UserDecision);
151
+ return Response.json({ accepted: true });
152
+ }
153
+
154
+ interaction.conversation!.handleConfirmationResponse(
133
155
  requestId,
134
156
  decision as UserDecision,
135
157
  selectedPattern,
@@ -187,7 +209,7 @@ export async function handleSecret(
187
209
  );
188
210
  }
189
211
 
190
- interaction.conversation.handleSecretResponse(
212
+ interaction.conversation!.handleSecretResponse(
191
213
  requestId,
192
214
  value,
193
215
  delivery as "store" | "transient_send" | undefined,
@@ -355,7 +377,9 @@ export function handleListPendingInteractions(
355
377
  resolvedConversationId,
356
378
  );
357
379
 
358
- const confirmation = interactions.find((i) => i.kind === "confirmation");
380
+ const confirmation = interactions.find(
381
+ (i) => i.kind === "confirmation" || i.kind === "acp_confirmation",
382
+ );
359
383
  const secret = interactions.find((i) => i.kind === "secret");
360
384
 
361
385
  return Response.json({
@@ -377,6 +401,8 @@ export function handleListPendingInteractions(
377
401
  confirmation.confirmationDetails?.persistentDecisionsAllowed,
378
402
  temporaryOptionsAvailable:
379
403
  confirmation.confirmationDetails?.temporaryOptionsAvailable,
404
+ acpToolKind: confirmation.confirmationDetails?.acpToolKind,
405
+ acpOptions: confirmation.confirmationDetails?.acpOptions,
380
406
  }
381
407
  : null,
382
408
  pendingSecret: secret
@@ -396,22 +422,101 @@ export function approvalRouteDefinitions(): RouteDefinition[] {
396
422
  {
397
423
  endpoint: "confirm",
398
424
  method: "POST",
425
+ summary: "Resolve a pending confirmation",
426
+ description: "Approve or deny a pending tool confirmation by requestId.",
427
+ tags: ["approvals"],
428
+ requestBody: z.object({
429
+ requestId: z.string().describe("Pending interaction request ID"),
430
+ decision: z
431
+ .string()
432
+ .describe(
433
+ "One of: allow, allow_10m, allow_conversation, deny, always_allow, always_deny, always_allow_high_risk",
434
+ ),
435
+ selectedPattern: z
436
+ .string()
437
+ .describe("Allowlist pattern for persistent decisions")
438
+ .optional(),
439
+ selectedScope: z
440
+ .string()
441
+ .describe("Scope for persistent decisions")
442
+ .optional(),
443
+ }),
444
+ responseBody: z.object({
445
+ accepted: z.boolean(),
446
+ }),
399
447
  handler: async ({ req, authContext }) => handleConfirm(req, authContext),
400
448
  },
401
449
  {
402
450
  endpoint: "secret",
403
451
  method: "POST",
452
+ summary: "Resolve a pending secret request",
453
+ description: "Provide a secret value for a pending secret request.",
454
+ tags: ["approvals"],
455
+ requestBody: z.object({
456
+ requestId: z.string().describe("Pending interaction request ID"),
457
+ value: z.string().describe("Secret value").optional(),
458
+ delivery: z
459
+ .string()
460
+ .describe("Delivery mode: store or transient_send")
461
+ .optional(),
462
+ }),
463
+ responseBody: z.object({
464
+ accepted: z.boolean(),
465
+ }),
404
466
  handler: async ({ req, authContext }) => handleSecret(req, authContext),
405
467
  },
406
468
  {
407
469
  endpoint: "trust-rules",
408
470
  method: "POST",
471
+ summary: "Add a trust rule for a pending confirmation",
472
+ description:
473
+ "Add a trust rule bound to a pending confirmation without resolving it.",
474
+ tags: ["approvals"],
475
+ requestBody: z.object({
476
+ requestId: z.string().describe("Pending confirmation request ID"),
477
+ pattern: z.string().describe("Allowlist pattern"),
478
+ scope: z.string().describe("Scope for the rule"),
479
+ decision: z.string().describe("allow or deny"),
480
+ allowHighRisk: z
481
+ .boolean()
482
+ .describe("Allow high-risk invocations")
483
+ .optional(),
484
+ }),
485
+ responseBody: z.object({
486
+ accepted: z.boolean(),
487
+ }),
409
488
  handler: async ({ req, authContext }) =>
410
489
  handleTrustRule(req, authContext),
411
490
  },
412
491
  {
413
492
  endpoint: "pending-interactions",
414
493
  method: "GET",
494
+ summary: "List pending interactions",
495
+ description:
496
+ "Return pending confirmations and secrets for a conversation.",
497
+ tags: ["approvals"],
498
+ queryParams: [
499
+ {
500
+ name: "conversationKey",
501
+ schema: { type: "string" },
502
+ description: "Conversation key",
503
+ },
504
+ {
505
+ name: "conversationId",
506
+ schema: { type: "string" },
507
+ description: "Conversation ID",
508
+ },
509
+ ],
510
+ responseBody: z.object({
511
+ pendingConfirmation: z
512
+ .object({})
513
+ .passthrough()
514
+ .describe("Pending confirmation details or null"),
515
+ pendingSecret: z
516
+ .object({})
517
+ .passthrough()
518
+ .describe("Pending secret request or null"),
519
+ }),
415
520
  handler: ({ url, authContext }) =>
416
521
  handleListPendingInteractions(url, authContext),
417
522
  },
@@ -3,6 +3,8 @@
3
3
  */
4
4
  import { existsSync, statSync } from "node:fs";
5
5
 
6
+ import { z } from "zod";
7
+
6
8
  import * as attachmentsStore from "../../memory/attachments-store.js";
7
9
  import {
8
10
  AttachmentUploadError,
@@ -261,23 +263,66 @@ export function attachmentRouteDefinitions(): RouteDefinition[] {
261
263
  {
262
264
  endpoint: "attachments",
263
265
  method: "POST",
266
+ summary: "Upload attachment",
267
+ description:
268
+ "Upload an attachment as base64 data or file path reference.",
269
+ tags: ["attachments"],
270
+ requestBody: z.object({
271
+ filename: z.string(),
272
+ mimeType: z.string(),
273
+ data: z.string().describe("Base64-encoded file data").optional(),
274
+ filePath: z
275
+ .string()
276
+ .describe("On-disk file path (file-backed upload)")
277
+ .optional(),
278
+ }),
279
+ responseBody: z.object({
280
+ id: z.string(),
281
+ original_filename: z.string(),
282
+ mime_type: z.string(),
283
+ size_bytes: z.number(),
284
+ kind: z.string(),
285
+ }),
264
286
  handler: async ({ req }) => handleUploadAttachment(req),
265
287
  },
266
288
  {
267
289
  endpoint: "attachments",
268
290
  method: "DELETE",
291
+ summary: "Delete attachment",
292
+ description: "Delete an attachment by ID.",
293
+ tags: ["attachments"],
294
+ requestBody: z.object({
295
+ attachmentId: z.string(),
296
+ }),
269
297
  handler: async ({ req }) => handleDeleteAttachment(req),
270
298
  },
271
299
  {
272
300
  endpoint: "attachments/:id/content",
273
301
  method: "GET",
274
302
  policyKey: "attachments/content",
303
+ summary: "Get attachment content",
304
+ description:
305
+ "Serve raw file bytes for an attachment. Supports Range headers.",
306
+ tags: ["attachments"],
275
307
  handler: ({ req, params }) => handleGetAttachmentContent(params.id, req),
276
308
  },
277
309
  {
278
310
  endpoint: "attachments/:id",
279
311
  method: "GET",
280
312
  policyKey: "attachments",
313
+ summary: "Get attachment metadata",
314
+ description:
315
+ "Return metadata and optional base64 data for an attachment.",
316
+ tags: ["attachments"],
317
+ responseBody: z.object({
318
+ id: z.string(),
319
+ filename: z.string(),
320
+ mimeType: z.string(),
321
+ sizeBytes: z.number(),
322
+ kind: z.string(),
323
+ data: z.string().describe("Base64-encoded content"),
324
+ fileBacked: z.boolean(),
325
+ }),
281
326
  handler: ({ params }) => handleGetAttachment(params.id),
282
327
  },
283
328
  ];
@@ -1,5 +1,7 @@
1
1
  import { join } from "node:path";
2
2
 
3
+ import { z } from "zod";
4
+
3
5
  import { getCharacterComponents } from "../../avatar/character-components.js";
4
6
  import {
5
7
  type CharacterTraits,
@@ -39,11 +41,25 @@ export function avatarRouteDefinitions(): RouteDefinition[] {
39
41
  {
40
42
  endpoint: "avatar/character-components",
41
43
  method: "GET",
44
+ summary: "Get character components",
45
+ description: "Return available avatar character components.",
46
+ tags: ["avatar"],
42
47
  handler: () => Response.json(getCharacterComponents()),
43
48
  },
44
49
  {
45
50
  endpoint: "avatar/render-from-traits",
46
51
  method: "POST",
52
+ summary: "Render avatar from traits",
53
+ description: "Write character traits and render an avatar PNG.",
54
+ tags: ["avatar"],
55
+ requestBody: z.object({
56
+ bodyShape: z.string(),
57
+ eyeStyle: z.string(),
58
+ color: z.string(),
59
+ }),
60
+ responseBody: z.object({
61
+ ok: z.boolean(),
62
+ }),
47
63
  handler: async ({ req }) => {
48
64
  let body: CharacterTraits;
49
65
  try {
@@ -9,6 +9,7 @@ import { readFileSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
 
11
11
  import { count } from "drizzle-orm";
12
+ import { z } from "zod";
12
13
 
13
14
  import { getDb } from "../../memory/db.js";
14
15
  import { memoryItems } from "../../memory/schema.js";
@@ -136,11 +137,28 @@ export function brainGraphRouteDefinitions(deps: {
136
137
  {
137
138
  endpoint: "brain-graph",
138
139
  method: "GET",
140
+ summary: "Get brain graph data",
141
+ description:
142
+ "Return a knowledge-graph shaped for brain-lobe visualization, with memory items mapped to brain regions.",
143
+ tags: ["brain-graph"],
144
+ responseBody: z.object({
145
+ entities: z.array(z.unknown()).describe("Graph entity nodes"),
146
+ relations: z.array(z.unknown()).describe("Graph relation edges"),
147
+ memorySummary: z
148
+ .array(z.unknown())
149
+ .describe("Memory kind counts and colors"),
150
+ totalKnowledgeCount: z.number().int(),
151
+ generatedAt: z.string().describe("ISO 8601 timestamp"),
152
+ }),
139
153
  handler: () => handleGetBrainGraph(),
140
154
  },
141
155
  {
142
156
  endpoint: "brain-graph-ui",
143
157
  method: "GET",
158
+ summary: "Serve brain graph UI",
159
+ description:
160
+ "Return the brain-graph HTML visualization page with an embedded auth token.",
161
+ tags: ["brain-graph"],
144
162
  handler: () => handleServeBrainGraphUI(deps.mintUiPageToken()),
145
163
  },
146
164
  ];
@@ -14,7 +14,13 @@
14
14
 
15
15
  import { existsSync, readFileSync } from "node:fs";
16
16
 
17
+ import { z } from "zod";
18
+
17
19
  import { getConversationByKey } from "../../memory/conversation-key-store.js";
20
+ import {
21
+ resolveChannelPersona,
22
+ resolveGuardianPersona,
23
+ } from "../../prompts/persona-resolver.js";
18
24
  import { getLogger } from "../../util/logger.js";
19
25
  import { getWorkspacePromptPath } from "../../util/platform.js";
20
26
  import type { AuthContext } from "../auth/types.js";
@@ -144,10 +150,14 @@ async function handleBtw(
144
150
  (async () => {
145
151
  try {
146
152
  const isIntroRequest = conversationKey === IDENTITY_INTRO_KEY;
153
+ const userPersona = resolveGuardianPersona();
154
+ const channelPersona = resolveChannelPersona(undefined);
147
155
  const result = await runBtwSidechain({
148
156
  content: trimmedContent,
149
157
  conversation,
150
158
  signal: req.signal,
159
+ userPersona,
160
+ channelPersona,
151
161
  onEvent: (event) => {
152
162
  if (event.type === "text_delta") {
153
163
  controller.enqueue(
@@ -222,6 +232,16 @@ export function btwRouteDefinitions(deps: {
222
232
  endpoint: "btw",
223
233
  method: "POST",
224
234
  policyKey: "btw",
235
+ summary: "Run ephemeral LLM side-chain",
236
+ description:
237
+ "Stream an ephemeral LLM call reusing the conversation's provider and message history. Response is SSE (btw_text_delta, btw_complete, btw_error).",
238
+ tags: ["btw"],
239
+ requestBody: z.object({
240
+ conversationKey: z
241
+ .string()
242
+ .describe("Conversation key to scope the call"),
243
+ content: z.string().describe("User prompt content"),
244
+ }),
225
245
  handler: async ({ req, authContext }) =>
226
246
  handleBtw(req, deps, authContext),
227
247
  },