vellum 0.2.1 → 0.2.2

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 (349) hide show
  1. package/README.md +15 -2
  2. package/bun.lock +5 -2
  3. package/package.json +4 -2
  4. package/scripts/capture-x-graphql.ts +562 -0
  5. package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
  6. package/scripts/test.sh +5 -0
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
  8. package/src/__tests__/account-registry.test.ts +2 -1
  9. package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
  10. package/src/__tests__/asset-materialize-tool.test.ts +16 -15
  11. package/src/__tests__/asset-search-tool.test.ts +23 -22
  12. package/src/__tests__/attachments-store.test.ts +56 -127
  13. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
  14. package/src/__tests__/browser-skill-endstate.test.ts +4 -3
  15. package/src/__tests__/call-bridge.test.ts +385 -0
  16. package/src/__tests__/call-constants.test.ts +40 -0
  17. package/src/__tests__/call-orchestrator.test.ts +130 -4
  18. package/src/__tests__/call-recovery.test.ts +518 -0
  19. package/src/__tests__/call-routes-http.test.ts +459 -0
  20. package/src/__tests__/call-state-machine.test.ts +143 -0
  21. package/src/__tests__/call-store.test.ts +216 -1
  22. package/src/__tests__/cli-discover.test.ts +1 -1
  23. package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
  24. package/src/__tests__/compaction.benchmark.test.ts +176 -0
  25. package/src/__tests__/computer-use-tools.test.ts +250 -0
  26. package/src/__tests__/config-schema.test.ts +299 -3
  27. package/src/__tests__/conflict-store.test.ts +2 -1
  28. package/src/__tests__/contacts-tools.test.ts +331 -0
  29. package/src/__tests__/conversation-store.test.ts +30 -32
  30. package/src/__tests__/credential-security-invariants.test.ts +4 -0
  31. package/src/__tests__/date-context.test.ts +373 -0
  32. package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
  33. package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
  34. package/src/__tests__/followup-tools.test.ts +303 -0
  35. package/src/__tests__/handlers-twitter-config.test.ts +718 -0
  36. package/src/__tests__/intent-routing.test.ts +64 -57
  37. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +62 -28
  39. package/src/__tests__/llm-usage-store.test.ts +3 -8
  40. package/src/__tests__/media-generate-image.test.ts +1 -1
  41. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
  42. package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
  43. package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
  44. package/src/__tests__/playbook-tools.test.ts +342 -0
  45. package/src/__tests__/profile-compiler.test.ts +2 -1
  46. package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
  47. package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
  48. package/src/__tests__/recurrence-engine.test.ts +69 -0
  49. package/src/__tests__/recurrence-types.test.ts +71 -0
  50. package/src/__tests__/registry.test.ts +5 -3
  51. package/src/__tests__/relay-server.test.ts +633 -0
  52. package/src/__tests__/reminder-store.test.ts +6 -3
  53. package/src/__tests__/reminder.test.ts +43 -77
  54. package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
  55. package/src/__tests__/run-orchestrator.test.ts +4 -4
  56. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
  57. package/src/__tests__/runtime-runs-http.test.ts +4 -4
  58. package/src/__tests__/runtime-runs.test.ts +4 -4
  59. package/src/__tests__/schedule-store.test.ts +482 -0
  60. package/src/__tests__/schedule-tools.test.ts +700 -0
  61. package/src/__tests__/scheduler-recurrence.test.ts +329 -0
  62. package/src/__tests__/server-history-render.test.ts +14 -13
  63. package/src/__tests__/session-error.test.ts +28 -0
  64. package/src/__tests__/session-init.benchmark.test.ts +462 -0
  65. package/src/__tests__/session-queue.test.ts +71 -48
  66. package/src/__tests__/session-runtime-assembly.test.ts +161 -0
  67. package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
  68. package/src/__tests__/signup-e2e.test.ts +2 -1
  69. package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
  70. package/src/__tests__/skill-script-runner.test.ts +159 -0
  71. package/src/__tests__/speaker-identification.test.ts +52 -0
  72. package/src/__tests__/subagent-manager-notify.test.ts +42 -10
  73. package/src/__tests__/subagent-tools.test.ts +141 -41
  74. package/src/__tests__/task-compiler.test.ts +2 -1
  75. package/src/__tests__/task-runner.test.ts +2 -1
  76. package/src/__tests__/task-scheduler.test.ts +2 -1
  77. package/src/__tests__/task-tools.test.ts +49 -56
  78. package/src/__tests__/tool-audit-listener.test.ts +1 -0
  79. package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
  80. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
  81. package/src/__tests__/tool-executor.test.ts +13 -17
  82. package/src/__tests__/turn-commit.test.ts +218 -3
  83. package/src/__tests__/twilio-provider.test.ts +143 -0
  84. package/src/__tests__/twilio-routes.test.ts +789 -0
  85. package/src/__tests__/twitter-auth-handler.test.ts +581 -0
  86. package/src/__tests__/view-image-tool.test.ts +217 -0
  87. package/src/__tests__/workspace-git-service.test.ts +186 -0
  88. package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
  89. package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
  90. package/src/bundler/app-bundler.ts +12 -8
  91. package/src/calls/call-bridge.ts +95 -0
  92. package/src/calls/call-constants.ts +43 -5
  93. package/src/calls/call-domain.ts +276 -0
  94. package/src/calls/call-orchestrator.ts +43 -17
  95. package/src/calls/call-recovery.ts +207 -0
  96. package/src/calls/call-state-machine.ts +68 -0
  97. package/src/calls/call-store.ts +192 -5
  98. package/src/calls/relay-server.ts +41 -4
  99. package/src/calls/speaker-identification.ts +213 -0
  100. package/src/calls/twilio-provider.ts +10 -6
  101. package/src/calls/twilio-routes.ts +90 -76
  102. package/src/calls/types.ts +1 -1
  103. package/src/cli/config-commands.ts +334 -0
  104. package/src/cli/core-commands.ts +776 -0
  105. package/src/cli/doordash.ts +251 -1
  106. package/src/cli/ipc-client.ts +82 -0
  107. package/src/cli/map.ts +246 -0
  108. package/src/cli/twitter.ts +575 -0
  109. package/src/cli.ts +7 -5
  110. package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
  111. package/src/commands/cc-command-registry.ts +209 -0
  112. package/src/config/bundled-skills/contacts/SKILL.md +39 -0
  113. package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
  114. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
  115. package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
  116. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
  117. package/src/config/bundled-skills/document/SKILL.md +18 -0
  118. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  119. package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
  120. package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
  121. package/src/config/bundled-skills/doordash/SKILL.md +82 -23
  122. package/src/config/bundled-skills/followups/SKILL.md +32 -0
  123. package/src/config/bundled-skills/followups/TOOLS.json +100 -0
  124. package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
  125. package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
  126. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
  127. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
  128. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
  129. package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
  130. package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
  131. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
  132. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
  133. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
  134. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
  135. package/src/config/bundled-skills/reminder/SKILL.md +20 -0
  136. package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
  137. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
  138. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
  139. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
  140. package/src/config/bundled-skills/schedule/SKILL.md +74 -0
  141. package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
  142. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
  143. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
  144. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
  145. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
  146. package/src/config/bundled-skills/subagent/SKILL.md +25 -0
  147. package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
  148. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
  149. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
  150. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
  151. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
  152. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
  153. package/src/config/bundled-skills/tasks/SKILL.md +28 -0
  154. package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
  155. package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
  156. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
  157. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
  158. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
  159. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
  160. package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
  161. package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
  162. package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
  163. package/src/config/bundled-skills/twitter/SKILL.md +134 -0
  164. package/src/config/bundled-skills/watcher/SKILL.md +27 -0
  165. package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
  166. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
  167. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
  168. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
  169. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
  170. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
  171. package/src/config/defaults.ts +33 -0
  172. package/src/config/loader.ts +4 -1
  173. package/src/config/schema.ts +161 -1
  174. package/src/config/system-prompt.ts +61 -16
  175. package/src/config/templates/IDENTITY.md +7 -0
  176. package/src/config/types.ts +4 -0
  177. package/src/contacts/contact-store.ts +4 -4
  178. package/src/daemon/assistant-attachments.ts +10 -0
  179. package/src/daemon/classifier.ts +3 -1
  180. package/src/daemon/computer-use-session.ts +3 -1
  181. package/src/daemon/date-context.ts +136 -0
  182. package/src/daemon/handlers/apps.ts +16 -1
  183. package/src/daemon/handlers/browser.ts +54 -0
  184. package/src/daemon/handlers/computer-use.ts +7 -1
  185. package/src/daemon/handlers/config.ts +163 -5
  186. package/src/daemon/handlers/diagnostics.ts +5 -1
  187. package/src/daemon/handlers/documents.ts +18 -29
  188. package/src/daemon/handlers/home-base.ts +5 -1
  189. package/src/daemon/handlers/index.ts +40 -277
  190. package/src/daemon/handlers/misc.ts +9 -1
  191. package/src/daemon/handlers/publish.ts +6 -1
  192. package/src/daemon/handlers/sessions.ts +65 -12
  193. package/src/daemon/handlers/shared.ts +36 -1
  194. package/src/daemon/handlers/signing.ts +37 -0
  195. package/src/daemon/handlers/skills.ts +20 -6
  196. package/src/daemon/handlers/subagents.ts +8 -3
  197. package/src/daemon/handlers/twitter-auth.ts +169 -0
  198. package/src/daemon/handlers/work-items.ts +384 -68
  199. package/src/daemon/ipc-contract-inventory.json +28 -4
  200. package/src/daemon/ipc-contract.ts +133 -37
  201. package/src/daemon/ipc-protocol.ts +7 -2
  202. package/src/daemon/lifecycle.ts +21 -0
  203. package/src/daemon/main.ts +10 -4
  204. package/src/daemon/ride-shotgun-handler.ts +74 -10
  205. package/src/daemon/server.ts +143 -26
  206. package/src/daemon/session-agent-loop.ts +887 -0
  207. package/src/daemon/session-attachments.ts +28 -5
  208. package/src/daemon/session-error.ts +24 -3
  209. package/src/daemon/session-lifecycle.ts +147 -0
  210. package/src/daemon/session-media-retry.ts +147 -0
  211. package/src/daemon/session-messaging.ts +145 -0
  212. package/src/daemon/session-notifiers.ts +164 -0
  213. package/src/daemon/session-process.ts +2 -2
  214. package/src/daemon/session-queue-manager.ts +1 -0
  215. package/src/daemon/session-runtime-assembly.ts +52 -0
  216. package/src/daemon/session-skill-tools.ts +124 -5
  217. package/src/daemon/session-slash.ts +3 -0
  218. package/src/daemon/session-surfaces.ts +77 -2
  219. package/src/daemon/session-tool-setup.ts +216 -2
  220. package/src/daemon/session-usage.ts +0 -2
  221. package/src/daemon/session.ts +114 -1404
  222. package/src/daemon/video-thumbnail.ts +60 -0
  223. package/src/doordash/client.ts +121 -27
  224. package/src/doordash/queries.ts +1 -2
  225. package/src/export/formatter.ts +3 -1
  226. package/src/followups/followup-store.ts +4 -2
  227. package/src/followups/types.ts +6 -0
  228. package/src/hooks/templates.ts +1 -1
  229. package/src/index.ts +32 -1153
  230. package/src/memory/attachments-store.ts +28 -83
  231. package/src/memory/channel-delivery-store.ts +7 -21
  232. package/src/memory/clarification-resolver.ts +6 -5
  233. package/src/memory/contradiction-checker.ts +3 -2
  234. package/src/memory/conversation-key-store.ts +10 -29
  235. package/src/memory/conversation-store.ts +2 -1
  236. package/src/memory/db.ts +96 -2
  237. package/src/memory/entity-extractor.ts +6 -3
  238. package/src/memory/items-extractor.ts +5 -4
  239. package/src/memory/jobs-store.ts +3 -2
  240. package/src/memory/llm-usage-store.ts +1 -2
  241. package/src/memory/runs-store.ts +1 -2
  242. package/src/memory/schema.ts +23 -2
  243. package/src/messaging/style-analyzer.ts +3 -2
  244. package/src/messaging/thread-summarizer.ts +8 -12
  245. package/src/messaging/triage-engine.ts +4 -2
  246. package/src/providers/openrouter/client.ts +20 -0
  247. package/src/providers/registry.ts +8 -0
  248. package/src/runtime/http-server.ts +108 -20
  249. package/src/runtime/routes/attachment-routes.ts +2 -3
  250. package/src/runtime/routes/call-routes.ts +140 -0
  251. package/src/runtime/routes/channel-routes.ts +5 -10
  252. package/src/runtime/routes/conversation-routes.ts +5 -5
  253. package/src/runtime/routes/run-routes.ts +2 -2
  254. package/src/runtime/run-orchestrator.ts +9 -3
  255. package/src/schedule/recurrence-engine.ts +138 -0
  256. package/src/schedule/recurrence-types.ts +67 -0
  257. package/src/schedule/schedule-store.ts +102 -57
  258. package/src/schedule/scheduler.ts +9 -6
  259. package/src/security/oauth2.ts +29 -4
  260. package/src/security/secret-allowlist.ts +46 -0
  261. package/src/skills/clawhub.ts +1 -1
  262. package/src/subagent/manager.ts +40 -8
  263. package/src/swarm/backend-claude-code.ts +64 -9
  264. package/src/swarm/worker-prompts.ts +2 -1
  265. package/src/tasks/SPEC.md +34 -28
  266. package/src/tasks/ephemeral-permissions.ts +16 -7
  267. package/src/tasks/task-compiler.ts +5 -4
  268. package/src/tasks/task-runner.ts +10 -5
  269. package/src/tasks/task-scheduler.ts +1 -1
  270. package/src/tasks/tool-sanitizer.ts +36 -0
  271. package/src/tools/assets/search.ts +4 -4
  272. package/src/tools/browser/api-map.ts +220 -0
  273. package/src/tools/browser/auto-navigate.ts +270 -0
  274. package/src/tools/browser/browser-execution.ts +2 -1
  275. package/src/tools/browser/browser-manager.ts +2 -2
  276. package/src/tools/browser/network-recorder.ts +5 -4
  277. package/src/tools/browser/x-auto-navigate.ts +207 -0
  278. package/src/tools/calls/call-end.ts +17 -67
  279. package/src/tools/calls/call-start.ts +24 -85
  280. package/src/tools/calls/call-status.ts +35 -51
  281. package/src/tools/claude-code/claude-code.ts +77 -11
  282. package/src/tools/contacts/contact-merge.ts +46 -78
  283. package/src/tools/contacts/contact-search.ts +35 -79
  284. package/src/tools/contacts/contact-upsert.ts +35 -108
  285. package/src/tools/credentials/vault.ts +20 -4
  286. package/src/tools/document/document-tool.ts +71 -144
  287. package/src/tools/executor.ts +129 -10
  288. package/src/tools/followups/followup_create.ts +46 -88
  289. package/src/tools/followups/followup_list.ts +34 -74
  290. package/src/tools/followups/followup_resolve.ts +31 -66
  291. package/src/tools/host-terminal/cli-discover.ts +2 -1
  292. package/src/tools/host-terminal/host-shell.ts +10 -0
  293. package/src/tools/memory/handlers.ts +5 -4
  294. package/src/tools/network/__tests__/web-search.test.ts +427 -0
  295. package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
  296. package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
  297. package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
  298. package/src/tools/network/web-fetch.ts +18 -6
  299. package/src/tools/playbooks/index.ts +4 -5
  300. package/src/tools/playbooks/playbook-create.ts +3 -47
  301. package/src/tools/playbooks/playbook-delete.ts +1 -25
  302. package/src/tools/playbooks/playbook-list.ts +1 -28
  303. package/src/tools/playbooks/playbook-update.ts +3 -51
  304. package/src/tools/reminder/reminder.ts +5 -78
  305. package/src/tools/schedule/create.ts +69 -74
  306. package/src/tools/schedule/delete.ts +21 -47
  307. package/src/tools/schedule/list.ts +55 -74
  308. package/src/tools/schedule/update.ts +77 -84
  309. package/src/tools/subagent/abort.ts +29 -58
  310. package/src/tools/subagent/message.ts +30 -63
  311. package/src/tools/subagent/read.ts +53 -84
  312. package/src/tools/subagent/spawn.ts +43 -82
  313. package/src/tools/subagent/status.ts +42 -71
  314. package/src/tools/swarm/delegate.ts +2 -1
  315. package/src/tools/tasks/index.ts +8 -8
  316. package/src/tools/tasks/task-delete.ts +60 -88
  317. package/src/tools/tasks/task-list.ts +31 -52
  318. package/src/tools/tasks/task-run.ts +72 -108
  319. package/src/tools/tasks/task-save.ts +33 -65
  320. package/src/tools/tasks/work-item-enqueue.ts +183 -215
  321. package/src/tools/tasks/work-item-list.ts +33 -63
  322. package/src/tools/tasks/work-item-remove.ts +45 -97
  323. package/src/tools/tasks/work-item-update.ts +91 -163
  324. package/src/tools/terminal/backends/native.ts +3 -1
  325. package/src/tools/tool-manifest.ts +0 -62
  326. package/src/tools/types.ts +6 -0
  327. package/src/tools/ui-surface/definitions.ts +3 -1
  328. package/src/tools/watch/screen-watch.ts +3 -1
  329. package/src/tools/watcher/create.ts +52 -98
  330. package/src/tools/watcher/delete.ts +20 -46
  331. package/src/tools/watcher/digest.ts +36 -70
  332. package/src/tools/watcher/list.ts +49 -79
  333. package/src/tools/watcher/update.ts +45 -91
  334. package/src/twitter/client.ts +690 -0
  335. package/src/twitter/session.ts +91 -0
  336. package/src/usage/types.ts +0 -1
  337. package/src/util/truncate.ts +6 -0
  338. package/src/watcher/providers/slack.ts +2 -1
  339. package/src/watcher/watcher-store.ts +3 -2
  340. package/src/work-items/work-item-store.ts +27 -2
  341. package/src/workspace/commit-message-enrichment-service.ts +31 -7
  342. package/src/workspace/git-service.ts +87 -22
  343. package/src/workspace/provider-commit-message-generator.ts +242 -0
  344. package/src/workspace/turn-commit.ts +62 -3
  345. package/src/tools/contacts/index.ts +0 -4
  346. package/src/tools/document/index.ts +0 -5
  347. package/src/tools/followups/index.ts +0 -3
  348. package/src/tools/subagent/index.ts +0 -5
  349. /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
