@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.
- package/ARCHITECTURE.md +211 -12
- package/Dockerfile +1 -1
- package/README.md +11 -5
- package/docs/architecture/http-token-refresh.md +274 -0
- package/docs/architecture/memory.md +5 -4
- package/docs/architecture/scheduling.md +4 -88
- package/docs/runbook-trusted-contacts.md +283 -0
- package/docs/trusted-contact-access.md +247 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2 -6
- package/src/__tests__/access-request-decision.test.ts +328 -0
- package/src/__tests__/asset-materialize-tool.test.ts +7 -7
- package/src/__tests__/asset-search-tool.test.ts +15 -15
- package/src/__tests__/attachments-store.test.ts +13 -13
- package/src/__tests__/call-controller.test.ts +150 -4
- package/src/__tests__/call-conversation-messages.test.ts +2 -2
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/call-start-guardian-guard.test.ts +93 -0
- package/src/__tests__/channel-approval-routes.test.ts +108 -12
- package/src/__tests__/channel-guardian.test.ts +19 -15
- package/src/__tests__/checker.test.ts +103 -48
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +356 -0
- package/src/__tests__/conversation-pairing.test.ts +127 -27
- package/src/__tests__/conversation-store.test.ts +36 -36
- package/src/__tests__/date-context.test.ts +179 -1
- package/src/__tests__/db-migration-rollback.test.ts +4 -7
- package/src/__tests__/deterministic-verification-control-plane.test.ts +5 -5
- package/src/__tests__/emit-signal-routing-intent.test.ts +179 -0
- package/src/__tests__/gateway-only-guard.test.ts +188 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +451 -0
- package/src/__tests__/guardian-action-copy-generator.test.ts +197 -0
- package/src/__tests__/guardian-action-followup-executor.test.ts +379 -0
- package/src/__tests__/guardian-action-followup-store.test.ts +376 -0
- package/src/__tests__/guardian-action-late-reply.test.ts +425 -0
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +71 -0
- package/src/__tests__/guardian-action-store.test.ts +182 -0
- package/src/__tests__/guardian-action-sweep.test.ts +9 -9
- package/src/__tests__/guardian-dispatch.test.ts +120 -0
- package/src/__tests__/guardian-outbound-http.test.ts +194 -2
- package/src/__tests__/guardian-verification-intent-routing.test.ts +179 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +141 -0
- package/src/__tests__/handlers-telegram-config.test.ts +6 -6
- package/src/__tests__/hooks-runner.test.ts +13 -4
- package/src/__tests__/ingress-routes-http.test.ts +443 -0
- package/src/__tests__/intent-routing.test.ts +14 -0
- package/src/__tests__/ipc-snapshot.test.ts +23 -5
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-regressions.test.ts +16 -12
- package/src/__tests__/non-member-access-request.test.ts +281 -0
- package/src/__tests__/notification-broadcaster.test.ts +115 -4
- package/src/__tests__/notification-decision-strategy.test.ts +138 -1
- package/src/__tests__/notification-deep-link.test.ts +44 -1
- package/src/__tests__/notification-guardian-path.test.ts +157 -0
- package/src/__tests__/notification-routing-intent.test.ts +11 -1
- package/src/__tests__/notification-thread-candidate-validation.test.ts +215 -0
- package/src/__tests__/notification-thread-candidates.test.ts +166 -0
- package/src/__tests__/recording-intent.test.ts +1 -0
- package/src/__tests__/recording-state-machine.test.ts +328 -17
- package/src/__tests__/registry.test.ts +17 -8
- package/src/__tests__/relay-server.test.ts +105 -0
- package/src/__tests__/reminder.test.ts +13 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -4
- package/src/__tests__/scheduler-recurrence.test.ts +50 -0
- package/src/__tests__/server-history-render.test.ts +8 -8
- package/src/__tests__/session-agent-loop.test.ts +1 -0
- package/src/__tests__/session-runtime-assembly.test.ts +49 -0
- package/src/__tests__/session-skill-tools.test.ts +1 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +11 -3
- package/src/__tests__/slack-channel-config.test.ts +230 -0
- package/src/__tests__/subagent-manager-notify.test.ts +4 -4
- package/src/__tests__/swarm-session-integration.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +43 -0
- package/src/__tests__/task-management-tools.test.ts +3 -3
- package/src/__tests__/task-tools.test.ts +3 -3
- package/src/__tests__/trust-store.test.ts +38 -22
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +489 -0
- package/src/__tests__/trusted-contact-multichannel.test.ts +405 -0
- package/src/__tests__/trusted-contact-verification.test.ts +360 -0
- package/src/__tests__/update-bulletin-format.test.ts +119 -0
- package/src/__tests__/update-bulletin-state.test.ts +129 -0
- package/src/__tests__/update-bulletin.test.ts +323 -0
- package/src/__tests__/update-template-contract.test.ts +24 -0
- package/src/__tests__/voice-session-bridge.test.ts +109 -9
- package/src/agent/loop.ts +2 -2
- package/src/amazon/client.ts +2 -3
- package/src/calls/call-controller.ts +241 -39
- package/src/calls/call-conversation-messages.ts +2 -2
- package/src/calls/call-domain.ts +10 -3
- package/src/calls/call-pointer-messages.ts +17 -5
- package/src/calls/guardian-action-sweep.ts +77 -36
- package/src/calls/guardian-dispatch.ts +8 -0
- package/src/calls/relay-server.ts +51 -12
- package/src/calls/twilio-routes.ts +3 -1
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +8 -6
- package/src/cli/core-commands.ts +43 -3
- package/src/cli/map.ts +8 -5
- package/src/config/bundled-skills/phone-calls/SKILL.md +16 -1
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +4 -4
- package/src/config/bundled-skills/time-based-actions/SKILL.md +11 -1
- package/src/config/computer-use-prompt.ts +1 -0
- package/src/config/core-schema.ts +16 -0
- package/src/config/env-registry.ts +1 -0
- package/src/config/env.ts +16 -1
- package/src/config/memory-schema.ts +5 -0
- package/src/config/schema.ts +4 -0
- package/src/config/system-prompt.ts +69 -2
- package/src/config/templates/BOOTSTRAP.md +1 -1
- package/src/config/templates/IDENTITY.md +8 -4
- package/src/config/templates/SOUL.md +14 -0
- package/src/config/templates/UPDATES.md +15 -0
- package/src/config/templates/USER.md +5 -1
- package/src/config/types.ts +1 -0
- package/src/config/update-bulletin-format.ts +54 -0
- package/src/config/update-bulletin-state.ts +49 -0
- package/src/config/update-bulletin-template-path.ts +6 -0
- package/src/config/update-bulletin.ts +97 -0
- package/src/config/vellum-skills/catalog.json +6 -0
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/vellum-skills/guardian-verify-setup/SKILL.md +44 -10
- package/src/config/vellum-skills/telegram-setup/SKILL.md +4 -4
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +147 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +2 -2
- package/src/context/window-manager.ts +43 -3
- package/src/daemon/config-watcher.ts +4 -2
- package/src/daemon/connection-policy.ts +21 -1
- package/src/daemon/daemon-control.ts +219 -8
- package/src/daemon/date-context.ts +174 -1
- package/src/daemon/guardian-action-generators.ts +175 -0
- package/src/daemon/guardian-verification-intent.ts +120 -0
- package/src/daemon/handlers/apps.ts +1 -3
- package/src/daemon/handlers/config-channels.ts +2 -2
- package/src/daemon/handlers/config-heartbeat.ts +1 -1
- package/src/daemon/handlers/config-inbox.ts +55 -159
- package/src/daemon/handlers/config-ingress.ts +1 -1
- package/src/daemon/handlers/config-integrations.ts +1 -1
- package/src/daemon/handlers/config-platform.ts +1 -1
- package/src/daemon/handlers/config-scheduling.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +190 -0
- package/src/daemon/handlers/config-telegram.ts +1 -1
- package/src/daemon/handlers/config-twilio.ts +1 -1
- package/src/daemon/handlers/config-voice.ts +100 -0
- package/src/daemon/handlers/config.ts +3 -0
- package/src/daemon/handlers/identity.ts +45 -25
- package/src/daemon/handlers/misc.ts +83 -5
- package/src/daemon/handlers/navigate-settings.ts +27 -0
- package/src/daemon/handlers/recording.ts +270 -144
- package/src/daemon/handlers/sessions.ts +100 -17
- package/src/daemon/handlers/subagents.ts +3 -3
- package/src/daemon/handlers/work-items.ts +10 -7
- package/src/daemon/ipc-contract/integrations.ts +9 -1
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +1 -1
- package/src/daemon/ipc-contract/settings.ts +26 -0
- package/src/daemon/ipc-contract/shared.ts +2 -0
- package/src/daemon/ipc-contract/work-items.ts +1 -7
- package/src/daemon/ipc-contract/workspace.ts +12 -1
- package/src/daemon/ipc-contract-inventory.json +6 -1
- package/src/daemon/ipc-contract.ts +5 -1
- package/src/daemon/lifecycle.ts +314 -266
- package/src/daemon/recording-intent.ts +0 -41
- package/src/daemon/response-tier.ts +2 -2
- package/src/daemon/server.ts +31 -9
- package/src/daemon/session-agent-loop-handlers.ts +34 -9
- package/src/daemon/session-agent-loop.ts +15 -8
- package/src/daemon/session-history.ts +3 -2
- package/src/daemon/session-media-retry.ts +3 -0
- package/src/daemon/session-messaging.ts +38 -4
- package/src/daemon/session-notifiers.ts +2 -2
- package/src/daemon/session-process.ts +546 -59
- package/src/daemon/session-queue-manager.ts +2 -0
- package/src/daemon/session-runtime-assembly.ts +39 -0
- package/src/daemon/session-skill-tools.ts +13 -4
- package/src/daemon/session-tool-setup.ts +5 -6
- package/src/daemon/session.ts +19 -8
- package/src/daemon/tls-certs.ts +60 -13
- package/src/daemon/tool-side-effects.ts +13 -5
- package/src/gallery/default-gallery.ts +32 -9
- package/src/influencer/client.ts +2 -1
- package/src/memory/channel-delivery-store.ts +35 -567
- package/src/memory/channel-guardian-store.ts +63 -1317
- package/src/memory/conflict-store.ts +4 -4
- package/src/memory/conversation-attention-store.ts +0 -3
- package/src/memory/conversation-crud.ts +668 -0
- package/src/memory/conversation-queries.ts +361 -0
- package/src/memory/conversation-store.ts +44 -983
- package/src/memory/db-connection.ts +3 -0
- package/src/memory/db-init.ts +33 -0
- package/src/memory/delivery-channels.ts +175 -0
- package/src/memory/delivery-crud.ts +211 -0
- package/src/memory/delivery-status.ts +199 -0
- package/src/memory/embedding-backend.ts +70 -4
- package/src/memory/embedding-local.ts +12 -2
- package/src/memory/entity-extractor.ts +3 -8
- package/src/memory/fts-reconciler.ts +136 -0
- package/src/memory/guardian-action-store.ts +418 -5
- package/src/memory/guardian-approvals.ts +569 -0
- package/src/memory/guardian-bindings.ts +130 -0
- package/src/memory/guardian-rate-limits.ts +196 -0
- package/src/memory/guardian-verification.ts +521 -0
- package/src/memory/job-handlers/index-maintenance.ts +2 -1
- package/src/memory/job-utils.ts +8 -5
- package/src/memory/jobs-store.ts +66 -6
- package/src/memory/jobs-worker.ts +23 -1
- package/src/memory/migrations/030-guardian-action-followup.ts +21 -0
- package/src/memory/migrations/030-guardian-verification-purpose.ts +17 -0
- package/src/memory/migrations/031-conversations-thread-type-index.ts +5 -0
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +15 -0
- package/src/memory/migrations/032-notification-delivery-thread-decision.ts +20 -0
- package/src/memory/migrations/100-core-tables.ts +1 -1
- package/src/memory/migrations/101-watchers-and-logs.ts +4 -0
- package/src/memory/migrations/108-tasks-and-work-items.ts +1 -1
- package/src/memory/migrations/112-assistant-inbox.ts +1 -1
- package/src/memory/migrations/113-late-migrations.ts +1 -1
- package/src/memory/migrations/116-messages-fts.ts +13 -0
- package/src/memory/migrations/119-schema-indexes-and-columns.ts +37 -0
- package/src/memory/migrations/120-fk-cascade-rebuilds.ts +161 -0
- package/src/memory/migrations/index.ts +10 -3
- package/src/memory/migrations/validate-migration-state.ts +114 -15
- package/src/memory/qdrant-circuit-breaker.ts +105 -0
- package/src/memory/retriever.ts +46 -13
- package/src/memory/schema-migration.ts +4 -0
- package/src/memory/schema.ts +31 -8
- package/src/memory/search/semantic.ts +8 -90
- package/src/notifications/README.md +159 -18
- package/src/notifications/broadcaster.ts +69 -33
- package/src/notifications/conversation-pairing.ts +99 -21
- package/src/notifications/decision-engine.ts +176 -8
- package/src/notifications/deliveries-store.ts +39 -8
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/preferences-store.ts +7 -7
- package/src/notifications/thread-candidates.ts +269 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/checker.ts +1 -16
- package/src/permissions/defaults.ts +25 -5
- package/src/permissions/prompter.ts +17 -0
- package/src/permissions/trust-store.ts +2 -0
- package/src/providers/failover.ts +19 -0
- package/src/providers/registry.ts +46 -1
- package/src/runtime/approval-message-composer.ts +1 -1
- package/src/runtime/channel-guardian-service.ts +15 -3
- package/src/runtime/channel-retry-sweep.ts +7 -2
- package/src/runtime/guardian-action-conversation-turn.ts +85 -0
- package/src/runtime/guardian-action-followup-executor.ts +301 -0
- package/src/runtime/guardian-action-message-composer.ts +245 -0
- package/src/runtime/guardian-outbound-actions.ts +26 -6
- package/src/runtime/guardian-verification-templates.ts +15 -9
- package/src/runtime/http-errors.ts +93 -0
- package/src/runtime/http-server.ts +133 -44
- package/src/runtime/http-types.ts +53 -0
- package/src/runtime/ingress-service.ts +237 -0
- package/src/runtime/middleware/error-handler.ts +4 -3
- package/src/runtime/middleware/rate-limiter.ts +160 -0
- package/src/runtime/middleware/request-logger.ts +71 -0
- package/src/runtime/middleware/twilio-validation.ts +7 -6
- package/src/runtime/pending-interactions.ts +12 -0
- package/src/runtime/routes/access-request-decision.ts +215 -0
- package/src/runtime/routes/app-routes.ts +25 -18
- package/src/runtime/routes/approval-routes.ts +18 -47
- package/src/runtime/routes/attachment-routes.ts +15 -41
- package/src/runtime/routes/call-routes.ts +20 -20
- package/src/runtime/routes/channel-delivery-routes.ts +6 -5
- package/src/runtime/routes/contact-routes.ts +4 -9
- package/src/runtime/routes/conversation-attention-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +26 -57
- package/src/runtime/routes/debug-routes.ts +71 -0
- package/src/runtime/routes/events-routes.ts +3 -2
- package/src/runtime/routes/guardian-approval-interception.ts +221 -0
- package/src/runtime/routes/identity-routes.ts +14 -10
- package/src/runtime/routes/inbound-conversation.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +527 -62
- package/src/runtime/routes/ingress-routes.ts +174 -0
- package/src/runtime/routes/integration-routes.ts +78 -16
- package/src/runtime/routes/pairing-routes.ts +11 -10
- package/src/runtime/routes/secret-routes.ts +10 -18
- package/src/runtime/verification-rate-limiter.ts +83 -0
- package/src/schedule/schedule-store.ts +13 -1
- package/src/schedule/scheduler.ts +1 -1
- package/src/security/secret-ingress.ts +5 -2
- package/src/security/secret-scanner.ts +72 -6
- package/src/subagent/manager.ts +6 -4
- package/src/swarm/plan-validator.ts +4 -1
- package/src/tasks/task-runner.ts +3 -1
- package/src/tools/browser/api-map.ts +9 -6
- package/src/tools/calls/call-start.ts +20 -0
- package/src/tools/executor.ts +50 -568
- package/src/tools/permission-checker.ts +271 -0
- package/src/tools/registry.ts +14 -6
- package/src/tools/reminder/reminder-store.ts +7 -7
- package/src/tools/reminder/reminder.ts +6 -3
- package/src/tools/secret-detection-handler.ts +301 -0
- package/src/tools/subagent/message.ts +1 -1
- package/src/tools/system/voice-config.ts +62 -0
- package/src/tools/tasks/index.ts +3 -3
- package/src/tools/tasks/work-item-list.ts +3 -3
- package/src/tools/tasks/work-item-update.ts +4 -5
- package/src/tools/tool-approval-handler.ts +192 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/version.ts +29 -2
- package/src/watcher/watcher-store.ts +9 -9
- package/src/work-items/work-item-runner.ts +9 -6
- /package/src/memory/migrations/{026-embeddings-nullable-vector-json.ts → 026a-embeddings-nullable-vector-json.ts} +0 -0
- /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
|
-
- **
|
|
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
|
|
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 —
|
|
192
|
+
## Task Queue — Conversation-Managed Task Execution
|
|
193
193
|
|
|
194
|
-
The Task Queue
|
|
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
|
+
```
|