@vellumai/assistant 0.3.15 → 0.3.18

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 (306) hide show
  1. package/ARCHITECTURE.md +211 -12
  2. package/Dockerfile +1 -1
  3. package/README.md +11 -5
  4. package/docs/architecture/http-token-refresh.md +274 -0
  5. package/docs/architecture/memory.md +5 -4
  6. package/docs/architecture/scheduling.md +4 -88
  7. package/docs/runbook-trusted-contacts.md +283 -0
  8. package/docs/trusted-contact-access.md +247 -0
  9. package/package.json +1 -1
  10. package/scripts/ipc/check-swift-decoder-drift.ts +2 -0
  11. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2 -6
  12. package/src/__tests__/access-request-decision.test.ts +328 -0
  13. package/src/__tests__/asset-materialize-tool.test.ts +7 -7
  14. package/src/__tests__/asset-search-tool.test.ts +15 -15
  15. package/src/__tests__/attachments-store.test.ts +13 -13
  16. package/src/__tests__/call-controller.test.ts +150 -4
  17. package/src/__tests__/call-conversation-messages.test.ts +2 -2
  18. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  19. package/src/__tests__/call-start-guardian-guard.test.ts +93 -0
  20. package/src/__tests__/channel-approval-routes.test.ts +108 -12
  21. package/src/__tests__/channel-guardian.test.ts +19 -15
  22. package/src/__tests__/checker.test.ts +103 -48
  23. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +2 -2
  24. package/src/__tests__/config-watcher.test.ts +356 -0
  25. package/src/__tests__/conversation-pairing.test.ts +127 -27
  26. package/src/__tests__/conversation-store.test.ts +36 -36
  27. package/src/__tests__/date-context.test.ts +179 -1
  28. package/src/__tests__/db-migration-rollback.test.ts +4 -7
  29. package/src/__tests__/deterministic-verification-control-plane.test.ts +5 -5
  30. package/src/__tests__/emit-signal-routing-intent.test.ts +179 -0
  31. package/src/__tests__/gateway-only-guard.test.ts +188 -0
  32. package/src/__tests__/guardian-action-conversation-turn.test.ts +451 -0
  33. package/src/__tests__/guardian-action-copy-generator.test.ts +197 -0
  34. package/src/__tests__/guardian-action-followup-executor.test.ts +379 -0
  35. package/src/__tests__/guardian-action-followup-store.test.ts +376 -0
  36. package/src/__tests__/guardian-action-late-reply.test.ts +425 -0
  37. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +71 -0
  38. package/src/__tests__/guardian-action-store.test.ts +182 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +9 -9
  40. package/src/__tests__/guardian-dispatch.test.ts +120 -0
  41. package/src/__tests__/guardian-outbound-http.test.ts +194 -2
  42. package/src/__tests__/guardian-verification-intent-routing.test.ts +179 -0
  43. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +141 -0
  44. package/src/__tests__/handlers-telegram-config.test.ts +6 -6
  45. package/src/__tests__/hooks-runner.test.ts +13 -4
  46. package/src/__tests__/ingress-routes-http.test.ts +443 -0
  47. package/src/__tests__/intent-routing.test.ts +14 -0
  48. package/src/__tests__/ipc-snapshot.test.ts +23 -5
  49. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
  50. package/src/__tests__/memory-regressions.test.ts +16 -12
  51. package/src/__tests__/non-member-access-request.test.ts +281 -0
  52. package/src/__tests__/notification-broadcaster.test.ts +115 -4
  53. package/src/__tests__/notification-decision-strategy.test.ts +138 -1
  54. package/src/__tests__/notification-deep-link.test.ts +44 -1
  55. package/src/__tests__/notification-guardian-path.test.ts +157 -0
  56. package/src/__tests__/notification-routing-intent.test.ts +11 -1
  57. package/src/__tests__/notification-thread-candidate-validation.test.ts +215 -0
  58. package/src/__tests__/notification-thread-candidates.test.ts +166 -0
  59. package/src/__tests__/recording-intent.test.ts +1 -0
  60. package/src/__tests__/recording-state-machine.test.ts +328 -17
  61. package/src/__tests__/registry.test.ts +17 -8
  62. package/src/__tests__/relay-server.test.ts +105 -0
  63. package/src/__tests__/reminder.test.ts +13 -0
  64. package/src/__tests__/runtime-attachment-metadata.test.ts +4 -4
  65. package/src/__tests__/scheduler-recurrence.test.ts +50 -0
  66. package/src/__tests__/server-history-render.test.ts +8 -8
  67. package/src/__tests__/session-agent-loop.test.ts +1 -0
  68. package/src/__tests__/session-runtime-assembly.test.ts +49 -0
  69. package/src/__tests__/session-skill-tools.test.ts +1 -0
  70. package/src/__tests__/skill-projection.benchmark.test.ts +11 -3
  71. package/src/__tests__/slack-channel-config.test.ts +230 -0
  72. package/src/__tests__/subagent-manager-notify.test.ts +4 -4
  73. package/src/__tests__/swarm-session-integration.test.ts +2 -2
  74. package/src/__tests__/system-prompt.test.ts +43 -0
  75. package/src/__tests__/task-management-tools.test.ts +3 -3
  76. package/src/__tests__/task-tools.test.ts +3 -3
  77. package/src/__tests__/trust-store.test.ts +38 -22
  78. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +489 -0
  79. package/src/__tests__/trusted-contact-multichannel.test.ts +405 -0
  80. package/src/__tests__/trusted-contact-verification.test.ts +360 -0
  81. package/src/__tests__/update-bulletin-format.test.ts +119 -0
  82. package/src/__tests__/update-bulletin-state.test.ts +129 -0
  83. package/src/__tests__/update-bulletin.test.ts +323 -0
  84. package/src/__tests__/update-template-contract.test.ts +24 -0
  85. package/src/__tests__/voice-session-bridge.test.ts +109 -9
  86. package/src/agent/loop.ts +2 -2
  87. package/src/amazon/client.ts +2 -3
  88. package/src/calls/call-controller.ts +241 -39
  89. package/src/calls/call-conversation-messages.ts +2 -2
  90. package/src/calls/call-domain.ts +10 -3
  91. package/src/calls/call-pointer-messages.ts +17 -5
  92. package/src/calls/guardian-action-sweep.ts +77 -36
  93. package/src/calls/guardian-dispatch.ts +8 -0
  94. package/src/calls/relay-server.ts +51 -12
  95. package/src/calls/twilio-routes.ts +3 -1
  96. package/src/calls/types.ts +1 -1
  97. package/src/calls/voice-session-bridge.ts +8 -6
  98. package/src/cli/core-commands.ts +43 -3
  99. package/src/cli/map.ts +8 -5
  100. package/src/config/bundled-skills/phone-calls/SKILL.md +16 -1
  101. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  102. package/src/config/bundled-skills/tasks/TOOLS.json +4 -4
  103. package/src/config/bundled-skills/time-based-actions/SKILL.md +11 -1
  104. package/src/config/computer-use-prompt.ts +1 -0
  105. package/src/config/core-schema.ts +16 -0
  106. package/src/config/env-registry.ts +1 -0
  107. package/src/config/env.ts +16 -1
  108. package/src/config/memory-schema.ts +5 -0
  109. package/src/config/schema.ts +4 -0
  110. package/src/config/system-prompt.ts +69 -2
  111. package/src/config/templates/BOOTSTRAP.md +1 -1
  112. package/src/config/templates/IDENTITY.md +8 -4
  113. package/src/config/templates/SOUL.md +14 -0
  114. package/src/config/templates/UPDATES.md +15 -0
  115. package/src/config/templates/USER.md +5 -1
  116. package/src/config/types.ts +1 -0
  117. package/src/config/update-bulletin-format.ts +54 -0
  118. package/src/config/update-bulletin-state.ts +49 -0
  119. package/src/config/update-bulletin-template-path.ts +6 -0
  120. package/src/config/update-bulletin.ts +97 -0
  121. package/src/config/vellum-skills/catalog.json +6 -0
  122. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  123. package/src/config/vellum-skills/guardian-verify-setup/SKILL.md +44 -10
  124. package/src/config/vellum-skills/telegram-setup/SKILL.md +4 -4
  125. package/src/config/vellum-skills/trusted-contacts/SKILL.md +147 -0
  126. package/src/config/vellum-skills/twilio-setup/SKILL.md +2 -2
  127. package/src/context/window-manager.ts +43 -3
  128. package/src/daemon/config-watcher.ts +4 -2
  129. package/src/daemon/connection-policy.ts +21 -1
  130. package/src/daemon/daemon-control.ts +219 -8
  131. package/src/daemon/date-context.ts +174 -1
  132. package/src/daemon/guardian-action-generators.ts +175 -0
  133. package/src/daemon/guardian-verification-intent.ts +120 -0
  134. package/src/daemon/handlers/apps.ts +1 -3
  135. package/src/daemon/handlers/config-channels.ts +2 -2
  136. package/src/daemon/handlers/config-heartbeat.ts +1 -1
  137. package/src/daemon/handlers/config-inbox.ts +55 -159
  138. package/src/daemon/handlers/config-ingress.ts +1 -1
  139. package/src/daemon/handlers/config-integrations.ts +1 -1
  140. package/src/daemon/handlers/config-platform.ts +1 -1
  141. package/src/daemon/handlers/config-scheduling.ts +2 -2
  142. package/src/daemon/handlers/config-slack-channel.ts +190 -0
  143. package/src/daemon/handlers/config-telegram.ts +1 -1
  144. package/src/daemon/handlers/config-twilio.ts +1 -1
  145. package/src/daemon/handlers/config-voice.ts +100 -0
  146. package/src/daemon/handlers/config.ts +3 -0
  147. package/src/daemon/handlers/identity.ts +45 -25
  148. package/src/daemon/handlers/misc.ts +83 -5
  149. package/src/daemon/handlers/navigate-settings.ts +27 -0
  150. package/src/daemon/handlers/recording.ts +270 -144
  151. package/src/daemon/handlers/sessions.ts +100 -17
  152. package/src/daemon/handlers/subagents.ts +3 -3
  153. package/src/daemon/handlers/work-items.ts +10 -7
  154. package/src/daemon/ipc-contract/integrations.ts +9 -1
  155. package/src/daemon/ipc-contract/messages.ts +4 -0
  156. package/src/daemon/ipc-contract/sessions.ts +1 -1
  157. package/src/daemon/ipc-contract/settings.ts +26 -0
  158. package/src/daemon/ipc-contract/shared.ts +2 -0
  159. package/src/daemon/ipc-contract/work-items.ts +1 -7
  160. package/src/daemon/ipc-contract/workspace.ts +12 -1
  161. package/src/daemon/ipc-contract-inventory.json +6 -1
  162. package/src/daemon/ipc-contract.ts +5 -1
  163. package/src/daemon/lifecycle.ts +314 -266
  164. package/src/daemon/recording-intent.ts +0 -41
  165. package/src/daemon/response-tier.ts +2 -2
  166. package/src/daemon/server.ts +31 -9
  167. package/src/daemon/session-agent-loop-handlers.ts +34 -9
  168. package/src/daemon/session-agent-loop.ts +15 -8
  169. package/src/daemon/session-history.ts +3 -2
  170. package/src/daemon/session-media-retry.ts +3 -0
  171. package/src/daemon/session-messaging.ts +38 -4
  172. package/src/daemon/session-notifiers.ts +2 -2
  173. package/src/daemon/session-process.ts +546 -59
  174. package/src/daemon/session-queue-manager.ts +2 -0
  175. package/src/daemon/session-runtime-assembly.ts +39 -0
  176. package/src/daemon/session-skill-tools.ts +13 -4
  177. package/src/daemon/session-tool-setup.ts +5 -6
  178. package/src/daemon/session.ts +19 -8
  179. package/src/daemon/tls-certs.ts +60 -13
  180. package/src/daemon/tool-side-effects.ts +13 -5
  181. package/src/gallery/default-gallery.ts +32 -9
  182. package/src/influencer/client.ts +2 -1
  183. package/src/memory/channel-delivery-store.ts +35 -567
  184. package/src/memory/channel-guardian-store.ts +63 -1317
  185. package/src/memory/conflict-store.ts +4 -4
  186. package/src/memory/conversation-attention-store.ts +0 -3
  187. package/src/memory/conversation-crud.ts +668 -0
  188. package/src/memory/conversation-queries.ts +361 -0
  189. package/src/memory/conversation-store.ts +44 -983
  190. package/src/memory/db-connection.ts +3 -0
  191. package/src/memory/db-init.ts +33 -0
  192. package/src/memory/delivery-channels.ts +175 -0
  193. package/src/memory/delivery-crud.ts +211 -0
  194. package/src/memory/delivery-status.ts +199 -0
  195. package/src/memory/embedding-backend.ts +70 -4
  196. package/src/memory/embedding-local.ts +12 -2
  197. package/src/memory/entity-extractor.ts +3 -8
  198. package/src/memory/fts-reconciler.ts +136 -0
  199. package/src/memory/guardian-action-store.ts +418 -5
  200. package/src/memory/guardian-approvals.ts +569 -0
  201. package/src/memory/guardian-bindings.ts +130 -0
  202. package/src/memory/guardian-rate-limits.ts +196 -0
  203. package/src/memory/guardian-verification.ts +521 -0
  204. package/src/memory/job-handlers/index-maintenance.ts +2 -1
  205. package/src/memory/job-utils.ts +8 -5
  206. package/src/memory/jobs-store.ts +66 -6
  207. package/src/memory/jobs-worker.ts +23 -1
  208. package/src/memory/migrations/030-guardian-action-followup.ts +21 -0
  209. package/src/memory/migrations/030-guardian-verification-purpose.ts +17 -0
  210. package/src/memory/migrations/031-conversations-thread-type-index.ts +5 -0
  211. package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +15 -0
  212. package/src/memory/migrations/032-notification-delivery-thread-decision.ts +20 -0
  213. package/src/memory/migrations/100-core-tables.ts +1 -1
  214. package/src/memory/migrations/101-watchers-and-logs.ts +4 -0
  215. package/src/memory/migrations/108-tasks-and-work-items.ts +1 -1
  216. package/src/memory/migrations/112-assistant-inbox.ts +1 -1
  217. package/src/memory/migrations/113-late-migrations.ts +1 -1
  218. package/src/memory/migrations/116-messages-fts.ts +13 -0
  219. package/src/memory/migrations/119-schema-indexes-and-columns.ts +37 -0
  220. package/src/memory/migrations/120-fk-cascade-rebuilds.ts +161 -0
  221. package/src/memory/migrations/index.ts +10 -3
  222. package/src/memory/migrations/validate-migration-state.ts +114 -15
  223. package/src/memory/qdrant-circuit-breaker.ts +105 -0
  224. package/src/memory/retriever.ts +46 -13
  225. package/src/memory/schema-migration.ts +4 -0
  226. package/src/memory/schema.ts +31 -8
  227. package/src/memory/search/semantic.ts +8 -90
  228. package/src/notifications/README.md +159 -18
  229. package/src/notifications/broadcaster.ts +69 -33
  230. package/src/notifications/conversation-pairing.ts +99 -21
  231. package/src/notifications/decision-engine.ts +176 -8
  232. package/src/notifications/deliveries-store.ts +39 -8
  233. package/src/notifications/emit-signal.ts +1 -0
  234. package/src/notifications/preferences-store.ts +7 -7
  235. package/src/notifications/thread-candidates.ts +269 -0
  236. package/src/notifications/types.ts +19 -0
  237. package/src/permissions/checker.ts +1 -16
  238. package/src/permissions/defaults.ts +25 -5
  239. package/src/permissions/prompter.ts +17 -0
  240. package/src/permissions/trust-store.ts +2 -0
  241. package/src/providers/failover.ts +19 -0
  242. package/src/providers/registry.ts +46 -1
  243. package/src/runtime/approval-message-composer.ts +1 -1
  244. package/src/runtime/channel-guardian-service.ts +15 -3
  245. package/src/runtime/channel-retry-sweep.ts +7 -2
  246. package/src/runtime/guardian-action-conversation-turn.ts +85 -0
  247. package/src/runtime/guardian-action-followup-executor.ts +301 -0
  248. package/src/runtime/guardian-action-message-composer.ts +245 -0
  249. package/src/runtime/guardian-outbound-actions.ts +26 -6
  250. package/src/runtime/guardian-verification-templates.ts +15 -9
  251. package/src/runtime/http-errors.ts +93 -0
  252. package/src/runtime/http-server.ts +133 -44
  253. package/src/runtime/http-types.ts +53 -0
  254. package/src/runtime/ingress-service.ts +237 -0
  255. package/src/runtime/middleware/error-handler.ts +4 -3
  256. package/src/runtime/middleware/rate-limiter.ts +160 -0
  257. package/src/runtime/middleware/request-logger.ts +71 -0
  258. package/src/runtime/middleware/twilio-validation.ts +7 -6
  259. package/src/runtime/pending-interactions.ts +12 -0
  260. package/src/runtime/routes/access-request-decision.ts +215 -0
  261. package/src/runtime/routes/app-routes.ts +25 -18
  262. package/src/runtime/routes/approval-routes.ts +18 -47
  263. package/src/runtime/routes/attachment-routes.ts +15 -41
  264. package/src/runtime/routes/call-routes.ts +20 -20
  265. package/src/runtime/routes/channel-delivery-routes.ts +6 -5
  266. package/src/runtime/routes/contact-routes.ts +4 -9
  267. package/src/runtime/routes/conversation-attention-routes.ts +2 -1
  268. package/src/runtime/routes/conversation-routes.ts +26 -57
  269. package/src/runtime/routes/debug-routes.ts +71 -0
  270. package/src/runtime/routes/events-routes.ts +3 -2
  271. package/src/runtime/routes/guardian-approval-interception.ts +221 -0
  272. package/src/runtime/routes/identity-routes.ts +14 -10
  273. package/src/runtime/routes/inbound-conversation.ts +3 -2
  274. package/src/runtime/routes/inbound-message-handler.ts +527 -62
  275. package/src/runtime/routes/ingress-routes.ts +174 -0
  276. package/src/runtime/routes/integration-routes.ts +78 -16
  277. package/src/runtime/routes/pairing-routes.ts +11 -10
  278. package/src/runtime/routes/secret-routes.ts +10 -18
  279. package/src/runtime/verification-rate-limiter.ts +83 -0
  280. package/src/schedule/schedule-store.ts +13 -1
  281. package/src/schedule/scheduler.ts +1 -1
  282. package/src/security/secret-ingress.ts +5 -2
  283. package/src/security/secret-scanner.ts +72 -6
  284. package/src/subagent/manager.ts +6 -4
  285. package/src/swarm/plan-validator.ts +4 -1
  286. package/src/tasks/task-runner.ts +3 -1
  287. package/src/tools/browser/api-map.ts +9 -6
  288. package/src/tools/calls/call-start.ts +20 -0
  289. package/src/tools/executor.ts +50 -568
  290. package/src/tools/permission-checker.ts +271 -0
  291. package/src/tools/registry.ts +14 -6
  292. package/src/tools/reminder/reminder-store.ts +7 -7
  293. package/src/tools/reminder/reminder.ts +6 -3
  294. package/src/tools/secret-detection-handler.ts +301 -0
  295. package/src/tools/subagent/message.ts +1 -1
  296. package/src/tools/system/voice-config.ts +62 -0
  297. package/src/tools/tasks/index.ts +3 -3
  298. package/src/tools/tasks/work-item-list.ts +3 -3
  299. package/src/tools/tasks/work-item-update.ts +4 -5
  300. package/src/tools/tool-approval-handler.ts +192 -0
  301. package/src/tools/tool-manifest.ts +2 -0
  302. package/src/version.ts +29 -2
  303. package/src/watcher/watcher-store.ts +9 -9
  304. package/src/work-items/work-item-runner.ts +9 -6
  305. /package/src/memory/migrations/{026-embeddings-nullable-vector-json.ts → 026a-embeddings-nullable-vector-json.ts} +0 -0
  306. /package/src/memory/migrations/{027-guardian-bootstrap-token.ts → 027a-guardian-bootstrap-token.ts} +0 -0
