@vellumai/assistant 0.3.5 → 0.3.7

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 (487) hide show
  1. package/README.md +51 -0
  2. package/eslint.config.mjs +31 -0
  3. package/package.json +1 -1
  4. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  5. package/scripts/ipc/generate-swift.ts +18 -2
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
  7. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  8. package/src/__tests__/browser-manager.test.ts +1 -0
  9. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  10. package/src/__tests__/call-orchestrator.test.ts +752 -271
  11. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  12. package/src/__tests__/call-recovery.test.ts +3 -0
  13. package/src/__tests__/call-routes-http.test.ts +5 -0
  14. package/src/__tests__/call-store.test.ts +3 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +1260 -85
  16. package/src/__tests__/channel-approval.test.ts +37 -0
  17. package/src/__tests__/channel-approvals.test.ts +4 -65
  18. package/src/__tests__/channel-guardian.test.ts +556 -0
  19. package/src/__tests__/channel-readiness-service.test.ts +74 -7
  20. package/src/__tests__/checker.test.ts +14 -7
  21. package/src/__tests__/clarification-resolver.test.ts +44 -24
  22. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  23. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  24. package/src/__tests__/config-schema.test.ts +12 -7
  25. package/src/__tests__/context-window-manager.test.ts +30 -2
  26. package/src/__tests__/contradiction-checker.test.ts +20 -5
  27. package/src/__tests__/credential-security-invariants.test.ts +6 -2
  28. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  29. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  30. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  31. package/src/__tests__/guardian-action-store.test.ts +123 -0
  32. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  33. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  34. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  35. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  36. package/src/__tests__/handlers-twilio-config.test.ts +126 -0
  37. package/src/__tests__/intent-routing.test.ts +2 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +228 -1
  39. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  40. package/src/__tests__/model-intents.test.ts +96 -0
  41. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  42. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  43. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  44. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  45. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  46. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  47. package/src/__tests__/qdrant-manager.test.ts +27 -20
  48. package/src/__tests__/relay-server.test.ts +779 -40
  49. package/src/__tests__/run-orchestrator-assistant-events.test.ts +2 -0
  50. package/src/__tests__/run-orchestrator.test.ts +20 -4
  51. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  52. package/src/__tests__/runtime-runs.test.ts +16 -0
  53. package/src/__tests__/schedule-store.test.ts +18 -4
  54. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  55. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  56. package/src/__tests__/session-agent-loop.test.ts +857 -0
  57. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  58. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  59. package/src/__tests__/session-profile-injection.test.ts +6 -0
  60. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  61. package/src/__tests__/session-queue.test.ts +6 -0
  62. package/src/__tests__/session-runtime-assembly.test.ts +237 -13
  63. package/src/__tests__/session-slash-known.test.ts +6 -0
  64. package/src/__tests__/session-slash-queue.test.ts +6 -0
  65. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  66. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  67. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  68. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  69. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  70. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  71. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  72. package/src/__tests__/skills.test.ts +2 -0
  73. package/src/__tests__/sms-messaging-provider.test.ts +2 -1
  74. package/src/__tests__/starter-task-flow.test.ts +2 -0
  75. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  76. package/src/__tests__/system-prompt.test.ts +2 -0
  77. package/src/__tests__/task-management-tools.test.ts +2 -2
  78. package/src/__tests__/task-runner.test.ts +14 -4
  79. package/src/__tests__/terminal-tools.test.ts +25 -19
  80. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  81. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  82. package/src/__tests__/tool-executor.test.ts +23 -24
  83. package/src/__tests__/trust-store.test.ts +3 -3
  84. package/src/__tests__/twilio-rest.test.ts +29 -0
  85. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  86. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  87. package/src/__tests__/twilio-routes.test.ts +141 -21
  88. package/src/__tests__/user-reference.test.ts +2 -0
  89. package/src/__tests__/voice-quality.test.ts +222 -0
  90. package/src/__tests__/web-search.test.ts +45 -29
  91. package/src/agent/loop.ts +1 -1
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  93. package/src/amazon/client.ts +1418 -0
  94. package/src/amazon/request-extractor.ts +135 -0
  95. package/src/amazon/session.ts +109 -0
  96. package/src/autonomy/autonomy-store.ts +5 -5
  97. package/src/browser-extension-relay/client.ts +124 -0
  98. package/src/browser-extension-relay/protocol.ts +63 -0
  99. package/src/browser-extension-relay/server.ts +177 -0
  100. package/src/bundler/app-bundler.ts +3 -3
  101. package/src/bundler/bundle-signer.ts +1 -1
  102. package/src/bundler/signature-verifier.ts +1 -1
  103. package/src/calls/call-conversation-messages.ts +33 -0
  104. package/src/calls/call-domain.ts +106 -5
  105. package/src/calls/call-orchestrator.ts +252 -54
  106. package/src/calls/call-pointer-messages.ts +53 -0
  107. package/src/calls/call-recovery.ts +3 -8
  108. package/src/calls/call-store.ts +69 -87
  109. package/src/calls/elevenlabs-config.ts +3 -2
  110. package/src/calls/guardian-action-sweep.ts +105 -0
  111. package/src/calls/guardian-dispatch.ts +203 -0
  112. package/src/calls/guardian-question-copy.ts +133 -0
  113. package/src/calls/relay-server.ts +466 -8
  114. package/src/calls/speaker-identification.ts +1 -1
  115. package/src/calls/twilio-config.ts +7 -5
  116. package/src/calls/twilio-provider.ts +6 -4
  117. package/src/calls/twilio-rest.ts +40 -15
  118. package/src/calls/twilio-routes.ts +60 -45
  119. package/src/calls/types.ts +3 -1
  120. package/src/channels/types.ts +25 -0
  121. package/src/cli/amazon.ts +815 -0
  122. package/src/cli/config-commands.ts +2 -2
  123. package/src/cli/core-commands.ts +4 -3
  124. package/src/cli/influencer.ts +244 -0
  125. package/src/cli/map.ts +89 -6
  126. package/src/cli.ts +1 -1
  127. package/src/config/agent-schema.ts +171 -0
  128. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  129. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  130. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  131. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  132. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  133. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  134. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  135. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  136. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  137. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  138. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  139. package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
  140. package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
  141. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  142. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  143. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  144. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  145. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  146. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  147. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  148. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +7 -9
  149. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  150. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
  151. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
  152. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  153. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
  154. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
  155. package/src/config/bundled-skills/messaging/SKILL.md +12 -2
  156. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  157. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  158. package/src/config/bundled-skills/phone-calls/SKILL.md +86 -21
  159. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  160. package/src/config/bundled-tool-registry.ts +310 -0
  161. package/src/config/calls-schema.ts +181 -0
  162. package/src/config/core-schema.ts +309 -0
  163. package/src/config/defaults.ts +27 -3
  164. package/src/config/env-registry.ts +169 -0
  165. package/src/config/env.ts +175 -0
  166. package/src/config/loader.ts +6 -6
  167. package/src/config/memory-schema.ts +528 -0
  168. package/src/config/sandbox-schema.ts +55 -0
  169. package/src/config/schema.ts +157 -1138
  170. package/src/config/skill-state.ts +1 -1
  171. package/src/config/skills-schema.ts +32 -0
  172. package/src/config/skills.ts +35 -24
  173. package/src/config/system-prompt.ts +107 -56
  174. package/src/config/templates/SOUL.md +1 -1
  175. package/src/config/types.ts +1 -0
  176. package/src/config/user-reference.ts +4 -9
  177. package/src/config/vellum-skills/catalog.json +0 -7
  178. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  179. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
  180. package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
  181. package/src/context/window-manager.ts +27 -7
  182. package/src/daemon/approval-generators.ts +186 -0
  183. package/src/daemon/approved-devices-store.ts +140 -0
  184. package/src/daemon/assistant-attachments.ts +1 -1
  185. package/src/daemon/classifier.ts +35 -32
  186. package/src/daemon/config-watcher.ts +1 -1
  187. package/src/daemon/daemon-control.ts +254 -0
  188. package/src/daemon/handlers/apps.ts +2 -3
  189. package/src/daemon/handlers/config-channels.ts +158 -0
  190. package/src/daemon/handlers/config-inbox.ts +540 -0
  191. package/src/daemon/handlers/config-ingress.ts +231 -0
  192. package/src/daemon/handlers/config-integrations.ts +258 -0
  193. package/src/daemon/handlers/config-model.ts +143 -0
  194. package/src/daemon/handlers/config-parental.ts +163 -0
  195. package/src/daemon/handlers/config-scheduling.ts +172 -0
  196. package/src/daemon/handlers/config-slack.ts +92 -0
  197. package/src/daemon/handlers/config-telegram.ts +301 -0
  198. package/src/daemon/handlers/config-tools.ts +177 -0
  199. package/src/daemon/handlers/config-trust.ts +104 -0
  200. package/src/daemon/handlers/config-twilio.ts +1080 -0
  201. package/src/daemon/handlers/config.ts +53 -2463
  202. package/src/daemon/handlers/diagnostics.ts +1 -1
  203. package/src/daemon/handlers/dictation.ts +4 -6
  204. package/src/daemon/handlers/documents.ts +18 -32
  205. package/src/daemon/handlers/index.ts +9 -0
  206. package/src/daemon/handlers/misc.ts +3 -5
  207. package/src/daemon/handlers/pairing.ts +98 -0
  208. package/src/daemon/handlers/sessions.ts +74 -5
  209. package/src/daemon/handlers/shared.ts +3 -1
  210. package/src/daemon/handlers/skills.ts +1 -1
  211. package/src/daemon/handlers/twitter-auth.ts +2 -0
  212. package/src/daemon/handlers/work-items.ts +2 -2
  213. package/src/daemon/handlers/workspace-files.ts +4 -3
  214. package/src/daemon/install-cli-launchers.ts +113 -0
  215. package/src/daemon/ipc-contract/apps.ts +356 -0
  216. package/src/daemon/ipc-contract/browser.ts +74 -0
  217. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  218. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  219. package/src/daemon/ipc-contract/documents.ts +74 -0
  220. package/src/daemon/ipc-contract/inbox.ts +209 -0
  221. package/src/daemon/ipc-contract/integrations.ts +284 -0
  222. package/src/daemon/ipc-contract/memory.ts +48 -0
  223. package/src/daemon/ipc-contract/messages.ts +211 -0
  224. package/src/daemon/ipc-contract/pairing.ts +45 -0
  225. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  226. package/src/daemon/ipc-contract/schedules.ts +97 -0
  227. package/src/daemon/ipc-contract/sessions.ts +321 -0
  228. package/src/daemon/ipc-contract/shared.ts +42 -0
  229. package/src/daemon/ipc-contract/skills.ts +120 -0
  230. package/src/daemon/ipc-contract/subagents.ts +58 -0
  231. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  232. package/src/daemon/ipc-contract/trust.ts +60 -0
  233. package/src/daemon/ipc-contract/work-items.ts +225 -0
  234. package/src/daemon/ipc-contract/workspace.ts +113 -0
  235. package/src/daemon/ipc-contract-inventory.json +62 -0
  236. package/src/daemon/ipc-contract-inventory.ts +55 -29
  237. package/src/daemon/ipc-contract.ts +227 -2527
  238. package/src/daemon/ipc-protocol.ts +1 -1
  239. package/src/daemon/ipc-validate.ts +7 -0
  240. package/src/daemon/lifecycle.ts +97 -379
  241. package/src/daemon/pairing-store.ts +177 -0
  242. package/src/daemon/providers-setup.ts +43 -0
  243. package/src/daemon/ride-shotgun-handler.ts +67 -2
  244. package/src/daemon/server.ts +60 -44
  245. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  246. package/src/daemon/session-agent-loop.ts +113 -275
  247. package/src/daemon/session-dynamic-profile.ts +1 -1
  248. package/src/daemon/session-history.ts +1 -1
  249. package/src/daemon/session-media-retry.ts +1 -1
  250. package/src/daemon/session-messaging.ts +37 -2
  251. package/src/daemon/session-notifiers.ts +5 -25
  252. package/src/daemon/session-process.ts +99 -59
  253. package/src/daemon/session-queue-manager.ts +98 -4
  254. package/src/daemon/session-runtime-assembly.ts +149 -15
  255. package/src/daemon/session-surfaces.ts +26 -4
  256. package/src/daemon/session-tool-setup.ts +28 -30
  257. package/src/daemon/session-workspace.ts +1 -1
  258. package/src/daemon/session.ts +24 -1
  259. package/src/daemon/shutdown-handlers.ts +122 -0
  260. package/src/daemon/trace-emitter.ts +1 -1
  261. package/src/daemon/watch-handler.ts +36 -33
  262. package/src/doordash/cart-queries.ts +787 -0
  263. package/src/doordash/client.ts +144 -127
  264. package/src/doordash/order-queries.ts +85 -0
  265. package/src/doordash/queries.ts +10 -1308
  266. package/src/doordash/search-queries.ts +203 -0
  267. package/src/doordash/session.ts +3 -2
  268. package/src/doordash/store-queries.ts +246 -0
  269. package/src/doordash/types.ts +367 -0
  270. package/src/email/providers/agentmail.ts +2 -1
  271. package/src/email/providers/index.ts +3 -2
  272. package/src/email/service.ts +3 -2
  273. package/src/errors.ts +43 -0
  274. package/src/home-base/prebuilt/seed.ts +1 -1
  275. package/src/hooks/cli.ts +6 -5
  276. package/src/hooks/config.ts +6 -8
  277. package/src/hooks/discovery.ts +6 -5
  278. package/src/hooks/manager.ts +4 -3
  279. package/src/hooks/runner.ts +2 -2
  280. package/src/hooks/templates.ts +5 -5
  281. package/src/inbound/public-ingress-urls.ts +3 -1
  282. package/src/index.ts +4 -2
  283. package/src/influencer/client.ts +1104 -0
  284. package/src/instrument.ts +4 -3
  285. package/src/logfire.ts +4 -3
  286. package/src/memory/admin.ts +25 -35
  287. package/src/memory/attachments-store.ts +4 -7
  288. package/src/memory/channel-delivery-store.ts +30 -1
  289. package/src/memory/channel-guardian-store.ts +200 -1
  290. package/src/memory/clarification-resolver.ts +37 -33
  291. package/src/memory/conflict-store.ts +67 -61
  292. package/src/memory/contradiction-checker.ts +141 -117
  293. package/src/memory/conversation-store.ts +335 -51
  294. package/src/memory/db-connection.ts +27 -4
  295. package/src/memory/db-init.ts +121 -4
  296. package/src/memory/db.ts +14 -1
  297. package/src/memory/embedding-backend.ts +27 -5
  298. package/src/memory/embedding-ollama.ts +2 -1
  299. package/src/memory/entity-extractor.ts +38 -35
  300. package/src/memory/guardian-action-store.ts +430 -0
  301. package/src/memory/inbox-escalation-projection.ts +59 -0
  302. package/src/memory/inbox-thread-store.ts +218 -0
  303. package/src/memory/ingress-invite-store.ts +338 -0
  304. package/src/memory/ingress-member-store.ts +350 -0
  305. package/src/memory/items-extractor.ts +91 -97
  306. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  307. package/src/memory/job-handlers/media-processing.ts +11 -42
  308. package/src/memory/job-handlers/summarization.ts +32 -26
  309. package/src/memory/job-utils.ts +3 -10
  310. package/src/memory/jobs-store.ts +6 -9
  311. package/src/memory/jobs-worker.ts +51 -36
  312. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  313. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  314. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  315. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  316. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  317. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  318. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  319. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  320. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  321. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  322. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  323. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  324. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  325. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  326. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  327. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  328. package/src/memory/migrations/017-memory-items-indexes.ts +12 -0
  329. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  330. package/src/memory/migrations/index.ts +24 -0
  331. package/src/memory/migrations/registry.ts +79 -0
  332. package/src/memory/migrations/validate-migration-state.ts +69 -0
  333. package/src/memory/qdrant-manager.ts +49 -8
  334. package/src/memory/query-builder.ts +1 -1
  335. package/src/memory/raw-query.ts +119 -0
  336. package/src/memory/recall-cache.ts +4 -1
  337. package/src/memory/retriever.ts +163 -47
  338. package/src/memory/schema-migration.ts +25 -984
  339. package/src/memory/schema.ts +130 -7
  340. package/src/memory/search/entity.ts +10 -19
  341. package/src/memory/search/lexical.ts +81 -52
  342. package/src/memory/search/ranking.ts +21 -22
  343. package/src/memory/search/semantic.ts +157 -19
  344. package/src/memory/shared-app-links-store.ts +4 -5
  345. package/src/memory/validation.ts +19 -0
  346. package/src/messaging/draft-store.ts +5 -6
  347. package/src/messaging/providers/sms/adapter.ts +3 -6
  348. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  349. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  350. package/src/messaging/providers/whatsapp/client.ts +67 -0
  351. package/src/messaging/style-analyzer.ts +5 -4
  352. package/src/messaging/thread-summarizer.ts +61 -69
  353. package/src/messaging/triage-engine.ts +62 -71
  354. package/src/migrations/config-merge.ts +53 -0
  355. package/src/migrations/data-layout.ts +68 -0
  356. package/src/migrations/data-merge.ts +33 -0
  357. package/src/migrations/hooks-merge.ts +90 -0
  358. package/src/migrations/index.ts +6 -0
  359. package/src/migrations/log.ts +23 -0
  360. package/src/migrations/skills-merge.ts +33 -0
  361. package/src/migrations/workspace-layout.ts +79 -0
  362. package/src/permissions/checker.ts +126 -11
  363. package/src/permissions/prompter.ts +14 -0
  364. package/src/permissions/shell-identity.ts +31 -1
  365. package/src/permissions/trust-store.ts +21 -1
  366. package/src/providers/anthropic/client.ts +4 -4
  367. package/src/providers/failover.ts +2 -2
  368. package/src/providers/model-intents.ts +70 -0
  369. package/src/providers/ollama/client.ts +2 -1
  370. package/src/providers/provider-send-message.ts +176 -0
  371. package/src/providers/registry.ts +71 -30
  372. package/src/providers/retry.ts +35 -1
  373. package/src/providers/types.ts +12 -1
  374. package/src/runtime/approval-conversation-turn.ts +97 -0
  375. package/src/runtime/approval-message-composer.ts +115 -5
  376. package/src/runtime/assistant-event-hub.ts +3 -1
  377. package/src/runtime/channel-approval-parser.ts +36 -2
  378. package/src/runtime/channel-approvals.ts +0 -21
  379. package/src/runtime/channel-guardian-service.ts +48 -7
  380. package/src/runtime/channel-readiness-service.ts +160 -34
  381. package/src/runtime/channel-readiness-types.ts +10 -4
  382. package/src/runtime/channel-retry-sweep.ts +184 -0
  383. package/src/runtime/guardian-context-resolver.ts +108 -0
  384. package/src/runtime/http-server.ts +289 -745
  385. package/src/runtime/http-types.ts +56 -3
  386. package/src/runtime/middleware/auth.ts +116 -0
  387. package/src/runtime/middleware/error-handler.ts +33 -0
  388. package/src/runtime/middleware/twilio-validation.ts +127 -0
  389. package/src/runtime/routes/app-routes.ts +1 -1
  390. package/src/runtime/routes/call-routes.ts +49 -6
  391. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  392. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  393. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  394. package/src/runtime/routes/channel-route-shared.ts +144 -0
  395. package/src/runtime/routes/channel-routes.ts +32 -1634
  396. package/src/runtime/routes/conversation-routes.ts +50 -7
  397. package/src/runtime/routes/events-routes.ts +2 -2
  398. package/src/runtime/routes/identity-routes.ts +126 -0
  399. package/src/runtime/routes/pairing-routes.ts +144 -0
  400. package/src/runtime/routes/run-routes.ts +15 -1
  401. package/src/runtime/run-orchestrator.ts +52 -34
  402. package/src/schedule/schedule-store.ts +36 -32
  403. package/src/schedule/scheduler.ts +3 -3
  404. package/src/security/encrypted-store.ts +5 -7
  405. package/src/security/oauth2.ts +45 -15
  406. package/src/security/parental-control-store.ts +183 -0
  407. package/src/security/secret-allowlist.ts +4 -3
  408. package/src/security/secret-scanner.ts +5 -5
  409. package/src/security/secure-keys.ts +1 -1
  410. package/src/security/token-manager.ts +3 -2
  411. package/src/services/vercel-deploy.ts +6 -2
  412. package/src/skills/tool-manifest.ts +3 -3
  413. package/src/skills/vellum-catalog-remote.ts +75 -16
  414. package/src/slack/slack-webhook.ts +2 -1
  415. package/src/swarm/orchestrator.ts +92 -1
  416. package/src/swarm/router-planner.ts +6 -9
  417. package/src/swarm/worker-prompts.ts +9 -12
  418. package/src/tasks/task-compiler.ts +19 -28
  419. package/src/tasks/task-runner.ts +1 -1
  420. package/src/tools/assets/search.ts +15 -14
  421. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  422. package/src/tools/browser/auto-navigate.ts +1 -0
  423. package/src/tools/browser/browser-execution.ts +13 -1
  424. package/src/tools/browser/browser-manager.ts +119 -4
  425. package/src/tools/browser/network-recorder.ts +5 -0
  426. package/src/tools/credentials/broker.ts +11 -2
  427. package/src/tools/credentials/metadata-store.ts +18 -14
  428. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  429. package/src/tools/credentials/vault.ts +49 -23
  430. package/src/tools/executor.ts +80 -18
  431. package/src/tools/host-terminal/cli-discover.ts +1 -1
  432. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  433. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  434. package/src/tools/network/script-proxy/server.ts +1 -1
  435. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  436. package/src/tools/network/web-fetch.ts +18 -2
  437. package/src/tools/network/web-search.ts +7 -3
  438. package/src/tools/reminder/reminder-store.ts +14 -15
  439. package/src/tools/schedule/create.ts +1 -0
  440. package/src/tools/schedule/list.ts +2 -1
  441. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  442. package/src/tools/skills/skill-script-runner.ts +24 -9
  443. package/src/tools/skills/skill-tool-factory.ts +1 -0
  444. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  445. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  446. package/src/tools/terminal/parser.ts +50 -0
  447. package/src/tools/watcher/delete.ts +6 -0
  448. package/src/tools/weather/service.ts +1 -1
  449. package/src/twitter/client.ts +190 -24
  450. package/src/twitter/session.ts +4 -3
  451. package/src/util/clipboard.ts +1 -1
  452. package/src/util/errors.ts +65 -8
  453. package/src/util/fs.ts +40 -0
  454. package/src/util/json.ts +10 -0
  455. package/src/util/log-redact.ts +189 -0
  456. package/src/util/logger.ts +25 -18
  457. package/src/util/object.ts +3 -0
  458. package/src/util/platform.ts +72 -365
  459. package/src/util/pricing.ts +1 -1
  460. package/src/util/promise-guard.ts +1 -1
  461. package/src/util/retry.ts +19 -0
  462. package/src/util/row-mapper.ts +79 -0
  463. package/src/util/silently.ts +21 -0
  464. package/src/watcher/engine.ts +5 -1
  465. package/src/watcher/provider-types.ts +20 -0
  466. package/src/watcher/providers/github.ts +156 -0
  467. package/src/watcher/providers/gmail.ts +1 -0
  468. package/src/watcher/providers/google-calendar.ts +1 -0
  469. package/src/watcher/providers/linear.ts +460 -0
  470. package/src/watcher/providers/slack.ts +1 -0
  471. package/src/work-items/work-item-runner.ts +1 -1
  472. package/src/workspace/git-service.ts +1 -1
  473. package/src/workspace/provider-commit-message-generator.ts +51 -22
  474. package/src/__tests__/call-bridge.test.ts +0 -517
  475. package/src/__tests__/session-process-bridge.test.ts +0 -244
  476. package/src/calls/call-bridge.ts +0 -168
  477. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +0 -137
  478. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
  479. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
  480. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
  481. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
  482. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
  483. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
  484. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
  485. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
  486. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
  487. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Reduce service — sends Map output to Claude as text-only for analysis.
