@vellumai/assistant 0.3.4 → 0.3.6

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 (506) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +88 -2
  3. package/eslint.config.mjs +31 -0
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  6. package/scripts/ipc/generate-swift.ts +31 -2
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
  8. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  9. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  10. package/src/__tests__/approval-message-composer.test.ts +253 -0
  11. package/src/__tests__/browser-manager.test.ts +1 -0
  12. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  13. package/src/__tests__/call-domain.test.ts +12 -2
  14. package/src/__tests__/call-orchestrator.test.ts +799 -249
  15. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  16. package/src/__tests__/call-recovery.test.ts +3 -0
  17. package/src/__tests__/call-routes-http.test.ts +32 -2
  18. package/src/__tests__/call-store.test.ts +3 -0
  19. package/src/__tests__/channel-approval-routes.test.ts +1277 -98
  20. package/src/__tests__/channel-approval.test.ts +37 -0
  21. package/src/__tests__/channel-approvals.test.ts +36 -50
  22. package/src/__tests__/channel-guardian.test.ts +630 -22
  23. package/src/__tests__/channel-readiness-service.test.ts +324 -0
  24. package/src/__tests__/checker.test.ts +14 -7
  25. package/src/__tests__/clarification-resolver.test.ts +44 -24
  26. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  27. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  28. package/src/__tests__/config-schema.test.ts +14 -8
  29. package/src/__tests__/context-window-manager.test.ts +30 -2
  30. package/src/__tests__/contradiction-checker.test.ts +20 -5
  31. package/src/__tests__/credential-security-invariants.test.ts +7 -2
  32. package/src/__tests__/daemon-lifecycle.test.ts +13 -12
  33. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  34. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  35. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  36. package/src/__tests__/entity-search.test.ts +615 -0
  37. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  38. package/src/__tests__/guardian-action-store.test.ts +123 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  40. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  41. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  42. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  43. package/src/__tests__/handlers-twilio-config.test.ts +533 -0
  44. package/src/__tests__/intent-routing.test.ts +2 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +291 -1
  46. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  47. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  48. package/src/__tests__/model-intents.test.ts +96 -0
  49. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  50. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  51. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  52. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  53. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  54. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  55. package/src/__tests__/qdrant-manager.test.ts +27 -20
  56. package/src/__tests__/relay-server.test.ts +779 -40
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
  58. package/src/__tests__/run-orchestrator.test.ts +42 -4
  59. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  60. package/src/__tests__/runtime-runs.test.ts +16 -0
  61. package/src/__tests__/schedule-store.test.ts +18 -4
  62. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  63. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  64. package/src/__tests__/session-agent-loop.test.ts +857 -0
  65. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  66. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  67. package/src/__tests__/session-profile-injection.test.ts +6 -0
  68. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  69. package/src/__tests__/session-queue.test.ts +6 -0
  70. package/src/__tests__/session-runtime-assembly.test.ts +321 -13
  71. package/src/__tests__/session-slash-known.test.ts +6 -0
  72. package/src/__tests__/session-slash-queue.test.ts +6 -0
  73. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  74. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  75. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  76. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  77. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  78. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  79. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  80. package/src/__tests__/skills.test.ts +2 -0
  81. package/src/__tests__/sms-messaging-provider.test.ts +126 -0
  82. package/src/__tests__/starter-task-flow.test.ts +2 -0
  83. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  84. package/src/__tests__/system-prompt.test.ts +2 -0
  85. package/src/__tests__/task-management-tools.test.ts +2 -2
  86. package/src/__tests__/task-runner.test.ts +14 -4
  87. package/src/__tests__/terminal-tools.test.ts +25 -19
  88. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  90. package/src/__tests__/tool-executor.test.ts +23 -24
  91. package/src/__tests__/trust-store.test.ts +3 -3
  92. package/src/__tests__/twilio-rest.test.ts +29 -0
  93. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  94. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  95. package/src/__tests__/twilio-routes.test.ts +167 -11
  96. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  97. package/src/__tests__/user-reference.test.ts +2 -0
  98. package/src/__tests__/voice-quality.test.ts +222 -0
  99. package/src/__tests__/web-search.test.ts +46 -30
  100. package/src/__tests__/work-item-output.test.ts +110 -0
  101. package/src/agent/loop.ts +1 -1
  102. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  103. package/src/amazon/client.ts +1418 -0
  104. package/src/amazon/request-extractor.ts +135 -0
  105. package/src/amazon/session.ts +109 -0
  106. package/src/autonomy/autonomy-store.ts +5 -5
  107. package/src/browser-extension-relay/client.ts +124 -0
  108. package/src/browser-extension-relay/protocol.ts +63 -0
  109. package/src/browser-extension-relay/server.ts +177 -0
  110. package/src/bundler/app-bundler.ts +3 -3
  111. package/src/bundler/bundle-signer.ts +1 -1
  112. package/src/bundler/signature-verifier.ts +1 -1
  113. package/src/calls/call-conversation-messages.ts +33 -0
  114. package/src/calls/call-domain.ts +114 -10
  115. package/src/calls/call-orchestrator.ts +268 -59
  116. package/src/calls/call-pointer-messages.ts +53 -0
  117. package/src/calls/call-recovery.ts +3 -8
  118. package/src/calls/call-store.ts +69 -87
  119. package/src/calls/elevenlabs-config.ts +3 -2
  120. package/src/calls/guardian-action-sweep.ts +105 -0
  121. package/src/calls/guardian-dispatch.ts +203 -0
  122. package/src/calls/guardian-question-copy.ts +133 -0
  123. package/src/calls/relay-server.ts +466 -8
  124. package/src/calls/speaker-identification.ts +1 -1
  125. package/src/calls/twilio-config.ts +22 -14
  126. package/src/calls/twilio-provider.ts +6 -4
  127. package/src/calls/twilio-rest.ts +308 -7
  128. package/src/calls/twilio-routes.ts +65 -12
  129. package/src/calls/types.ts +3 -1
  130. package/src/channels/types.ts +25 -0
  131. package/src/cli/amazon.ts +815 -0
  132. package/src/cli/config-commands.ts +2 -2
  133. package/src/cli/core-commands.ts +4 -3
  134. package/src/cli/influencer.ts +244 -0
  135. package/src/cli/map.ts +89 -6
  136. package/src/cli.ts +1 -1
  137. package/src/config/agent-schema.ts +171 -0
  138. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  139. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  140. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  141. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  142. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  143. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  144. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  145. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  146. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  147. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  148. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  149. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  150. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  151. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  152. package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
  153. package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
  154. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  155. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  156. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  157. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  158. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  159. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  160. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  161. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
  162. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  163. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
  164. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
  165. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  166. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  167. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
  168. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  169. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
  170. package/src/config/bundled-skills/messaging/SKILL.md +33 -8
  171. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  172. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  174. package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
  175. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  176. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  177. package/src/config/bundled-tool-registry.ts +310 -0
  178. package/src/config/calls-schema.ts +181 -0
  179. package/src/config/core-schema.ts +309 -0
  180. package/src/config/defaults.ts +28 -3
  181. package/src/config/env-registry.ts +162 -0
  182. package/src/config/env.ts +175 -0
  183. package/src/config/loader.ts +6 -6
  184. package/src/config/memory-schema.ts +528 -0
  185. package/src/config/sandbox-schema.ts +55 -0
  186. package/src/config/schema.ts +158 -1133
  187. package/src/config/skill-state.ts +1 -1
  188. package/src/config/skills-schema.ts +32 -0
  189. package/src/config/skills.ts +35 -24
  190. package/src/config/system-prompt.ts +131 -56
  191. package/src/config/templates/IDENTITY.md +2 -2
  192. package/src/config/templates/SOUL.md +1 -1
  193. package/src/config/types.ts +1 -0
  194. package/src/config/user-reference.ts +4 -9
  195. package/src/config/vellum-skills/catalog.json +6 -7
  196. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  197. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
  198. package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
  199. package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
  200. package/src/context/window-manager.ts +27 -7
  201. package/src/daemon/approval-generators.ts +186 -0
  202. package/src/daemon/approved-devices-store.ts +140 -0
  203. package/src/daemon/assistant-attachments.ts +1 -1
  204. package/src/daemon/classifier.ts +35 -32
  205. package/src/daemon/config-watcher.ts +1 -1
  206. package/src/daemon/daemon-control.ts +217 -0
  207. package/src/daemon/handlers/apps.ts +2 -3
  208. package/src/daemon/handlers/config-channels.ts +158 -0
  209. package/src/daemon/handlers/config-inbox.ts +540 -0
  210. package/src/daemon/handlers/config-ingress.ts +231 -0
  211. package/src/daemon/handlers/config-integrations.ts +258 -0
  212. package/src/daemon/handlers/config-model.ts +143 -0
  213. package/src/daemon/handlers/config-parental.ts +163 -0
  214. package/src/daemon/handlers/config-scheduling.ts +172 -0
  215. package/src/daemon/handlers/config-slack.ts +92 -0
  216. package/src/daemon/handlers/config-telegram.ts +301 -0
  217. package/src/daemon/handlers/config-tools.ts +177 -0
  218. package/src/daemon/handlers/config-trust.ts +104 -0
  219. package/src/daemon/handlers/config-twilio.ts +1080 -0
  220. package/src/daemon/handlers/config.ts +53 -1689
  221. package/src/daemon/handlers/diagnostics.ts +1 -1
  222. package/src/daemon/handlers/dictation.ts +180 -0
  223. package/src/daemon/handlers/documents.ts +18 -32
  224. package/src/daemon/handlers/identity.ts +14 -23
  225. package/src/daemon/handlers/index.ts +11 -0
  226. package/src/daemon/handlers/misc.ts +3 -5
  227. package/src/daemon/handlers/pairing.ts +98 -0
  228. package/src/daemon/handlers/sessions.ts +56 -5
  229. package/src/daemon/handlers/shared.ts +6 -1
  230. package/src/daemon/handlers/skills.ts +1 -1
  231. package/src/daemon/handlers/twitter-auth.ts +2 -0
  232. package/src/daemon/handlers/work-items.ts +17 -9
  233. package/src/daemon/handlers/workspace-files.ts +4 -3
  234. package/src/daemon/install-cli-launchers.ts +113 -0
  235. package/src/daemon/ipc-contract/apps.ts +356 -0
  236. package/src/daemon/ipc-contract/browser.ts +74 -0
  237. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  238. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  239. package/src/daemon/ipc-contract/documents.ts +74 -0
  240. package/src/daemon/ipc-contract/inbox.ts +209 -0
  241. package/src/daemon/ipc-contract/integrations.ts +284 -0
  242. package/src/daemon/ipc-contract/memory.ts +48 -0
  243. package/src/daemon/ipc-contract/messages.ts +211 -0
  244. package/src/daemon/ipc-contract/pairing.ts +45 -0
  245. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  246. package/src/daemon/ipc-contract/schedules.ts +97 -0
  247. package/src/daemon/ipc-contract/sessions.ts +315 -0
  248. package/src/daemon/ipc-contract/shared.ts +42 -0
  249. package/src/daemon/ipc-contract/skills.ts +120 -0
  250. package/src/daemon/ipc-contract/subagents.ts +58 -0
  251. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  252. package/src/daemon/ipc-contract/trust.ts +60 -0
  253. package/src/daemon/ipc-contract/work-items.ts +225 -0
  254. package/src/daemon/ipc-contract/workspace.ts +113 -0
  255. package/src/daemon/ipc-contract-inventory.json +70 -0
  256. package/src/daemon/ipc-contract-inventory.ts +55 -29
  257. package/src/daemon/ipc-contract.ts +229 -2426
  258. package/src/daemon/ipc-protocol.ts +1 -1
  259. package/src/daemon/ipc-validate.ts +7 -0
  260. package/src/daemon/lifecycle.ts +97 -377
  261. package/src/daemon/pairing-store.ts +177 -0
  262. package/src/daemon/providers-setup.ts +43 -0
  263. package/src/daemon/ride-shotgun-handler.ts +68 -3
  264. package/src/daemon/server.ts +66 -46
  265. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  266. package/src/daemon/session-agent-loop.ts +117 -275
  267. package/src/daemon/session-dynamic-profile.ts +1 -1
  268. package/src/daemon/session-history.ts +1 -1
  269. package/src/daemon/session-media-retry.ts +1 -1
  270. package/src/daemon/session-messaging.ts +37 -2
  271. package/src/daemon/session-notifiers.ts +5 -25
  272. package/src/daemon/session-process.ts +99 -59
  273. package/src/daemon/session-queue-manager.ts +96 -4
  274. package/src/daemon/session-runtime-assembly.ts +199 -10
  275. package/src/daemon/session-surfaces.ts +19 -4
  276. package/src/daemon/session-tool-setup.ts +30 -30
  277. package/src/daemon/session-workspace.ts +1 -1
  278. package/src/daemon/session.ts +35 -2
  279. package/src/daemon/shutdown-handlers.ts +122 -0
  280. package/src/daemon/trace-emitter.ts +1 -1
  281. package/src/daemon/watch-handler.ts +36 -33
  282. package/src/doordash/cart-queries.ts +787 -0
  283. package/src/doordash/client.ts +144 -127
  284. package/src/doordash/order-queries.ts +85 -0
  285. package/src/doordash/queries.ts +10 -1308
  286. package/src/doordash/search-queries.ts +203 -0
  287. package/src/doordash/session.ts +3 -2
  288. package/src/doordash/store-queries.ts +246 -0
  289. package/src/doordash/types.ts +367 -0
  290. package/src/email/providers/agentmail.ts +2 -1
  291. package/src/email/providers/index.ts +3 -2
  292. package/src/email/service.ts +3 -2
  293. package/src/errors.ts +43 -0
  294. package/src/home-base/prebuilt/seed.ts +1 -1
  295. package/src/hooks/cli.ts +6 -5
  296. package/src/hooks/config.ts +6 -8
  297. package/src/hooks/discovery.ts +6 -5
  298. package/src/hooks/manager.ts +4 -3
  299. package/src/hooks/runner.ts +2 -2
  300. package/src/hooks/templates.ts +5 -5
  301. package/src/inbound/public-ingress-urls.ts +6 -4
  302. package/src/index.ts +4 -2
  303. package/src/influencer/client.ts +1104 -0
  304. package/src/instrument.ts +4 -3
  305. package/src/logfire.ts +4 -3
  306. package/src/memory/admin.ts +25 -35
  307. package/src/memory/attachments-store.ts +4 -7
  308. package/src/memory/channel-delivery-store.ts +30 -1
  309. package/src/memory/channel-guardian-store.ts +202 -2
  310. package/src/memory/clarification-resolver.ts +37 -33
  311. package/src/memory/conflict-store.ts +67 -61
  312. package/src/memory/contradiction-checker.ts +141 -117
  313. package/src/memory/conversation-store.ts +335 -51
  314. package/src/memory/db-connection.ts +27 -4
  315. package/src/memory/db-init.ts +265 -4
  316. package/src/memory/db.ts +14 -1
  317. package/src/memory/embedding-backend.ts +27 -5
  318. package/src/memory/embedding-ollama.ts +2 -1
  319. package/src/memory/entity-extractor.ts +38 -35
  320. package/src/memory/guardian-action-store.ts +430 -0
  321. package/src/memory/inbox-escalation-projection.ts +59 -0
  322. package/src/memory/inbox-thread-store.ts +218 -0
  323. package/src/memory/ingress-invite-store.ts +338 -0
  324. package/src/memory/ingress-member-store.ts +350 -0
  325. package/src/memory/items-extractor.ts +91 -97
  326. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  327. package/src/memory/job-handlers/media-processing.ts +69 -0
  328. package/src/memory/job-handlers/summarization.ts +32 -26
  329. package/src/memory/job-utils.ts +3 -10
  330. package/src/memory/jobs-store.ts +8 -10
  331. package/src/memory/jobs-worker.ts +55 -36
  332. package/src/memory/media-store.ts +759 -0
  333. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  334. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  335. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  336. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  337. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  338. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  339. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  340. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  341. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  342. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  343. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  344. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  345. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  346. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  347. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  348. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  349. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  350. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  351. package/src/memory/migrations/index.ts +24 -0
  352. package/src/memory/migrations/registry.ts +79 -0
  353. package/src/memory/migrations/validate-migration-state.ts +69 -0
  354. package/src/memory/qdrant-manager.ts +49 -8
  355. package/src/memory/query-builder.ts +1 -1
  356. package/src/memory/raw-query.ts +119 -0
  357. package/src/memory/recall-cache.ts +4 -1
  358. package/src/memory/retriever.ts +165 -47
  359. package/src/memory/schema-migration.ts +25 -984
  360. package/src/memory/schema.ts +228 -7
  361. package/src/memory/search/entity.ts +205 -31
  362. package/src/memory/search/lexical.ts +81 -52
  363. package/src/memory/search/ranking.ts +27 -23
  364. package/src/memory/search/semantic.ts +157 -19
  365. package/src/memory/search/types.ts +24 -0
  366. package/src/memory/shared-app-links-store.ts +4 -5
  367. package/src/memory/validation.ts +19 -0
  368. package/src/messaging/draft-store.ts +5 -6
  369. package/src/messaging/provider-types.ts +2 -0
  370. package/src/messaging/providers/sms/adapter.ts +201 -0
  371. package/src/messaging/providers/sms/client.ts +93 -0
  372. package/src/messaging/providers/sms/types.ts +7 -0
  373. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  374. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  375. package/src/messaging/providers/whatsapp/client.ts +67 -0
  376. package/src/messaging/style-analyzer.ts +5 -4
  377. package/src/messaging/thread-summarizer.ts +61 -69
  378. package/src/messaging/triage-engine.ts +62 -71
  379. package/src/migrations/config-merge.ts +53 -0
  380. package/src/migrations/data-layout.ts +68 -0
  381. package/src/migrations/data-merge.ts +33 -0
  382. package/src/migrations/hooks-merge.ts +90 -0
  383. package/src/migrations/index.ts +6 -0
  384. package/src/migrations/log.ts +23 -0
  385. package/src/migrations/skills-merge.ts +33 -0
  386. package/src/migrations/workspace-layout.ts +79 -0
  387. package/src/permissions/checker.ts +133 -11
  388. package/src/permissions/prompter.ts +14 -0
  389. package/src/permissions/shell-identity.ts +31 -1
  390. package/src/permissions/trust-store.ts +21 -1
  391. package/src/providers/anthropic/client.ts +4 -4
  392. package/src/providers/failover.ts +2 -2
  393. package/src/providers/model-intents.ts +70 -0
  394. package/src/providers/ollama/client.ts +2 -1
  395. package/src/providers/provider-send-message.ts +176 -0
  396. package/src/providers/registry.ts +71 -30
  397. package/src/providers/retry.ts +35 -1
  398. package/src/providers/types.ts +12 -1
  399. package/src/runtime/approval-conversation-turn.ts +97 -0
  400. package/src/runtime/approval-message-composer.ts +253 -0
  401. package/src/runtime/channel-approval-parser.ts +36 -2
  402. package/src/runtime/channel-approvals.ts +11 -24
  403. package/src/runtime/channel-guardian-service.ts +88 -21
  404. package/src/runtime/channel-readiness-service.ts +418 -0
  405. package/src/runtime/channel-readiness-types.ts +35 -0
  406. package/src/runtime/channel-retry-sweep.ts +184 -0
  407. package/src/runtime/guardian-context-resolver.ts +108 -0
  408. package/src/runtime/http-server.ts +275 -717
  409. package/src/runtime/http-types.ts +59 -3
  410. package/src/runtime/middleware/auth.ts +116 -0
  411. package/src/runtime/middleware/error-handler.ts +33 -0
  412. package/src/runtime/middleware/twilio-validation.ts +127 -0
  413. package/src/runtime/routes/app-routes.ts +1 -1
  414. package/src/runtime/routes/call-routes.ts +51 -7
  415. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  416. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  417. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  418. package/src/runtime/routes/channel-route-shared.ts +144 -0
  419. package/src/runtime/routes/channel-routes.ts +32 -1588
  420. package/src/runtime/routes/conversation-routes.ts +50 -7
  421. package/src/runtime/routes/events-routes.ts +2 -2
  422. package/src/runtime/routes/identity-routes.ts +126 -0
  423. package/src/runtime/routes/pairing-routes.ts +143 -0
  424. package/src/runtime/routes/run-routes.ts +15 -1
  425. package/src/runtime/run-orchestrator.ts +86 -35
  426. package/src/schedule/schedule-store.ts +36 -32
  427. package/src/schedule/scheduler.ts +3 -3
  428. package/src/security/encrypted-store.ts +5 -7
  429. package/src/security/oauth2.ts +45 -15
  430. package/src/security/parental-control-store.ts +183 -0
  431. package/src/security/secret-allowlist.ts +4 -3
  432. package/src/security/secret-scanner.ts +5 -5
  433. package/src/security/secure-keys.ts +1 -1
  434. package/src/security/token-manager.ts +3 -2
  435. package/src/services/vercel-deploy.ts +6 -2
  436. package/src/skills/tool-manifest.ts +3 -3
  437. package/src/skills/vellum-catalog-remote.ts +75 -16
  438. package/src/slack/slack-webhook.ts +2 -1
  439. package/src/swarm/orchestrator.ts +92 -1
  440. package/src/swarm/router-planner.ts +6 -9
  441. package/src/swarm/worker-prompts.ts +9 -12
  442. package/src/tasks/task-compiler.ts +19 -28
  443. package/src/tasks/task-runner.ts +1 -1
  444. package/src/tools/assets/materialize.ts +2 -2
  445. package/src/tools/assets/search.ts +15 -14
  446. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  447. package/src/tools/browser/auto-navigate.ts +1 -0
  448. package/src/tools/browser/browser-execution.ts +10 -1
  449. package/src/tools/browser/browser-manager.ts +119 -4
  450. package/src/tools/browser/network-recorder.ts +5 -0
  451. package/src/tools/calls/call-start.ts +1 -0
  452. package/src/tools/credentials/broker.ts +11 -2
  453. package/src/tools/credentials/metadata-store.ts +18 -14
  454. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  455. package/src/tools/credentials/vault.ts +49 -23
  456. package/src/tools/execution-target.ts +11 -1
  457. package/src/tools/executor.ts +68 -9
  458. package/src/tools/host-terminal/cli-discover.ts +1 -1
  459. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  460. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  461. package/src/tools/network/script-proxy/server.ts +1 -1
  462. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  463. package/src/tools/network/web-fetch.ts +18 -2
  464. package/src/tools/network/web-search.ts +8 -4
  465. package/src/tools/reminder/reminder-store.ts +14 -15
  466. package/src/tools/schedule/create.ts +1 -0
  467. package/src/tools/schedule/list.ts +2 -1
  468. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  469. package/src/tools/skills/skill-script-runner.ts +24 -9
  470. package/src/tools/skills/skill-tool-factory.ts +1 -0
  471. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  472. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  473. package/src/tools/terminal/parser.ts +50 -0
  474. package/src/tools/types.ts +2 -0
  475. package/src/tools/watcher/delete.ts +6 -0
  476. package/src/tools/weather/service.ts +1 -1
  477. package/src/twitter/client.ts +190 -24
  478. package/src/twitter/router.ts +1 -1
  479. package/src/twitter/session.ts +4 -3
  480. package/src/util/clipboard.ts +1 -1
  481. package/src/util/errors.ts +65 -8
  482. package/src/util/fs.ts +40 -0
  483. package/src/util/json.ts +10 -0
  484. package/src/util/log-redact.ts +189 -0
  485. package/src/util/logger.ts +19 -17
  486. package/src/util/object.ts +3 -0
  487. package/src/util/platform.ts +105 -363
  488. package/src/util/pricing.ts +1 -1
  489. package/src/util/promise-guard.ts +1 -1
  490. package/src/util/retry.ts +19 -0
  491. package/src/util/row-mapper.ts +79 -0
  492. package/src/util/silently.ts +21 -0
  493. package/src/watcher/engine.ts +5 -1
  494. package/src/watcher/provider-types.ts +20 -0
  495. package/src/watcher/providers/github.ts +156 -0
  496. package/src/watcher/providers/gmail.ts +1 -0
  497. package/src/watcher/providers/google-calendar.ts +1 -0
  498. package/src/watcher/providers/linear.ts +460 -0
  499. package/src/watcher/providers/slack.ts +1 -0
  500. package/src/work-items/work-item-runner.ts +1 -1
  501. package/src/workspace/git-service.ts +1 -1
  502. package/src/workspace/provider-commit-message-generator.ts +51 -22
  503. package/src/__tests__/call-bridge.test.ts +0 -517
  504. package/src/__tests__/session-process-bridge.test.ts +0 -244
  505. package/src/calls/call-bridge.ts +0 -168
  506. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
