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
@@ -70,7 +70,7 @@ export function upsertContact(params: {
70
70
  responseExpectation?: string | null;
71
71
  preferredTone?: string | null;
72
72
  channels?: Array<{ type: string; address: string; isPrimary?: boolean }>;
73
- }): ContactWithChannels {
73
+ }): ContactWithChannels & { created: boolean } {
74
74
  const db = getDb();
75
75
  const now = Date.now();
76
76
 
@@ -96,7 +96,7 @@ export function upsertContact(params: {
96
96
  syncChannels(contactId, params.channels, now);
97
97
  }
98
98
 
99
- return getContact(contactId)!;
99
+ return { ...getContact(contactId)!, created: false };
100
100
  }
101
101
  }
102
102
 
@@ -124,7 +124,7 @@ export function upsertContact(params: {
124
124
  .run();
125
125
 
126
126
  syncChannels(contactId, params.channels, now);
127
- return getContact(contactId)!;
127
+ return { ...getContact(contactId)!, created: false };
128
128
  }
129
129
  }
130
130
  }
@@ -148,7 +148,7 @@ export function upsertContact(params: {
148
148
  syncChannels(contactId, params.channels, now);
149
149
  }
150
150
 
151
- return getContact(contactId)!;
151
+ return { ...getContact(contactId)!, created: true };
152
152
  }
153
153
 
