@vellumai/assistant 0.5.13 → 0.5.14
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -2,21 +2,23 @@ import { execSync, spawn } from "node:child_process";
|
|
|
2
2
|
import {
|
|
3
3
|
closeSync,
|
|
4
4
|
existsSync,
|
|
5
|
-
mkdirSync,
|
|
6
5
|
openSync,
|
|
7
6
|
readFileSync,
|
|
8
7
|
unlinkSync,
|
|
9
8
|
writeFileSync,
|
|
10
9
|
} from "node:fs";
|
|
11
|
-
import {
|
|
10
|
+
import { resolve } from "node:path";
|
|
12
11
|
|
|
13
12
|
import { getRuntimeHttpHost, getRuntimeHttpPort } from "../config/env.js";
|
|
14
13
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
14
|
+
import { loadOrCreateSigningKey } from "../runtime/auth/token-service.js";
|
|
15
15
|
import { DaemonError } from "../util/errors.js";
|
|
16
16
|
import { getLogger } from "../util/logger.js";
|
|
17
17
|
import {
|
|
18
|
+
ensureDataDir,
|
|
19
|
+
getDaemonStartupLockPath,
|
|
20
|
+
getDaemonStderrLogPath,
|
|
18
21
|
getPidPath,
|
|
19
|
-
getRootDir,
|
|
20
22
|
getWorkspaceConfigPath,
|
|
21
23
|
} from "../util/platform.js";
|
|
22
24
|
|
|
@@ -66,7 +68,7 @@ function readDaemonTimeouts(): typeof DAEMON_TIMEOUT_DEFAULTS {
|
|
|
66
68
|
/**
|
|
67
69
|
* Kill the stale daemon recorded in this workspace's PID file, if any.
|
|
68
70
|
* Only targets the exact PID from our PID file — never scans globally —
|
|
69
|
-
* so isolated daemons (e.g., dev instances with a different
|
|
71
|
+
* so isolated daemons (e.g., dev instances with a different VELLUM_WORKSPACE_DIR)
|
|
70
72
|
* are never affected.
|
|
71
73
|
*/
|
|
72
74
|
function killStaleDaemon(): void {
|
|
@@ -240,7 +242,7 @@ export async function getDaemonStatus(): Promise<{
|
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
function getStartupLockPath(): string {
|
|
243
|
-
return
|
|
245
|
+
return getDaemonStartupLockPath();
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
/** Attempt to acquire a startup lock. Returns true on success. Stale locks
|
|
@@ -253,7 +255,7 @@ function acquireStartupLock(): boolean {
|
|
|
253
255
|
// On a first-time run, getRootDir() may not exist yet, and writeFileSync
|
|
254
256
|
// with 'wx' would throw ENOENT — which the catch block misinterprets as
|
|
255
257
|
// "lock already held."
|
|
256
|
-
|
|
258
|
+
ensureDataDir();
|
|
257
259
|
// O_CREAT | O_EXCL — fails atomically if the file already exists.
|
|
258
260
|
writeFileSync(lockPath, String(Date.now()), { flag: "wx" });
|
|
259
261
|
return true;
|
|
@@ -343,29 +345,43 @@ async function startDaemonLocked(): Promise<{
|
|
|
343
345
|
// a crash where the process is alive but non-responsive).
|
|
344
346
|
killStaleDaemon();
|
|
345
347
|
|
|
346
|
-
//
|
|
347
|
-
// handles
|
|
348
|
-
//
|
|
349
|
-
|
|
350
|
-
const rootDir = getRootDir();
|
|
351
|
-
if (!existsSync(rootDir)) {
|
|
352
|
-
mkdirSync(rootDir, { recursive: true });
|
|
353
|
-
}
|
|
348
|
+
// Ensure root + workspace dirs exist before spawning. The daemon itself
|
|
349
|
+
// handles full ensureDataDir() during runDaemon(), but we need at least
|
|
350
|
+
// the root dir for the PID file and stderr log.
|
|
351
|
+
ensureDataDir();
|
|
354
352
|
|
|
355
353
|
// Spawn the daemon as a detached child process
|
|
356
354
|
const mainPath = resolve(import.meta.dirname ?? __dirname, "main.ts");
|
|
357
355
|
|
|
356
|
+
// Pre-load the signing key so the daemon receives it via env var and
|
|
357
|
+
// never needs to access the protected directory for key material.
|
|
358
|
+
// Done before opening stderrFd to avoid leaking the file descriptor if
|
|
359
|
+
// loadOrCreateSigningKey throws.
|
|
360
|
+
const spawnEnv = { ...process.env };
|
|
361
|
+
if (!spawnEnv.ACTOR_TOKEN_SIGNING_KEY) {
|
|
362
|
+
try {
|
|
363
|
+
const key = loadOrCreateSigningKey();
|
|
364
|
+
spawnEnv.ACTOR_TOKEN_SIGNING_KEY = key.toString("hex");
|
|
365
|
+
} catch (err) {
|
|
366
|
+
throw new DaemonError(
|
|
367
|
+
`Failed to pre-load signing key for daemon: ${
|
|
368
|
+
err instanceof Error ? err.message : String(err)
|
|
369
|
+
}`,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
358
374
|
// Redirect the child's stderr to a file instead of piping it back to the
|
|
359
375
|
// parent. A pipe's read end is destroyed when the parent exits, leaving
|
|
360
376
|
// fd 2 broken in the child. Bun (unlike Node.js) does not ignore SIGPIPE,
|
|
361
377
|
// so any later stderr write would silently kill the daemon.
|
|
362
|
-
const stderrPath =
|
|
378
|
+
const stderrPath = getDaemonStderrLogPath();
|
|
363
379
|
const stderrFd = openSync(stderrPath, "w");
|
|
364
380
|
|
|
365
381
|
const child = spawn("bun", ["run", mainPath], {
|
|
366
382
|
detached: true,
|
|
367
383
|
stdio: ["ignore", "ignore", stderrFd],
|
|
368
|
-
env:
|
|
384
|
+
env: spawnEnv,
|
|
369
385
|
});
|
|
370
386
|
|
|
371
387
|
// The child inherited the fd; close the parent's copy.
|
|
@@ -18,14 +18,14 @@ export interface TemporalContextOptions {
|
|
|
18
18
|
userTimeZone?: string | null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
21
|
+
const WEEKDAY_SHORT = [
|
|
22
|
+
"Sun",
|
|
23
|
+
"Mon",
|
|
24
|
+
"Tue",
|
|
25
|
+
"Wed",
|
|
26
|
+
"Thu",
|
|
27
|
+
"Fri",
|
|
28
|
+
"Sat",
|
|
29
29
|
] as const;
|
|
30
30
|
const UTC_GMT_OFFSET_TOKEN_RE = /^(?:UTC|GMT)([+-])(\d{1,2})(?::?(\d{2}))?$/i;
|
|
31
31
|
|
|
@@ -183,35 +183,41 @@ const TIMEZONE_TOKEN_RE = new RegExp(
|
|
|
183
183
|
);
|
|
184
184
|
|
|
185
185
|
/**
|
|
186
|
-
* Extract the user's timezone from
|
|
186
|
+
* Extract the user's timezone from memory recall injected text.
|
|
187
187
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
188
|
+
* Prefers identity items (`<item kind="identity" ...>`) rendered inside the
|
|
189
|
+
* `<recalled>` section of `<memory_context>`. Falls back to scanning the
|
|
190
|
+
* full injected text for lines mentioning "timezone".
|
|
191
191
|
*/
|
|
192
192
|
export function extractUserTimeZoneFromRecall(
|
|
193
193
|
injectedText: string,
|
|
194
194
|
): string | null {
|
|
195
195
|
if (!injectedText || injectedText.trim().length === 0) return null;
|
|
196
196
|
|
|
197
|
-
// Prefer
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
197
|
+
// Prefer identity items: <item ... kind="identity" ...>content</item>
|
|
198
|
+
const identityItemRe = /<item\s[^>]*kind="identity"[^>]*>([\s\S]*?)<\/item>/g;
|
|
199
|
+
let match: RegExpExecArray | null;
|
|
200
|
+
const identityTexts: string[] = [];
|
|
201
|
+
while ((match = identityItemRe.exec(injectedText)) !== null) {
|
|
202
|
+
identityTexts.push(match[1]);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (identityTexts.length > 0) {
|
|
206
|
+
// First pass: identity items whose text mentions "timezone"
|
|
207
|
+
for (const text of identityTexts) {
|
|
208
|
+
if (/time\s*zone/i.test(text)) {
|
|
209
|
+
for (const token of extractTimeZoneCandidates(text)) {
|
|
206
210
|
const canonical = canonicalizeTimeZone(token);
|
|
207
211
|
if (canonical) return canonical;
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
}
|
|
211
|
-
//
|
|
212
|
-
for (const
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
+
// Second pass: any timezone token in any identity item
|
|
216
|
+
for (const text of identityTexts) {
|
|
217
|
+
for (const token of extractTimeZoneCandidates(text)) {
|
|
218
|
+
const canonical = canonicalizeTimeZone(token);
|
|
219
|
+
if (canonical) return canonical;
|
|
220
|
+
}
|
|
215
221
|
}
|
|
216
222
|
}
|
|
217
223
|
|
|
@@ -284,30 +290,25 @@ function formatLocalDate(date: Date, timeZone: string): string {
|
|
|
284
290
|
}
|
|
285
291
|
|
|
286
292
|
/**
|
|
287
|
-
* Format
|
|
293
|
+
* Format HH:MM and UTC offset for the given instant in the given timezone.
|
|
288
294
|
*/
|
|
289
|
-
function
|
|
295
|
+
function formatCompactTimeAndOffset(
|
|
296
|
+
date: Date,
|
|
297
|
+
timeZone: string,
|
|
298
|
+
): { time: string; offset: string } {
|
|
290
299
|
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
291
300
|
timeZone,
|
|
292
|
-
year: "numeric",
|
|
293
|
-
month: "2-digit",
|
|
294
|
-
day: "2-digit",
|
|
295
301
|
hour: "2-digit",
|
|
296
302
|
minute: "2-digit",
|
|
297
|
-
second: "2-digit",
|
|
298
303
|
hourCycle: "h23",
|
|
299
304
|
timeZoneName: "shortOffset",
|
|
300
305
|
});
|
|
301
306
|
const parts = fmt.formatToParts(date);
|
|
302
307
|
const get = (t: string) => parts.find((p) => p.type === t)?.value ?? "";
|
|
303
|
-
const offset = normalizeOffsetToken(get("timeZoneName"));
|
|
304
|
-
const year = get("year");
|
|
305
|
-
const month = get("month");
|
|
306
|
-
const day = get("day");
|
|
307
308
|
const hour = get("hour");
|
|
308
309
|
const minute = get("minute");
|
|
309
|
-
const
|
|
310
|
-
return `${
|
|
310
|
+
const offset = normalizeOffsetToken(get("timeZoneName"));
|
|
311
|
+
return { time: `${hour}:${minute}`, offset };
|
|
311
312
|
}
|
|
312
313
|
|
|
313
314
|
/**
|
|
@@ -345,22 +346,23 @@ export function buildTemporalContext(
|
|
|
345
346
|
: "assistant_host_fallback";
|
|
346
347
|
const todayParts = localDateParts(now, timeZone);
|
|
347
348
|
const todayStr = formatLocalDate(now, timeZone);
|
|
348
|
-
const todayWeekday =
|
|
349
|
+
const todayWeekday = WEEKDAY_SHORT[todayParts.weekday];
|
|
350
|
+
const { time, offset } = formatCompactTimeAndOffset(now, timeZone);
|
|
351
|
+
|
|
352
|
+
const tzSuffix =
|
|
353
|
+
timeZoneSource === "assistant_host_fallback" ? " (host fallback)" : "";
|
|
349
354
|
|
|
350
355
|
const lines = [
|
|
351
356
|
`<temporal_context>`,
|
|
352
|
-
`Today: ${todayStr} (${todayWeekday})`,
|
|
353
|
-
`
|
|
354
|
-
`Current local time: ${formatLocalIsoWithOffset(now, timeZone)}`,
|
|
355
|
-
`Current UTC time: ${now.toISOString()}`,
|
|
356
|
-
`Timezone source: ${timeZoneSource}`,
|
|
357
|
+
`Today: ${todayStr} (${todayWeekday}) ${time} ${offset}`,
|
|
358
|
+
`TZ: ${timeZone}${tzSuffix}`,
|
|
357
359
|
];
|
|
358
360
|
|
|
359
361
|
if (userTimeZone && userTimeZone !== timeZone) {
|
|
360
|
-
lines.push(`User
|
|
362
|
+
lines.push(`User TZ: ${userTimeZone}`);
|
|
361
363
|
}
|
|
362
364
|
if (resolvedHostTimeZone !== timeZone) {
|
|
363
|
-
lines.push(`
|
|
365
|
+
lines.push(`Host TZ: ${resolvedHostTimeZone}`);
|
|
364
366
|
}
|
|
365
367
|
|
|
366
368
|
lines.push(`</temporal_context>`);
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
|
|
18
18
|
import { getLogger } from "../util/logger.js";
|
|
19
|
-
import {
|
|
19
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
20
20
|
|
|
21
21
|
const log = getLogger("dictation-profile-store");
|
|
22
22
|
|
|
@@ -97,7 +97,7 @@ let storePathOverride: string | null = null;
|
|
|
97
97
|
|
|
98
98
|
function getStorePath(): string {
|
|
99
99
|
if (storePathOverride) return storePathOverride;
|
|
100
|
-
return join(
|
|
100
|
+
return join(getWorkspaceDir(), "dictation-profiles.json");
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
let cachedConfig: DictationProfilesConfig | null = null;
|
|
@@ -24,7 +24,9 @@ import {
|
|
|
24
24
|
UNTITLED_FALLBACK,
|
|
25
25
|
} from "../../memory/conversation-title-service.js";
|
|
26
26
|
import * as pendingInteractions from "../../runtime/pending-interactions.js";
|
|
27
|
+
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
27
28
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
29
|
+
import { summarizeToolInput } from "../../tools/tool-input-summary.js";
|
|
28
30
|
import { truncate } from "../../util/truncate.js";
|
|
29
31
|
import type { Conversation } from "../conversation.js";
|
|
30
32
|
import { HostBashProxy } from "../host-bash-proxy.js";
|
|
@@ -84,6 +86,14 @@ export function makeEventSender(params: {
|
|
|
84
86
|
|
|
85
87
|
try {
|
|
86
88
|
const trustContext = conversation.trustContext;
|
|
89
|
+
const inputRecord = event.input as Record<string, unknown>;
|
|
90
|
+
const activityRaw =
|
|
91
|
+
(typeof inputRecord.activity === "string"
|
|
92
|
+
? inputRecord.activity
|
|
93
|
+
: undefined) ??
|
|
94
|
+
(typeof inputRecord.reason === "string"
|
|
95
|
+
? inputRecord.reason
|
|
96
|
+
: undefined);
|
|
87
97
|
createCanonicalGuardianRequest({
|
|
88
98
|
id: event.requestId,
|
|
89
99
|
kind: "tool_approval",
|
|
@@ -92,6 +102,15 @@ export function makeEventSender(params: {
|
|
|
92
102
|
conversationId,
|
|
93
103
|
guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
|
|
94
104
|
toolName: event.toolName,
|
|
105
|
+
commandPreview:
|
|
106
|
+
redactSecrets(
|
|
107
|
+
summarizeToolInput(event.toolName, inputRecord),
|
|
108
|
+
) || undefined,
|
|
109
|
+
riskLevel: event.riskLevel,
|
|
110
|
+
activityText: activityRaw
|
|
111
|
+
? redactSecrets(activityRaw)
|
|
112
|
+
: undefined,
|
|
113
|
+
executionTarget: event.executionTarget,
|
|
95
114
|
status: "pending",
|
|
96
115
|
requestCode: generateCanonicalRequestCode(),
|
|
97
116
|
expiresAt: Date.now() + 5 * 60 * 1000,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
3
|
import { getConfig } from "../../config/loader.js";
|
|
4
|
+
import type { Speed } from "../../config/schemas/inference.js";
|
|
4
5
|
import type { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
|
|
5
6
|
import type { SecretPromptResult } from "../../permissions/secret-prompter.js";
|
|
6
7
|
import type { AuthContext } from "../../runtime/auth/types.js";
|
|
@@ -91,27 +92,8 @@ export interface RenderedHistoryContent {
|
|
|
91
92
|
contentOrder: string[];
|
|
92
93
|
/** UI surfaces (widgets) embedded in the message. */
|
|
93
94
|
surfaces: HistorySurface[];
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
export interface SubagentNotificationData {
|
|
97
|
-
subagentId: string;
|
|
98
|
-
label: string;
|
|
99
|
-
status: "completed" | "failed" | "aborted";
|
|
100
|
-
error?: string;
|
|
101
|
-
conversationId?: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export interface ParsedHistoryMessage {
|
|
105
|
-
id?: string;
|
|
106
|
-
role: string;
|
|
107
|
-
text: string;
|
|
108
|
-
timestamp: number;
|
|
109
|
-
toolCalls: HistoryToolCall[];
|
|
110
|
-
toolCallsBeforeText: boolean;
|
|
111
|
-
textSegments: string[];
|
|
112
|
-
contentOrder: string[];
|
|
113
|
-
surfaces: HistorySurface[];
|
|
114
|
-
subagentNotification?: SubagentNotificationData;
|
|
95
|
+
/** Thinking segments extracted from thinking blocks. */
|
|
96
|
+
thinkingSegments: string[];
|
|
115
97
|
}
|
|
116
98
|
|
|
117
99
|
/**
|
|
@@ -120,6 +102,7 @@ export interface ParsedHistoryMessage {
|
|
|
120
102
|
export interface ConversationCreateOptions {
|
|
121
103
|
systemPromptOverride?: string;
|
|
122
104
|
maxResponseTokens?: number;
|
|
105
|
+
speed?: Speed;
|
|
123
106
|
transport?: ConversationTransportMetadata;
|
|
124
107
|
assistantId?: string;
|
|
125
108
|
trustContext?: TrustContext;
|
|
@@ -220,6 +203,7 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
220
203
|
textSegments: text ? [text] : [],
|
|
221
204
|
contentOrder: text ? ["text:0"] : [],
|
|
222
205
|
surfaces: [],
|
|
206
|
+
thinkingSegments: [],
|
|
223
207
|
};
|
|
224
208
|
}
|
|
225
209
|
|
|
@@ -227,6 +211,7 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
227
211
|
const attachmentParts: string[] = [];
|
|
228
212
|
const toolCalls: HistoryToolCall[] = [];
|
|
229
213
|
const surfaces: HistorySurface[] = [];
|
|
214
|
+
const thinkingSegments: string[] = [];
|
|
230
215
|
const pendingToolUses = new Map<string, HistoryToolCall>();
|
|
231
216
|
let seenText = false;
|
|
232
217
|
let seenToolUse = false;
|
|
@@ -300,6 +285,13 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
300
285
|
continue;
|
|
301
286
|
}
|
|
302
287
|
|
|
288
|
+
if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
289
|
+
finalizeSegment();
|
|
290
|
+
thinkingSegments.push(block.thinking);
|
|
291
|
+
contentOrder.push(`thinking:${thinkingSegments.length - 1}`);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
303
295
|
if (block.type === "text" && typeof block.text === "string") {
|
|
304
296
|
textParts.push(block.text);
|
|
305
297
|
ensureSegment();
|
|
@@ -410,6 +402,7 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
410
402
|
textSegments,
|
|
411
403
|
contentOrder,
|
|
412
404
|
surfaces,
|
|
405
|
+
thinkingSegments,
|
|
413
406
|
};
|
|
414
407
|
}
|
|
415
408
|
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
|
|
3
1
|
import { config as dotenvConfig } from "dotenv";
|
|
4
2
|
|
|
5
3
|
import { setPointerMessageProcessor } from "../calls/call-pointer-messages.js";
|
|
@@ -89,8 +87,8 @@ import { getDeviceId } from "../util/device-id.js";
|
|
|
89
87
|
import { getLogger, initLogger } from "../util/logger.js";
|
|
90
88
|
import {
|
|
91
89
|
ensureDataDir,
|
|
90
|
+
getDotEnvPath,
|
|
92
91
|
getInterfacesDir,
|
|
93
|
-
getRootDir,
|
|
94
92
|
getWorkspaceDir,
|
|
95
93
|
} from "../util/platform.js";
|
|
96
94
|
import {
|
|
@@ -149,7 +147,7 @@ export {
|
|
|
149
147
|
const log = getLogger("lifecycle");
|
|
150
148
|
|
|
151
149
|
function loadDotEnv(): void {
|
|
152
|
-
dotenvConfig({ path:
|
|
150
|
+
dotenvConfig({ path: getDotEnvPath(), quiet: true });
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
export interface CesStartupResult {
|
|
@@ -266,8 +264,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
266
264
|
ensureDataDir();
|
|
267
265
|
|
|
268
266
|
// Load (or generate + persist) the auth signing key so tokens survive
|
|
269
|
-
// daemon restarts.
|
|
270
|
-
// protected directory.
|
|
267
|
+
// daemon restarts.
|
|
271
268
|
const signingKey = resolveSigningKey();
|
|
272
269
|
initAuthSigningKey(signingKey);
|
|
273
270
|
|
|
@@ -1155,12 +1152,13 @@ export async function runDaemon(): Promise<void> {
|
|
|
1155
1152
|
|
|
1156
1153
|
const heartbeatConfig = config.heartbeat;
|
|
1157
1154
|
const heartbeat = new HeartbeatService({
|
|
1158
|
-
processMessage: (conversationId, content) =>
|
|
1155
|
+
processMessage: (conversationId, content, options) =>
|
|
1159
1156
|
server.processMessage(conversationId, content, undefined, {
|
|
1160
1157
|
trustContext: {
|
|
1161
1158
|
sourceChannel: "vellum",
|
|
1162
1159
|
trustClass: "guardian",
|
|
1163
1160
|
},
|
|
1161
|
+
...options,
|
|
1164
1162
|
}),
|
|
1165
1163
|
alerter: (alert) => server.broadcast(alert),
|
|
1166
1164
|
onConversationCreated: (info) =>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Enables desktop clients to fetch pending guardian prompts and submit
|
|
3
3
|
// button decisions deterministically (without text parsing).
|
|
4
4
|
|
|
5
|
+
import type { GuardianDecisionPrompt } from "../../runtime/guardian-decision-types.js";
|
|
6
|
+
|
|
5
7
|
// === Client -> Server ===
|
|
6
8
|
|
|
7
9
|
export interface GuardianActionsPendingRequest {
|
|
@@ -21,23 +23,7 @@ export interface GuardianActionDecision {
|
|
|
21
23
|
export interface GuardianActionsPendingResponse {
|
|
22
24
|
type: "guardian_actions_pending_response";
|
|
23
25
|
conversationId: string;
|
|
24
|
-
prompts:
|
|
25
|
-
requestId: string;
|
|
26
|
-
requestCode: string;
|
|
27
|
-
state: string;
|
|
28
|
-
questionText: string;
|
|
29
|
-
toolName: string | null;
|
|
30
|
-
actions: Array<{ action: string; label: string }>;
|
|
31
|
-
expiresAt: number;
|
|
32
|
-
conversationId: string;
|
|
33
|
-
callSessionId: string | null;
|
|
34
|
-
/**
|
|
35
|
-
* Canonical request kind (e.g. 'tool_approval', 'pending_question').
|
|
36
|
-
* Present when the prompt originates from the canonical guardian request
|
|
37
|
-
* store. Absent for legacy-only prompts.
|
|
38
|
-
*/
|
|
39
|
-
kind?: string;
|
|
40
|
-
}>;
|
|
26
|
+
prompts: GuardianDecisionPrompt[];
|
|
41
27
|
}
|
|
42
28
|
|
|
43
29
|
export interface GuardianActionDecisionResponse {
|
|
@@ -211,6 +211,14 @@ export interface NavigateSettings {
|
|
|
211
211
|
tab: string;
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
export interface ShowPlatformLogin {
|
|
215
|
+
type: "show_platform_login";
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface PlatformDisconnected {
|
|
219
|
+
type: "platform_disconnected";
|
|
220
|
+
}
|
|
221
|
+
|
|
214
222
|
// --- Domain-level union aliases (consumed by the barrel file) ---
|
|
215
223
|
|
|
216
224
|
export type _IntegrationsClientMessages =
|
|
@@ -237,4 +245,6 @@ export type _IntegrationsServerMessages =
|
|
|
237
245
|
| IntegrationConnectResult
|
|
238
246
|
| OAuthConnectResultResponse
|
|
239
247
|
| OpenUrl
|
|
240
|
-
| NavigateSettings
|
|
248
|
+
| NavigateSettings
|
|
249
|
+
| ShowPlatformLogin
|
|
250
|
+
| PlatformDisconnected;
|
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* In-memory pairing request store with TTL.
|
|
3
3
|
*
|
|
4
4
|
* Each pairing request lives for at most TTL_MS (5 minutes) before
|
|
5
5
|
* being swept as expired. Status transitions:
|
|
6
6
|
* registered → pending → approved | denied | expired
|
|
7
|
-
*
|
|
8
|
-
* Entries are persisted to ~/.vellum/protected/pairing-requests.json
|
|
9
|
-
* using the same atomic-write pattern as approved-devices-store.ts
|
|
10
|
-
* so that device bindings survive daemon restarts.
|
|
11
7
|
*/
|
|
12
8
|
|
|
13
9
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
14
|
-
import {
|
|
15
|
-
chmodSync,
|
|
16
|
-
existsSync,
|
|
17
|
-
mkdirSync,
|
|
18
|
-
readFileSync,
|
|
19
|
-
renameSync,
|
|
20
|
-
writeFileSync,
|
|
21
|
-
} from "node:fs";
|
|
22
|
-
import { dirname, join } from "node:path";
|
|
23
10
|
|
|
24
11
|
import { getLogger } from "../util/logger.js";
|
|
25
|
-
import { getRootDir } from "../util/platform.js";
|
|
26
12
|
|
|
27
13
|
const log = getLogger("pairing-store");
|
|
28
14
|
|
|
@@ -59,60 +45,11 @@ function timingSafeCompare(a: string, b: string): boolean {
|
|
|
59
45
|
return timingSafeEqual(bufA, bufB);
|
|
60
46
|
}
|
|
61
47
|
|
|
62
|
-
interface PairingStoreFile {
|
|
63
|
-
version: 1;
|
|
64
|
-
requests: PairingRequest[];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function getStorePath(): string {
|
|
68
|
-
return join(getRootDir(), "protected", "pairing-requests.json");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function loadFromDisk(): Map<string, PairingRequest> {
|
|
72
|
-
const path = getStorePath();
|
|
73
|
-
if (!existsSync(path)) {
|
|
74
|
-
return new Map();
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
const raw = readFileSync(path, "utf-8");
|
|
78
|
-
const data = JSON.parse(raw) as PairingStoreFile;
|
|
79
|
-
if (data.version !== 1 || !Array.isArray(data.requests)) {
|
|
80
|
-
log.warn("Invalid pairing-requests.json format, starting fresh");
|
|
81
|
-
return new Map();
|
|
82
|
-
}
|
|
83
|
-
const map = new Map<string, PairingRequest>();
|
|
84
|
-
for (const entry of data.requests) {
|
|
85
|
-
map.set(entry.pairingRequestId, entry);
|
|
86
|
-
}
|
|
87
|
-
return map;
|
|
88
|
-
} catch (err) {
|
|
89
|
-
log.error({ err }, "Failed to load pairing-requests.json");
|
|
90
|
-
return new Map();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function saveToDisk(requests: Map<string, PairingRequest>): void {
|
|
95
|
-
const path = getStorePath();
|
|
96
|
-
const dir = dirname(path);
|
|
97
|
-
if (!existsSync(dir)) {
|
|
98
|
-
mkdirSync(dir, { recursive: true });
|
|
99
|
-
}
|
|
100
|
-
const data: PairingStoreFile = {
|
|
101
|
-
version: 1,
|
|
102
|
-
requests: Array.from(requests.values()),
|
|
103
|
-
};
|
|
104
|
-
const tmpPath = path + ".tmp." + process.pid;
|
|
105
|
-
writeFileSync(tmpPath, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
106
|
-
renameSync(tmpPath, path);
|
|
107
|
-
chmodSync(path, 0o600);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
48
|
export class PairingStore {
|
|
111
49
|
private requests = new Map<string, PairingRequest>();
|
|
112
50
|
private sweepTimer: ReturnType<typeof setInterval> | null = null;
|
|
113
51
|
|
|
114
52
|
start(): void {
|
|
115
|
-
this.requests = loadFromDisk();
|
|
116
53
|
this.sweepTimer = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|
117
54
|
}
|
|
118
55
|
|
|
@@ -168,8 +105,6 @@ export class PairingStore {
|
|
|
168
105
|
localLanUrl: params.localLanUrl ?? null,
|
|
169
106
|
createdAt: Date.now(),
|
|
170
107
|
});
|
|
171
|
-
this.persist();
|
|
172
|
-
|
|
173
108
|
log.info(
|
|
174
109
|
{ pairingRequestId: params.pairingRequestId },
|
|
175
110
|
"Pairing request registered",
|
|
@@ -226,8 +161,6 @@ export class PairingStore {
|
|
|
226
161
|
if (entry.status === "registered") {
|
|
227
162
|
entry.status = "pending";
|
|
228
163
|
}
|
|
229
|
-
this.persist();
|
|
230
|
-
|
|
231
164
|
return { ok: true, entry };
|
|
232
165
|
}
|
|
233
166
|
|
|
@@ -242,7 +175,6 @@ export class PairingStore {
|
|
|
242
175
|
if (!entry) return null;
|
|
243
176
|
entry.status = "approved";
|
|
244
177
|
entry.bearerToken = bearerToken;
|
|
245
|
-
this.persist();
|
|
246
178
|
return entry;
|
|
247
179
|
}
|
|
248
180
|
|
|
@@ -253,7 +185,6 @@ export class PairingStore {
|
|
|
253
185
|
const entry = this.requests.get(pairingRequestId);
|
|
254
186
|
if (!entry) return null;
|
|
255
187
|
entry.status = "denied";
|
|
256
|
-
this.persist();
|
|
257
188
|
return entry;
|
|
258
189
|
}
|
|
259
190
|
|
|
@@ -274,14 +205,6 @@ export class PairingStore {
|
|
|
274
205
|
return timingSafeCompare(entry.hashedPairingSecret, hashedSecret);
|
|
275
206
|
}
|
|
276
207
|
|
|
277
|
-
private persist(): void {
|
|
278
|
-
try {
|
|
279
|
-
saveToDisk(this.requests);
|
|
280
|
-
} catch (err) {
|
|
281
|
-
log.error({ err }, "Failed to persist pairing requests to disk");
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
208
|
private sweep(): void {
|
|
286
209
|
const now = Date.now();
|
|
287
210
|
let changed = false;
|
|
@@ -300,7 +223,7 @@ export class PairingStore {
|
|
|
300
223
|
}
|
|
301
224
|
}
|
|
302
225
|
if (changed) {
|
|
303
|
-
|
|
226
|
+
log.debug("Sweep completed with changes");
|
|
304
227
|
}
|
|
305
228
|
}
|
|
306
229
|
}
|