@@ -366,14 +366,14 @@ The Anthropic provider places `cache_control: { type: 'ephemeral' }` on the **la
366
366
 
367
367
  ## Temporal Context Injection — Date Grounding
368
368
 
369
- The session injects a `<temporal_context>` block into every user message at runtime, giving the model awareness of the current date, timezone, upcoming weekend/work week windows, and a 14-day horizon of labelled future dates. This enables reliable reasoning about future dates (e.g. "plan a trip for next weekend") without persisting volatile temporal data in conversation history.
369
+ The session injects a `<temporal_context>` block into every user message at runtime, giving the model awareness of the current date, current local time, current UTC time, timezone source metadata, upcoming weekend/work week windows, and a 14-day horizon of labelled future dates. This enables reliable reasoning about future dates (e.g. "plan a trip for next weekend") without persisting volatile temporal data in conversation history.
370
370
 
371
371
  ### Per-turn flow
372
372
 
373
373
  ```mermaid
374
374
  graph TB
375
375
  subgraph "Per-Turn Flow"
376
- BUILD["buildTemporalContext(timeZone)<br/>→ compact XML block"]
376
+ BUILD["buildTemporalContext(timeZone, hostTimeZone, userTimeZone)<br/>→ compact XML block"]
377
377
  INJECT["applyRuntimeInjections<br/>prepend temporal block<br/>to user message"]
378
378
  AGENT["AgentLoop.run(runMessages)"]
379
379
  STRIP["stripTemporalContext<br/>remove block from persisted history"]
@@ -387,7 +387,9 @@ graph TB
387
387
  ### Key design decisions
388
388
 
389
389
  - **Fresh each turn**: `buildTemporalContext()` is called at the start of every agent loop invocation, ensuring the model always sees the current date even in long-running conversations.
390
- - **Timezone-aware**: Uses `Intl.DateTimeFormat` APIs for DST-safe date arithmetic. The host timezone (`Intl.DateTimeFormat().resolvedOptions().timeZone`) is used by default.
390
+ - **Clock source invariant**: Absolute time (`now`) always comes from the assistant host clock (`Date.now()`), never from channel/client clocks.
391
+ - **Timezone precedence**: If `ui.userTimezone` is configured, temporal context uses it for local-date interpretation. Otherwise it falls back to dynamic profile memory, then assistant host timezone.
392
+ - **Timezone-aware**: Uses `Intl.DateTimeFormat` APIs for DST-safe date arithmetic and timezone validation/canonicalization.
391
393
  - **Bounded output**: Hard-capped at 1500 characters and 14 horizon entries to prevent prompt bloat.
392
394
  - **Runtime-only**: The injected `<temporal_context>` block is stripped from `this.messages` after the agent loop completes via `stripTemporalContext`. It never persists in conversation history.
393
395
  - **Specific strip prefix**: The strip function matches the exact injected prefix (`<temporal_context>\nToday:`) to avoid accidentally removing user-authored text that starts with `<temporal_context>`.
@@ -512,4 +514,3 @@ graph TB
512
514
  | `assistant/src/config/schema.ts` | `WorkspaceGitConfigSchema`: timeout, backoff, and enrichment queue configuration |
513
515
 
514
516
  ---
515
-
@@ -91,7 +91,7 @@ The `enforceRoutingIntent()` step runs after the LLM produces a channel selectio
91
91
  | Intent | Enforcement Rule |
92
92
  |--------|-----------------|
93
93
  | `single_channel` | No override. The LLM's channel selection stands. |
94
- | `multi_channel` | If the LLM selected < 2 channels and 2+ are connected, expand to all connected channels. |
94
+ | `multi_channel` | If the LLM selected < 2 channels and 2+ are connected, expand to at least two connected channels. |
95
95
  | `all_channels` | Replace the LLM's selection with all connected channels. |
96
96
 
97
97
  When enforcement changes the decision, the updated `selectedChannels` and annotated `reasoningSummary` are re-persisted to `notification_decisions` so the audit trail reflects what was actually dispatched.
@@ -189,9 +189,9 @@ graph TD
189
189
 
190
190
  **Data tables:** `watchers` (config, watermark, status, error tracking) and `watcher_events` (detected events, dedup on `(watcher_id, external_id)`, disposition tracking).
191
191
 
192
- ## Task Queue — Queued Task Execution and Review
192
+ ## Task Queue — Conversation-Managed Task Execution
193
193
 
194
- The Task Queue builds on top of the existing Tasks system to provide an ordered execution pipeline with human-in-the-loop review.
194
+ The Task Queue provides an ordered execution pipeline with human-in-the-loop review. Task management happens entirely through conversation — the user creates, updates, runs, and reviews tasks by talking to the assistant. There is no standalone Tasks UI window.
195
195
 
196
196
  ### Terminology
197
197
 
@@ -258,19 +258,6 @@ flowchart TD
258
258
  DB[(SQLite)]
259
259
  end
260
260
 
261
- subgraph "Daemon IPC Handlers"
262
- HC[handleWorkItemCreate]
263
- HU[handleWorkItemUpdate]
264
- HCo[handleWorkItemComplete]
265
- HR[handleWorkItemRunTask]
266
- BC[tasks_changed broadcast]
267
- end
268
-
269
- subgraph "macOS Client"
270
- TW[TasksWindowView]
271
- DC[DaemonClient]
272
- end
273
-
274
261
  TLA -->|"if_exists check"| DUPE
275
262
  DUPE -->|"no match"| WIS
276
263
  DUPE -->|"match found → reuse/update"| TLU
@@ -278,81 +265,10 @@ flowchart TD
278
265
  RWI --> WIS
279
266
  TLS --> WIS
280
267
  WIS --> DB
281
-
282
- HC --> WIS
283
- HU --> WIS
284
- HCo --> WIS
285
- HR --> WIS
286
- HC --> BC
287
- HU --> BC
288
- HCo --> BC
289
- HR --> BC
290
-
291
- BC -->|"via socket"| DC
292
- DC -->|"onTasksChanged"| TW
293
- TW -->|"debounced refetch (300ms)"| DC
294
268
  ```
