@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
@@ -1,3 +1,5 @@
1
+ import { z } from "zod";
2
+
1
3
  import {
2
4
  setPlatformAssistantId,
3
5
  setPlatformBaseUrl,
@@ -532,7 +534,7 @@ export async function handleListSecrets(): Promise<Response> {
532
534
  return { type: "api_key" as const, name: account };
533
535
  });
534
536
 
535
- return Response.json({ secrets });
537
+ return Response.json({ secrets, accounts: secrets });
536
538
  } catch (err) {
537
539
  const message = err instanceof Error ? err.message : String(err);
538
540
  return httpError("INTERNAL_ERROR", message, 500);
@@ -556,21 +558,81 @@ export function secretRouteDefinitions(
556
558
  endpoint: "secrets",
557
559
  method: "POST",
558
560
  handler: async ({ req }) => handleAddSecret(req, deps?.getCesClient),
561
+ summary: "Add a secret",
562
+ description:
563
+ "Store a new secret (API key, OAuth token, etc.) in the credential vault.",
564
+ tags: ["secrets"],
565
+ requestBody: z.object({
566
+ type: z.string().describe("Secret type: 'api_key' or 'credential'"),
567
+ name: z.string().describe("Unique name for the secret"),
568
+ value: z.string().describe("Secret value to store"),
569
+ }),
570
+ responseBody: z.object({
571
+ success: z.boolean(),
572
+ type: z.string(),
573
+ name: z.string(),
574
+ }),
559
575
  },
560
576
  {
561
577
  endpoint: "secrets",
562
578
  method: "DELETE",
563
579
  handler: async ({ req }) => handleDeleteSecret(req),
580
+ summary: "Delete a secret",
581
+ description: "Remove a secret from the credential vault by name.",
582
+ tags: ["secrets"],
583
+ requestBody: z.object({
584
+ type: z.string().describe("Secret type: 'api_key' or 'credential'"),
585
+ name: z.string().describe("Name of the secret to delete"),
586
+ }),
587
+ responseBody: z.object({
588
+ success: z.boolean(),
589
+ type: z.string(),
590
+ name: z.string(),
591
+ }),
564
592
  },
565
593
  {
566
594
  endpoint: "secrets",
567
595
  method: "GET",
568
596
  handler: async () => handleListSecrets(),
597
+ summary: "List secrets",
598
+ description: "Return the names (not values) of all stored secrets.",
599
+ tags: ["secrets"],
600
+ responseBody: z.object({
601
+ secrets: z
602
+ .array(z.unknown())
603
+ .describe("List of secret metadata entries, each with type and name"),
604
+ accounts: z
605
+ .array(z.unknown())
606
+ .describe("Alias for secrets (same data)"),
607
+ }),
569
608
  },
570
609
  {
571
610
  endpoint: "secrets/read",
572
611
  method: "POST",
573
612
  handler: async ({ req }) => handleReadSecret(req),
613
+ summary: "Read a secret value",
614
+ description: "Retrieve the decrypted value of a stored secret by name.",
615
+ tags: ["secrets"],
616
+ requestBody: z.object({
617
+ type: z.string().describe("Secret type: 'api_key' or 'credential'"),
618
+ name: z.string().describe("Name of the secret to read"),
619
+ reveal: z
620
+ .boolean()
621
+ .describe(
622
+ "If true, return the decrypted value; otherwise return a masked version",
623
+ )
624
+ .optional(),
625
+ }),
626
+ responseBody: z.object({
627
+ found: z.boolean(),
628
+ value: z
629
+ .string()
630
+ .describe("Decrypted value (only when reveal=true and found)"),
631
+ masked: z
632
+ .string()
633
+ .describe("Masked value (when reveal=false and found)"),
634
+ unreachable: z.boolean(),
635
+ }),
574
636
  },
575
637
  ];
576
638
  }