3
+ *
4
+ * Two modes:
5
+ * - One-shot merge: assembles all Map results into a single document,
6
+ * sends to Claude with the provided system prompt for analysis.
7
+ * - Interactive Q&A: loads existing map output + user query, sends to
8
+ * Claude, returns the answer.
9
+ *
10
+ * Uses the existing provider infrastructure (getConfiguredProvider) so
11
+ * it works with whatever LLM provider is configured.
12
+ */
13
+
14
+ import { readFile } from 'node:fs/promises';
15
+ import { join, dirname } from 'node:path';
16
+ import { getMediaAssetById } from '../../../../memory/media-store.js';
17
+ import {
18
+ getConfiguredProvider,
19
+ createTimeout,
20
+ extractAllText,
21
+ userMessage,
22
+ } from '../../../../providers/provider-send-message.js';
23
+ import type { MapOutput } from './gemini-map.js';
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Types
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export interface ReduceOptions {
30
+ /** Natural language query about the video data. Optional for one-shot merge mode. */
31
+ query?: string;
32
+ /** Optional system prompt for Claude. */
33
+ systemPrompt?: string;
34
+ /** Model override. When omitted, the configured provider's default is used. */
35
+ model?: string;
36
+ }
37
+
38
+ export interface ReduceResult {
39
+ answer: string;
40
+ model: string;
41
+ inputTokens: number;
42
+ outputTokens: number;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Helpers
47
+ // ---------------------------------------------------------------------------
48
+
49
+ const REDUCE_TIMEOUT_MS = 120_000;
50
+
51
+ /**
52
+ * Load map-output.json for an asset from its pipeline directory.
53
+ */
54
+ async function loadMapOutput(assetId: string): Promise<MapOutput> {
55
+ const asset = getMediaAssetById(assetId);
56
+ if (!asset) {
57
+ throw new Error(`Media asset not found: ${assetId}`);
58
+ }
59
+
60
+ const pipelineDir = join(dirname(asset.filePath), 'pipeline', assetId);
61
+ const mapOutputPath = join(pipelineDir, 'map-output.json');
62
+
63
+ let raw: string;
64
+ try {
65
+ raw = await readFile(mapOutputPath, 'utf-8');
66
+ } catch {
67
+ throw new Error(
68
+ 'No map output found. Run analyze_keyframes first to generate map-output.json.',
69
+ );
70
+ }
71
+
72
+ return JSON.parse(raw) as MapOutput;
73
+ }
74
+
75
+ /**
76
+ * Format map output segments into a text document for Claude.
77
+ * Strips image data — text only.
78
+ */
79
+ function formatMapOutputAsText(mapOutput: MapOutput): string {
80
+ const lines: string[] = [];
81
+
82
+ lines.push(`Video Analysis Data (asset: ${mapOutput.assetId})`);
83
+ lines.push(`Model: ${mapOutput.model}`);
84
+ lines.push(`Segments analyzed: ${mapOutput.successCount}/${mapOutput.segmentCount}`);
85
+ if (mapOutput.failedCount > 0) {
86
+ lines.push(`Failed segments: ${mapOutput.failedCount}`);
87
+ }
88
+ if (mapOutput.skippedCount > 0) {
89
+ lines.push(`Skipped segments: ${mapOutput.skippedCount}`);
90
+ }
91
+ lines.push('');
92
+ lines.push('--- Segment Results ---');
93
+ lines.push('');
94
+
95
+ for (const segment of mapOutput.segments) {
96
+ const startMin = Math.floor(segment.startSeconds / 60);
97
+ const startSec = Math.floor(segment.startSeconds % 60);
98
+ const endMin = Math.floor(segment.endSeconds / 60);
99
+ const endSec = Math.floor(segment.endSeconds % 60);
100
+ const timeRange = `${startMin}:${String(startSec).padStart(2, '0')} - ${endMin}:${String(endSec).padStart(2, '0')}`;
101
+
102
+ lines.push(`[Segment ${segment.segmentId}] ${timeRange}`);
103
+ lines.push(JSON.stringify(segment.result, null, 2));
104
+ lines.push('');
105
+ }
106
+
107
+ return lines.join('\n');
108
+ }
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // Core: send to Claude via provider infrastructure
112
+ // ---------------------------------------------------------------------------
113
+
114
+ async function sendToClaude(
115
+ mapText: string,
116
+ query: string,
117
+ systemPrompt?: string,
118
+ model?: string,
119
+ onProgress?: (msg: string) => void,
120
+ ): Promise<ReduceResult> {
121
+ const provider = getConfiguredProvider();
122
+ if (!provider) {
123
+ throw new Error('No LLM provider available. Please configure an API key.');
124
+ }
125
+
126
+ const effectiveSystemPrompt = systemPrompt
127
+ ?? 'You are an expert video analyst. You have been given structured analysis data extracted from a video. Answer the user\'s question based on this data. Be specific, reference timestamps when relevant, and provide clear, actionable insights.';
128
+
129
+ const userContent = `Here is the video analysis data:\n\n${mapText}\n\n---\n\nUser query: ${query}`;
130
+
131
+ onProgress?.('Sending map output to Claude for analysis...\n');
132
+
133
+ const { signal, cleanup } = createTimeout(REDUCE_TIMEOUT_MS);
134
+
135
+ try {
136
+ const response = await provider.sendMessage(
137
+ [userMessage(userContent)],
138
+ [],
139
+ effectiveSystemPrompt,
140
+ {
141
+ config: model ? { model } : {},
142
+ signal,
143
+ },
144
+ );
145
+ cleanup();
146
+
147
+ const answer = extractAllText(response);
148
+
149
+ onProgress?.(`Reduce complete (${response.usage.inputTokens} input + ${response.usage.outputTokens} output tokens).\n`);
150
+
151
+ return {
152
+ answer,
153
+ model: response.model,
154
+ inputTokens: response.usage.inputTokens,
155
+ outputTokens: response.usage.outputTokens,
156
+ };
157
+ } finally {
158
+ cleanup();
159
+ }
160
+ }
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // Public API
164
+ // ---------------------------------------------------------------------------
165
+
166
+ /**
167
+ * One-shot merge mode: load map output for an asset, send all data to
168
+ * Claude with a system prompt, and return the analysis.
169
+ */
170
+ export async function reduceForAsset(
171
+ assetId: string,
172
+ options: ReduceOptions,
173
+ onProgress?: (msg: string) => void,
174
+ ): Promise<ReduceResult> {
175
+ const mapOutput = await loadMapOutput(assetId);
176
+
177
+ if (mapOutput.segments.length === 0) {
178
+ throw new Error('Map output contains no segments. Run analyze_keyframes first.');
179
+ }
180
+
181
+ const mapText = formatMapOutputAsText(mapOutput);
182
+ const effectiveQuery = options.query ?? 'Summarize the video content.';
183
+ return sendToClaude(mapText, effectiveQuery, options.systemPrompt, options.model, onProgress);
184
+ }
185
+
186
+ /**
187
+ * Interactive Q&A mode: load existing map output + user query, send to
188
+ * Claude, return the answer. Functionally identical to reduceForAsset
189
+ * but named distinctly for clarity in call sites.
190
+ */
191
+ export async function queryMapOutput(
192
+ assetId: string,
193
+ options: ReduceOptions,
194
+ onProgress?: (msg: string) => void,
195
+ ): Promise<ReduceResult> {
196
+ return reduceForAsset(assetId, options, onProgress);
197
+ }
@@ -1,42 +1,36 @@
1
+ import { join, dirname } from 'node:path';
1
2
  import { readFile } from 'node:fs/promises';
2
- import Anthropic from '@anthropic-ai/sdk';
3
- import { getConfig } from '../../../../config/loader.js';
4
3
  import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
4
+ import { getConfig } from '../../../../config/loader.js';
5
5
  import {
6
6
  getMediaAssetById,
7
- getKeyframesForAsset,
8
- getVisionOutputsForAsset,
9
- insertVisionOutputsBatch,
10
- createProcessingStage,
11
- updateProcessingStage,
12
- getProcessingStagesForAsset,
13
- type MediaKeyframe,
14
- type ProcessingStage,
15
7
  } from '../../../../memory/media-store.js';
16
-
17
- const VLM_PROMPT = `Analyze this image frame extracted from a video. Return a JSON object with the following fields:
18
-
19
- {
20
- "sceneDescription": "A concise description of the overall scene",
21
- "subjects": ["List of identifiable subjects/objects/people in the frame"],
22
- "actions": ["List of actions or activities occurring"],
23
- "context": "Environmental or situational context (setting, conditions, etc.)"
8
+ import { mapSegments, type MapOutput } from '../services/gemini-map.js';
9
+ import type { PreprocessManifest } from '../services/preprocess.js';
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Exported function for job handler use
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export interface MapSegmentsOptions {
16
+ systemPrompt: string;
17
+ outputSchema: Record<string, unknown>;
18
+ context?: Record<string, unknown>;
19
+ model?: string;
20
+ concurrency?: number;
21
+ maxRetries?: number;
24
22
  }
25
23
 
26
- Return ONLY the JSON object, no additional text.`;
27
-
28
- export async function analyzeKeyframesForAsset(
24
+ export async function mapSegmentsForAsset(
29
25
  assetId: string,
30
- analysisType?: string,
31
- batchSize?: number,
26
+ options: MapSegmentsOptions,
32
27
  onProgress?: (msg: string) => void,
33
- signal?: AbortSignal,
34
- ): Promise<void> {
35
- const type = analysisType ?? 'scene_description';
36
- const batch = batchSize ?? 10;
28
+ ): Promise<MapOutput> {
29
+ const config = getConfig();
30
+ const apiKey = config.apiKeys.gemini;
37
31
 
38
- if (batch <= 0) {
39
- throw new Error('batch_size must be greater than 0.');
32
+ if (!apiKey) {
33
+ throw new Error('No Gemini API key configured. Please set your Gemini API key to use keyframe analysis.');
40
34
  }
41
35
 
42
36
  const asset = getMediaAssetById(assetId);
@@ -44,123 +38,37 @@ export async function analyzeKeyframesForAsset(
44
38
  throw new Error(`Media asset not found: ${assetId}`);
45
39
  }
46
40
 
47
- // Get all keyframes for this asset
48
- const keyframes = getKeyframesForAsset(assetId);
49
- if (keyframes.length === 0) {
50
- throw new Error('No keyframes found for this asset. Run extract_keyframes first.');
51
- }
52
-
53
- // Resumability: find already-analyzed keyframe IDs for this analysis type
54
- const existingOutputs = getVisionOutputsForAsset(assetId, type);
55
- const analyzedKeyframeIds = new Set(existingOutputs.map((o) => o.keyframeId));
56
- const pendingKeyframes = keyframes.filter((kf) => !analyzedKeyframeIds.has(kf.id));
41
+ // Load preprocess manifest
42
+ const pipelineDir = join(dirname(asset.filePath), 'pipeline', assetId);
43
+ const manifestPath = join(pipelineDir, 'manifest.json');
57
44
 
58
- if (pendingKeyframes.length === 0) {
59
- // Nothing to do — all keyframes already analyzed
60
- return;
45
+ let manifest: PreprocessManifest;
46
+ try {
47
+ const raw = await readFile(manifestPath, 'utf-8');
48
+ manifest = JSON.parse(raw) as PreprocessManifest;
49
+ } catch {
50
+ throw new Error('No preprocess manifest found. Run extract_keyframes first.');
61
51
  }
62
52
 
63
- // Find or create the vision_analysis processing stage
64
- let stage: ProcessingStage | undefined;
65
- const existingStages = getProcessingStagesForAsset(assetId);
66
- stage = existingStages.find((s) => s.stage === 'vision_analysis');
67
- if (!stage) {
68
- stage = createProcessingStage({ assetId, stage: 'vision_analysis' });
53
+ if (manifest.segments.length === 0) {
54
+ throw new Error('No segments found in preprocess manifest. Run extract_keyframes first.');
69
55
  }
70
56
 
71
- updateProcessingStage(stage.id, { status: 'running', startedAt: Date.now() });
72
-
73
- const config = getConfig();
74
- const apiKey = config.apiKeys.anthropic ?? process.env.ANTHROPIC_API_KEY;
75
- if (!apiKey) {
76
- updateProcessingStage(stage.id, {
77
- status: 'failed',
78
- lastError: 'Anthropic API key not configured',
79
- });
80
- throw new Error('No Anthropic API key available. Configure it in settings or set ANTHROPIC_API_KEY.');
81
- }
82
-
83
- const client = new Anthropic({ apiKey });
84
- let analyzedCount = analyzedKeyframeIds.size;
85
- const totalKeyframes = keyframes.length;
86
-
87
- onProgress?.(`Analyzing ${pendingKeyframes.length} keyframes (${analyzedKeyframeIds.size} already done)...\n`);
88
-
89
- let aborted = false;
90
-
91
- try {
92
- // Process in batches
93
- for (let i = 0; i < pendingKeyframes.length; i += batch) {
94
- if (signal?.aborted) {
95
- onProgress?.('Aborted.\n');
96
- aborted = true;
97
- break;
98
- }
99
-
100
- const currentBatch = pendingKeyframes.slice(i, i + batch);
101
- const batchResults: Array<{
102
- assetId: string;
103
- keyframeId: string;
104
- analysisType: string;
105
- output: Record<string, unknown>;
106
- confidence?: number;
107
- }> = [];
108
-
109
- for (const keyframe of currentBatch) {
110
- if (signal?.aborted) {
111
- onProgress?.('Aborted.\n');
112
- aborted = true;
113
- break;
114
- }
115
-
116
- try {
117
- const result = await analyzeKeyframe(client, keyframe);
118
- batchResults.push({
119
- assetId,
120
- keyframeId: keyframe.id,
121
- analysisType: type,
122
- output: result.output,
123
- confidence: result.confidence,
124
- });
125
- analyzedCount++;
126
- } catch (err) {
127
- onProgress?.(` Warning: failed to analyze frame at ${keyframe.timestamp}s: ${(err as Error).message}\n`);
128
- }
129
- }
130
-
131
- // Batch insert results
132
- if (batchResults.length > 0) {
133
- insertVisionOutputsBatch(batchResults);
134
- }
135
-
136
- // Update progress
137
- const progress = Math.round((analyzedCount / totalKeyframes) * 100);
138
- updateProcessingStage(stage.id, { progress });
139
-
140
- onProgress?.(` Batch ${Math.floor(i / batch) + 1}: analyzed ${batchResults.length}/${currentBatch.length} frames (${progress}% total)\n`);
141
- }
142
-
143
- if (aborted) {
144
- throw new Error('Analysis aborted');
145
- }
146
-
147
- const finalProgress = Math.round((analyzedCount / totalKeyframes) * 100);
148
- const isComplete = analyzedCount >= totalKeyframes;
149
-
150
- updateProcessingStage(stage.id, {
151
- status: isComplete ? 'completed' : 'running',
152
- progress: finalProgress,
153
- ...(isComplete ? { completedAt: Date.now() } : {}),
154
- });
155
- } catch (err) {
156
- updateProcessingStage(stage.id, {
157
- status: 'failed',
158
- lastError: (err as Error).message.slice(0, 500),
159
- });
160
- throw err;
161
- }
57
+ return mapSegments(assetId, pipelineDir, manifest.segments, {
58
+ apiKey,
59
+ systemPrompt: options.systemPrompt,
60
+ outputSchema: options.outputSchema,
61
+ context: options.context,
62
+ model: options.model,
63
+ concurrency: options.concurrency,
64
+ maxRetries: options.maxRetries,
65
+ }, onProgress);
162
66
  }
163
67
 
68
+ // ---------------------------------------------------------------------------
69
+ // Tool entry point
70
+ // ---------------------------------------------------------------------------
71
+
164
72
  export async function run(
165
73
  input: Record<string, unknown>,
166
74
  context: ToolContext,
@@ -170,132 +78,59 @@ export async function run(
170
78
  return { content: 'asset_id is required.', isError: true };
171
79
  }
172
80
 
173
- const analysisType = (input.analysis_type as string) || 'scene_description';
174
- const batchSize = (input.batch_size as number) || 10;
81
+ const systemPrompt = input.system_prompt as string | undefined;
82
+ if (!systemPrompt) {
83
+ return { content: 'system_prompt is required.', isError: true };
84
+ }
175
85
 
176
- try {
177
- // Check if all keyframes are already analyzed before calling the core function
178
- const keyframes = getKeyframesForAsset(assetId);
179
- const existingOutputs = getVisionOutputsForAsset(assetId, analysisType);
180
- const analyzedKeyframeIds = new Set(existingOutputs.map((o) => o.keyframeId));
181
- const pendingKeyframes = keyframes.filter((kf) => !analyzedKeyframeIds.has(kf.id));
86
+ const outputSchema = input.output_schema as Record<string, unknown> | undefined;
87
+ if (!outputSchema) {
88
+ return { content: 'output_schema is required.', isError: true };
89
+ }
182
90
 
183
- if (keyframes.length > 0 && pendingKeyframes.length === 0) {
184
- return {
185
- content: JSON.stringify({
186
- message: 'All keyframes already analyzed',
187
- assetId,
188
- analysisType,
189
- totalKeyframes: keyframes.length,
190
- alreadyAnalyzed: existingOutputs.length,
191
- }, null, 2),
192
- isError: false,
193
- };
194
- }
91
+ const contextObj = input.context as Record<string, unknown> | undefined;
92
+ const model = input.model as string | undefined;
93
+ const concurrency = input.concurrency as number | undefined;
94
+ const maxRetries = input.max_retries as number | undefined;
195
95
 
196
- await analyzeKeyframesForAsset(assetId, analysisType, batchSize, context.onOutput, context.signal);
96
+ if (concurrency !== undefined && concurrency < 1) {
97
+ return { content: 'concurrency must be at least 1.', isError: true };
98
+ }
99
+ if (maxRetries !== undefined && maxRetries < 0) {
100
+ return { content: 'max_retries must be non-negative.', isError: true };
101
+ }
197
102
 
198
- // Gather final stats
199
- const allKeyframes = getKeyframesForAsset(assetId);
200
- const allOutputs = getVisionOutputsForAsset(assetId, analysisType);
201
- const totalKeyframes = allKeyframes.length;
202
- const analyzedCount = allOutputs.length;
203
- const finalProgress = Math.round((analyzedCount / totalKeyframes) * 100);
204
- const isComplete = analyzedCount >= totalKeyframes;
103
+ try {
104
+ const output = await mapSegmentsForAsset(
105
+ assetId,
106
+ {
107
+ systemPrompt,
108
+ outputSchema,
109
+ context: contextObj,
110
+ model,
111
+ concurrency,
112
+ maxRetries,
113
+ },
114
+ context.onOutput,
115
+ );
205
116
 
206
117
  return {
207
118
  content: JSON.stringify({
208
- message: `Vision analysis ${isComplete ? 'completed' : 'in progress'}`,
119
+ message: `Map ${output.failedCount === 0 ? 'completed' : 'completed with errors'}`,
209
120
  assetId,
210
- analysisType,
211
- totalKeyframes,
212
- analyzedCount,
213
- newlyAnalyzed: analyzedCount - analyzedKeyframeIds.size,
214
- errorCount: pendingKeyframes.length - (analyzedCount - analyzedKeyframeIds.size),
215
- progress: finalProgress,
121
+ model: output.model,
122
+ segmentCount: output.segmentCount,
123
+ successCount: output.successCount,
124
+ failedCount: output.failedCount,
125
+ skippedCount: output.skippedCount,
126
+ totalInputTokens: output.costSummary.totalInputTokens,
127
+ totalOutputTokens: output.costSummary.totalOutputTokens,
128
+ estimatedCostUSD: output.costSummary.totalEstimatedUSD,
216
129
  }, null, 2),
217
130
  isError: false,
218
131
  };
219
132
  } catch (err) {
220
133
  const msg = (err as Error).message;
221
- // Preserve original error message format
222
- if (
223
- msg === 'batch_size must be greater than 0.' ||
224
- msg.startsWith('Media asset not found:') ||
225
- msg === 'No keyframes found for this asset. Run extract_keyframes first.' ||
226
- msg === 'No Anthropic API key available. Configure it in settings or set ANTHROPIC_API_KEY.'
227
- ) {
228
- return { content: msg, isError: true };
229
- }
230
- return { content: `Vision analysis failed: ${(err as Error).message}`, isError: true };
134
+ return { content: msg, isError: true };
231
135
  }
232
136
  }
233
-
234
- async function analyzeKeyframe(
235
- client: Anthropic,
236
- keyframe: MediaKeyframe,
237
- ): Promise<{ output: Record<string, unknown>; confidence: number }> {
238
- // Read the image file and encode as base64
239
- const imageData = await readFile(keyframe.filePath);
240
- const base64 = imageData.toString('base64');
241
-
242
- // Determine media type from file extension
243
- const ext = keyframe.filePath.split('.').pop()?.toLowerCase() ?? 'jpg';
244
- const mediaTypeMap: Record<string, string> = {
245
- jpg: 'image/jpeg',
246
- jpeg: 'image/jpeg',
247
- png: 'image/png',
248
- gif: 'image/gif',
249
- webp: 'image/webp',
250
- };
251
- const mediaType = mediaTypeMap[ext] ?? 'image/jpeg';
252
-
253
- const response = await client.messages.create({
254
- model: 'claude-sonnet-4-6-20250514',
255
- max_tokens: 1024,
256
- messages: [
257
- {
258
- role: 'user',
259
- content: [
260
- {
261
- type: 'image',
262
- source: {
263
- type: 'base64',
264
- media_type: mediaType as 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp',
265
- data: base64,
266
- },
267
- },
268
- {
269
- type: 'text',
270
- text: VLM_PROMPT,
271
- },
272
- ],
273
- },
274
- ],
275
- });
276
-
277
- // Extract text from response
278
- const textBlock = response.content.find((block) => block.type === 'text');
279
- const responseText = textBlock && 'text' in textBlock ? textBlock.text : '';
280
-
281
- // Parse JSON from response
282
- let output: Record<string, unknown>;
283
- try {
284
- // Try to extract JSON from the response (handle markdown code fences)
285
- const jsonMatch = responseText.match(/```(?:json)?\s*([\s\S]*?)```/) ?? [null, responseText];
286
- output = JSON.parse(jsonMatch[1]!.trim()) as Record<string, unknown>;
287
- } catch {
288
- // If JSON parsing fails, wrap raw text as output
289
- output = {
290
- sceneDescription: responseText,
291
- subjects: [],
292
- actions: [],
293
- context: '',
294
- };
295
- }
296
-
297
- // Add timestamp context to the output
298
- output.timestamp = keyframe.timestamp;
299
-
300
- return { output, confidence: 0.8 };
301
- }