154
154
  /**
@@ -313,6 +313,16 @@ export function drainDirectiveDisplayBuffer(buffer: string): DirectiveDisplayDra
313
313
 
314
314
  if (!isValidDirective) {
315
315
  emitText += tag;
316
+ } else {
317
+ // Only trim the trailing newline when the directive occupied its own
318
+ // line (preceded by \n and followed by \n or \r\n). We intentionally
319
+ // do NOT trim when nextChar is undefined (end-of-buffer) because in
320
+ // streaming mode more data may arrive in the next chunk — eagerly
321
+ // trimming would merge words across the directive boundary.
322
+ const nextChar = buffer[end + 2];
323
+ if (emitText.endsWith('\n') && (nextChar === '\n' || nextChar === '\r')) {
324
+ emitText = emitText.slice(0, -1);
325
+ }
316
326
  }
317
327
 
318
328
  cursor = end + 2;
@@ -4,6 +4,8 @@ import { getLogger } from '../util/logger.js';
4
4
 
5
5
  const log = getLogger('classifier');
6
6
 
7
+ const CLASSIFICATION_TIMEOUT_MS = 5000;
8
+
7
9
  export type InteractionType = 'computer_use' | 'text_qa';
8
10
 
9
11
  /**
@@ -53,7 +55,7 @@ export async function classifyInteraction(task: string, source?: 'voice' | 'text
53
55
  messages: [{ role: 'user' as const, content: task }],
54
56
  }),
55
57
  new Promise<never>((_, reject) =>
56
- setTimeout(() => reject(new Error('Classification timeout')), 5000),
58
+ setTimeout(() => reject(new Error('Classification timeout')), CLASSIFICATION_TIMEOUT_MS),
57
59
  ),
58
60
  ]);
59
61
 
@@ -21,7 +21,7 @@ import { registerSkillTools } from '../tools/registry.js';
21
21
  import { buildComputerUseSystemPrompt } from '../config/computer-use-prompt.js';
22
22
  import { getSandboxWorkingDir } from '../util/platform.js';
23
23
  import { getConfig } from '../config/loader.js';
24
- import { projectSkillTools, resetSkillToolProjection } from './session-skill-tools.js';
24
+ import { projectSkillTools, resetSkillToolProjection, type SkillProjectionCache } from './session-skill-tools.js';
25
25
  import { getLogger } from '../util/logger.js';
26
26
 
27
27
  const log = getLogger('computer-use-session');
@@ -60,6 +60,7 @@ export class ComputerUseSession {
60
60
  private readonly onTerminal?: (sessionId: string) => void;
61
61
  private readonly preactivatedSkillIds: string[];
62
62
  private readonly skillProjectionState = new Map<string, string>();
63
+ private readonly skillProjectionCache: SkillProjectionCache = {};
63
64
 
64
65
  private state: SessionState = 'idle';
65
66
  private stepCount = 0;
@@ -235,6 +236,7 @@ export class ComputerUseSession {
235
236
  const projection = projectSkillTools([], {
236
237
  preactivatedSkillIds: this.preactivatedSkillIds,
237
238
  previouslyActiveSkillIds: this.skillProjectionState,
239
+ cache: this.skillProjectionCache,
238
240
  });
239
241
 
240
242
  if (projection.toolDefinitions.length === 0) {
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Temporal context formatter for future weekday/weekend grounding.
3
+ *
4
+ * Produces a compact, deterministic payload describing the current date,
5
+ * upcoming weekend/work-week windows, and a short horizon of labelled
6
+ * future dates. Intended for runtime injection into the model context.
7
+ */
8
+
9
+ export interface TemporalContextOptions {
10
+ /** Override current time (epoch ms) for deterministic tests. */
11
+ nowMs?: number;
12
+ /** IANA timezone (e.g. "America/New_York"). Defaults to host timezone. */
13
+ timeZone?: string;
14
+ /** Number of future days to list (default 14, hard-capped at 14). */
15
+ horizonDays?: number;
16
+ }
17
+
18
+ const MAX_OUTPUT_CHARS = 1500;
19
+ const MAX_HORIZON_ENTRIES = 14;
20
+
21
+ const WEEKDAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] as const;
22
+
23
+ /**
24
+ * Get the local date parts for a given instant in the specified timezone.
25
+ */
26
+ function localDateParts(date: Date, timeZone: string): { year: number; month: number; day: number; weekday: number } {
27
+ const fmt = new Intl.DateTimeFormat('en-US', {
28
+ timeZone,
29
+ year: 'numeric',
30
+ month: '2-digit',
31
+ day: '2-digit',
32
+ weekday: 'short',
33
+ });
34
+ const parts = fmt.formatToParts(date);
35
+ const get = (t: string) => parts.find((p) => p.type === t)?.value ?? '';
36
+ // Weekday as 0-6 (Sun-Sat)
37
+ const weekdayShort = get('weekday');
38
+ const weekdayMap: Record<string, number> = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
39
+ return {
40
+ year: parseInt(get('year'), 10),
41
+ month: parseInt(get('month'), 10),
42
+ day: parseInt(get('day'), 10),
43
+ weekday: weekdayMap[weekdayShort] ?? 0,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Format a Date as YYYY-MM-DD in the given timezone.
49
+ */
50
+ function formatLocalDate(date: Date, timeZone: string): string {
51
+ const p = localDateParts(date, timeZone);
52
+ return `${p.year}-${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
53
+ }
54
+
55
+ /**
56
+ * Advance a date by `days` calendar days in the given timezone.
57
+ *
58
+ * Computes the local date, adds days to the day component, then anchors
59
+ * the result at noon local time to avoid DST-transition edge cases.
60
+ */
61
+ function addDays(date: Date, days: number, timeZone: string): Date {
62
+ const parts = localDateParts(date, timeZone);
63
+ // Use Date.UTC for calendar overflow (e.g. Jan 32 → Feb 1).
64
+ const ref = new Date(Date.UTC(parts.year, parts.month - 1, parts.day + days));
65
+ const tY = ref.getUTCFullYear();
66
+ const tM = ref.getUTCMonth() + 1;
67
+ const tD = ref.getUTCDate();
68
+ // Noon UTC covers UTC-12 through ~UTC+11. For far-east timezones
69
+ // (UTC+12/+13/+14) noon UTC is already the next local day, so fall
70
+ // back to midnight UTC which resolves correctly there.
71
+ const noonUTC = new Date(Date.UTC(tY, tM - 1, tD, 12, 0, 0));
72
+ const r = localDateParts(noonUTC, timeZone);
73
+ if (r.year === tY && r.month === tM && r.day === tD) {
74
+ return noonUTC;
75
+ }
76
+ return new Date(Date.UTC(tY, tM - 1, tD, 0, 0, 0));
77
+ }
78
+
79
+ /**
80
+ * Build a compact temporal context string for model injection.
81
+ *
82
+ * Output is hard-capped at {@link MAX_OUTPUT_CHARS} characters and
83
+ * {@link MAX_HORIZON_ENTRIES} horizon entries.
84
+ */
85
+ export function buildTemporalContext(options: TemporalContextOptions = {}): string {
86
+ const now = new Date(options.nowMs ?? Date.now());
87
+ const timeZone = options.timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
88
+ const horizonDays = Math.min(options.horizonDays ?? MAX_HORIZON_ENTRIES, MAX_HORIZON_ENTRIES);
89
+
90
+ const todayParts = localDateParts(now, timeZone);
91
+ const todayStr = formatLocalDate(now, timeZone);
92
+ const todayWeekday = WEEKDAY_NAMES[todayParts.weekday];
93
+
94
+ // ── Next weekend (Saturday-Sunday) ──
95
+ const daysUntilSaturday = (6 - todayParts.weekday + 7) % 7 || 7;
96
+ const nextSaturday = addDays(now, daysUntilSaturday, timeZone);
97
+ const nextSunday = addDays(now, daysUntilSaturday + 1, timeZone);
98
+
99
+ // ── Next work week (Monday-Friday) ──
100
+ const daysUntilMonday = (1 - todayParts.weekday + 7) % 7 || 7;
101
+ const nextMonday = addDays(now, daysUntilMonday, timeZone);
102
+ const nextFriday = addDays(now, daysUntilMonday + 4, timeZone);
103
+
104
+ // ── Horizon list ──
105
+ const horizonLines: string[] = [];
106
+ for (let i = 1; i <= horizonDays; i++) {
107
+ const futureDate = addDays(now, i, timeZone);
108
+ const futureParts = localDateParts(futureDate, timeZone);
109
+ const label = WEEKDAY_NAMES[futureParts.weekday];
110
+ horizonLines.push(` ${formatLocalDate(futureDate, timeZone)} ${label}`);
111
+ }
112
+
113
+ const lines = [
114
+ `<temporal_context>`,
115
+ `Today: ${todayStr} (${todayWeekday})`,
116
+ `Timezone: ${timeZone}`,
117
+ ``,
118
+ `Week definitions: work week = Monday–Friday, weekend = Saturday–Sunday`,
119
+ ``,
120
+ `Next weekend: ${formatLocalDate(nextSaturday, timeZone)} – ${formatLocalDate(nextSunday, timeZone)}`,
121
+ `Next work week: ${formatLocalDate(nextMonday, timeZone)} – ${formatLocalDate(nextFriday, timeZone)}`,
122
+ ``,
123
+ `Upcoming dates:`,
124
+ ...horizonLines,
125
+ `</temporal_context>`,
126
+ ];
127
+
128
+ let output = lines.join('\n');
129
+
130
+ // Hard cap: truncate if somehow over budget (shouldn't happen with 14 entries).
131
+ if (output.length > MAX_OUTPUT_CHARS) {
132
+ output = output.slice(0, MAX_OUTPUT_CHARS - 25) + '\n</temporal_context>';
133
+ }
134
+
135
+ return output;
136
+ }
@@ -18,7 +18,7 @@ import type {
18
18
  AppUpdatePreviewRequest,
19
19
  UiSurfaceShow,
20
20
  } from '../ipc-protocol.js';
21
- import { log, compareSemver, createSigningCallback, type HandlerContext } from './shared.js';
21
+ import { log, compareSemver, createSigningCallback, defineHandlers, type HandlerContext } from './shared.js';
22
22
 
23
23
  export function handleAppDataRequest(
24
24
  msg: AppDataRequest,
@@ -444,3 +444,18 @@ export function handleGalleryInstall(
444
444
  });
445
445
  }
446
446
  }
447
+
448
+ export const appHandlers = defineHandlers({
449
+ app_data_request: handleAppDataRequest,
450
+ app_open_request: handleAppOpenRequest,
451
+ app_update_preview: handleAppUpdatePreview,
452
+ app_preview_request: handleAppPreview,
453
+ apps_list: (_msg, socket, ctx) => handleAppsList(socket, ctx),
454
+ shared_apps_list: (_msg, socket, ctx) => handleSharedAppsList(socket, ctx),
455
+ shared_app_delete: handleSharedAppDelete,
456
+ fork_shared_app: handleForkSharedApp,
457
+ share_app_cloud: handleShareAppCloud,
458
+ bundle_app: handleBundleApp,
459
+ gallery_list: (_msg, socket, ctx) => handleGalleryList(socket, ctx),
460
+ gallery_install: handleGalleryInstall,
461
+ });
@@ -0,0 +1,54 @@
1
+ import { browserManager } from '../../tools/browser/browser-manager.js';
2
+ import { log, defineHandlers } from './shared.js';
3
+
4
+ export const browserHandlers = defineHandlers({
5
+ browser_cdp_response: (msg) => {
6
+ browserManager.resolveCDPResponse(msg.sessionId, msg.success, msg.declined);
7
+ },
8
+
9
+ browser_user_click: async (msg) => {
10
+ try {
11
+ const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
12
+ const viewport = await page.evaluate('(() => ({ vw: window.innerWidth, vh: window.innerHeight }))()') as { vw: number; vh: number };
13
+ const scale = Math.min(1280 / viewport.vw, 960 / viewport.vh);
14
+ const pageX = msg.x / scale;
15
+ const pageY = msg.y / scale;
16
+ const options: Record<string, unknown> = {};
17
+ if (msg.button === 'right') options.button = 'right';
18
+ if (msg.doubleClick) options.clickCount = 2;
19
+ await page.mouse.click(pageX, pageY, options);
20
+ } catch (err) {
21
+ log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user click');
22
+ }
23
+ },
24
+
25
+ browser_user_scroll: async (msg) => {
26
+ try {
27
+ const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
28
+ await page.mouse.wheel(msg.deltaX, msg.deltaY);
29
+ } catch (err) {
30
+ log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user scroll');
31
+ }
32
+ },
33
+
34
+ browser_user_keypress: async (msg) => {
35
+ try {
36
+ const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
37
+ const combo = msg.modifiers?.length ? [...msg.modifiers, msg.key].join('+') : msg.key;
38
+ await page.keyboard.press(combo);
39
+ } catch (err) {
40
+ log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user keypress');
41
+ }
42
+ },
43
+
44
+ browser_interactive_mode: (msg, socket, ctx) => {
45
+ log.info({ sessionId: msg.sessionId, enabled: msg.enabled }, 'Interactive mode toggled');
46
+ browserManager.setInteractiveMode(msg.sessionId, msg.enabled);
47
+ ctx.send(socket, {
48
+ type: 'browser_interactive_mode_changed',
49
+ sessionId: msg.sessionId,
50
+ surfaceId: msg.surfaceId,
51
+ enabled: msg.enabled,
52
+ });
53
+ },
54
+ });
@@ -10,7 +10,7 @@ import type {
10
10
  CuObservation,
11
11
  ServerMessage,
12
12
  } from '../ipc-protocol.js';
13
- import { log, type HandlerContext } from './shared.js';
13
+ import { log, defineHandlers, type HandlerContext } from './shared.js';
14
14
 
15
15
  const cuObservationSequenceBySession = new Map<string, number>();
16
16
 
@@ -179,3 +179,9 @@ export async function handleCuObservation(
179
179
  log.error({ err, sessionId: msg.sessionId }, 'Error handling CU observation');
180
180
  });
181
181
  }
182
+
183
+ export const computerUseHandlers = defineHandlers({
184
+ cu_session_create: handleCuSessionCreate,
185
+ cu_session_abort: handleCuSessionAbort,
186
+ cu_observation: handleCuObservation,
187
+ });
@@ -5,7 +5,7 @@ import { addRule, removeRule, updateRule, getAllRules, acceptStarterBundle } fro
5
5
  import { listSchedules, updateSchedule, deleteSchedule, describeCronExpression } from '../../schedule/schedule-store.js';
6
6
  import { listReminders, cancelReminder } from '../../tools/reminder/reminder-store.js';
7
7
  import { getSecureKey, setSecureKey, deleteSecureKey } from '../../security/secure-keys.js';
8
- import { upsertCredentialMetadata, deleteCredentialMetadata } from '../../tools/credentials/metadata-store.js';
8
+ import { upsertCredentialMetadata, deleteCredentialMetadata, getCredentialMetadata } from '../../tools/credentials/metadata-store.js';
9
9
  import { postToSlackWebhook } from '../../slack/slack-webhook.js';
10
10
  import { getApp } from '../../memory/app-store.js';
11
11
  import type {
@@ -20,8 +20,9 @@ import type {
20
20
  ShareToSlackRequest,
21
21
  SlackWebhookConfigRequest,
22
22
  VercelApiConfigRequest,
23
+ TwitterIntegrationConfigRequest,
23
24
  } from '../ipc-protocol.js';
24
- import { log, type HandlerContext } from './shared.js';
25
+ import { log, CONFIG_RELOAD_DEBOUNCE_MS, defineHandlers, type HandlerContext } from './shared.js';
25
26
  import { MODEL_TO_PROVIDER } from '../session-slash.js';
26
27
 
27
28
  export function handleModelGet(socket: net.Socket, ctx: HandlerContext): void {
@@ -96,7 +97,7 @@ export function handleModelSet(
96
97
  }
97
98
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
98
99
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
99
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
100
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
100
101
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
101
102
 
102
103
  // Re-initialize provider with the new model so LLM calls use it
@@ -145,7 +146,7 @@ export function handleImageGenModelSet(
145
146
  }
146
147
  const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
147
148
  if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
148
- const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, 300);
149
+ const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
149
150
  ctx.debounceTimers.set('__suppress_reset__', resetTimer);
150
151
 
151
152
  ctx.updateConfigFingerprint();
@@ -237,13 +238,15 @@ export function handleSchedulesList(socket: net.Socket, ctx: HandlerContext): vo
237
238
  id: j.id,
238
239
  name: j.name,
239
240
  enabled: j.enabled,
241
+ syntax: j.syntax,
242
+ expression: j.expression,
240
243
  cronExpression: j.cronExpression,
241
244
  timezone: j.timezone,
242
245
  message: j.message,
243
246
  nextRunAt: j.nextRunAt,
244
247
  lastRunAt: j.lastRunAt,
245
248
  lastStatus: j.lastStatus,
246
- description: describeCronExpression(j.cronExpression),
249
+ description: j.syntax === 'cron' ? describeCronExpression(j.cronExpression) : j.expression,
247
250
  })),
248
251
  });
249
252
  }
@@ -455,6 +458,140 @@ export function handleVercelApiConfig(
455
458
  }
456
459
  }
457
460
 
461
+ export function handleTwitterIntegrationConfig(
462
+ msg: TwitterIntegrationConfigRequest,
463
+ socket: net.Socket,
464
+ ctx: HandlerContext,
465
+ ): void {
466
+ try {
467
+ if (msg.action === 'get') {
468
+ const raw = loadRawConfig();
469
+ const mode = (raw.twitterIntegrationMode as 'local_byo' | 'managed' | undefined) ?? 'local_byo';
470
+ const localClientConfigured = !!getSecureKey('credential:integration:twitter:oauth_client_id');
471
+ const connected = !!getSecureKey('credential:integration:twitter:access_token');
472
+ const meta = getCredentialMetadata('integration:twitter', 'access_token');
473
+ ctx.send(socket, {
474
+ type: 'twitter_integration_config_response',
475
+ success: true,
476
+ mode,
477
+ managedAvailable: false,
478
+ localClientConfigured,
479
+ connected,
480
+ accountInfo: meta?.accountInfo ?? undefined,
481
+ });
482
+ } else if (msg.action === 'set_mode') {
483
+ const raw = loadRawConfig();
484
+ raw.twitterIntegrationMode = msg.mode ?? 'local_byo';
485
+ saveRawConfig(raw);
486
+ ctx.send(socket, {
487
+ type: 'twitter_integration_config_response',
488
+ success: true,
489
+ mode: msg.mode ?? 'local_byo',
490
+ managedAvailable: false,
491
+ localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
492
+ connected: !!getSecureKey('credential:integration:twitter:access_token'),
493
+ });
494
+ } else if (msg.action === 'set_local_client') {
495
+ if (!msg.clientId) {
496
+ ctx.send(socket, {
497
+ type: 'twitter_integration_config_response',
498
+ success: false,
499
+ managedAvailable: false,
500
+ localClientConfigured: false,
501
+ connected: false,
502
+ error: 'clientId is required for set_local_client action',
503
+ });
504
+ return;
505
+ }
506
+ const storedId = setSecureKey('credential:integration:twitter:oauth_client_id', msg.clientId);
507
+ if (!storedId) {
508
+ ctx.send(socket, {
509
+ type: 'twitter_integration_config_response',
510
+ success: false,
511
+ managedAvailable: false,
512
+ localClientConfigured: false,
513
+ connected: false,
514
+ error: 'Failed to store client ID in secure storage',
515
+ });
516
+ return;
517
+ }
518
+ if (msg.clientSecret) {
519
+ const storedSecret = setSecureKey('credential:integration:twitter:oauth_client_secret', msg.clientSecret);
520
+ if (!storedSecret) {
521
+ // Roll back the already-persisted client ID to avoid inconsistent OAuth state
522
+ deleteSecureKey('credential:integration:twitter:oauth_client_id');
523
+ ctx.send(socket, {
524
+ type: 'twitter_integration_config_response',
525
+ success: false,
526
+ managedAvailable: false,
527
+ localClientConfigured: false,
528
+ connected: false,
529
+ error: 'Failed to store client secret in secure storage',
530
+ });
531
+ return;
532
+ }
533
+ } else {
534
+ // Clear any stale secret when updating client without a secret (e.g. switching to PKCE)
535
+ deleteSecureKey('credential:integration:twitter:oauth_client_secret');
536
+ }
537
+ ctx.send(socket, {
538
+ type: 'twitter_integration_config_response',
539
+ success: true,
540
+ managedAvailable: false,
541
+ localClientConfigured: true,
542
+ connected: !!getSecureKey('credential:integration:twitter:access_token'),
543
+ });
544
+ } else if (msg.action === 'clear_local_client') {
545
+ // If connected, disconnect first
546
+ if (getSecureKey('credential:integration:twitter:access_token')) {
547
+ deleteSecureKey('credential:integration:twitter:access_token');
548
+ deleteSecureKey('credential:integration:twitter:refresh_token');
549
+ deleteCredentialMetadata('integration:twitter', 'access_token');
550
+ }
551
+ deleteSecureKey('credential:integration:twitter:oauth_client_id');
552
+ deleteSecureKey('credential:integration:twitter:oauth_client_secret');
553
+ ctx.send(socket, {
554
+ type: 'twitter_integration_config_response',
555
+ success: true,
556
+ managedAvailable: false,
557
+ localClientConfigured: false,
558
+ connected: false,
559
+ });
560
+ } else if (msg.action === 'disconnect') {
561
+ deleteSecureKey('credential:integration:twitter:access_token');
562
+ deleteSecureKey('credential:integration:twitter:refresh_token');
563
+ deleteCredentialMetadata('integration:twitter', 'access_token');
564
+ ctx.send(socket, {
565
+ type: 'twitter_integration_config_response',
566
+ success: true,
567
+ managedAvailable: false,
568
+ localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
569
+ connected: false,
570
+ });
571
+ } else {
572
+ ctx.send(socket, {
573
+ type: 'twitter_integration_config_response',
574
+ success: false,
575
+ managedAvailable: false,
576
+ localClientConfigured: false,
577
+ connected: false,
578
+ error: `Unknown action: ${String((msg as unknown as Record<string, unknown>).action)}`,
579
+ });
580
+ }
581
+ } catch (err) {
582
+ const message = err instanceof Error ? err.message : String(err);
583
+ log.error({ err }, 'Failed to handle Twitter integration config');
584
+ ctx.send(socket, {
585
+ type: 'twitter_integration_config_response',
586
+ success: false,
587
+ managedAvailable: false,
588
+ localClientConfigured: false,
589
+ connected: false,
590
+ error: message,
591
+ });
592
+ }
593
+ }
594
+
458
595
  export function handleEnvVarsRequest(socket: net.Socket, ctx: HandlerContext): void {
459
596
  const vars: Record<string, string> = {};
460
597
  for (const [key, value] of Object.entries(process.env)) {
@@ -462,3 +599,24 @@ export function handleEnvVarsRequest(socket: net.Socket, ctx: HandlerContext): v
462
599
  }
463
600
  ctx.send(socket, { type: 'env_vars_response', vars });
464
601
  }
602
+
603
+ export const configHandlers = defineHandlers({
604
+ model_get: (_msg, socket, ctx) => handleModelGet(socket, ctx),
605
+ model_set: handleModelSet,
606
+ image_gen_model_set: handleImageGenModelSet,
607
+ add_trust_rule: handleAddTrustRule,
608
+ trust_rules_list: (_msg, socket, ctx) => handleTrustRulesList(socket, ctx),
609
+ remove_trust_rule: handleRemoveTrustRule,
610
+ update_trust_rule: handleUpdateTrustRule,
611
+ accept_starter_bundle: (_msg, socket, ctx) => handleAcceptStarterBundle(socket, ctx),
612
+ schedules_list: (_msg, socket, ctx) => handleSchedulesList(socket, ctx),
613
+ schedule_toggle: handleScheduleToggle,
614
+ schedule_remove: handleScheduleRemove,
615
+ reminders_list: (_msg, socket, ctx) => handleRemindersList(socket, ctx),
616
+ reminder_cancel: handleReminderCancel,
617
+ share_to_slack: handleShareToSlack,
618
+ slack_webhook_config: handleSlackWebhookConfig,
619
+ vercel_api_config: handleVercelApiConfig,
620
+ twitter_integration_config: handleTwitterIntegrationConfig,
621
+ env_vars_request: (_msg, socket, ctx) => handleEnvVarsRequest(socket, ctx),
622
+ });
@@ -9,7 +9,7 @@ import archiver from 'archiver';
9
9
  import { getDb } from '../../memory/db.js';
10
10
  import { messages, toolInvocations, llmUsageEvents, llmRequestLogs } from '../../memory/schema.js';
11
11
  import type { DiagnosticsExportRequest } from '../ipc-protocol.js';
12
- import { log, type HandlerContext } from './shared.js';
12
+ import { log, defineHandlers, type HandlerContext } from './shared.js';
13
13
 
14
14
  const MAX_CONTENT_LENGTH = 500;
15
15
 
@@ -332,3 +332,7 @@ export async function handleDiagnosticsExport(
332
332
  });
333
333
  }
334
334
  }
335
+
336
+ export const diagnosticsHandlers = defineHandlers({
337
+ diagnostics_export_request: handleDiagnosticsExport,
338
+ });