@@ -5,7 +5,7 @@ import { ComputerUseSession } from '../computer-use-session.js';
5
5
  import { getLogger } from '../../util/logger.js';
6
6
  import { execSync } from 'node:child_process';
7
7
  import { estimateBase64Bytes } from '../assistant-attachments.js';
8
- import type { CuSessionCreate, ServerMessage, SessionTransportMetadata } from '../ipc-protocol.js';
8
+ import type { ClientMessage, CuSessionCreate, ServerMessage, SessionTransportMetadata } from '../ipc-protocol.js';
9
9
  import type { SecretPromptResult } from '../../permissions/secret-prompter.js';
10
10
  import { getConfig } from '../../config/loader.js';
11
11
 
@@ -13,6 +13,9 @@ const log = getLogger('handlers');
13
13
 
14
14
  export { log };
15
15
 
16
+ /** Debounce window for suppressing file-watcher config reloads after programmatic saves. */
17
+ export const CONFIG_RELOAD_DEBOUNCE_MS = 300;
18
+
16
19
  const HISTORY_ATTACHMENT_TEXT_LIMIT = 500;
17
20
 
18
21
  export const FALLBACK_SCREEN = { width: 1920, height: 1080 };
@@ -67,6 +70,13 @@ export interface RenderedHistoryContent {
67
70
  surfaces: HistorySurface[];
68
71
  }