@@ -81,7 +81,7 @@ export function registerConfigCommand(program: Command): void {
81
81
  const { validateAllowlistFile } = require('../security/secret-allowlist.js') as typeof import('../security/secret-allowlist.js');
82
82
  try {
83
83
  const errors = validateAllowlistFile();
84
- if (errors === null) {
84
+ if (errors == null) {
85
85
  log.info('No secret-allowlist.json file found');
86
86
  return;
87
87
  }
@@ -256,7 +256,7 @@ export function registerMemoryCommand(program: Command): void {
256
256
  log.info(`Embeddings: ${status.counts.embeddings.toLocaleString()}`);
257
257
  log.info(`Pending conflicts: ${status.conflicts.pending.toLocaleString()}`);
258
258
  log.info(`Resolved conflicts: ${status.conflicts.resolved.toLocaleString()}`);
259
- if (status.conflicts.oldestPendingAgeMs !== null) {
259
+ if (status.conflicts.oldestPendingAgeMs != null) {
260
260
  const oldestMinutes = Math.floor(status.conflicts.oldestPendingAgeMs / 60_000);
261
261
  log.info(`Oldest pending conflict age: ${oldestMinutes} min`);
262
262
  } else {
@@ -13,6 +13,7 @@ import {
13
13
  } from '../daemon/lifecycle.js';
14
14
  import { startCli } from '../cli.js';
15
15
  import { getSocketPath, getRootDir, getDataDir, getDbPath, getLogPath, getWorkspaceDir, getWorkspaceSkillsDir, getWorkspaceHooksDir } from '../util/platform.js';
16
+ import { getQdrantUrlEnv } from '../config/env.js';
16
17
  import { IpcError } from '../util/errors.js';
17
18
  import { getCliLogger } from '../util/logger.js';
18
19
  import { timeAgo } from '../util/time.js';
@@ -271,7 +272,7 @@ export function registerSessionsCommand(program: Command): void {
271
272
  }
272
273
 
273
274
  const config = getConfig();
274
- const qdrantUrl = process.env.QDRANT_URL?.trim() || config.memory.qdrant.url;
275
+ const qdrantUrl = getQdrantUrlEnv() || config.memory.qdrant.url;
275
276
  const qdrant = initQdrantClient({
276
277
  url: qdrantUrl,
277
278
  collection: config.memory.qdrant.collection,
@@ -526,7 +527,7 @@ export function registerDoctorCommand(program: Command): void {
526
527
  try {
527
528
  const rawTrust = readFileSync(trustPath, 'utf-8');
528
529
  const data = JSON.parse(rawTrust);
529
- if (typeof data !== 'object' || data === null) {
530
+ if (typeof data !== 'object' || data == null) {
530
531
  fail('Trust rule syntax', 'trust.json is not a JSON object');
531
532
  } else if (typeof data.version !== 'number') {
532
533
  fail('Trust rule syntax', 'missing or invalid "version" field');
@@ -535,7 +536,7 @@ export function registerDoctorCommand(program: Command): void {
535
536
  } else {
536
537
  const invalid = data.rules.filter(
537
538
  (r: unknown) =>
538
- typeof r !== 'object' || r === null ||
539
+ typeof r !== 'object' || r == null ||
539
540
  typeof (r as Record<string, unknown>).tool !== 'string' ||
540
541
  typeof (r as Record<string, unknown>).pattern !== 'string' ||
541
542
  typeof (r as Record<string, unknown>).scope !== 'string',
@@ -0,0 +1,244 @@
1
+ /**
2
+ * CLI command group: `vellum influencer`
3
+ *
4
+ * Research influencers on Instagram, TikTok, and X/Twitter via the Chrome extension relay.
5
+ * All commands output JSON to stdout. Use --json for machine-readable output.
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import {
10
+ searchInfluencers,
11
+ getInfluencerProfile,
12
+ compareInfluencers,
13
+ type InfluencerSearchCriteria,
14
+ } from '../influencer/client.js';
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Helpers
18
+ // ---------------------------------------------------------------------------
19
+
20
+ function output(data: unknown, json: boolean): void {
21
+ process.stdout.write(
22
+ json ? JSON.stringify(data) + '\n' : JSON.stringify(data, null, 2) + '\n',
23
+ );
24
+ }
25
+
26
+ function outputError(message: string, code = 1): void {
27
+ output({ ok: false, error: message }, true);
28
+ process.exitCode = code;
29
+ }
30
+
31
+ function getJson(cmd: Command): boolean {
32
+ let c: Command | null = cmd;
33
+ while (c) {
34
+ if ((c.opts() as { json?: boolean }).json) return true;
35
+ c = c.parent;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ async function run(cmd: Command, fn: () => Promise<unknown>): Promise<void> {
41
+ try {
42
+ const result = await fn();
43
+ output(
44
+ { ok: true, ...(result as Record<string, unknown>) },
45
+ getJson(cmd),
46
+ );
47
+ } catch (err) {
48
+ outputError(err instanceof Error ? err.message : String(err));
49
+ }
50
+ }
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Command registration
54
+ // ---------------------------------------------------------------------------
55
+
56
+ export function registerInfluencerCommand(program: Command): void {
57
+ const inf = program
58
+ .command('influencer')
59
+ .description(
60
+ 'Research influencers on Instagram, TikTok, and X/Twitter. ' +
61
+ 'Uses the Chrome extension relay to browse each platform. ' +
62
+ 'Requires the user to be logged in on each platform in Chrome.',
63
+ )
64
+ .option('--json', 'Machine-readable JSON output');
65
+
66
+ // =========================================================================
67
+ // search — search for influencers across platforms
68
+ // =========================================================================
69
+ inf
70
+ .command('search')
71
+ .description(
72
+ 'Search for influencers matching criteria across Instagram, TikTok, and X/Twitter',
73
+ )
74
+ .argument('<query>', 'Search query — niche, topic, or keywords (e.g. "fitness coach", "vegan food")')
75
+ .option(
76
+ '--platforms <platforms>',
77
+ 'Comma-separated list of platforms to search (instagram,tiktok,twitter)',
78
+ 'instagram,tiktok,twitter',
79
+ )
80
+ .option('--min-followers <n>', 'Minimum follower count (e.g. 10000, 10k, 1m)')
81
+ .option('--max-followers <n>', 'Maximum follower count (e.g. 100000, 100k, 1m)')
82
+ .option('--limit <n>', 'Max results per platform', '10')
83
+ .option('--verified', 'Only return verified accounts')
84
+ .action(
85
+ async (
86
+ query: string,
87
+ opts: {
88
+ platforms: string;
89
+ minFollowers?: string;
90
+ maxFollowers?: string;
91
+ limit: string;
92
+ verified?: boolean;
93
+ },
94
+ cmd: Command,
95
+ ) => {
96
+ await run(cmd, async () => {
97
+ const platforms = opts.platforms
98
+ .split(',')
99
+ .map((p) => p.trim().toLowerCase())
100
+ .filter((p): p is 'instagram' | 'tiktok' | 'twitter' =>
101
+ ['instagram', 'tiktok', 'twitter'].includes(p),
102
+ );
103
+
104
+ const criteria: InfluencerSearchCriteria = {
105
+ query,
106
+ platforms,
107
+ minFollowers: opts.minFollowers
108
+ ? parseHumanNumber(opts.minFollowers)
109
+ : undefined,
110
+ maxFollowers: opts.maxFollowers
111
+ ? parseHumanNumber(opts.maxFollowers)
112
+ : undefined,
113
+ limit: parseInt(opts.limit, 10),
114
+ verifiedOnly: opts.verified,
115
+ };
116
+
117
+ const results = await searchInfluencers(criteria);
118
+
119
+ const totalProfiles = results.reduce((sum, r) => sum + r.count, 0);
120
+
121
+ return {
122
+ results,
123
+ totalProfiles,
124
+ platforms: platforms.length,
125
+ query,
126
+ };
127
+ });
128
+ },
129
+ );
130
+
131
+ // =========================================================================
132
+ // profile — get detailed profile data for a specific influencer
133
+ // =========================================================================
134
+ inf
135
+ .command('profile')
136
+ .description('Get detailed profile data for a specific influencer')
137
+ .argument('<username>', 'Username/handle (without @ prefix)')
138
+ .option(
139
+ '--platform <platform>',
140
+ 'Platform (instagram, tiktok, or twitter)',
141
+ 'instagram',
142
+ )
143
+ .action(
144
+ async (
145
+ username: string,
146
+ opts: { platform: string },
147
+ cmd: Command,
148
+ ) => {
149
+ await run(cmd, async () => {
150
+ const platform = opts.platform.toLowerCase() as
151
+ | 'instagram'
152
+ | 'tiktok'
153
+ | 'twitter';
154
+ if (!['instagram', 'tiktok', 'twitter'].includes(platform)) {
155
+ throw new Error(
156
+ `Invalid platform: ${opts.platform}. Use instagram, tiktok, or twitter.`,
157
+ );
158
+ }
159
+
160
+ const cleanUsername = username.replace(/^@/, '');
161
+ const profile = await getInfluencerProfile(platform, cleanUsername);
162
+
163
+ if (!profile) {
164
+ throw new Error(
165
+ `Could not find profile @${cleanUsername} on ${platform}`,
166
+ );
167
+ }
168
+
169
+ return { profile };
170
+ });
171
+ },
172
+ );
173
+
174
+ // =========================================================================
175
+ // compare — compare multiple influencers side by side
176
+ // =========================================================================
177
+ inf
178
+ .command('compare')
179
+ .description(
180
+ 'Compare multiple influencers side by side. ' +
181
+ 'Provide usernames as platform:username pairs.',
182
+ )
183
+ .argument(
184
+ '<influencers...>',
185
+ 'Space-separated list of platform:username pairs (e.g. instagram:nike twitter:nike tiktok:nike)',
186
+ )
187
+ .action(async (influencers: string[], _opts: unknown, cmd: Command) => {
188
+ await run(cmd, async () => {
189
+ const parsed = influencers.map((inf) => {
190
+ const [platform, username] = inf.includes(':')
191
+ ? inf.split(':', 2)
192
+ : ['instagram', inf];
193
+
194
+ const cleanPlatform = platform.toLowerCase() as
195
+ | 'instagram'
196
+ | 'tiktok'
197
+ | 'twitter';
198
+ if (!['instagram', 'tiktok', 'twitter'].includes(cleanPlatform)) {
199
+ throw new Error(
200
+ `Invalid platform "${platform}" in "${inf}". Use instagram, tiktok, or twitter.`,
201
+ );
202
+ }
203
+
204
+ return {
205
+ platform: cleanPlatform,
206
+ username: username.replace(/^@/, ''),
207
+ };
208
+ });
209
+
210
+ const profiles = await compareInfluencers(parsed);
211
+
212
+ return {
213
+ profiles,
214
+ count: profiles.length,
215
+ requested: parsed.length,
216
+ };
217
+ });
218
+ });
219
+ }
220
+
221
+ // ---------------------------------------------------------------------------
222
+ // Utilities
223
+ // ---------------------------------------------------------------------------
224
+
225
+ /**
226
+ * Parse human-friendly numbers like "10k", "1.5m", "100000" into integers.
227
+ */
228
+ function parseHumanNumber(text: string): number {
229
+ const cleaned = text.toLowerCase().replace(/,/g, '').trim();
230
+ const match = cleaned.match(/^([\d.]+)\s*([kmbt]?)$/);
231
+ if (!match) return parseInt(text, 10) || 0;
232
+
233
+ const num = parseFloat(match[1]);
234
+ const suffix = match[2];
235
+ const multipliers: Record<string, number> = {
236
+ '': 1,
237
+ k: 1_000,
238
+ m: 1_000_000,
239
+ b: 1_000_000_000,
240
+ t: 1_000_000_000_000,
241
+ };
242
+
243
+ return Math.round(num * (multipliers[suffix] || 1));
244
+ }
package/src/cli/map.ts CHANGED
@@ -72,6 +72,75 @@ async function isCdpReady(): Promise<boolean> {
72
72
  }
73
73
  }
74
74
 
75
+ /**
76
+ * Bring the Chrome CDP tab to the foreground so the user sees the right window.
77
+ * Optionally navigates to a URL first (used when Chrome was already running).
78
+ */
79
+ async function bringChromeToFront(navigateUrl?: string): Promise<string | null> {
80
+ try {
81
+ const res = await fetch(`${CDP_BASE}/json/list`);
82
+ if (!res.ok) return null;
83
+ const targets = (await res.json()) as Array<{ type: string; url: string; webSocketDebuggerUrl: string }>;
84
+ const pageTarget = targets.find(t => t.type === 'page');
85
+ if (!pageTarget?.webSocketDebuggerUrl) return null;
86
+
87
+ const ws = new WebSocket(pageTarget.webSocketDebuggerUrl);
88
+ await new Promise<void>((resolve, reject) => {
89
+ ws.onopen = () => resolve();
90
+ ws.onerror = (e) => reject(new Error(`CDP WebSocket error: ${e}`));
91
+ });
92
+
93
+ let nextId = 1;
94
+ const cdpSend = (method: string, params?: Record<string, unknown>): Promise<unknown> =>
95
+ new Promise((resolve, reject) => {
96
+ const id = nextId++;
97
+ const cleanup = () => {
98
+ clearTimeout(timeout);
99
+ ws.removeEventListener('message', handler);
100
+ ws.removeEventListener('close', onClose);
101
+ ws.removeEventListener('error', onError);
102
+ };
103
+ const timeout = setTimeout(() => {
104
+ cleanup();
105
+ reject(new Error(`CDP command ${method} timed out`));
106
+ }, 5000);
107
+ const onClose = () => {
108
+ cleanup();
109
+ reject(new Error('WebSocket closed before CDP response'));
110
+ };
111
+ const onError = (e: Event) => {
112
+ cleanup();
113
+ reject(new Error(`WebSocket error: ${e}`));
114
+ };
115
+ const handler = (event: MessageEvent) => {
116
+ const msg = JSON.parse(String(event.data));
117
+ if (msg.id === id) {
118
+ cleanup();
119
+ if (msg.error) reject(new Error(msg.error.message));
120
+ else resolve(msg.result);
121
+ }
122
+ };
123
+ ws.addEventListener('message', handler);
124
+ ws.addEventListener('close', onClose);
125
+ ws.addEventListener('error', onError);
126
+ ws.send(JSON.stringify({ id, method, params }));
127
+ });
128
+
129
+ if (navigateUrl) {
130
+ await cdpSend('Page.navigate', { url: navigateUrl });
131
+ // Brief wait for navigation to start
132
+ await new Promise(r => setTimeout(r, 500));
133
+ }
134
+
135
+ await cdpSend('Page.bringToFront');
136
+ const tabUrl = navigateUrl ?? pageTarget.url;
137
+ ws.close();
138
+ return tabUrl;
139
+ } catch {
140
+ return null;
141
+ }
142
+ }
143
+
75
144
  async function ensureChromeWithCDP(domain: string): Promise<void> {
76
145
  // Already running with CDP?
77
146
  if (await isCdpReady()) return;
@@ -110,9 +179,16 @@ async function startLearnSession(
110
179
  navigateDomain: string,
111
180
  recordDomain: string,
112
181
  durationSeconds: number,
182
+ autoNavigate: boolean = true,
113
183
  ): Promise<LearnResult> {
114
184
  await ensureChromeWithCDP(navigateDomain);
115
185
 
186
+ // Activate the Chrome window so the user knows which tab to watch
187
+ const tabUrl = await bringChromeToFront(`https://${navigateDomain}/`);
188
+ if (tabUrl) {
189
+ process.stderr.write(`Chrome is ready — using tab at ${tabUrl}\n`);
190
+ }
191
+
116
192
  return new Promise((resolve, reject) => {
117
193
  const socketPath = getSocketPath();
118
194
  const sessionToken = readSessionToken();
@@ -140,7 +216,7 @@ async function startLearnSession(
140
216
  mode: 'learn',
141
217
  targetDomain: recordDomain,
142
218
  navigateDomain,
143
- autoNavigate: true,
219
+ autoNavigate,
144
220
  } as unknown as import('../daemon/ipc-protocol.js').ClientMessage),
145
221
  );
146
222
  };
@@ -211,11 +287,15 @@ export function registerMapCommand(program: Command): void {
211
287
  'then analyzes captured network traffic.',
212
288
  )
213
289
  .argument('<domain>', 'Domain to map (e.g., example.com)')
214
- .option('--duration <seconds>', 'Recording duration in seconds', '120')
290
+ .option('--duration <seconds>', 'Recording duration in seconds')
291
+ .option('--manual', 'Manual mode: browse the site yourself while network traffic is recorded')
215
292
  .option('--json', 'Machine-readable JSON output')
216
- .action(async (domain: string, opts: { duration: string; json?: boolean }, cmd: Command) => {
293
+ .action(async (domain: string, opts: { duration?: string; manual?: boolean; json?: boolean }, cmd: Command) => {
217
294
  const json = getJson(cmd);
218
- const duration = parseInt(opts.duration, 10);
295
+ const manual = opts.manual ?? false;
296
+ const duration = opts.duration
297
+ ? parseInt(opts.duration, 10)
298
+ : manual ? 60 : 120;
219
299
 
220
300
  try {
221
301
  // Split into navigation domain (what Chrome browses) and recording domain (network filter).
@@ -224,13 +304,16 @@ export function registerMapCommand(program: Command): void {
224
304
  const recordDomain = getBaseDomain(domain);
225
305
 
226
306
  if (!json) {
227
- if (navigateDomain !== recordDomain) {
307
+ if (manual) {
308
+ console.log(`Starting manual API map session for ${domain} (${duration}s)...`);
309
+ console.log('Browse the site manually. Press Ctrl+C or wait for idle detection to stop recording.');
310
+ } else if (navigateDomain !== recordDomain) {
228
311
  console.log(`Starting API map session: navigating ${navigateDomain}, recording *.${recordDomain} (${duration}s)...`);
229
312
  } else {
230
313
  console.log(`Starting API map session for ${domain} (${duration}s)...`);
231
314
  }
232
315
  }
233
- const result = await startLearnSession(navigateDomain, recordDomain, duration);
316
+ const result = await startLearnSession(navigateDomain, recordDomain, duration, !manual);
234
317
 
235
318
  if (!result.recordingId) {
236
319
  outputError('Recording completed but no recording ID returned');
package/src/cli.ts CHANGED
@@ -785,7 +785,7 @@ export async function startCli(): Promise<void> {
785
785
 
786
786
  if (content === '/copy-code') {
787
787
  const code = extractLastCodeBlock(lastResponse);
788
- if (code === null) {
788
+ if (code == null) {
789
789
  process.stdout.write('No code block found.\n');
790
790
  } else {
791
791
  try {
@@ -0,0 +1,171 @@
1
+ import { z } from 'zod';
2
+
3
+ export const AgentHeartbeatConfigSchema = z.object({
4
+ enabled: z
5
+ .boolean({ error: 'agentHeartbeat.enabled must be a boolean' })
6
+ .default(false),
7
+ intervalMs: z
8
+ .number({ error: 'agentHeartbeat.intervalMs must be a number' })
9
+ .int('agentHeartbeat.intervalMs must be an integer')
10
+ .positive('agentHeartbeat.intervalMs must be a positive integer')
11
+ .default(3_600_000),
12
+ activeHoursStart: z
13
+ .number({ error: 'agentHeartbeat.activeHoursStart must be a number' })
14
+ .int('agentHeartbeat.activeHoursStart must be an integer')
15
+ .min(0, 'agentHeartbeat.activeHoursStart must be >= 0')
16
+ .max(23, 'agentHeartbeat.activeHoursStart must be <= 23')
17
+ .optional(),
18
+ activeHoursEnd: z
19
+ .number({ error: 'agentHeartbeat.activeHoursEnd must be a number' })
20
+ .int('agentHeartbeat.activeHoursEnd must be an integer')
21
+ .min(0, 'agentHeartbeat.activeHoursEnd must be >= 0')
22
+ .max(23, 'agentHeartbeat.activeHoursEnd must be <= 23')
23
+ .optional(),
24
+ }).superRefine((config, ctx) => {
25
+ const hasStart = config.activeHoursStart != null;
26
+ const hasEnd = config.activeHoursEnd != null;
27
+ if (hasStart !== hasEnd) {
28
+ ctx.addIssue({
29
+ code: z.ZodIssueCode.custom,
30
+ path: [hasStart ? 'activeHoursEnd' : 'activeHoursStart'],
31
+ message: 'agentHeartbeat.activeHoursStart and agentHeartbeat.activeHoursEnd must both be set or both be omitted',
32
+ });
33
+ }
34
+ if (hasStart && hasEnd && config.activeHoursStart === config.activeHoursEnd) {
35
+ ctx.addIssue({
36
+ code: z.ZodIssueCode.custom,
37
+ path: ['activeHoursEnd'],
38
+ message: 'agentHeartbeat.activeHoursStart and agentHeartbeat.activeHoursEnd must not be equal (would create an empty window)',
39
+ });
40
+ }
41
+ });
42
+
43
+ export const SwarmConfigSchema = z.object({
44
+ enabled: z
45
+ .boolean({ error: 'swarm.enabled must be a boolean' })
46
+ .default(true),
47
+ maxWorkers: z
48
+ .number({ error: 'swarm.maxWorkers must be a number' })
49
+ .int('swarm.maxWorkers must be an integer')
50
+ .positive('swarm.maxWorkers must be a positive integer')
51
+ .max(6, 'swarm.maxWorkers must be at most 6')
52
+ .default(3),
53
+ maxTasks: z
54
+ .number({ error: 'swarm.maxTasks must be a number' })
55
+ .int('swarm.maxTasks must be an integer')
56
+ .positive('swarm.maxTasks must be a positive integer')
57
+ .max(20, 'swarm.maxTasks must be at most 20')
58
+ .default(8),
59
+ maxRetriesPerTask: z
60
+ .number({ error: 'swarm.maxRetriesPerTask must be a number' })
61
+ .int('swarm.maxRetriesPerTask must be an integer')
62
+ .nonnegative('swarm.maxRetriesPerTask must be a non-negative integer')
63
+ .max(3, 'swarm.maxRetriesPerTask must be at most 3')
64
+ .default(1),
65
+ workerTimeoutSec: z
66
+ .number({ error: 'swarm.workerTimeoutSec must be a number' })
67
+ .int('swarm.workerTimeoutSec must be an integer')
68
+ .positive('swarm.workerTimeoutSec must be a positive integer')
69
+ .default(900),
70
+ plannerModel: z
71
+ .string({ error: 'swarm.plannerModel must be a string' })
72
+ .default('claude-haiku-4-5-20251001'),
73
+ synthesizerModel: z
74
+ .string({ error: 'swarm.synthesizerModel must be a string' })
75
+ .default('claude-sonnet-4-6'),
76
+ });
77
+
78
+ export const WorkspaceGitConfigSchema = z.object({
79
+ turnCommitMaxWaitMs: z
80
+ .number({ error: 'workspaceGit.turnCommitMaxWaitMs must be a number' })
81
+ .int('workspaceGit.turnCommitMaxWaitMs must be an integer')
82
+ .positive('workspaceGit.turnCommitMaxWaitMs must be a positive integer')
83
+ .default(4000),
84
+ failureBackoffBaseMs: z
85
+ .number({ error: 'workspaceGit.failureBackoffBaseMs must be a number' })
86
+ .int('workspaceGit.failureBackoffBaseMs must be an integer')
87
+ .positive('workspaceGit.failureBackoffBaseMs must be a positive integer')
88
+ .default(2000),
89
+ failureBackoffMaxMs: z
90
+ .number({ error: 'workspaceGit.failureBackoffMaxMs must be a number' })
91
+ .int('workspaceGit.failureBackoffMaxMs must be an integer')
92
+ .positive('workspaceGit.failureBackoffMaxMs must be a positive integer')
93
+ .default(60000),
94
+ interactiveGitTimeoutMs: z
95
+ .number({ error: 'workspaceGit.interactiveGitTimeoutMs must be a number' })
96
+ .int('workspaceGit.interactiveGitTimeoutMs must be an integer')
97
+ .positive('workspaceGit.interactiveGitTimeoutMs must be a positive integer')
98
+ .default(10000),
99
+ enrichmentQueueSize: z
100
+ .number({ error: 'workspaceGit.enrichmentQueueSize must be a number' })
101
+ .int('workspaceGit.enrichmentQueueSize must be an integer')
102
+ .positive('workspaceGit.enrichmentQueueSize must be a positive integer')
103
+ .default(50),
104
+ enrichmentConcurrency: z
105
+ .number({ error: 'workspaceGit.enrichmentConcurrency must be a number' })
106
+ .int('workspaceGit.enrichmentConcurrency must be an integer')
107
+ .positive('workspaceGit.enrichmentConcurrency must be a positive integer')
108
+ .default(1),
109
+ enrichmentJobTimeoutMs: z
110
+ .number({ error: 'workspaceGit.enrichmentJobTimeoutMs must be a number' })
111
+ .int('workspaceGit.enrichmentJobTimeoutMs must be an integer')
112
+ .positive('workspaceGit.enrichmentJobTimeoutMs must be a positive integer')
113
+ .default(30000),
114
+ enrichmentMaxRetries: z
115
+ .number({ error: 'workspaceGit.enrichmentMaxRetries must be a number' })
116
+ .int('workspaceGit.enrichmentMaxRetries must be an integer')
117
+ .nonnegative('workspaceGit.enrichmentMaxRetries must be non-negative')
118
+ .default(2),
119
+ commitMessageLLM: z.object({
120
+ enabled: z.boolean({ error: 'workspaceGit.commitMessageLLM.enabled must be a boolean' }).default(false),
121
+ useConfiguredProvider: z.boolean({ error: 'workspaceGit.commitMessageLLM.useConfiguredProvider must be a boolean' }).default(true),
122
+ providerFastModelOverrides: z.record(z.string(), z.string()).default({}),
123
+ timeoutMs: z.number({ error: 'workspaceGit.commitMessageLLM.timeoutMs must be a number' })
124
+ .int('workspaceGit.commitMessageLLM.timeoutMs must be an integer')
125
+ .positive('workspaceGit.commitMessageLLM.timeoutMs must be a positive integer')
126
+ .default(600),
127
+ maxTokens: z.number({ error: 'workspaceGit.commitMessageLLM.maxTokens must be a number' })
128
+ .int('workspaceGit.commitMessageLLM.maxTokens must be an integer')
129
+ .positive('workspaceGit.commitMessageLLM.maxTokens must be a positive integer')
130
+ .default(120),
131
+ temperature: z.number({ error: 'workspaceGit.commitMessageLLM.temperature must be a number' })
132
+ .min(0, 'workspaceGit.commitMessageLLM.temperature must be >= 0')
133
+ .max(2, 'workspaceGit.commitMessageLLM.temperature must be <= 2')
134
+ .default(0.2),
135
+ maxFilesInPrompt: z.number({ error: 'workspaceGit.commitMessageLLM.maxFilesInPrompt must be a number' })
136
+ .int('workspaceGit.commitMessageLLM.maxFilesInPrompt must be an integer')
137
+ .positive('workspaceGit.commitMessageLLM.maxFilesInPrompt must be a positive integer')
138
+ .default(30),
139
+ maxDiffBytes: z.number({ error: 'workspaceGit.commitMessageLLM.maxDiffBytes must be a number' })
140
+ .int('workspaceGit.commitMessageLLM.maxDiffBytes must be an integer')
141
+ .positive('workspaceGit.commitMessageLLM.maxDiffBytes must be a positive integer')
142
+ .default(12000),
143
+ minRemainingTurnBudgetMs: z.number({ error: 'workspaceGit.commitMessageLLM.minRemainingTurnBudgetMs must be a number' })
144
+ .int('workspaceGit.commitMessageLLM.minRemainingTurnBudgetMs must be an integer')
145
+ .nonnegative('workspaceGit.commitMessageLLM.minRemainingTurnBudgetMs must be non-negative')
146
+ .default(1000),
147
+ breaker: z.object({
148
+ openAfterFailures: z.number({ error: 'workspaceGit.commitMessageLLM.breaker.openAfterFailures must be a number' })
149
+ .int().positive().default(3),
150
+ backoffBaseMs: z.number({ error: 'workspaceGit.commitMessageLLM.breaker.backoffBaseMs must be a number' })
151
+ .int().positive().default(2000),
152
+ backoffMaxMs: z.number({ error: 'workspaceGit.commitMessageLLM.breaker.backoffMaxMs must be a number' })
153
+ .int().positive().default(60000),
154
+ }).default({ openAfterFailures: 3, backoffBaseMs: 2000, backoffMaxMs: 60000 }),
155
+ }).default({
156
+ enabled: false,
157
+ useConfiguredProvider: true,
158
+ providerFastModelOverrides: {},
159
+ timeoutMs: 600,
160
+ maxTokens: 120,
161
+ temperature: 0.2,
162
+ maxFilesInPrompt: 30,
163
+ maxDiffBytes: 12000,
164
+ minRemainingTurnBudgetMs: 1000,
165
+ breaker: { openAfterFailures: 3, backoffBaseMs: 2000, backoffMaxMs: 60000 },
166
+ }),
167
+ });
168
+
169
+ export type AgentHeartbeatConfig = z.infer<typeof AgentHeartbeatConfigSchema>;
170
+ export type SwarmConfig = z.infer<typeof SwarmConfigSchema>;
171
+ export type WorkspaceGitConfig = z.infer<typeof WorkspaceGitConfigSchema>;