295
269
 
296
270
  **Key behaviors:**
297
271
 
272
+ - **Conversation-first management** — All task operations (create, update, run, review, delete) are performed through natural language conversation with the assistant, which invokes the model tools (`task_list_add`, `task_list_update`, `task_list_show`) on the user's behalf.
298
273
  - **`task_list_update`** uses `resolveWorkItem` to find the target work item by work item ID, task ID, or title (case-insensitive exact match). When multiple items match by task ID or title, the resolver applies a deterministic tie-break (lowest priority tier, then earliest `createdAt`).
299
274
  - **`task_list_add`** has duplicate prevention via the `if_exists` parameter (default: `reuse_existing`). Before creating, it calls `findActiveWorkItemsByTitle` to check for active items with the same title. If a match is found, the tool either returns the existing item (`reuse_existing`), updates it in place (`update_existing`), or proceeds to create a duplicate (`create_duplicate`).
300
- - **All daemon work-item handlers** (`handleWorkItemCreate`, `handleWorkItemUpdate`, `handleWorkItemComplete`, `handleWorkItemRunTask`) emit a `tasks_changed` broadcast after mutations via `ctx.broadcast({ type: 'tasks_changed' })`. They also emit the more specific `work_item_status_changed` with the affected item's current state.
301
- - **The macOS Tasks window** (`TasksWindowView`) subscribes to both `tasks_changed` and `work_item_status_changed` callbacks on `DaemonClient`. Both trigger a debounced refetch (300ms) so rapid successive mutations coalesce into a single re-fetch.
302
-
303
- ### IPC Messages
304
-
305
- **Client → Server:**
306
-
307
- | Message | Purpose |
308
- |---------|---------|
309
- | `work_items_list` | List work items, filterable by status |
310
- | `work_item_get` | Fetch a single work item with full details |
311
- | `work_item_create` | Create a new work item pointing to a Task |
312
- | `work_item_update` | Update title, notes, priority, or sort order |
313
- | `work_item_complete` | Mark an item as `done` after review |
314
- | `work_item_run_task` | Trigger execution of a queued work item |
315
- | `work_item_delete` | Delete a work item from the queue |
316
-
317
- **Server → Client (push):**
318
-
319
- | Message | Purpose |
320
- |---------|---------|
321
- | `work_item_status_changed` | Notify the client when a work item transitions state (includes item snapshot) |
322
- | `tasks_changed` | Lightweight broadcast after any work-item mutation; triggers client-side refetch |
323
-
324
- ### Run-Button State Machine
325
-
326
- When the user clicks "Run" on a queued work item, the button follows a deterministic state machine:
327
-
328
- ```
329
- idle (visible) → in-flight (hidden) → success/failure → re-enabled (via refetch)
330
- ```
331
-
332
- **Sequence:**
333
-
334
- 1. **Idle** — The run button is visible only when `item.status == "queued"`. The `TasksWindowRow` renders it conditionally based on the `WorkItemStatus` enum.
335
- 2. **In-flight** — The client sends `work_item_run_task` with the work item ID. The daemon validates the request, sets the item's status to `running`, and returns `work_item_run_task_response` with `success: true`. It then broadcasts `work_item_status_changed` and `tasks_changed`. The client's debounced refetch picks up the `running` status, which hides the run button and shows a spinner in the status column.
336
- 3. **Completion** — The daemon executes the task asynchronously. On success, the item transitions to `awaiting_review`; on failure, to `failed`. Both trigger another `work_item_status_changed` + `tasks_changed` broadcast, which the client refetches and renders accordingly (showing a "Reviewed" button for `awaiting_review`, or the run button again for `failed` to allow retry).
337
-
338
- **Error handling in `work_item_run_task_response`:**
339
-
340
- The response includes a typed `errorCode` field (`WorkItemRunTaskErrorCode`) so the client can deterministically decide what to do without parsing error strings:
341
-
342
- | `errorCode` | Meaning | Client behavior |
343
- |-------------|---------|-----------------|
344
- | `not_found` | Work item does not exist (deleted concurrently) | Refetch removes the stale row |
345
- | `already_running` | Item is already executing | No-op; status column already shows spinner |
346
- | `invalid_status` | Item is `done` or `archived` and cannot be run | Refetch updates the row to reflect terminal status |
347
- | `no_task` | The associated Task template was deleted | Refetch; row may show an error state |
348
-
349
- In all error cases, the subsequent `tasks_changed` broadcast triggers a refetch that brings the UI back to a consistent state, so the button is never stuck in a disabled/hidden state without a path to recovery.
350
-
351
- ### Delete Flow
352
-
353
- Deletion uses optimistic UI with rollback:
354
-
355
- 1. **Optimistic removal** — `TasksWindowViewModel.removeTask()` snapshots the current `items` array, then immediately removes the target item with animation.
356
- 2. **IPC request** — Sends `work_item_delete` with the item ID. The daemon looks up the item; if found, deletes it and responds with `work_item_delete_response { success: true }`, then broadcasts `tasks_changed`.
357
- 3. **Failure rollback** — If the send throws (socket error), the view model restores the snapshot with animation. If the daemon responds with `success: false` (item not found), the `onWorkItemDeleteResponse` callback triggers a full refetch to reconcile.
358
-
@@ -0,0 +1,283 @@
1
+ # Trusted Contacts — Operator Runbook
2
+
3
+ Operational procedures for inspecting, managing, and debugging the trusted contact access flow. All HTTP commands use the gateway API (default `http://localhost:7830`) with bearer authentication.
4
+
5
+ ## Prerequisites
6
+
7
+ ```bash
8
+ # Read the bearer token
9
+ TOKEN=$(cat ~/.vellum/http-token)
10
+
11
+ # Base URL (adjust if using a non-default port)
12
+ BASE=http://localhost:7830
13
+ ```
14
+
15
+ ## 1. Inspect Trusted Contacts (Members)
16
+
17
+ ### List all active trusted contacts
18
+
19
+ ```bash
20
+ curl -s "$BASE/v1/ingress/members?status=active" \
21
+ -H "Authorization: Bearer $TOKEN" | jq
22
+ ```
23
+
24
+ ### Filter by channel
25
+
26
+ ```bash
27
+ # Telegram contacts only
28
+ curl -s "$BASE/v1/ingress/members?sourceChannel=telegram&status=active" \
29
+ -H "Authorization: Bearer $TOKEN" | jq
30
+
31
+ # SMS contacts only
32
+ curl -s "$BASE/v1/ingress/members?sourceChannel=sms&status=active" \
33
+ -H "Authorization: Bearer $TOKEN" | jq
34
+ ```
35
+
36
+ ### List all members (including revoked and blocked)
37
+
38
+ ```bash
39
+ curl -s "$BASE/v1/ingress/members" \
40
+ -H "Authorization: Bearer $TOKEN" | jq
41
+ ```
42
+
43
+ Response shape:
44
+ ```json
45
+ {
46
+ "ok": true,
47
+ "members": [
48
+ {
49
+ "id": "uuid",
50
+ "sourceChannel": "telegram",
51
+ "externalUserId": "123456789",
52
+ "externalChatId": "123456789",
53
+ "displayName": "Alice",
54
+ "username": "alice_handle",
55
+ "status": "active",
56
+ "policy": "allow",
57
+ "lastSeenAt": 1700000000000,
58
+ "createdAt": 1699000000000
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ ## 2. Inspect Pending Access Requests
65
+
66
+ Access requests are stored in the `channel_guardian_approval_requests` table. Use SQLite to inspect pending requests directly.
67
+
68
+ ### Via SQLite CLI
69
+
70
+ ```bash
71
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
72
+ "SELECT id, channel, requester_external_user_id, requester_chat_id, \
73
+ guardian_external_user_id, status, tool_name, created_at, expires_at \
74
+ FROM channel_guardian_approval_requests \
75
+ WHERE tool_name = 'ingress_access_request' AND status = 'pending' \
76
+ ORDER BY created_at DESC;"
77
+ ```
78
+
79
+ ### Check all access requests (including resolved)
80
+
81
+ ```bash
82
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
83
+ "SELECT id, channel, requester_external_user_id, status, \
84
+ decided_by_external_user_id, created_at \
85
+ FROM channel_guardian_approval_requests \
86
+ WHERE tool_name = 'ingress_access_request' \
87
+ ORDER BY created_at DESC LIMIT 20;"
88
+ ```
89
+
90
+ ## 3. Inspect Pending Verification Sessions
91
+
92
+ Verification challenges are stored in `channel_guardian_verification_challenges`. Active sessions have `status = 'awaiting_response'` and `expires_at > now`.
93
+
94
+ ```bash
95
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
96
+ "SELECT id, channel, status, identity_binding_status, \
97
+ expected_external_user_id, expected_chat_id, expected_phone_e164, \
98
+ expires_at, created_at \
99
+ FROM channel_guardian_verification_challenges \
100
+ WHERE status IN ('awaiting_response', 'pending_bootstrap') \
101
+ AND expires_at > $(date +%s)000 \
102
+ ORDER BY created_at DESC;"
103
+ ```
104
+
105
+ ## 4. Force-Revoke a Trusted Contact
106
+
107
+ ### Via HTTP API
108
+
109
+ First, find the member's `id` from the list endpoint, then revoke:
110
+
111
+ ```bash
112
+ # Find the member
113
+ MEMBER_ID=$(curl -s "$BASE/v1/ingress/members?sourceChannel=telegram&status=active" \
114
+ -H "Authorization: Bearer $TOKEN" | jq -r '.members[] | select(.externalUserId == "TARGET_USER_ID") | .id')
115
+
116
+ # Revoke with reason
117
+ curl -s -X DELETE "$BASE/v1/ingress/members/$MEMBER_ID" \
118
+ -H "Authorization: Bearer $TOKEN" \
119
+ -H "Content-Type: application/json" \
120
+ -d '{"reason": "Revoked by operator"}' | jq
121
+ ```
122
+
123
+ ### Block a member (stronger than revoke)
124
+
125
+ Blocking prevents the member from re-entering the flow without explicit unblocking.
126
+
127
+ ```bash
128
+ curl -s -X POST "$BASE/v1/ingress/members/$MEMBER_ID/block" \
129
+ -H "Authorization: Bearer $TOKEN" \
130
+ -H "Content-Type: application/json" \
131
+ -d '{"reason": "Blocked by operator"}' | jq
132
+ ```
133
+
134
+ ### Via SQLite (emergency)
135
+
136
+ If the HTTP API is unavailable:
137
+
138
+ ```bash
139
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
140
+ "UPDATE assistant_ingress_members \
141
+ SET status = 'revoked', revoked_reason = 'Emergency operator revocation', \
142
+ updated_at = $(date +%s)000 \
143
+ WHERE external_user_id = 'TARGET_USER_ID' AND source_channel = 'telegram';"
144
+ ```
145
+
146
+ ## 5. Debug Verification Failures
147
+
148
+ ### Check rate limit state
149
+
150
+ If a user is getting "invalid or expired code" errors, they may be rate-limited:
151
+
152
+ ```bash
153
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
154
+ "SELECT * FROM channel_guardian_rate_limits \
155
+ WHERE external_user_id = 'TARGET_USER_ID' \
156
+ OR chat_id = 'TARGET_CHAT_ID' \
157
+ ORDER BY created_at DESC LIMIT 5;"
158
+ ```
159
+
160
+ ### Reset rate limits for a user
161
+
162
+ ```bash
163
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
164
+ "DELETE FROM channel_guardian_rate_limits \
165
+ WHERE external_user_id = 'TARGET_USER_ID' AND channel = 'telegram';"
166
+ ```
167
+
168
+ ### Check verification challenge state
169
+
170
+ ```bash
171
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
172
+ "SELECT id, channel, status, identity_binding_status, \
173
+ expected_external_user_id, expected_chat_id, expected_phone_e164, \
174
+ expires_at, consumed_by_external_user_id \
175
+ FROM channel_guardian_verification_challenges \
176
+ WHERE expected_external_user_id = 'TARGET_USER_ID' \
177
+ OR expected_chat_id = 'TARGET_CHAT_ID' \
178
+ ORDER BY created_at DESC LIMIT 5;"
179
+ ```
180
+
181
+ ### Common verification failure causes
182
+
183
+ | Symptom | Likely cause | Resolution |
184
+ |---------|-------------|------------|
185
+ | "Invalid or expired code" (correct code) | Identity mismatch: the code was entered from a different user/chat than expected | Verify the requester is using the same account that originally requested access |
186
+ | "Invalid or expired code" (correct code, correct user) | Rate-limited (5+ failures in 15 min window) | Wait 30 minutes or reset rate limits via SQLite |
187
+ | "Invalid or expired code" (old code) | Code TTL expired (10 min) | Guardian must re-approve to generate a new code |
188
+ | Code never delivered to guardian | `deliverChannelReply` failed | Check daemon logs for "Failed to deliver verification code to guardian" |
189
+ | No notification to guardian | No guardian binding for channel | Verify guardian is bound: check `channel_guardian_bindings` table |
190
+
191
+ ## 6. Check Notification Delivery Status
192
+
193
+ ### Check if the access request notification was delivered
194
+
195
+ ```bash
196
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
197
+ "SELECT ne.id, ne.source_event_name, ne.dedupe_key, ne.created_at, \
198
+ nd.channel, nd.status, nd.confidence \
199
+ FROM notification_events ne \
200
+ LEFT JOIN notification_decisions nd ON nd.event_id = ne.id \
201
+ WHERE ne.source_event_name LIKE 'ingress.%' \
202
+ ORDER BY ne.created_at DESC LIMIT 20;"
203
+ ```
204
+
205
+ ### Check delivery records
206
+
207
+ ```bash
208
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
209
+ "SELECT ndel.id, ndel.channel, ndel.status, ndel.error_message, \
210
+ ndel.created_at, ne.source_event_name \
211
+ FROM notification_deliveries ndel \
212
+ JOIN notification_events ne ON ne.id = ndel.event_id \
213
+ WHERE ne.source_event_name LIKE 'ingress.%' \
214
+ ORDER BY ndel.created_at DESC LIMIT 20;"
215
+ ```
216
+
217
+ ### Check lifecycle signals
218
+
219
+ ```bash
220
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
221
+ "SELECT source_event_name, source_channel, dedupe_key, created_at \
222
+ FROM notification_events \
223
+ WHERE source_event_name LIKE 'ingress.trusted_contact.%' \
224
+ ORDER BY created_at DESC LIMIT 20;"
225
+ ```
226
+
227
+ ## 7. Manually Add a Trusted Contact (Bypass Verification)
228
+
229
+ If the verification flow cannot be completed, an operator can directly create an active member:
230
+
231
+ ```bash
232
+ curl -s -X POST "$BASE/v1/ingress/members" \
233
+ -H "Authorization: Bearer $TOKEN" \
234
+ -H "Content-Type: application/json" \
235
+ -d '{
236
+ "sourceChannel": "telegram",
237
+ "externalUserId": "123456789",
238
+ "externalChatId": "123456789",
239
+ "displayName": "Alice",
240
+ "policy": "allow",
241
+ "status": "active"
242
+ }' | jq
243
+ ```
244
+
245
+ For SMS contacts, use the E.164 phone number as the external user/chat ID:
246
+
247
+ ```bash
248
+ curl -s -X POST "$BASE/v1/ingress/members" \
249
+ -H "Authorization: Bearer $TOKEN" \
250
+ -H "Content-Type: application/json" \
251
+ -d '{
252
+ "sourceChannel": "sms",
253
+ "externalUserId": "+15551234567",
254
+ "externalChatId": "+15551234567",
255
+ "displayName": "Bob",
256
+ "policy": "allow",
257
+ "status": "active"
258
+ }' | jq
259
+ ```
260
+
261
+ ## 8. Clean Up Expired Data
262
+
263
+ ### Purge expired verification sessions
264
+
265
+ Expired sessions are already invisible to the verification flow (filtered by `expires_at`), but you can clean them up:
266
+
267
+ ```bash
268
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
269
+ "DELETE FROM channel_guardian_verification_challenges \
270
+ WHERE expires_at < $(date +%s)000 \
271
+ AND status IN ('awaiting_response', 'pending_bootstrap');"
272
+ ```
273
+
274
+ ### Purge expired approval requests
275
+
276
+ The `sweepExpiredGuardianApprovals()` timer handles this automatically every 60 seconds, but manual cleanup:
277
+
278
+ ```bash
279
+ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
280
+ "UPDATE channel_guardian_approval_requests \
281
+ SET status = 'expired' \
282
+ WHERE status = 'pending' AND expires_at < $(date +%s)000;"
283
+ ```