69
72
 
73
+ export interface SubagentNotificationData {
74
+ subagentId: string;
75
+ label: string;
76
+ status: 'completed' | 'failed' | 'aborted';
77
+ error?: string;
78
+ }
79
+
70
80
  export interface ParsedHistoryMessage {
71
81
  id?: string;
72
82
  role: string;
@@ -77,6 +87,7 @@ export interface ParsedHistoryMessage {
77
87
  textSegments: string[];
78
88
  contentOrder: string[];
79
89
  surfaces: HistorySurface[];
90
+ subagentNotification?: SubagentNotificationData;
80
91
  }
81
92
 
82
93
  /**
@@ -120,6 +131,30 @@ export interface HandlerContext {
120
131
  touchSession(sessionId: string): void;
121
132
  }
122
133
 
134
+ // ─── Typed dispatch ──────────────────────────────────────────────────────────
135
+
136
+ type MessageType = ClientMessage['type'];
137
+ // 'auth' is handled at the transport layer (server.ts) and never reaches dispatch.
138
+ export type DispatchableType = Exclude<MessageType, 'auth'>;
139
+ type MessageOfType<T extends MessageType> = Extract<ClientMessage, { type: T }>;
140
+ type MessageHandler<T extends MessageType> = (
141
+ msg: MessageOfType<T>,
142
+ socket: net.Socket,
143
+ ctx: HandlerContext,
144
+ ) => void | Promise<void>;
145
+ export type DispatchMap = { [T in DispatchableType]: MessageHandler<T> };
146
+
147
+ /**
148
+ * Type-safe handler group definition. Preserves exact key types so the
149
+ * combined spread in index.ts can be checked for exhaustiveness via
150
+ * `satisfies DispatchMap` instead of an unsafe `as DispatchMap` cast.
151
+ */
152
+ export function defineHandlers<K extends DispatchableType>(
153
+ handlers: Pick<DispatchMap, K>,
154
+ ): Pick<DispatchMap, K> {
155
+ return handlers;
156
+ }
157
+
123
158
  /**
124
159
  * Query the main display dimensions via CoreGraphics.
125
160
  * Cached after the first successful call; falls back to 1920x1080.
@@ -0,0 +1,37 @@
1
+ import { log, pendingSignBundlePayload, pendingSigningIdentity, defineHandlers } from './shared.js';
2
+
3
+ export const signingHandlers = defineHandlers({
4
+ sign_bundle_payload_response: (msg) => {
5
+ const pending = pendingSignBundlePayload.get(msg.requestId);
6
+ if (pending) {
7
+ clearTimeout(pending.timer);
8
+ pendingSignBundlePayload.delete(msg.requestId);
9
+ if (msg.error) {
10
+ pending.reject(new Error(msg.error));
11
+ } else if (msg.signature && msg.keyId && msg.publicKey) {
12
+ pending.resolve({ signature: msg.signature, keyId: msg.keyId, publicKey: msg.publicKey });
13
+ } else {
14
+ pending.reject(new Error('Missing required fields in sign_bundle_payload_response'));
15
+ }
16
+ } else {
17
+ log.warn({ requestId: msg.requestId }, 'Received sign_bundle_payload_response with no pending request');
18
+ }
19
+ },
20
+
21
+ get_signing_identity_response: (msg) => {
22
+ const pending = pendingSigningIdentity.get(msg.requestId);
23
+ if (pending) {
24
+ clearTimeout(pending.timer);
25
+ pendingSigningIdentity.delete(msg.requestId);
26
+ if (msg.error) {
27
+ pending.reject(new Error(msg.error));
28
+ } else if (msg.keyId && msg.publicKey) {
29
+ pending.resolve({ keyId: msg.keyId, publicKey: msg.publicKey });
30
+ } else {
31
+ pending.reject(new Error('Missing required fields in get_signing_identity_response'));
32
+ }
33
+ } else {
34
+ log.warn({ requestId: msg.requestId }, 'Received get_signing_identity_response with no pending request');
35
+ }
36
+ },
37
+ });
@@ -19,7 +19,7 @@ import type {
19
19
  SkillsSearchRequest,
20
20
  SkillsInspectRequest,
21
21
  } from '../ipc-protocol.js';
22
- import { log, ensureSkillEntry, type HandlerContext } from './shared.js';
22
+ import { log, CONFIG_RELOAD_DEBOUNCE_MS, ensureSkillEntry, defineHandlers, type HandlerContext } from './shared.js';
23
23
 
24
24
  export function handleSkillsList(socket: net.Socket, ctx: HandlerContext): void {
25
25
  const config = getConfig();
@@ -63,7 +63,7 @@ export function handleSkillsEnable(
63
63
 
64
64
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
65
65
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
66
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
66
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
67
67
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
68
68
 
69
69
  ctx.updateConfigFingerprint();
@@ -110,7 +110,7 @@ export function handleSkillsDisable(
110
110
 
111
111
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
112
112
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
113
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
113
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
114
114
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
115
115
 
116
116
  ctx.updateConfigFingerprint();
@@ -167,7 +167,7 @@ export function handleSkillsConfigure(
167
167
 
168
168
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
169
169
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
170
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
170
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
171
171
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
172
172
 
173
173
  ctx.updateConfigFingerprint();
@@ -228,7 +228,7 @@ export async function handleSkillsInstall(
228
228
  invalidateConfigCache();
229
229
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
230
230
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
231
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
231
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
232
232
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
233
233
  ctx.updateConfigFingerprint();
234
234
  } catch (err) {
@@ -323,7 +323,7 @@ export async function handleSkillsUninstall(
323
323
 
324
324
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
325
325
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
326
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
326
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
327
327
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
328
328
 
329
329
  ctx.updateConfigFingerprint();
@@ -485,3 +485,17 @@ export async function handleSkillDetail(
485
485
  });
486
486
  }
487
487
  }
488
+
489
+ export const skillHandlers = defineHandlers({
490
+ skills_list: (_msg, socket, ctx) => handleSkillsList(socket, ctx),
491
+ skill_detail: handleSkillDetail,
492
+ skills_enable: handleSkillsEnable,
493
+ skills_disable: handleSkillsDisable,
494
+ skills_configure: handleSkillsConfigure,
495
+ skills_install: handleSkillsInstall,
496
+ skills_uninstall: handleSkillsUninstall,
497
+ skills_update: handleSkillsUpdate,
498
+ skills_check_updates: handleSkillsCheckUpdates,
499
+ skills_search: handleSkillsSearch,
500
+ skills_inspect: handleSkillsInspect,
501
+ });
@@ -6,7 +6,7 @@ import * as net from 'node:net';
6
6
  import type { SubagentAbortRequest, SubagentStatusRequest, SubagentMessageRequest } from '../ipc-protocol.js';
7
7
  import type { HandlerContext } from './shared.js';
8
8
  import { getSubagentManager } from '../../subagent/index.js';
9
- import { log } from './shared.js';
9
+ import { log, defineHandlers } from './shared.js';
10
10
 
11
11
  export function handleSubagentAbort(
12
12
  msg: SubagentAbortRequest,
@@ -66,8 +66,7 @@ export function handleSubagentStatus(
66
66
  }
67
67
 
68
68
  // Return all subagents for the caller's session.
69
- const sessionId = callerSessionId;
70
- const children = manager.getChildrenOf(sessionId);
69
+ const children = manager.getChildrenOf(callerSessionId);
71
70
  for (const child of children) {
72
71
  ctx.send(socket, {
73
72
  type: 'subagent_status_changed',
@@ -120,3 +119,9 @@ export function handleSubagentMessage(
120
119
  });
121
120
  }
122
121
  }
122
+
123
+ export const subagentHandlers = defineHandlers({
124
+ subagent_abort: handleSubagentAbort,
125
+ subagent_status: handleSubagentStatus,
126
+ subagent_message: handleSubagentMessage,
127
+ });
@@ -0,0 +1,169 @@
1
+ import * as net from 'node:net';
2
+ import { loadRawConfig } from '../../config/loader.js';
3
+ import { getSecureKey, setSecureKey, deleteSecureKey } from '../../security/secure-keys.js';
4
+ import { startOAuth2Flow } from '../../security/oauth2.js';
5
+ import { upsertCredentialMetadata, getCredentialMetadata } from '../../tools/credentials/metadata-store.js';
6
+ import type { TwitterAuthStartRequest, TwitterAuthStatusRequest } from '../ipc-protocol.js';
7
+ import { log, defineHandlers, type HandlerContext } from './shared.js';
8
+ import type { OAuth2Config } from '../../security/oauth2.js';
9
+
10
+ export async function handleTwitterAuthStart(
11
+ _msg: TwitterAuthStartRequest,
12
+ socket: net.Socket,
13
+ ctx: HandlerContext,
14
+ ): Promise<void> {
15
+ try {
16
+ const raw = loadRawConfig();
17
+ const mode = (raw.twitterIntegrationMode as string | undefined) ?? 'local_byo';
18
+ if (mode !== 'local_byo') {
19
+ ctx.send(socket, {
20
+ type: 'twitter_auth_result',
21
+ success: false,
22
+ error: 'Twitter integration mode must be "local_byo" to use this flow.',
23
+ });
24
+ return;
25
+ }
26
+
27
+ const clientId = getSecureKey('credential:integration:twitter:oauth_client_id');
28
+ if (!clientId) {
29
+ ctx.send(socket, {
30
+ type: 'twitter_auth_result',
31
+ success: false,
32
+ error: 'No Twitter client credentials configured. Please set up your Client ID first.',
33
+ });
34
+ return;
35
+ }
36
+
37
+ const clientSecret = getSecureKey('credential:integration:twitter:oauth_client_secret') || undefined;
38
+
39
+ const oauthConfig: OAuth2Config = {
40
+ authUrl: 'https://twitter.com/i/oauth2/authorize',
41
+ tokenUrl: 'https://api.x.com/2/oauth2/token',
42
+ scopes: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'],
43
+ clientId,
44
+ clientSecret,
45
+ extraParams: {},
46
+ };
47
+
48
+ const result = await startOAuth2Flow(oauthConfig, {
49
+ openUrl: (url: string) => {
50
+ ctx.send(socket, { type: 'open_url', url });
51
+ },
52
+ });
53
+
54
+ // Verify identity via Twitter API before persisting any tokens
55
+ let accountInfo: string;
56
+ try {
57
+ const userResp = await fetch('https://api.x.com/2/users/me', {
58
+ headers: { Authorization: `Bearer ${result.tokens.accessToken}` },
59
+ });
60
+ if (!userResp.ok) {
61
+ log.error({ status: userResp.status }, 'Twitter identity verification returned non-2xx');
62
+ ctx.send(socket, {
63
+ type: 'twitter_auth_result',
64
+ success: false,
65
+ error: 'Failed to verify Twitter identity. Please try again.',
66
+ });
67
+ return;
68
+ }
69
+ const userData = (await userResp.json()) as { data?: { username?: string } };
70
+ if (!userData.data?.username) {
71
+ log.error({ userData }, 'Twitter identity verification returned no username');
72
+ ctx.send(socket, {
73
+ type: 'twitter_auth_result',
74
+ success: false,
75
+ error: 'Failed to verify Twitter identity. Please try again.',
76
+ });
77
+ return;
78
+ }
79
+ accountInfo = `@${userData.data.username}`;
80
+ } catch (err) {
81
+ log.error({ err }, 'Twitter identity verification fetch failed');
82
+ ctx.send(socket, {
83
+ type: 'twitter_auth_result',
84
+ success: false,
85
+ error: 'Failed to verify Twitter identity. Please try again.',
86
+ });
87
+ return;
88
+ }
89
+
90
+ // Persist tokens only after successful verification
91
+ setSecureKey('credential:integration:twitter:access_token', result.tokens.accessToken);
92
+ if (result.tokens.refreshToken) {
93
+ setSecureKey('credential:integration:twitter:refresh_token', result.tokens.refreshToken);
94
+ } else {
95
+ deleteSecureKey('credential:integration:twitter:refresh_token');
96
+ }
97
+
98
+ upsertCredentialMetadata('integration:twitter', 'access_token', {
99
+ accountInfo,
100
+ allowedTools: ['twitter_post'],
101
+ allowedDomains: [],
102
+ oauth2TokenUrl: 'https://api.x.com/2/oauth2/token',
103
+ oauth2ClientId: clientId,
104
+ oauth2ClientSecret: clientSecret ?? null,
105
+ grantedScopes: result.grantedScopes,
106
+ expiresAt: result.tokens.expiresIn ? Date.now() + result.tokens.expiresIn * 1000 : null,
107
+ });
108
+
109
+ ctx.send(socket, {
110
+ type: 'twitter_auth_result',
111
+ success: true,
112
+ accountInfo,
113
+ });
114
+ } catch (err) {
115
+ const message = err instanceof Error ? err.message : String(err);
116
+ log.error({ err }, 'Twitter OAuth flow failed');
117
+
118
+ let userError: string;
119
+ const lower = message.toLowerCase();
120
+ if (lower.includes('timed out')) {
121
+ userError = 'Twitter authentication timed out. Please try again.';
122
+ } else if (lower.includes('user_cancelled') || lower.includes('cancelled')) {
123
+ userError = 'Twitter authentication was cancelled.';
124
+ } else if (lower.includes('denied') || lower.includes('invalid_grant')) {
125
+ userError = 'Twitter denied the authorization request. Please try again.';
126
+ } else {
127
+ userError = 'Twitter authentication failed. Please try again.';
128
+ }
129
+
130
+ ctx.send(socket, {
131
+ type: 'twitter_auth_result',
132
+ success: false,
133
+ error: userError,
134
+ });
135
+ }
136
+ }
137
+
138
+ export function handleTwitterAuthStatus(
139
+ _msg: TwitterAuthStatusRequest,
140
+ socket: net.Socket,
141
+ ctx: HandlerContext,
142
+ ): void {
143
+ try {
144
+ const accessToken = getSecureKey('credential:integration:twitter:access_token');
145
+ const raw = loadRawConfig();
146
+ const mode = (raw.twitterIntegrationMode as 'local_byo' | 'managed' | undefined) ?? 'local_byo';
147
+ const meta = getCredentialMetadata('integration:twitter', 'access_token');
148
+
149
+ ctx.send(socket, {
150
+ type: 'twitter_auth_status_response',
151
+ connected: !!accessToken,
152
+ accountInfo: meta?.accountInfo ?? undefined,
153
+ mode,
154
+ });
155
+ } catch (err) {
156
+ const message = err instanceof Error ? err.message : String(err);
157
+ log.error({ err }, 'Failed to get Twitter auth status');
158
+ ctx.send(socket, {
159
+ type: 'twitter_auth_status_response',
160
+ connected: false,
161
+ error: message,
162
+ });
163
+ }
164
+ }
165
+
166
+ export const twitterAuthHandlers = defineHandlers({
167
+ twitter_auth_start: handleTwitterAuthStart,
168
+ twitter_auth_status: handleTwitterAuthStatus,
169
+ });