@vellumai/assistant 0.4.56 → 0.5.0

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 (457) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +3 -0
  3. package/README.md +11 -11
  4. package/docs/architecture/integrations.md +2 -2
  5. package/docs/architecture/memory.md +3 -4
  6. package/docs/credential-execution-service.md +13 -20
  7. package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +7 -7
  10. package/src/__tests__/anthropic-provider.test.ts +172 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
  12. package/src/__tests__/approval-cascade.test.ts +2 -2
  13. package/src/__tests__/approval-routes-http.test.ts +3 -4
  14. package/src/__tests__/asset-materialize-tool.test.ts +5 -5
  15. package/src/__tests__/asset-search-tool.test.ts +1 -1
  16. package/src/__tests__/assistant-attachments.test.ts +5 -5
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
  19. package/src/__tests__/attachments-store.test.ts +2 -2
  20. package/src/__tests__/avatar-e2e.test.ts +5 -3
  21. package/src/__tests__/browser-skill-endstate.test.ts +0 -1
  22. package/src/__tests__/call-routes-http.test.ts +2 -2
  23. package/src/__tests__/callback-handoff-copy.test.ts +1 -1
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
  25. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  26. package/src/__tests__/channel-readiness-service.test.ts +0 -1
  27. package/src/__tests__/checker.test.ts +31 -32
  28. package/src/__tests__/chrome-cdp.test.ts +47 -18
  29. package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
  30. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  31. package/src/__tests__/config-schema.test.ts +9 -18
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
  34. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
  35. package/src/__tests__/conversation-agent-loop.test.ts +11 -4
  36. package/src/__tests__/conversation-attachments.test.ts +1 -1
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
  38. package/src/__tests__/conversation-error.test.ts +33 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
  40. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  41. package/src/__tests__/conversation-pairing.test.ts +1 -1
  42. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
  44. package/src/__tests__/conversation-queue.test.ts +23 -14
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +204 -185
  47. package/src/__tests__/conversation-seed-composer.test.ts +1 -1
  48. package/src/__tests__/conversation-slash-queue.test.ts +4 -4
  49. package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
  50. package/src/__tests__/conversation-starter-routes.test.ts +291 -0
  51. package/src/__tests__/conversation-wipe.test.ts +438 -0
  52. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
  53. package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
  54. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
  55. package/src/__tests__/credential-security-e2e.test.ts +20 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault-unit.test.ts +227 -0
  58. package/src/__tests__/credentials-cli.test.ts +3 -0
  59. package/src/__tests__/date-context.test.ts +59 -377
  60. package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
  61. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
  62. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  63. package/src/__tests__/encrypted-store.test.ts +249 -15
  64. package/src/__tests__/ephemeral-permissions.test.ts +4 -5
  65. package/src/__tests__/event-bus.test.ts +3 -3
  66. package/src/__tests__/file-read-tool.test.ts +40 -0
  67. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  68. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  69. package/src/__tests__/gemini-image-service.test.ts +4 -4
  70. package/src/__tests__/gemini-provider.test.ts +6 -9
  71. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  72. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  73. package/src/__tests__/host-file-read-tool.test.ts +87 -0
  74. package/src/__tests__/host-shell-tool.test.ts +6 -6
  75. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  76. package/src/__tests__/identity-intro-cache.test.ts +209 -0
  77. package/src/__tests__/intent-routing.test.ts +51 -99
  78. package/src/__tests__/invite-routes-http.test.ts +5 -0
  79. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  80. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  81. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  82. package/src/__tests__/media-generate-image.test.ts +32 -15
  83. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  84. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  86. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  87. package/src/__tests__/memory-regressions.test.ts +86 -90
  88. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  89. package/src/__tests__/migration-export-http.test.ts +26 -27
  90. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  91. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  92. package/src/__tests__/migration-validate-http.test.ts +16 -16
  93. package/src/__tests__/model-intents.test.ts +2 -2
  94. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  95. package/src/__tests__/non-member-access-request.test.ts +3 -3
  96. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  97. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  98. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  99. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  100. package/src/__tests__/notification-deep-link.test.ts +1 -1
  101. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  102. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  103. package/src/__tests__/oauth-store.test.ts +1 -3
  104. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  105. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  106. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  107. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  108. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  109. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  110. package/src/__tests__/qdrant-manager.test.ts +7 -7
  111. package/src/__tests__/ratelimit.test.ts +0 -74
  112. package/src/__tests__/recording-handler.test.ts +0 -1
  113. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  115. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  116. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  117. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  118. package/src/__tests__/schema-transforms.test.ts +114 -54
  119. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  120. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  121. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  122. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  123. package/src/__tests__/send-notification-tool.test.ts +2 -2
  124. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  125. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  126. package/src/__tests__/skill-memory.test.ts +549 -0
  127. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  128. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  129. package/src/__tests__/slack-channel-config.test.ts +109 -94
  130. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  131. package/src/__tests__/swarm-recursion.test.ts +2 -2
  132. package/src/__tests__/swarm-tool.test.ts +2 -2
  133. package/src/__tests__/system-prompt.test.ts +19 -66
  134. package/src/__tests__/telegram-config.test.ts +121 -0
  135. package/src/__tests__/terminal-tools.test.ts +1 -1
  136. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  138. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  139. package/src/__tests__/tool-executor.test.ts +1 -1
  140. package/src/__tests__/trace-emitter.test.ts +8 -1
  141. package/src/__tests__/trust-store.test.ts +7 -8
  142. package/src/__tests__/twilio-routes.test.ts +1 -18
  143. package/src/__tests__/user-reference.test.ts +82 -2
  144. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  145. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  146. package/src/approvals/guardian-request-resolvers.ts +3 -3
  147. package/src/avatar/ascii-renderer.ts +2 -2
  148. package/src/avatar/png-renderer.ts +2 -2
  149. package/src/avatar/resvg-lazy.ts +21 -0
  150. package/src/calls/guardian-dispatch.ts +1 -1
  151. package/src/calls/relay-access-wait.ts +2 -2
  152. package/src/calls/twilio-rest.ts +0 -248
  153. package/src/cli/AGENTS.md +5 -8
  154. package/src/cli/__tests__/notifications.test.ts +5 -5
  155. package/src/cli/commands/avatar.ts +64 -2
  156. package/src/cli/commands/conversations.ts +131 -1
  157. package/src/cli/commands/credentials.ts +2 -0
  158. package/src/cli/commands/notifications.ts +3 -3
  159. package/src/cli.ts +10 -0
  160. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  161. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  162. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  163. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  164. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  165. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  166. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  167. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  168. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  169. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  170. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  171. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  172. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  173. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  174. package/src/config/bundled-skills/document/SKILL.md +4 -4
  175. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  176. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  177. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  178. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  179. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  180. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  181. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  182. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  183. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  184. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  185. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  186. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  187. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  188. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  189. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  190. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  192. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  193. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  195. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  196. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  197. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  198. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  199. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  200. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  201. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  202. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  203. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  204. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  205. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  206. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  207. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  208. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  209. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  210. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  211. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  212. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  213. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  214. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  215. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  216. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  217. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  218. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  219. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  220. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  221. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  222. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  223. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  224. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  225. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  226. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  227. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  228. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  229. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  230. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  231. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  232. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  233. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  234. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  235. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  236. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  237. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  238. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  240. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  241. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  242. package/src/config/feature-flag-registry.json +33 -17
  243. package/src/config/schemas/sandbox.ts +1 -1
  244. package/src/config/schemas/services.ts +13 -3
  245. package/src/config/schemas/timeouts.ts +0 -10
  246. package/src/contacts/contact-store.ts +63 -0
  247. package/src/contacts/contacts-write.ts +1 -1
  248. package/src/daemon/assistant-attachments.ts +2 -2
  249. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  250. package/src/daemon/conversation-agent-loop.ts +7 -30
  251. package/src/daemon/conversation-error.ts +24 -0
  252. package/src/daemon/conversation-memory.ts +8 -7
  253. package/src/daemon/conversation-runtime-assembly.ts +141 -275
  254. package/src/daemon/conversation-slash.ts +7 -26
  255. package/src/daemon/conversation-surfaces.ts +14 -0
  256. package/src/daemon/conversation-tool-setup.ts +9 -8
  257. package/src/daemon/conversation.ts +2 -0
  258. package/src/daemon/daemon-control.ts +1 -1
  259. package/src/daemon/date-context.ts +10 -83
  260. package/src/daemon/handlers/config-channels.ts +12 -2
  261. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  262. package/src/daemon/handlers/config-telegram.ts +6 -1
  263. package/src/daemon/handlers/conversations.ts +2 -2
  264. package/src/daemon/handlers/skills.ts +4 -0
  265. package/src/daemon/lifecycle.ts +28 -4
  266. package/src/daemon/providers-setup.ts +1 -1
  267. package/src/daemon/server.ts +1 -5
  268. package/src/daemon/shutdown-handlers.ts +9 -3
  269. package/src/daemon/tool-side-effects.ts +40 -0
  270. package/src/daemon/trace-emitter.ts +26 -2
  271. package/src/events/domain-events.ts +1 -1
  272. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  273. package/src/inbound/platform-callback-registration.ts +0 -18
  274. package/src/media/app-icon-generator.ts +15 -8
  275. package/src/media/avatar-router.ts +15 -8
  276. package/src/media/gemini-image-service.ts +125 -21
  277. package/src/memory/attachments-store.ts +3 -3
  278. package/src/memory/channel-verification-sessions.ts +6 -6
  279. package/src/memory/conversation-crud.ts +196 -1
  280. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  281. package/src/memory/conversation-title-service.ts +2 -3
  282. package/src/memory/db-init.ts +25 -1
  283. package/src/memory/invite-store.ts +4 -4
  284. package/src/memory/items-extractor.ts +4 -4
  285. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  286. package/src/memory/jobs-store.ts +3 -2
  287. package/src/memory/jobs-worker.ts +7 -5
  288. package/src/memory/lifecycle-events-store.ts +63 -0
  289. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  290. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  291. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  292. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  293. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  294. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  295. package/src/memory/migrations/index.ts +6 -0
  296. package/src/memory/migrations/registry.ts +13 -0
  297. package/src/memory/retriever.test.ts +223 -96
  298. package/src/memory/retriever.ts +115 -138
  299. package/src/memory/schema/calls.ts +1 -1
  300. package/src/memory/schema/contacts.ts +1 -1
  301. package/src/memory/schema/infrastructure.ts +29 -0
  302. package/src/memory/schema/memory-core.ts +7 -17
  303. package/src/memory/schema/notifications.ts +1 -1
  304. package/src/memory/search/formatting.ts +23 -6
  305. package/src/memory/search/lexical.ts +2 -0
  306. package/src/memory/search/semantic.ts +2 -0
  307. package/src/memory/search/staleness.ts +5 -1
  308. package/src/memory/search/types.ts +4 -0
  309. package/src/memory/task-memory-cleanup.ts +96 -6
  310. package/src/memory/trace-event-store.ts +148 -0
  311. package/src/notifications/README.md +1 -1
  312. package/src/notifications/decision-engine.ts +45 -4
  313. package/src/notifications/emit-signal.ts +5 -4
  314. package/src/notifications/events-store.ts +4 -4
  315. package/src/notifications/signal.ts +1 -1
  316. package/src/oauth/manual-token-connection.ts +49 -25
  317. package/src/permissions/checker.ts +6 -5
  318. package/src/permissions/defaults.ts +4 -4
  319. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  320. package/src/prompts/cache-boundary.ts +8 -0
  321. package/src/prompts/system-prompt.ts +105 -634
  322. package/src/prompts/templates/BOOTSTRAP.md +172 -33
  323. package/src/prompts/templates/IDENTITY.md +8 -24
  324. package/src/prompts/templates/SOUL.md +20 -41
  325. package/src/prompts/templates/USER.md +3 -19
  326. package/src/prompts/user-reference.ts +14 -16
  327. package/src/providers/anthropic/client.ts +51 -19
  328. package/src/providers/gemini/client.ts +6 -9
  329. package/src/providers/managed-proxy/constants.ts +1 -7
  330. package/src/providers/managed-proxy/context.ts +0 -1
  331. package/src/providers/model-intents.ts +5 -5
  332. package/src/providers/openai/client.ts +10 -1
  333. package/src/providers/openrouter/client.ts +1 -0
  334. package/src/providers/ratelimit.ts +0 -35
  335. package/src/providers/registry.ts +3 -5
  336. package/src/providers/retry.ts +18 -1
  337. package/src/runtime/access-request-helper.ts +16 -2
  338. package/src/runtime/auth/route-policy.ts +7 -0
  339. package/src/runtime/channel-verification-service.ts +1 -1
  340. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  341. package/src/runtime/guardian-vellum-migration.ts +61 -1
  342. package/src/runtime/http-server.ts +8 -4
  343. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  344. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  345. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  346. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  347. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  348. package/src/runtime/routes/attachment-routes.ts +2 -2
  349. package/src/runtime/routes/btw-routes.ts +93 -0
  350. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  351. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  352. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-routes.ts +49 -5
  354. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  355. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  356. package/src/runtime/routes/identity-intro-cache.ts +105 -0
  357. package/src/runtime/routes/identity-routes.ts +51 -0
  358. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  359. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  360. package/src/runtime/routes/migration-routes.ts +25 -13
  361. package/src/runtime/routes/secret-routes.ts +18 -0
  362. package/src/runtime/routes/settings-routes.ts +9 -9
  363. package/src/runtime/routes/telemetry-routes.ts +53 -0
  364. package/src/runtime/routes/trace-event-routes.ts +62 -0
  365. package/src/runtime/tool-grant-request-helper.ts +1 -1
  366. package/src/runtime/verification-outbound-actions.ts +47 -31
  367. package/src/security/encrypted-store.ts +262 -78
  368. package/src/skills/catalog-install.ts +10 -0
  369. package/src/skills/managed-store.ts +2 -0
  370. package/src/skills/skill-memory.ts +222 -0
  371. package/src/subagent/manager.ts +1 -4
  372. package/src/telemetry/types.ts +10 -1
  373. package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
  374. package/src/telemetry/usage-telemetry-reporter.ts +53 -4
  375. package/src/tools/AGENTS.md +11 -11
  376. package/src/tools/acp/spawn.ts +1 -1
  377. package/src/tools/apps/executors.ts +8 -8
  378. package/src/tools/apps/registry.ts +1 -1
  379. package/src/tools/assets/materialize.ts +6 -6
  380. package/src/tools/assets/search.ts +10 -10
  381. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  382. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  383. package/src/tools/browser/auth-detector.ts +6 -6
  384. package/src/tools/browser/browser-execution.ts +13 -13
  385. package/src/tools/browser/browser-manager.ts +3 -3
  386. package/src/tools/browser/chrome-cdp.ts +5 -5
  387. package/src/tools/browser/jit-auth.ts +2 -2
  388. package/src/tools/browser/network-recorder.test.ts +2 -2
  389. package/src/tools/browser/network-recorder.ts +3 -3
  390. package/src/tools/browser/runtime-check.ts +3 -3
  391. package/src/tools/claude-code/claude-code.ts +2 -2
  392. package/src/tools/computer-use/definitions.ts +18 -18
  393. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  394. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  395. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  396. package/src/tools/credentials/broker-types.ts +5 -5
  397. package/src/tools/credentials/broker.ts +15 -15
  398. package/src/tools/credentials/metadata-store.ts +2 -2
  399. package/src/tools/credentials/resolve.ts +1 -1
  400. package/src/tools/credentials/selection.ts +1 -1
  401. package/src/tools/credentials/tool-policy.ts +1 -1
  402. package/src/tools/credentials/vault.ts +115 -25
  403. package/src/tools/execution-target.ts +2 -2
  404. package/src/tools/executor.ts +7 -7
  405. package/src/tools/filesystem/edit.ts +2 -2
  406. package/src/tools/filesystem/read.ts +15 -4
  407. package/src/tools/filesystem/write.ts +1 -1
  408. package/src/tools/host-filesystem/edit.ts +2 -1
  409. package/src/tools/host-filesystem/read.ts +18 -1
  410. package/src/tools/host-filesystem/write.ts +1 -1
  411. package/src/tools/host-terminal/host-shell.ts +9 -8
  412. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  413. package/src/tools/memory/definitions.ts +6 -5
  414. package/src/tools/memory/handlers.test.ts +1 -1
  415. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  416. package/src/tools/network/domain-normalize.ts +2 -2
  417. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  418. package/src/tools/network/web-fetch.ts +1 -1
  419. package/src/tools/network/web-search.ts +3 -3
  420. package/src/tools/permission-checker.ts +8 -8
  421. package/src/tools/registry.ts +7 -7
  422. package/src/tools/schedule/list.ts +2 -2
  423. package/src/tools/schema-transforms.ts +31 -21
  424. package/src/tools/secret-detection-handler.ts +1 -1
  425. package/src/tools/sensitive-output-placeholders.ts +1 -1
  426. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  427. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  428. package/src/tools/shared/filesystem/image-read.ts +25 -5
  429. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  430. package/src/tools/shared/shell-output.ts +1 -1
  431. package/src/tools/side-effects.ts +1 -1
  432. package/src/tools/skills/execute.ts +1 -1
  433. package/src/tools/skills/load.ts +3 -3
  434. package/src/tools/skills/sandbox-runner.ts +3 -3
  435. package/src/tools/subagent/read.ts +1 -1
  436. package/src/tools/subagent/spawn.ts +2 -2
  437. package/src/tools/swarm/delegate.ts +3 -3
  438. package/src/tools/system/request-permission.ts +5 -4
  439. package/src/tools/terminal/backends/native.ts +4 -4
  440. package/src/tools/terminal/parser.ts +6 -6
  441. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  442. package/src/tools/terminal/shell.ts +16 -16
  443. package/src/tools/tool-approval-handler.ts +21 -12
  444. package/src/tools/tool-manifest.ts +4 -4
  445. package/src/tools/types.ts +3 -3
  446. package/src/tools/ui-surface/definitions.ts +9 -37
  447. package/src/tools/watcher/list.ts +1 -1
  448. package/src/util/logger.ts +7 -2
  449. package/src/util/pricing.ts +4 -0
  450. package/src/util/retry.ts +29 -1
  451. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  452. package/src/workspace/migrations/registry.ts +2 -0
  453. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  454. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  455. package/src/cli/reference.ts +0 -38
  456. package/src/memory/job-handlers/capability-cards.ts +0 -420
  457. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -1,420 +0,0 @@