@@ -10,6 +10,8 @@
10
10
  import { readFileSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
 
13
+ import { z } from "zod";
14
+
13
15
  import {
14
16
  getPlatformBaseUrl,
15
17
  setIngressPublicBaseUrl,
@@ -213,7 +215,7 @@ async function handleOAuthConnectStart(body: {
213
215
  if (requiresSecret && !clientSecret) {
214
216
  return httpError(
215
217
  "BAD_REQUEST",
216
- `client_secret is required for "${body.service}" but not found in the keychain. Store it first via the credential vault.`,
218
+ `client_secret is required for "${body.service}" but not found in the credential store. Store it first via the credential vault.`,
217
219
  400,
218
220
  );
219
221
  }
@@ -612,6 +614,12 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
612
614
  endpoint: "settings/voice",
613
615
  method: "PUT",
614
616
  policyKey: "settings/voice",
617
+ summary: "Update voice activation key",
618
+ description: "Validate and normalize a voice activation key.",
619
+ tags: ["settings"],
620
+ requestBody: z.object({
621
+ activationKey: z.string(),
622
+ }),
615
623
  handler: async ({ req }) => {
616
624
  const body = (await req.json()) as { activationKey?: string };
617
625
  if (!body.activationKey) {
@@ -626,6 +634,12 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
626
634
  endpoint: "settings/avatar/generate",
627
635
  method: "POST",
628
636
  policyKey: "settings/avatar/generate",
637
+ summary: "Generate avatar",
638
+ description: "Generate an AI avatar image from a text description.",
639
+ tags: ["settings"],
640
+ requestBody: z.object({
641
+ description: z.string(),
642
+ }),
629
643
  handler: async ({ req }) => {
630
644
  const body = (await req.json()) as { description?: string };
631
645
  return handleGenerateAvatar(body.description ?? "");
@@ -637,6 +651,13 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
637
651
  endpoint: "settings/client",
638
652
  method: "PUT",
639
653
  policyKey: "settings/client",
654
+ summary: "Update client setting",
655
+ description: "Set a single client-side setting key/value pair.",
656
+ tags: ["settings"],
657
+ requestBody: z.object({
658
+ key: z.string(),
659
+ value: z.string(),
660
+ }),
640
661
  handler: async ({ req }) => {
641
662
  const body = (await req.json()) as { key?: string; value?: string };
642
663
  if (!body.key || body.value === undefined) {
@@ -651,6 +672,14 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
651
672
  endpoint: "oauth/start",
652
673
  method: "POST",
653
674
  policyKey: "oauth/start",
675
+ summary: "Start OAuth flow",
676
+ description:
677
+ "Initiate an OAuth authorization flow for a third-party service.",
678
+ tags: ["oauth"],
679
+ requestBody: z.object({
680
+ service: z.string(),
681
+ requestedScopes: z.array(z.unknown()),
682
+ }),
654
683
  handler: async ({ req }) => {
655
684
  const body = (await req.json()) as {
656
685
  service?: string;
@@ -665,6 +694,13 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
665
694
  endpoint: "integrations/oauth/start",
666
695
  method: "POST",
667
696
  policyKey: "integrations/oauth/start",
697
+ summary: "Start OAuth flow (legacy)",
698
+ description: "Legacy alias for oauth/start.",
699
+ tags: ["oauth"],
700
+ requestBody: z.object({
701
+ service: z.string(),
702
+ requestedScopes: z.array(z.unknown()),
703
+ }),
668
704
  handler: async ({ req }) => {
669
705
  const body = (await req.json()) as {
670
706
  service?: string;
@@ -679,12 +715,18 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
679
715
  endpoint: "workspace-files",
680
716
  method: "GET",
681
717
  policyKey: "workspace-files",
718
+ summary: "List workspace files",
719
+ description: "Return an array of files in the workspace directory.",
720
+ tags: ["workspace"],
682
721
  handler: () => handleWorkspaceFilesList(),
683
722
  },
684
723
  {
685
724
  endpoint: "workspace-files/read",
686
725
  method: "GET",
687
726
  policyKey: "workspace-files/read",
727
+ summary: "Read a workspace file",
728
+ description: "Return the contents of a single file by path.",
729
+ tags: ["workspace"],
688
730
  handler: ({ url }) => {
689
731
  const filePath = url.searchParams.get("path") ?? "";
690
732
  if (!filePath) {
@@ -703,6 +745,10 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
703
745
  endpoint: "tools",
704
746
  method: "GET",
705
747
  policyKey: "tools",
748
+ summary: "List tools",
749
+ description:
750
+ "Return available tool names with their descriptions, risk levels, and categories.",
751
+ tags: ["tools"],
706
752
  handler: () => handleToolNamesList(),
707
753
  },
708
754
 
@@ -711,6 +757,17 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
711
757
  endpoint: "tools/simulate-permission",
712
758
  method: "POST",
713
759
  policyKey: "tools/simulate-permission",
760
+ summary: "Simulate tool permission check",
761
+ description:
762
+ "Dry-run a permission check for a tool invocation without executing it.",
763
+ tags: ["tools"],
764
+ requestBody: z.object({
765
+ toolName: z.string(),
766
+ input: z.object({}).passthrough(),
767
+ workingDir: z.string(),
768
+ forcePromptSideEffects: z.boolean(),
769
+ isInteractive: z.boolean(),
770
+ }),
714
771
  handler: async ({ req }) => {
715
772
  const body = (await req.json()) as {
716
773
  toolName?: string;
@@ -728,6 +785,10 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
728
785
  endpoint: "diagnostics/env-vars",
729
786
  method: "GET",
730
787
  policyKey: "diagnostics/env-vars",
788
+ summary: "List safe environment variables",
789
+ description:
790
+ "Return environment variable names and values that are safe to expose (no secrets).",
791
+ tags: ["diagnostics"],
731
792
  handler: () => handleEnvVars(),
732
793
  },
733
794
 
@@ -736,6 +797,13 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
736
797
  endpoint: "config/platform",
737
798
  method: "GET",
738
799
  policyKey: "config/platform:GET",
800
+ summary: "Get platform config",
801
+ description: "Return the platform base URL configuration.",
802
+ tags: ["config"],
803
+ responseBody: z.object({
804
+ baseUrl: z.string(),
805
+ success: z.boolean(),
806
+ }),
739
807
  handler: () => {
740
808
  const raw = loadRawConfig();
741
809
  const platform = (raw?.platform ?? {}) as Record<string, unknown>;
@@ -748,6 +816,12 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
748
816
  endpoint: "config/platform",
749
817
  method: "PUT",
750
818
  policyKey: "config/platform",
819
+ summary: "Update platform config",
820
+ description: "Set the platform base URL.",
821
+ tags: ["config"],
822
+ requestBody: z.object({
823
+ baseUrl: z.string(),
824
+ }),
751
825
  handler: async ({ req }) => {
752
826
  try {
753
827
  const body = (await req.json()) as { baseUrl?: string };
@@ -773,12 +847,28 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
773
847
  endpoint: "integrations/ingress/config",
774
848
  method: "GET",
775
849
  policyKey: "integrations/ingress/config:GET",
850
+ summary: "Get ingress config",
851
+ description: "Return the current ingress tunnel configuration.",
852
+ tags: ["config"],
776
853
  handler: () => Response.json(getIngressConfigResult()),
777
854
  },
778
855
  {
779
856
  endpoint: "integrations/ingress/config",
780
857
  method: "PUT",
781
858
  policyKey: "integrations/ingress/config",
859
+ summary: "Update ingress config",
860
+ description: "Set the ingress public base URL and enabled state.",
861
+ tags: ["config"],
862
+ requestBody: z.object({
863
+ publicBaseUrl: z.string(),
864
+ enabled: z.boolean(),
865
+ }),
866
+ responseBody: z.object({
867
+ enabled: z.boolean(),
868
+ publicBaseUrl: z.string(),
869
+ localGatewayTarget: z.string(),
870
+ success: z.boolean(),
871
+ }),
782
872
  handler: async ({ req }) => {
783
873
  try {
784
874
  const body = (await req.json()) as {
@@ -5,6 +5,8 @@
5
5
  * using the standalone functions extracted in `../../daemon/handlers/skills.ts`.
6
6
  */
7
7
 
8
+ import { z } from "zod";
9
+
8
10
  import type {
9
11
  CreateSkillParams,
10
12
  SkillOperationContext,
@@ -40,22 +42,29 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
40
42
  const ctx = () => deps.getSkillContext();
41
43
 
42
44
  return [
43
- // GET /v1/skills — list all skills
44
45
  {
45
46
  endpoint: "skills",
46
47
  method: "GET",
47
48
  policyKey: "skills",
49
+ summary: "List all skills",
50
+ description: "Return all installed skills.",
51
+ tags: ["skills"],
52
+ responseBody: z.object({
53
+ skills: z.array(z.unknown()).describe("Skill objects"),
54
+ }),
48
55
  handler: () => {
49
56
  const skills = listSkills(ctx());
50
57
  return Response.json({ skills });
51
58
  },
52
59
  },
53
60
 
54
- // GET /v1/skills/:id/files — skill metadata + directory contents
55
61
  {
56
62
  endpoint: "skills/:id/files",
57
63
  method: "GET",
58
64
  policyKey: "skills",
65
+ summary: "Get skill files",
66
+ description: "Return skill metadata and directory contents.",
67
+ tags: ["skills"],
59
68
  handler: ({ params }) => {
60
69
  const result = getSkillFiles(params.id, ctx());
61
70
  if ("error" in result) {
@@ -68,11 +77,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
68
77
  },
69
78
  },
70
79
 
71
- // POST /v1/skills/:id/enable — enable skill
72
80
  {
73
81
  endpoint: "skills/:id/enable",
74
82
  method: "POST",
75
83
  policyKey: "skills",
84
+ summary: "Enable skill",
85
+ description: "Enable an installed skill.",
86
+ tags: ["skills"],
87
+ responseBody: z.object({
88
+ ok: z.boolean(),
89
+ }),
76
90
  handler: ({ params }) => {
77
91
  const result = enableSkill(params.id, ctx());
78
92
  if (!result.success) {
@@ -82,11 +96,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
82
96
  },
83
97
  },
84
98
 
85
- // POST /v1/skills/:id/disable — disable skill
86
99
  {
87
100
  endpoint: "skills/:id/disable",
88
101
  method: "POST",
89
102
  policyKey: "skills",
103
+ summary: "Disable skill",
104
+ description: "Disable an installed skill.",
105
+ tags: ["skills"],
106
+ responseBody: z.object({
107
+ ok: z.boolean(),
108
+ }),
90
109
  handler: ({ params }) => {
91
110
  const result = disableSkill(params.id, ctx());
92
111
  if (!result.success) {
@@ -96,11 +115,21 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
96
115
  },
97
116
  },
98
117
 
99
- // PATCH /v1/skills/:id/config — configure skill
100
118
  {
101
119
  endpoint: "skills/:id/config",
102
120
  method: "PATCH",
103
121
  policyKey: "skills",
122
+ summary: "Configure skill",
123
+ description: "Update skill configuration (env, apiKey, config).",
124
+ tags: ["skills"],
125
+ requestBody: z.object({
126
+ env: z.object({}).passthrough().describe("Environment variables"),
127
+ apiKey: z.string(),
128
+ config: z.object({}).passthrough().describe("Arbitrary config"),
129
+ }),
130
+ responseBody: z.object({
131
+ ok: z.boolean(),
132
+ }),
104
133
  handler: async ({ req, params }) => {
105
134
  const body = (await req.json()) as {
106
135
  env?: Record<string, string>;
@@ -119,11 +148,22 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
119
148
  },
120
149
  },
121
150
 
122
- // POST /v1/skills/install — install skill
123
151
  {
124
152
  endpoint: "skills/install",
125
153
  method: "POST",
126
154
  policyKey: "skills",
155
+ summary: "Install skill",
156
+ description: "Install a skill by slug, URL, or spec.",
157
+ tags: ["skills"],
158
+ requestBody: z.object({
159
+ slug: z.string().describe("Skill slug"),
160
+ url: z.string().describe("Skill URL"),
161
+ spec: z.string().describe("Skill spec"),
162
+ version: z.string(),
163
+ }),
164
+ responseBody: z.object({
165
+ ok: z.boolean(),
166
+ }),
127
167
  handler: async ({ req }) => {
128
168
  const body = (await req.json()) as {
129
169
  slug?: string;
@@ -150,11 +190,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
150
190
  },
151
191
  },
152
192
 
153
- // POST /v1/skills/check-updates — check for updates
154
193
  {
155
194
  endpoint: "skills/check-updates",
156
195
  method: "POST",
157
196
  policyKey: "skills",
197
+ summary: "Check skill updates",
198
+ description: "Check for available updates to installed skills.",
199
+ tags: ["skills"],
200
+ responseBody: z.object({
201
+ data: z.object({}).passthrough().describe("Update availability info"),
202
+ }),
158
203
  handler: async () => {
159
204
  const result = await checkSkillUpdates(ctx());
160
205
  if (!result.success) {
@@ -164,11 +209,23 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
164
209
  },
165
210
  },
166
211
 
167
- // GET /v1/skills/search — search catalog
168
212
  {
169
213
  endpoint: "skills/search",
170
214
  method: "GET",
171
215
  policyKey: "skills",
216
+ summary: "Search skill catalog",
217
+ description: "Search the skill catalog by query string.",
218
+ tags: ["skills"],
219
+ queryParams: [
220
+ {
221
+ name: "q",
222
+ schema: { type: "string" },
223
+ description: "Search query (required)",
224
+ },
225
+ ],
226
+ responseBody: z.object({
227
+ data: z.object({}).passthrough().describe("Search results"),
228
+ }),
172
229
  handler: async ({ url }) => {
173
230
  const query = url.searchParams.get("q") ?? "";
174
231
  if (!query) {
@@ -182,11 +239,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
182
239
  },
183
240
  },
184
241
 
185
- // POST /v1/skills/draft — draft new skill
186
242
  {
187
243
  endpoint: "skills/draft",
188
244
  method: "POST",
189
245
  policyKey: "skills",
246
+ summary: "Draft a skill",
247
+ description: "Generate a skill draft from source text.",
248
+ tags: ["skills"],
249
+ requestBody: z.object({
250
+ sourceText: z.string().describe("Source text for drafting"),
251
+ }),
190
252
  handler: async ({ req }) => {
191
253
  const body = (await req.json()) as { sourceText?: string };
192
254
  if (!body.sourceText || typeof body.sourceText !== "string") {
@@ -204,11 +266,22 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
204
266
  },
205
267
  },
206
268
 
207
- // POST /v1/skills — create skill
208
269
  {
209
270
  endpoint: "skills",
210
271
  method: "POST",
211
272
  policyKey: "skills",
273
+ summary: "Create skill",
274
+ description: "Create a new skill.",
275
+ tags: ["skills"],
276
+ requestBody: z.object({
277
+ skillId: z.string(),
278
+ name: z.string(),
279
+ description: z.string(),
280
+ bodyMarkdown: z.string(),
281
+ }),
282
+ responseBody: z.object({
283
+ ok: z.boolean(),
284
+ }),
212
285
  handler: async ({ req }) => {
213
286
  const body = (await req.json()) as CreateSkillParams;
214
287
  if (
@@ -231,11 +304,16 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
231
304
  },
232
305
  },
233
306
 
234
- // POST /v1/skills/:id/update — update skill
235
307
  {
236
308
  endpoint: "skills/:id/update",
237
309
  method: "POST",
238
310
  policyKey: "skills",
311
+ summary: "Update skill",
312
+ description: "Update an installed skill to the latest version.",
313
+ tags: ["skills"],
314
+ responseBody: z.object({
315
+ ok: z.boolean(),
316
+ }),
239
317
  handler: async ({ params }) => {
240
318
  const result = await updateSkill(params.id, ctx());
241
319
  if (!result.success) {
@@ -245,11 +323,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
245
323
  },
246
324
  },
247
325
 
248
- // GET /v1/skills/:id/inspect — inspect skill details
249
326
  {
250
327
  endpoint: "skills/:id/inspect",
251
328
  method: "GET",
252
329
  policyKey: "skills",
330
+ summary: "Inspect skill",
331
+ description: "Return detailed skill information.",
332
+ tags: ["skills"],
253
333
  handler: async ({ params }) => {
254
334
  const result = await inspectSkill(params.id, ctx());
255
335
  if (result.error && !result.data) {
@@ -269,13 +349,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
269
349
  },
270
350
  },
271
351
 
272
- // GET /v1/skills/:id — single skill metadata
273
- // Registered after all literal-segment GET routes (search, inspect, files)
274
- // to avoid shadowing them with the parametric :id match.
275
352
  {
276
353
  endpoint: "skills/:id",
277
354
  method: "GET",
278
355
  policyKey: "skills",
356
+ summary: "Get skill",
357
+ description: "Return a single skill by ID.",
358
+ tags: ["skills"],
279
359
  handler: ({ params }) => {
280
360
  const result = getSkill(params.id, ctx());
281
361
  if ("error" in result) {
@@ -288,11 +368,13 @@ export function skillRouteDefinitions(deps: SkillRouteDeps): RouteDefinition[] {
288
368
  },
289
369
  },
290
370
 
291
- // DELETE /v1/skills/:id — uninstall skill
292
371
  {
293
372
  endpoint: "skills/:id",
294
373
  method: "DELETE",
295
374
  policyKey: "skills",
375
+ summary: "Uninstall skill",
376
+ description: "Remove an installed skill.",
377
+ tags: ["skills"],
296
378
  handler: async ({ params }) => {
297
379
  const result = await uninstallSkill(params.id, ctx());
298
380
  if (!result.success) {
@@ -5,6 +5,8 @@
5
5
  * sharing business logic with the handlers in
6
6
  * `daemon/handlers/subagents.ts`.
7
7
  */
8
+ import { z } from "zod";
9
+
8
10
  import { getMessages } from "../../memory/conversation-crud.js";
9
11
  import { getSubagentManager } from "../../subagent/index.js";
10
12
  import { getLogger } from "../../util/logger.js";
@@ -121,11 +123,25 @@ export function getSubagentDetail(
121
123
 
122
124
  export function subagentRouteDefinitions(): RouteDefinition[] {
123
125
  return [
124
- // GET /v1/subagents/:id — get subagent detail
125
126
  {
126
127
  endpoint: "subagents/:id",
127
128
  method: "GET",
128
129
  policyKey: "subagents",
130
+ summary: "Get subagent detail",
131
+ description: "Return subagent objective and event history.",
132
+ tags: ["subagents"],
133
+ queryParams: [
134
+ {
135
+ name: "conversationId",
136
+ schema: { type: "string" },
137
+ description: "Parent conversation ID (required)",
138
+ },
139
+ ],
140
+ responseBody: z.object({
141
+ subagentId: z.string(),
142
+ objective: z.string(),
143
+ events: z.array(z.unknown()).describe("Subagent event objects"),
144
+ }),
129
145
  handler: ({ url, params }) => {
130
146
  const conversationId = url.searchParams.get("conversationId");
131
147
  if (!conversationId) {
@@ -152,11 +168,20 @@ export function subagentRouteDefinitions(): RouteDefinition[] {
152
168
  },
153
169
  },
154
170
 
155
- // POST /v1/subagents/:id/abort — abort subagent
156
171
  {
157
172
  endpoint: "subagents/:id/abort",
158
173
  method: "POST",
159
174
  policyKey: "subagents/abort",
175
+ summary: "Abort subagent",
176
+ description: "Abort a running subagent.",
177
+ tags: ["subagents"],
178
+ requestBody: z.object({
179
+ conversationId: z.string(),
180
+ }),
181
+ responseBody: z.object({
182
+ subagentId: z.string(),
183
+ aborted: z.boolean(),
184
+ }),
160
185
  handler: async ({ req, params }) => {
161
186
  const body = (await req.json()) as { conversationId?: string };
162
187
  const conversationId = body.conversationId;
@@ -187,11 +212,21 @@ export function subagentRouteDefinitions(): RouteDefinition[] {
187
212
  },
188
213
  },
189
214
 
190
- // POST /v1/subagents/:id/message — send message to subagent
191
215
  {
192
216
  endpoint: "subagents/:id/message",
193
217
  method: "POST",
194
218
  policyKey: "subagents/message",
219
+ summary: "Send message to subagent",
220
+ description: "Send a text message to a running subagent.",
221
+ tags: ["subagents"],
222
+ requestBody: z.object({
223
+ conversationId: z.string(),
224
+ content: z.string(),
225
+ }),
226
+ responseBody: z.object({
227
+ subagentId: z.string(),
228
+ sent: z.boolean(),
229
+ }),
195
230
  handler: async ({ req, params }) => {
196
231
  const body = (await req.json()) as {
197
232
  conversationId?: string;