1
- /**
2
- * Job handler for generating capability cards.
3
- *
4
- * Each job generates cards for a single capability category, running in
5
- * parallel with jobs for other categories. Cards are personalized to the
6
- * user's memory items and available skills.
7
- */
8
-
9
- import { and, eq } from "drizzle-orm";
10
- import { v4 as uuid } from "uuid";
11
-
12
- import {
13
- createTimeout,
14
- extractToolUse,
15
- getConfiguredProvider,
16
- userMessage,
17
- } from "../../providers/provider-send-message.js";
18
- import { getLogger } from "../../util/logger.js";
19
- import { truncate } from "../../util/truncate.js";
20
- import { getDb } from "../db.js";
21
- import { asString } from "../job-utils.js";
22
- import type { MemoryJob } from "../jobs-store.js";
23
- import {
24
- capabilityCardCategories,
25
- memoryCheckpoints,
26
- threadStarters,
27
- } from "../schema.js";
28
- import { buildMemoryRollup, buildSkillsSummary } from "./thread-starters.js";
29
-
30
- const log = getLogger("capability-cards-gen");
31
-
32
- /** Capability categories for the feed (knowledge dropped — not actionable). */
33
- export const CAPABILITY_CARD_CATEGORIES = [
34
- "communication",
35
- "productivity",
36
- "development",
37
- "media",
38
- "automation",
39
- "web_social",
40
- "integration",
41
- ] as const;
42
-
43
- export type CapabilityCardCategory =
44
- (typeof CAPABILITY_CARD_CATEGORIES)[number];
45
-
46
- /** Human-readable descriptions for each category, used in the LLM prompt. */
47
- const CATEGORY_DESCRIPTIONS: Record<CapabilityCardCategory, string> = {
48
- communication: "Email, Slack, messaging, drafting messages, replying",
49
- productivity:
50
- "Calendar, tasks, planning, scheduling, meeting prep, time management",
51
- development:
52
- "Code, debugging, architecture, PR reviews, build systems, documentation",
53
- media: "Images, video, audio, 3D, creative assets, media editing",
54
- automation:
55
- "Workflows, scheduling, scripts, recurring tasks, integrations orchestration",
56
- web_social:
57
- "Web browsing, social media, research, competitive analysis, news",
58
- integration:
59
- "Third-party services, APIs, syncing data across tools, connecting services",
60
- };
61
-
62
- /**
63
- * Curated subset of VIcon names the LLM can choose from, organized by
64
- * category relevance. Kept small (~30) to avoid bloating the prompt.
65
- */
66
- const ICON_PALETTE: Record<string, string[]> = {
67
- communication: [
68
- "lucide-mail",
69
- "lucide-message-circle",
70
- "lucide-message-square",
71
- "lucide-phone",
72
- "lucide-send",
73
- ],
74
- productivity: [
75
- "lucide-calendar",
76
- "lucide-clock",
77
- "lucide-clipboard-list",
78
- "lucide-list-checks",
79
- "lucide-flag",
80
- ],
81
- development: [
82
- "lucide-terminal",
83
- "lucide-git-branch",
84
- "lucide-file-code",
85
- "lucide-bug",
86
- "lucide-cpu",
87
- ],
88
- media: [
89
- "lucide-image",
90
- "lucide-video",
91
- "lucide-music-2",
92
- "lucide-camera",
93
- "lucide-palette",
94
- ],
95
- automation: [
96
- "lucide-zap",
97
- "lucide-refresh-cw",
98
- "lucide-settings",
99
- "lucide-layers",
100
- "lucide-rocket",
101
- ],
102
- web_social: [
103
- "lucide-globe",
104
- "lucide-search",
105
- "lucide-trending-up",
106
- "lucide-chart-bar",
107
- "lucide-binoculars",
108
- ],
109
- integration: [
110
- "lucide-puzzle",
111
- "lucide-link",
112
- "lucide-network",
113
- "lucide-package",
114
- "lucide-share-2",
115
- ],
116
- };
117
-
118
- const CK_BATCH = "capability_cards:generation_batch";
119
-
120
- function checkpointKey(base: string, scopeId: string): string {
121
- return `${base}:${scopeId}`;
122
- }
123
-
124
- interface GeneratedCard {
125
- icon: string;
126
- title: string;
127
- description: string;
128
- prompt: string;
129
- tags: string[];
130
- }
131
-
132
- interface GenerationResult {
133
- relevance: number;
134
- cards: GeneratedCard[];
135
- }
136
-
137
- async function generateCardsForCategory(
138
- scopeId: string,
139
- category: CapabilityCardCategory,
140
- ): Promise<GenerationResult> {
141
- const provider = await getConfiguredProvider();
142
- if (!provider) {
143
- log.info("No configured provider for capability card generation");
144
- return { relevance: 0, cards: [] };
145
- }
146
-
147
- const rollup = buildMemoryRollup(scopeId);
148
- if (!rollup) {
149
- log.info("No memory items to generate capability cards from");
150
- return { relevance: 0, cards: [] };
151
- }
152
-
153
- const skills = buildSkillsSummary();
154
- const icons = ICON_PALETTE[category] ?? [];
155
- const allIcons = [
156
- ...icons,
157
- // A few general-purpose icons always available
158
- "lucide-sparkles",
159
- "lucide-wand",
160
- "lucide-lightbulb",
161
- "lucide-star",
162
- "lucide-briefcase",
163
- ];
164
-
165
- const systemPrompt = `You are generating capability cards for a personal AI assistant's new thread page. These cards showcase what the assistant can do, personalized to the user's context.
166
-
167
- You are generating cards for the "${category}" category: ${CATEGORY_DESCRIPTIONS[category]}.
168
-
169
- Given the user's memories below, do two things:
170
- 1. Assess how relevant this category is to this user (0.0–1.0). A score of 0.7+ means the user has clear context that makes this category actionable. Score lower if the user's memories have little relation to this category.
171
- 2. If relevant (0.7+), generate 2–3 capability cards that cross the user's context with what the assistant can do in this area.
172
-
173
- For each card, provide:
174
- - icon: One of these Lucide icon names: ${allIcons.join(", ")}
175
- - title: Action-oriented, verb-first, max 50 chars (e.g., "Triage your inbox", "Debug the auth middleware")
176
- - description: One line explaining the outcome, personalized to the user's context, max 120 chars
177
- - prompt: The full message that will be sent when clicked (1-2 natural sentences, as if the user typed it)
178
- - tags: 1-3 short labels for integrations/tools involved (e.g., "Gmail", "Calendar", "Linear")
179
-
180
- Rules:
181
- - Be specific to THIS user — generic suggestions are not useful.
182
- - Titles should be concise and scannable, starting with a verb.
183
- - Prompts should be natural, as if the user typed them.
184
- - Tags should reference actual tools/services relevant to the suggestion.
185
- - If relevance is below 0.7, you may return an empty cards array.
186
-
187
- ${rollup}
188
- ${skills}`;
189
-
190
- const { signal, cleanup } = createTimeout(25000);
191
- try {
192
- const response = await provider.sendMessage(
193
- [
194
- userMessage(
195
- `Generate capability cards for the "${category}" category based on my context.`,
196
- ),
197
- ],
198
- [
199
- {
200
- name: "store_capability_cards",
201
- description:
202
- "Store the relevance assessment and generated capability cards",
203
- input_schema: {
204
- type: "object" as const,
205
- properties: {
206
- relevance: {
207
- type: "number",
208
- description:
209
- "How relevant this category is to the user (0.0–1.0)",
210
- },
211
- cards: {
212
- type: "array",
213
- items: {
214
- type: "object",
215
- properties: {
216
- icon: {
217
- type: "string",
218
- description: "Lucide icon name from the provided list",
219
- },
220
- title: {
221
- type: "string",
222
- description:
223
- "Action-oriented title, verb-first, max 50 chars",
224
- },
225
- description: {
226
- type: "string",
227
- description:
228
- "One-line outcome description, max 120 chars",
229
- },
230
- prompt: {
231
- type: "string",
232
- description: "Full message sent on click (1-2 sentences)",
233
- },
234
- tags: {
235
- type: "array",
236
- items: { type: "string" },
237
- description: "1-3 integration/tool tags",
238
- },
239
- },
240
- required: ["icon", "title", "description", "prompt", "tags"],
241
- },
242
- },
243
- },
244
- required: ["relevance", "cards"],
245
- },
246
- },
247
- ],
248
- systemPrompt,
249
- {
250
- config: {
251
- modelIntent: "quality-optimized",
252
- max_tokens: 1024,
253
- tool_choice: {
254
- type: "tool" as const,
255
- name: "store_capability_cards",
256
- },
257
- },
258
- signal,
259
- },
260
- );
261
- cleanup();
262
-
263
- const toolBlock = extractToolUse(response);
264
- if (!toolBlock) {
265
- log.warn("No tool_use block in capability card generation response");
266
- return { relevance: 0, cards: [] };
267
- }
268
-
269
- const input = toolBlock.input as {
270
- relevance?: number;
271
- cards?: GeneratedCard[];
272
- };
273
-
274
- const relevance =
275
- typeof input.relevance === "number"
276
- ? Math.max(0, Math.min(1, input.relevance))
277
- : 0;
278
-
279
- if (!Array.isArray(input.cards)) {
280
- return { relevance, cards: [] };
281
- }
282
-
283
- const cards = input.cards
284
- .filter(
285
- (c) =>
286
- typeof c.title === "string" &&
287
- c.title.length > 0 &&
288
- typeof c.prompt === "string" &&
289
- c.prompt.length > 0,
290
- )
291
- .map((c) => ({
292
- icon:
293
- typeof c.icon === "string" && c.icon.length > 0
294
- ? c.icon
295
- : "lucide-sparkles",
296
- title: truncate(c.title, 50, ""),
297
- description: truncate(c.description ?? "", 120, ""),
298
- prompt: truncate(c.prompt, 500, ""),
299
- tags: Array.isArray(c.tags)
300
- ? c.tags.filter((t): t is string => typeof t === "string").slice(0, 3)
301
- : [],
302
- }));
303
-
304
- return { relevance, cards };
305
- } catch (err) {
306
- cleanup();
307
- throw err;
308
- }
309
- }
310
-
311
- // ── Job handler ───────────────────────────────────────────────────
312
-
313
- export async function generateCapabilityCardsJob(
314
- job: MemoryJob,
315
- ): Promise<void> {
316
- const scopeId = asString(job.payload.scopeId) ?? "default";
317
- const category = asString(job.payload.category) as
318
- | CapabilityCardCategory
319
- | undefined;
320
-
321
- if (
322
- !category ||
323
- !(CAPABILITY_CARD_CATEGORIES as readonly string[]).includes(category)
324
- ) {
325
- log.warn({ category }, "Invalid or missing category for capability cards");
326
- return;
327
- }
328
-
329
- const result = await generateCardsForCategory(scopeId, category);
330
-
331
- const db = getDb();
332
- const now = Date.now();
333
-
334
- // Determine next batch number
335
- const batchCheckpoint = db
336
- .select({ value: memoryCheckpoints.value })
337
- .from(memoryCheckpoints)
338
- .where(eq(memoryCheckpoints.key, checkpointKey(CK_BATCH, scopeId)))
339
- .get();
340
- const nextBatch = batchCheckpoint
341
- ? parseInt(batchCheckpoint.value, 10) + 1
342
- : 1;
343
-
344
- // Upsert category relevance
345
- db.insert(capabilityCardCategories)
346
- .values({
347
- scopeId,
348
- category,
349
- relevance: result.relevance,
350
- generationBatch: nextBatch,
351
- createdAt: now,
352
- })
353
- .onConflictDoUpdate({
354
- target: [
355
- capabilityCardCategories.scopeId,
356
- capabilityCardCategories.category,
357
- ],
358
- set: {
359
- relevance: result.relevance,
360
- generationBatch: nextBatch,
361
- createdAt: now,
362
- },
363
- })
364
- .run();
365
-
366
- // Delete old cards for this category+scope, then insert new ones
367
- db.delete(threadStarters)
368
- .where(
369
- and(
370
- eq(threadStarters.scopeId, scopeId),
371
- eq(threadStarters.category, category),
372
- eq(threadStarters.cardType, "card"),
373
- ),
374
- )
375
- .run();
376
-
377
- // Insert new cards
378
- for (const card of result.cards) {
379
- db.insert(threadStarters)
380
- .values({
381
- id: uuid(),
382
- label: card.title,
383
- prompt: card.prompt,
384
- icon: card.icon,
385
- description: card.description,
386
- tags: card.tags.join(","),
387
- category,
388
- cardType: "card",
389
- generationBatch: nextBatch,
390
- scopeId,
391
- sourceMemoryKinds: null,
392
- createdAt: now,
393
- })
394
- .run();
395
- }
396
-
397
- // Update batch checkpoint
398
- db.insert(memoryCheckpoints)
399
- .values({
400
- key: checkpointKey(CK_BATCH, scopeId),
401
- value: String(nextBatch),
402
- updatedAt: now,
403
- })
404
- .onConflictDoUpdate({
405
- target: memoryCheckpoints.key,
406
- set: { value: String(nextBatch), updatedAt: now },
407
- })
408
- .run();
409
-
410
- log.info(
411
- {
412
- scopeId,
413
- category,
414
- relevance: result.relevance,
415
- cardCount: result.cards.length,
416
- batch: nextBatch,
417
- },
418
- "Generated capability cards",
419
- );
420
- }
@@ -1,294 +0,0 @@
1
- /**
2
- * Route handlers for thread starter endpoints.
3
- *
4
- * GET /v1/thread-starters — list thread starters (chips) or capability cards
5
- */
6
-
7
- import { and, desc, eq, inArray, like } from "drizzle-orm";
8
-
9
- import { getDb } from "../../memory/db.js";
10
- import { CAPABILITY_CARD_CATEGORIES } from "../../memory/job-handlers/capability-cards.js";
11
- import { enqueueMemoryJob } from "../../memory/jobs-store.js";
12
- import { rawAll, rawGet } from "../../memory/raw-query.js";
13
- import {
14
- capabilityCardCategories,
15
- memoryJobs,
16
- threadStarters,
17
- } from "../../memory/schema.js";
18
- import type { RouteDefinition } from "../http-router.js";
19
-
20
- // ---------------------------------------------------------------------------
21
- // GET /v1/thread-starters?card_type=chip (default, backwards-compat)
22
- // ---------------------------------------------------------------------------
23
-
24
- function handleListThreadStarters(url: URL): Response {
25
- const cardType = url.searchParams.get("card_type") ?? "chip";
26
-
27
- if (cardType === "card") {
28
- return handleListCapabilityCards(url);
29
- }
30
-
31
- const limitParam = Math.min(
32
- Math.max(1, Number(url.searchParams.get("limit") ?? 4)),
33
- 20,
34
- );
35
- const offsetParam = Math.max(0, Number(url.searchParams.get("offset") ?? 0));
36
- const scopeId = url.searchParams.get("scope_id") ?? "default";
37
-
38
- const db = getDb();
39
-
40
- const items = db
41
- .select({
42
- id: threadStarters.id,
43
- label: threadStarters.label,
44
- prompt: threadStarters.prompt,
45
- category: threadStarters.category,
46
- batch: threadStarters.generationBatch,
47
- })
48
- .from(threadStarters)
49
- .where(
50
- and(
51
- eq(threadStarters.scopeId, scopeId),
52
- eq(threadStarters.cardType, "chip"),
53
- ),
54
- )
55
- .orderBy(
56
- desc(threadStarters.generationBatch),
57
- desc(threadStarters.createdAt),
58
- )
59
- .limit(limitParam)
60
- .offset(offsetParam)
61
- .all();
62
-
63
- const countRow = rawGet<{ c: number }>(
64
- `SELECT COUNT(*) AS c FROM thread_starters WHERE scope_id = ? AND card_type = 'chip'`,
65
- scopeId,
66
- );
67
- const total = countRow?.c ?? 0;
68
-
69
- // If starters exist, return them immediately.
70
- if (total > 0) {
71
- return Response.json({ starters: items, total, status: "ready" });
72
- }
73
-
74
- // No starters — check whether we have memory items to generate from.
75
- const memoryCount = rawGet<{ c: number }>(
76
- `SELECT COUNT(*) AS c FROM memory_items WHERE status = 'active' AND scope_id = ?`,
77
- scopeId,
78
- );
79
-
80
- if (!memoryCount || memoryCount.c === 0) {
81
- return Response.json({ starters: [], total: 0, status: "empty" });
82
- }
83
-
84
- // Memory items exist but no starters yet — ensure a generation job is queued.
85
- const existing = db
86
- .select({ id: memoryJobs.id })
87
- .from(memoryJobs)
88
- .where(
89
- and(
90
- eq(memoryJobs.type, "generate_thread_starters"),
91
- inArray(memoryJobs.status, ["pending", "running"]),
92
- like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
93
- ),
94
- )
95
- .get();
96
-
97
- if (!existing) {
98
- enqueueMemoryJob("generate_thread_starters", { scopeId });
99
- }
100
-
101
- return Response.json({ starters: [], total: 0, status: "generating" });
102
- }
103
-
104
- // ---------------------------------------------------------------------------
105
- // GET /v1/thread-starters?card_type=card — capability cards feed
106
- // ---------------------------------------------------------------------------
107
-
108
- function handleListCapabilityCards(url: URL): Response {
109
- const limitParam = Math.min(
110
- Math.max(1, Number(url.searchParams.get("limit") ?? 24)),
111
- 50,
112
- );
113
- const scopeId = url.searchParams.get("scope_id") ?? "default";
114
- const categoryFilter = url.searchParams.get("category");
115
-
116
- const db = getDb();
117
-
118
- // Build WHERE conditions for cards
119
- const conditions = [
120
- eq(threadStarters.scopeId, scopeId),
121
- eq(threadStarters.cardType, "card"),
122
- ];
123
- if (categoryFilter) {
124
- conditions.push(eq(threadStarters.category, categoryFilter));
125
- }
126
-
127
- const cards = db
128
- .select({
129
- id: threadStarters.id,
130
- icon: threadStarters.icon,
131
- label: threadStarters.label,
132
- description: threadStarters.description,
133
- prompt: threadStarters.prompt,
134
- category: threadStarters.category,
135
- tags: threadStarters.tags,
136
- batch: threadStarters.generationBatch,
137
- })
138
- .from(threadStarters)
139
- .where(and(...conditions))
140
- .orderBy(
141
- desc(threadStarters.generationBatch),
142
- desc(threadStarters.createdAt),
143
- )
144
- .limit(limitParam)
145
- .all();
146
-
147
- // Transform tags from comma-separated string to array
148
- const transformedCards = cards.map((c) => ({
149
- ...c,
150
- tags: c.tags ? c.tags.split(",").filter(Boolean) : [],
151
- }));
152
-
153
- // Build per-category status map
154
- const categoryStatuses = buildCategoryStatuses(scopeId);
155
-
156
- // Proper COUNT query — transformedCards.length would be LIMIT-capped
157
- const countCondition = categoryFilter ? `AND category = ?` : ``;
158
- const countParams = categoryFilter ? [scopeId, categoryFilter] : [scopeId];
159
- const countRow = rawGet<{ c: number }>(
160
- `SELECT COUNT(*) AS c FROM thread_starters WHERE scope_id = ? AND card_type = 'card' ${countCondition}`,
161
- ...countParams,
162
- );
163
- const total = countRow?.c ?? 0;
164
-
165
- // If we have cards, return them
166
- if (total > 0) {
167
- const overallStatus = Object.values(categoryStatuses).some(
168
- (s) => s.status === "generating",
169
- )
170
- ? "generating"
171
- : "ready";
172
-
173
- return Response.json({
174
- cards: transformedCards,
175
- total,
176
- status: overallStatus,
177
- categories: categoryStatuses,
178
- });
179
- }
180
-
181
- // No cards — check whether we have memory items to generate from
182
- const memoryCount = rawGet<{ c: number }>(
183
- `SELECT COUNT(*) AS c FROM memory_items WHERE status = 'active' AND scope_id = ?`,
184
- scopeId,
185
- );
186
-
187
- if (!memoryCount || memoryCount.c === 0) {
188
- return Response.json({
189
- cards: [],
190
- total: 0,
191
- status: "empty",
192
- categories: {},
193
- });
194
- }
195
-
196
- // Memory items exist but no cards — enqueue generation for all categories
197
- enqueueCapabilityCardJobs(scopeId);
198
-
199
- return Response.json({
200
- cards: [],
201
- total: 0,
202
- status: "generating",
203
- categories: Object.fromEntries(
204
- CAPABILITY_CARD_CATEGORIES.map((cat) => [
205
- cat,
206
- { status: "generating" as const },
207
- ]),
208
- ),
209
- });
210
- }
211
-
212
- /** Build a status map for each category: ready (with relevance) or generating. */
213
- function buildCategoryStatuses(
214
- scopeId: string,
215
- ): Record<string, { status: string; relevance?: number }> {
216
- const db = getDb();
217
- const statuses: Record<string, { status: string; relevance?: number }> = {};
218
-
219
- // Get completed categories with relevance scores
220
- const completed = db
221
- .select({
222
- category: capabilityCardCategories.category,
223
- relevance: capabilityCardCategories.relevance,
224
- })
225
- .from(capabilityCardCategories)
226
- .where(eq(capabilityCardCategories.scopeId, scopeId))
227
- .all();
228
-
229
- const completedSet = new Set(completed.map((c) => c.category));
230
- for (const row of completed) {
231
- statuses[row.category] = { status: "ready", relevance: row.relevance };
232
- }
233
-
234
- // Check for in-flight generation jobs
235
- const pendingJobs = rawAll<{ payload: string }>(
236
- `SELECT payload FROM memory_jobs
237
- WHERE type = 'generate_capability_cards'
238
- AND status IN ('pending', 'running')
239
- AND payload LIKE ?`,
240
- `%"scopeId":"${scopeId}"%`,
241
- );
242
-
243
- for (const job of pendingJobs) {
244
- try {
245
- const parsed = JSON.parse(job.payload) as { category?: string };
246
- if (parsed.category && !completedSet.has(parsed.category)) {
247
- statuses[parsed.category] = { status: "generating" };
248
- }
249
- } catch {
250
- // Skip malformed payloads
251
- }
252
- }
253
-
254
- return statuses;
255
- }
256
-
257
- /** Enqueue one generation job per category, skipping those already in-flight. */
258
- function enqueueCapabilityCardJobs(scopeId: string): void {
259
- const db = getDb();
260
-
261
- for (const category of CAPABILITY_CARD_CATEGORIES) {
262
- // Check if already pending/running for this scope+category
263
- const existing = db
264
- .select({ id: memoryJobs.id })
265
- .from(memoryJobs)
266
- .where(
267
- and(
268
- eq(memoryJobs.type, "generate_capability_cards"),
269
- inArray(memoryJobs.status, ["pending", "running"]),
270
- like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
271
- like(memoryJobs.payload, `%"category":"${category}"%`),
272
- ),
273
- )
274
- .get();
275
-
276
- if (!existing) {
277
- enqueueMemoryJob("generate_capability_cards", { scopeId, category });
278
- }
279
- }
280
- }
281
-
282
- // ---------------------------------------------------------------------------
283
- // Route definitions
284
- // ---------------------------------------------------------------------------
285
-
286
- export function threadStarterRouteDefinitions(): RouteDefinition[] {
287
- return [
288
- {
289
- endpoint: "thread-starters",
290
- method: "GET",
291
- handler: (ctx) => handleListThreadStarters(ctx.url),
292
- },
293
- ];
294
- }