@vellumai/assistant 0.4.56 → 0.4.57
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 +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +237 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +547 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +139 -274
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +25 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +1 -0
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/emit-signal.ts +4 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +166 -33
- package/src/prompts/templates/IDENTITY.md +8 -23
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +46 -2
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +1 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +63 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +9 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +8 -8
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +263 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +220 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/telemetry/usage-telemetry-reporter.ts +51 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
package/ARCHITECTURE.md
CHANGED
|
@@ -4,11 +4,11 @@ This document owns assistant-runtime architecture details. The repo-level archit
|
|
|
4
4
|
|
|
5
5
|
### Channel Onboarding Playbook Bootstrap
|
|
6
6
|
|
|
7
|
-
- Transport metadata arrives via `
|
|
7
|
+
- Transport metadata arrives via `conversation_create.transport` (HTTP) or `/channels/inbound` (`channelId`, optional `hints`, optional `uxBrief`).
|
|
8
8
|
- Telegram webhook ingress injects deterministic channel-safe transport metadata (`hints` + `uxBrief`) so non-dashboard channels defer dashboard-only UI tasks cleanly.
|
|
9
9
|
- `OnboardingPlaybookManager` resolves `<channel>_onboarding.md`, checks `onboarding/playbooks/registry.json`, and applies per-channel first-time fast-path onboarding.
|
|
10
10
|
- `OnboardingOrchestrator` derives onboarding-mode guidance (post-hatch sequence, USER.md capture) from playbook + transport context.
|
|
11
|
-
-
|
|
11
|
+
- Conversation runtime assembly injects both `<channel_onboarding_playbook>` and `<onboarding_mode>` context before provider calls, then strips both from persisted conversation history.
|
|
12
12
|
- Permission setup remains user-initiated and hatch + first-conversation flows avoid proactive permission asks.
|
|
13
13
|
|
|
14
14
|
### Guardian Actor Context (Unified Across Channels)
|
|
@@ -17,7 +17,7 @@ This document owns assistant-runtime architecture details. The repo-level archit
|
|
|
17
17
|
- The same resolver is used by:
|
|
18
18
|
- `/channels/inbound` (Telegram/WhatsApp path) before run orchestration.
|
|
19
19
|
- Inbound Twilio voice setup (`RelayConnection.handleSetup`) to seed call-time actor context.
|
|
20
|
-
- Runtime channel runs pass this as `trustContext`, and
|
|
20
|
+
- Runtime channel runs pass this as `trustContext`, and conversation runtime assembly injects `<inbound_actor_context>` (via `inboundActorContextFromTrustContext()`) into provider-facing prompts.
|
|
21
21
|
- Voice calls mirror the same prompt contract: `CallController` receives guardian context on setup and refreshes it immediately after successful voice challenge verification, so the first post-verification turn is grounded as `actor_role: guardian`.
|
|
22
22
|
- Voice-specific behavior (DTMF/speech verification flow, relay state machine) remains voice-local; only actor-role resolution is shared.
|
|
23
23
|
|
|
@@ -138,7 +138,7 @@ All guardian approval decisions — regardless of how they arrive — route thro
|
|
|
138
138
|
| `src/runtime/routes/guardian-action-routes.ts` | HTTP route handlers for `GET /v1/guardian-actions/pending` and `POST /v1/guardian-actions/decision` |
|
|
139
139
|
| `src/runtime/channel-approval-types.ts` | Channel-facing approval action types and `toApprovalActionOptions` bridge |
|
|
140
140
|
|
|
141
|
-
### Temporary Approval Modes (
|
|
141
|
+
### Temporary Approval Modes (Conversation-Scoped Overrides)
|
|
142
142
|
|
|
143
143
|
In addition to persistent trust rules (`always_allow` / `always_deny`), the approval system supports two **temporary** approval modes that auto-approve tool confirmations for the duration of a conversation or a fixed time window. These exist to reduce prompt fatigue during intensive sessions without permanently altering the trust configuration.
|
|
144
144
|
|
|
@@ -147,7 +147,7 @@ In addition to persistent trust rules (`always_allow` / `always_deny`), the appr
|
|
|
147
147
|
1. **`allow_conversation`** — Auto-approve all tool confirmations for the remainder of the current conversation. The override persists until the session ends, the conversation is closed, or the mode is explicitly cleared.
|
|
148
148
|
2. **`allow_10m`** — Auto-approve all tool confirmations for 10 minutes (configurable). The override expires lazily on the next read after the TTL elapses — no background sweep runs.
|
|
149
149
|
|
|
150
|
-
**
|
|
150
|
+
**Conversation-scoped, in-memory only:** Overrides are keyed by `conversationId` and stored in an in-memory `Map` inside `conversation-approval-overrides.ts`. They do not survive daemon restarts, which is intentional — temporary approvals should not outlive the conversation that created them.
|
|
151
151
|
|
|
152
152
|
**Integration with the permission pipeline:** The permission checker (`src/tools/permission-checker.ts`) checks for an active temporary override via `getEffectiveMode()` before prompting the user. If an active override exists for the current conversation, the confirmation is auto-approved without surfacing a prompt. This check runs after persistent trust rules, so a persistent `deny` rule still takes precedence.
|
|
153
153
|
|
|
@@ -783,10 +783,10 @@ All client-server communication uses HTTP for request/response operations and Se
|
|
|
783
783
|
|
|
784
784
|
The daemon emits two distinct error message types via SSE:
|
|
785
785
|
|
|
786
|
-
| Message type | Scope
|
|
787
|
-
| -------------------- |
|
|
788
|
-
| `conversation_error` |
|
|
789
|
-
| `error` | Global
|
|
786
|
+
| Message type | Scope | Purpose | Payload |
|
|
787
|
+
| -------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
788
|
+
| `conversation_error` | Conversation-scoped | Typed, actionable failures during conversation runtime (e.g., provider network error, rate limit, API failure) | `sessionId`, `code` (typed enum), `userMessage`, `retryable`, `debugDetails?` |
|
|
789
|
+
| `error` | Global | Generic, non-session failures (e.g., daemon startup errors, unknown message types) | `message` (string) |
|
|
790
790
|
|
|
791
791
|
**Design rationale:** `conversation_error` carries structured metadata (error code, retryable flag, debug details) so the client can present actionable UI — a toast with retry/dismiss buttons — rather than a generic error banner. The older `error` type is retained for backward compatibility with non-session contexts.
|
|
792
792
|
|
|
@@ -845,7 +845,7 @@ sequenceDiagram
|
|
|
845
845
|
end
|
|
846
846
|
```
|
|
847
847
|
|
|
848
|
-
1. **Daemon** encounters a
|
|
848
|
+
1. **Daemon** encounters a conversation-scoped failure, classifies it via `classifyConversationError()`, and sends a `conversation_error` SSE event with the conversation ID, typed error code, user-facing message, retryable flag, and optional debug details. Conversation-scoped failures emit _only_ `conversation_error` (never the generic `error` type) to prevent cross-conversation bleed.
|
|
849
849
|
2. **ChatViewModel** receives the error via DaemonClient's `subscribe()` stream (each view model gets an independent stream), sets the `conversationError` property, and transitions out of the streaming/loading state so the UI is interactive. If the error arrives during an active cancel (`wasCancelling == true`), it is suppressed — cancel only shows `generation_cancelled` behavior.
|
|
850
850
|
3. **ChatView** observes the published `conversationError` and displays an actionable toast with a category-specific icon and accent color:
|
|
851
851
|
- **Retry** (shown when `retryable` is true): calls `retryAfterConversationError()`, which clears the error and sends a `regenerate` message to the daemon.
|
package/Dockerfile
CHANGED
|
@@ -89,6 +89,9 @@ RUN echo 'Dir::State "/data/dpkg";' > /etc/apt/apt.conf.d/99data-dir && \
|
|
|
89
89
|
ENV PATH="/data/usr/bin:/data/usr/sbin:${PATH}"
|
|
90
90
|
ENV LD_LIBRARY_PATH="/data/usr/lib:/data/usr/lib/x86_64-linux-gnu:/data/usr/lib/aarch64-linux-gnu"
|
|
91
91
|
|
|
92
|
+
# Ensure the CES bootstrap socket volume is writable by the non-root CES user.
|
|
93
|
+
RUN mkdir -p /run/ces-bootstrap && chmod 777 /run/ces-bootstrap
|
|
94
|
+
|
|
92
95
|
USER root
|
|
93
96
|
|
|
94
97
|
EXPOSE 3001
|
package/README.md
CHANGED
|
@@ -77,17 +77,17 @@ bun run src/index.ts # interactive CLI session
|
|
|
77
77
|
|
|
78
78
|
### CLI commands
|
|
79
79
|
|
|
80
|
-
| Command
|
|
81
|
-
|
|
|
82
|
-
| `vellum wake`
|
|
83
|
-
| `vellum sleep`
|
|
84
|
-
| `vellum ps`
|
|
85
|
-
| `assistant`
|
|
86
|
-
| `assistant
|
|
87
|
-
| `assistant config set\|get\|list`
|
|
88
|
-
| `assistant keys set\|list\|delete`
|
|
89
|
-
| `assistant trust list\|remove\|clear`
|
|
90
|
-
| `assistant doctor`
|
|
80
|
+
| Command | Description |
|
|
81
|
+
| -------------------------------------------------- | ------------------------------------------------ |
|
|
82
|
+
| `vellum wake` | Start assistant + gateway from current checkout |
|
|
83
|
+
| `vellum sleep` | Stop assistant + gateway processes |
|
|
84
|
+
| `vellum ps` | List assistants and per-assistant process status |
|
|
85
|
+
| `assistant` | Launch interactive CLI session |
|
|
86
|
+
| `assistant conversations list\|new\|export\|clear` | Manage conversations |
|
|
87
|
+
| `assistant config set\|get\|list` | Manage configuration |
|
|
88
|
+
| `assistant keys set\|list\|delete` | Manage API keys in secure storage |
|
|
89
|
+
| `assistant trust list\|remove\|clear` | Manage trust rules |
|
|
90
|
+
| `assistant doctor` | Run diagnostic checks |
|
|
91
91
|
|
|
92
92
|
## Project Structure
|
|
93
93
|
|
|
@@ -542,7 +542,7 @@ sequenceDiagram
|
|
|
542
542
|
Materialize->>DB: load attachment (including base64 data)
|
|
543
543
|
Materialize->>Visibility: isAttachmentVisible(attachmentCtx, currentCtx)
|
|
544
544
|
Note over Visibility: Second visibility check at materialize time<br/>prevents TOCTOU between search and materialize
|
|
545
|
-
Materialize->>Materialize: size check (max
|
|
545
|
+
Materialize->>Materialize: size check (max 100 MB)
|
|
546
546
|
Materialize->>Sandbox: write decoded bytes to destination
|
|
547
547
|
Materialize-->>Model: "Materialized 'photo.jpg' to /workspace/media/photo.jpg"
|
|
548
548
|
```
|
|
@@ -586,7 +586,7 @@ graph TB
|
|
|
586
586
|
### Materialize Safeguards
|
|
587
587
|
|
|
588
588
|
- **Sandbox path enforcement**: Destination path must resolve inside the sandbox working directory
|
|
589
|
-
- **Size limit**:
|
|
589
|
+
- **Size limit**: 100 MB ceiling prevents materializing excessively large attachments
|
|
590
590
|
- **Double visibility check**: Both `asset_search` and `asset_materialize` independently verify visibility, preventing TOCTOU races between search and use
|
|
591
591
|
- **Risk level**: Both tools are `RiskLevel.Low` since they read existing data and write only within the sandbox
|
|
592
592
|
|
|
@@ -250,7 +250,7 @@ The recall pipeline runs on every turn that passes the `needsMemory` gate (skips
|
|
|
250
250
|
9. **Two-layer XML injection** (`formatting.ts`): Budget-aware rendering into four XML sections:
|
|
251
251
|
|
|
252
252
|
```xml
|
|
253
|
-
<memory_context>
|
|
253
|
+
<memory_context __injected>
|
|
254
254
|
|
|
255
255
|
<user_identity>
|
|
256
256
|
<!-- identity-kind tier 1 items (plain statements) -->
|
|
@@ -273,7 +273,7 @@ The recall pipeline runs on every turn that passes the `needsMemory` gate (skips
|
|
|
273
273
|
|
|
274
274
|
Empty sections are omitted. Each section has a per-item token budget (150 tokens for tier 1, 100 for tier 2). Tier 1 sections consume budget first; tier 2 uses the remainder.
|
|
275
275
|
|
|
276
|
-
10. **Injection strategy**: The rendered `<memory_context>` block is
|
|
276
|
+
10. **Injection strategy**: The rendered `<memory_context __injected>` block is prepended as a text content block to the last user message (`injectMemoryRecallAsUserBlock`), following the same pattern as workspace, temporal, and other runtime injections. Stripping is handled by the generic `stripUserTextBlocksByPrefix` mechanism matching the `<memory_context __injected>` prefix (with a backward-compat entry for the legacy `<memory_context>` prefix from older history). This avoids synthetic message pairs and preserves prompt prefix caching between turns.
|
|
277
277
|
|
|
278
278
|
### Internal-Only Trust Gating
|
|
279
279
|
|
|
@@ -464,7 +464,7 @@ The Anthropic provider places `cache_control: { type: 'ephemeral' }` on the **la
|
|
|
464
464
|
|
|
465
465
|
## Temporal Context Injection — Date Grounding
|
|
466
466
|
|
|
467
|
-
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
|
|
467
|
+
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, and timezone source metadata. This enables reliable reasoning about dates and times without persisting volatile temporal data in conversation history.
|
|
468
468
|
|
|
469
469
|
### Per-turn flow
|
|
470
470
|
|
|
@@ -488,7 +488,6 @@ graph TB
|
|
|
488
488
|
- **Clock source invariant**: Absolute time (`now`) always comes from the assistant host clock (`Date.now()`), never from channel/client clocks.
|
|
489
489
|
- **Timezone precedence**: If `ui.userTimezone` is configured, temporal context uses it for local-date interpretation. Otherwise it falls back to memory-stored timezone, then assistant host timezone.
|
|
490
490
|
- **Timezone-aware**: Uses `Intl.DateTimeFormat` APIs for DST-safe date arithmetic and timezone validation/canonicalization.
|
|
491
|
-
- **Bounded output**: Hard-capped at 1500 characters and 14 horizon entries to prevent prompt bloat.
|
|
492
491
|
- **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.
|
|
493
492
|
- **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>`.
|
|
494
493
|
- **Retry paths**: Temporal context is included in all three `applyRuntimeInjections` call sites (main path, compact retry, media-trim retry).
|
|
@@ -60,36 +60,29 @@ The existing `host_bash` tool executes commands on the host machine without any
|
|
|
60
60
|
|
|
61
61
|
**Implication**: `host_bash` represents a weaker security tier. Agents that require the strong secrecy guarantee must use `run_authenticated_command` instead. Trust rules and permission policies should reflect this distinction — managed deployments may deny `host_bash` entirely for untrusted agents while allowing `run_authenticated_command`.
|
|
62
62
|
|
|
63
|
-
### 2. Local static secrets are local-mode only — by
|
|
63
|
+
### 2. Local static secrets are local-mode only — by policy
|
|
64
64
|
|
|
65
|
-
For the
|
|
65
|
+
For the current implementation, local static secrets (API keys, tokens stored via the credential store in `~/.vellum/protected/`) are only accessible to CES in **local mode**, where CES runs as a child process of the assistant. CES reads them at materialization time via direct filesystem access.
|
|
66
66
|
|
|
67
|
-
In **managed mode**, `local_static` handles are not supported and the CES returns a clear error for any `local_static` handle. Managed deployments use `platform_oauth` handles exclusively.
|
|
67
|
+
In **managed mode**, `local_static` handles are not supported and the CES returns a clear error for any `local_static` handle. Managed deployments use `platform_oauth` handles exclusively. With v2 `store.key`, this is a **policy choice** (simpler lifecycle, centralized token management) rather than a technical limitation — the UID-independent key file could be shared via volume mount.
|
|
68
68
|
|
|
69
|
-
####
|
|
69
|
+
#### Historical: v1 key derivation blocker (resolved in v2)
|
|
70
70
|
|
|
71
|
-
The
|
|
71
|
+
The v1 encrypted key store uses PBKDF2 key derivation where the encryption key is derived from `userInfo().username` and `userInfo().homedir`. In managed deployments the assistant and CES sidecar run as different OS users, producing different derived keys — making it impossible for CES to decrypt secrets stored by the assistant.
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
v2 stores replaced PBKDF2 derivation with a random 32-byte key stored at `<vellumRoot>/protected/store.key`. This key is UID-independent and can be shared via volume mount, removing the technical barrier to `local_static` in managed mode.
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
- The **CES sidecar container** runs as `ces` / uid 1001 (homedir `/home/ces`)
|
|
75
|
+
The policy decision to use `platform_oauth` exclusively in managed mode still stands for operational reasons: simpler credential lifecycle, centralized token management, and no need to synchronize key files across containers. Future iterations may enable `local_static` in managed mode via shared `store.key` volume mounts if there is a compelling use case.
|
|
77
76
|
|
|
78
|
-
|
|
77
|
+
#### Rejected alternatives (v1-era, historical context)
|
|
79
78
|
|
|
80
|
-
|
|
79
|
+
These alternatives were evaluated for the v1 key store and rejected. They are retained for historical context — the v2 `store.key` format resolves the underlying issue without hitting these trade-offs.
|
|
81
80
|
|
|
82
|
-
|
|
81
|
+
1. **Mount decrypted secrets into the CES container** — Breaks the "secrets never in assistant process memory" boundary (Boundary Invariant #2).
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
2. **Use shared key derivation independent of UID** — Was rejected for v1 because it weakened the encrypted-at-rest model. The v2 `store.key` approach achieves UID-independent decryption without the per-user identity trade-off, since the random key file is protected by filesystem permissions rather than derivation entropy.
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
3. **Pre-decrypt and pass via the RPC socket** — Having the assistant decrypt the secret and send it to CES over the Unix socket would mean the assistant process handles plaintext credential values, directly violating the CES process-boundary isolation guarantee.
|
|
89
|
-
|
|
90
|
-
Since all alternatives break security invariants that CES exists to enforce, managed deployments route credential access through `platform_oauth` where the platform manages token lifecycle and CES requests materialized tokens via the platform proxy endpoint.
|
|
91
|
-
|
|
92
|
-
Future iterations may move secret storage to a dedicated secret manager (e.g., cloud KMS, Vault) with CES as the only authorized reader, which would enable static secrets in managed mode without compromising the process-boundary isolation.
|
|
85
|
+
3. **Pre-decrypt and pass via the RPC socket** — Violates the CES process-boundary isolation guarantee.
|
|
93
86
|
|
|
94
87
|
### 3. Platform OAuth materialization stays on the platform
|
|
95
88
|
|
|
@@ -396,7 +389,7 @@ This means the helper is subject to the same cooperative egress limitation as th
|
|
|
396
389
|
|
|
397
390
|
The following capabilities are intentionally deferred beyond v1:
|
|
398
391
|
|
|
399
|
-
- **`local_static` handles in managed mode** —
|
|
392
|
+
- **`local_static` handles in managed mode** — Technically feasible with v2 `store.key` (UID-independent), but managed mode currently uses `platform_oauth` exclusively as a policy choice (see Locked Decision #2). May be enabled in the future via shared `store.key` volume mount if there is a compelling use case.
|
|
400
393
|
- **Cloud KMS/Vault integration for secret storage** — v1 reads secrets from filesystem (`~/.vellum/protected/` locally, `/ces-data` in managed). Moving to a dedicated secrets manager is a future enhancement.
|
|
401
394
|
- **Multi-CES-instance support** — Each assistant pod runs exactly one CES sidecar. Horizontal scaling of CES within a pod is not supported.
|
|
402
395
|
- **Cross-pod credential sharing** — CES grants are scoped to a single pod. There is no grant federation across pods or assistant instances.
|
|
@@ -15,10 +15,11 @@ export type RpcError = z.infer<typeof RpcErrorSchema>;
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Error returned when a local_static credential handle is used in managed
|
|
18
|
-
* mode.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* mode. v2 stores use a UID-independent `store.key` file that removes the
|
|
19
|
+
* technical barrier (legacy v1 relied on PBKDF2 key derivation from user
|
|
20
|
+
* identity, which broke across container users). The restriction is now a
|
|
21
|
+
* policy choice: managed deployments use platform_oauth handles exclusively
|
|
22
|
+
* for simpler lifecycle and centralized token management.
|
|
22
23
|
*/
|
|
23
24
|
export const MANAGED_LOCAL_STATIC_REJECTION_ERROR =
|
|
24
25
|
"local_static credential handles are not supported in managed mode. " +
|
package/package.json
CHANGED
|
@@ -93,8 +93,8 @@ function mockServer(address: string): ServerWithRequestIP {
|
|
|
93
93
|
/** Mock loopback server -- returns 127.0.0.1 for all requests. */
|
|
94
94
|
const loopbackServer = mockServer("127.0.0.1");
|
|
95
95
|
|
|
96
|
-
/** Mock non-loopback server -- returns a
|
|
97
|
-
const nonLoopbackServer = mockServer("
|
|
96
|
+
/** Mock non-loopback server -- returns a public IP for all requests. */
|
|
97
|
+
const nonLoopbackServer = mockServer("203.0.113.50");
|
|
98
98
|
|
|
99
99
|
initializeDb();
|
|
100
100
|
|
|
@@ -676,11 +676,11 @@ describe("pairing credential flow", () => {
|
|
|
676
676
|
});
|
|
677
677
|
|
|
678
678
|
// ---------------------------------------------------------------------------
|
|
679
|
-
// Bootstrap
|
|
679
|
+
// Bootstrap private-network guard tests
|
|
680
680
|
// ---------------------------------------------------------------------------
|
|
681
681
|
|
|
682
|
-
describe("bootstrap
|
|
683
|
-
test("rejects bootstrap request with X-Forwarded-For
|
|
682
|
+
describe("bootstrap private-network guard", () => {
|
|
683
|
+
test("rejects bootstrap request with public X-Forwarded-For", async () => {
|
|
684
684
|
const { handleGuardianBootstrap } =
|
|
685
685
|
await import("../runtime/routes/guardian-bootstrap-routes.js");
|
|
686
686
|
|
|
@@ -688,7 +688,7 @@ describe("bootstrap loopback guard", () => {
|
|
|
688
688
|
method: "POST",
|
|
689
689
|
headers: {
|
|
690
690
|
"Content-Type": "application/json",
|
|
691
|
-
"X-Forwarded-For": "
|
|
691
|
+
"X-Forwarded-For": "203.0.113.1",
|
|
692
692
|
},
|
|
693
693
|
body: JSON.stringify({ platform: "macos", deviceId: "test-device" }),
|
|
694
694
|
});
|
|
@@ -699,7 +699,7 @@ describe("bootstrap loopback guard", () => {
|
|
|
699
699
|
expect(body.error.message).toContain("local-only");
|
|
700
700
|
});
|
|
701
701
|
|
|
702
|
-
test("rejects bootstrap request from
|
|
702
|
+
test("rejects bootstrap request from public IP peer", async () => {
|
|
703
703
|
const { handleGuardianBootstrap } =
|
|
704
704
|
await import("../runtime/routes/guardian-bootstrap-routes.js");
|
|
705
705
|
|
|
@@ -62,6 +62,7 @@ mock.module("@anthropic-ai/sdk", () => ({
|
|
|
62
62
|
}));
|
|
63
63
|
|
|
64
64
|
// Import after mocking
|
|
65
|
+
import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "../prompts/system-prompt.js";
|
|
65
66
|
import {
|
|
66
67
|
AnthropicProvider,
|
|
67
68
|
PLACEHOLDER_BLOCKS_OMITTED,
|
|
@@ -155,6 +156,25 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
155
156
|
expect(lastStreamParams!.system).toBeUndefined();
|
|
156
157
|
});
|
|
157
158
|
|
|
159
|
+
test("splits system prompt into two cache blocks on boundary marker", async () => {
|
|
160
|
+
const staticBlock = "You are a helpful assistant.";
|
|
161
|
+
const dynamicBlock = "User workspace files here.";
|
|
162
|
+
const prompt = staticBlock + SYSTEM_PROMPT_CACHE_BOUNDARY + dynamicBlock;
|
|
163
|
+
|
|
164
|
+
await provider.sendMessage([userMsg("Hi")], undefined, prompt);
|
|
165
|
+
|
|
166
|
+
const system = lastStreamParams!.system as Array<{
|
|
167
|
+
type: string;
|
|
168
|
+
text: string;
|
|
169
|
+
cache_control?: { type: string };
|
|
170
|
+
}>;
|
|
171
|
+
expect(system).toHaveLength(2);
|
|
172
|
+
expect(system[0].text).toBe(staticBlock);
|
|
173
|
+
expect(system[0].cache_control).toEqual({ type: "ephemeral" });
|
|
174
|
+
expect(system[1].text).toBe(dynamicBlock);
|
|
175
|
+
expect(system[1].cache_control).toEqual({ type: "ephemeral" });
|
|
176
|
+
});
|
|
177
|
+
|
|
158
178
|
// -----------------------------------------------------------------------
|
|
159
179
|
// Tool cache control
|
|
160
180
|
// -----------------------------------------------------------------------
|
|
@@ -1307,6 +1327,158 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1307
1327
|
expect(userMsgs[2].content[0].cache_control).toBeUndefined();
|
|
1308
1328
|
expect(userMsgs[2].content[1].cache_control).toBeUndefined();
|
|
1309
1329
|
});
|
|
1330
|
+
|
|
1331
|
+
// -----------------------------------------------------------------------
|
|
1332
|
+
// is_error + contentBlocks — non-text blocks must be stripped
|
|
1333
|
+
// -----------------------------------------------------------------------
|
|
1334
|
+
|
|
1335
|
+
test("is_error tool_result strips non-text contentBlocks (images)", async () => {
|
|
1336
|
+
const messages: Message[] = [
|
|
1337
|
+
userMsg("Do something"),
|
|
1338
|
+
toolUseMsg("tu_img", "file_read"),
|
|
1339
|
+
{
|
|
1340
|
+
role: "user",
|
|
1341
|
+
content: [
|
|
1342
|
+
{
|
|
1343
|
+
type: "tool_result",
|
|
1344
|
+
tool_use_id: "tu_img",
|
|
1345
|
+
content: "Error: file not found",
|
|
1346
|
+
is_error: true,
|
|
1347
|
+
contentBlocks: [
|
|
1348
|
+
{
|
|
1349
|
+
type: "image",
|
|
1350
|
+
source: {
|
|
1351
|
+
type: "base64",
|
|
1352
|
+
media_type: "image/png",
|
|
1353
|
+
data: "iVBOR",
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
{ type: "text", text: "extra error detail" },
|
|
1357
|
+
],
|
|
1358
|
+
},
|
|
1359
|
+
],
|
|
1360
|
+
},
|
|
1361
|
+
];
|
|
1362
|
+
await provider.sendMessage(messages);
|
|
1363
|
+
|
|
1364
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1365
|
+
role: string;
|
|
1366
|
+
content: Array<{
|
|
1367
|
+
type: string;
|
|
1368
|
+
tool_use_id?: string;
|
|
1369
|
+
is_error?: boolean;
|
|
1370
|
+
content?: unknown;
|
|
1371
|
+
}>;
|
|
1372
|
+
}>;
|
|
1373
|
+
|
|
1374
|
+
const toolResult = sent[2].content.find(
|
|
1375
|
+
(b) => b.type === "tool_result" && b.tool_use_id === "tu_img",
|
|
1376
|
+
)!;
|
|
1377
|
+
expect(toolResult.is_error).toBe(true);
|
|
1378
|
+
|
|
1379
|
+
// Content should be an array with only text blocks (no images)
|
|
1380
|
+
const parts = toolResult.content as Array<{ type: string }>;
|
|
1381
|
+
expect(Array.isArray(parts)).toBe(true);
|
|
1382
|
+
expect(parts.every((p) => p.type === "text")).toBe(true);
|
|
1383
|
+
// Original text + the extra text contentBlock
|
|
1384
|
+
expect(parts).toHaveLength(2);
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
test("is_error tool_result with only image contentBlocks falls back to text-only", async () => {
|
|
1388
|
+
const messages: Message[] = [
|
|
1389
|
+
userMsg("Do something"),
|
|
1390
|
+
toolUseMsg("tu_img2", "file_read"),
|
|
1391
|
+
{
|
|
1392
|
+
role: "user",
|
|
1393
|
+
content: [
|
|
1394
|
+
{
|
|
1395
|
+
type: "tool_result",
|
|
1396
|
+
tool_use_id: "tu_img2",
|
|
1397
|
+
content: "Error: file not found",
|
|
1398
|
+
is_error: true,
|
|
1399
|
+
contentBlocks: [
|
|
1400
|
+
{
|
|
1401
|
+
type: "image",
|
|
1402
|
+
source: {
|
|
1403
|
+
type: "base64",
|
|
1404
|
+
media_type: "image/png",
|
|
1405
|
+
data: "iVBOR",
|
|
1406
|
+
},
|
|
1407
|
+
},
|
|
1408
|
+
],
|
|
1409
|
+
},
|
|
1410
|
+
],
|
|
1411
|
+
},
|
|
1412
|
+
];
|
|
1413
|
+
await provider.sendMessage(messages);
|
|
1414
|
+
|
|
1415
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1416
|
+
role: string;
|
|
1417
|
+
content: Array<{
|
|
1418
|
+
type: string;
|
|
1419
|
+
tool_use_id?: string;
|
|
1420
|
+
is_error?: boolean;
|
|
1421
|
+
content?: unknown;
|
|
1422
|
+
}>;
|
|
1423
|
+
}>;
|
|
1424
|
+
|
|
1425
|
+
const toolResult = sent[2].content.find(
|
|
1426
|
+
(b) => b.type === "tool_result" && b.tool_use_id === "tu_img2",
|
|
1427
|
+
)!;
|
|
1428
|
+
expect(toolResult.is_error).toBe(true);
|
|
1429
|
+
|
|
1430
|
+
// All images stripped → no usable blocks → falls back to text-only content
|
|
1431
|
+
expect(toolResult.content).toBe("Error: file not found");
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
test("non-error tool_result preserves image contentBlocks", async () => {
|
|
1435
|
+
const messages: Message[] = [
|
|
1436
|
+
userMsg("Do something"),
|
|
1437
|
+
toolUseMsg("tu_img3", "file_read"),
|
|
1438
|
+
{
|
|
1439
|
+
role: "user",
|
|
1440
|
+
content: [
|
|
1441
|
+
{
|
|
1442
|
+
type: "tool_result",
|
|
1443
|
+
tool_use_id: "tu_img3",
|
|
1444
|
+
content: "Success",
|
|
1445
|
+
is_error: false,
|
|
1446
|
+
contentBlocks: [
|
|
1447
|
+
{
|
|
1448
|
+
type: "image",
|
|
1449
|
+
source: {
|
|
1450
|
+
type: "base64",
|
|
1451
|
+
media_type: "image/png",
|
|
1452
|
+
data: "iVBOR",
|
|
1453
|
+
},
|
|
1454
|
+
},
|
|
1455
|
+
],
|
|
1456
|
+
},
|
|
1457
|
+
],
|
|
1458
|
+
},
|
|
1459
|
+
];
|
|
1460
|
+
await provider.sendMessage(messages);
|
|
1461
|
+
|
|
1462
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1463
|
+
role: string;
|
|
1464
|
+
content: Array<{
|
|
1465
|
+
type: string;
|
|
1466
|
+
tool_use_id?: string;
|
|
1467
|
+
is_error?: boolean;
|
|
1468
|
+
content?: unknown;
|
|
1469
|
+
}>;
|
|
1470
|
+
}>;
|
|
1471
|
+
|
|
1472
|
+
const toolResult = sent[2].content.find(
|
|
1473
|
+
(b) => b.type === "tool_result" && b.tool_use_id === "tu_img3",
|
|
1474
|
+
)!;
|
|
1475
|
+
expect(toolResult.is_error).toBe(false);
|
|
1476
|
+
|
|
1477
|
+
// Non-error: images should be preserved in the content array
|
|
1478
|
+
const parts = toolResult.content as Array<{ type: string }>;
|
|
1479
|
+
expect(Array.isArray(parts)).toBe(true);
|
|
1480
|
+
expect(parts.some((p) => p.type === "image")).toBe(true);
|
|
1481
|
+
});
|
|
1310
1482
|
});
|
|
1311
1483
|
|
|
1312
1484
|
// ---------------------------------------------------------------------------
|
|
@@ -63,7 +63,21 @@ function makeContext(overrides: Partial<ToolContext> = {}): ToolContext {
|
|
|
63
63
|
|
|
64
64
|
const mockStore = makeMockStore();
|
|
65
65
|
|
|
66
|
-
mock.module("../memory/app-store.js", () =>
|
|
66
|
+
mock.module("../memory/app-store.js", () => ({
|
|
67
|
+
...mockStore,
|
|
68
|
+
getAppsDir: () => "/tmp/test-apps",
|
|
69
|
+
isMultifileApp: (app: AppDefinition) => app.formatVersion === 2,
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
// Mock compileApp for multifile scaffold path
|
|
73
|
+
mock.module("../bundler/app-compiler.js", () => ({
|
|
74
|
+
compileApp: async () => ({
|
|
75
|
+
ok: true,
|
|
76
|
+
errors: [],
|
|
77
|
+
warnings: [],
|
|
78
|
+
durationMs: 0,
|
|
79
|
+
}),
|
|
80
|
+
}));
|
|
67
81
|
|
|
68
82
|
// ---------------------------------------------------------------------------
|
|
69
83
|
// Import skill scripts (after mocking)
|
|
@@ -76,7 +76,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
76
76
|
summaryModel: "mock-model",
|
|
77
77
|
maxSummaryTokens: 512,
|
|
78
78
|
},
|
|
79
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
79
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
80
80
|
timeouts: { permissionTimeoutSec: 300 },
|
|
81
81
|
skills: { entries: {}, allowBundled: true },
|
|
82
82
|
permissions: { mode: "workspace" },
|
|
@@ -166,7 +166,7 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
166
166
|
injectedTokens: 0,
|
|
167
167
|
latencyMs: 0,
|
|
168
168
|
}),
|
|
169
|
-
|
|
169
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
170
170
|
}));
|
|
171
171
|
|
|
172
172
|
mock.module("../context/window-manager.js", () => ({
|
|
@@ -42,9 +42,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
42
42
|
model: "test",
|
|
43
43
|
provider: "test",
|
|
44
44
|
memory: { enabled: false },
|
|
45
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
45
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
46
46
|
secretDetection: { enabled: false },
|
|
47
|
-
sandbox: { enabled: false },
|
|
48
47
|
contextWindow: { maxInputTokens: 200000 },
|
|
49
48
|
services: {
|
|
50
49
|
inference: {
|
|
@@ -55,9 +54,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
55
54
|
"image-generation": {
|
|
56
55
|
mode: "your-own",
|
|
57
56
|
provider: "gemini",
|
|
58
|
-
model: "gemini-
|
|
57
|
+
model: "gemini-3.1-flash-image-preview",
|
|
59
58
|
},
|
|
60
|
-
"web-search": { mode: "your-own", provider: "
|
|
59
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
61
60
|
},
|
|
62
61
|
}),
|
|
63
62
|
}));
|
|
@@ -34,7 +34,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
34
34
|
model: "test",
|
|
35
35
|
provider: "test",
|
|
36
36
|
memory: { enabled: false },
|
|
37
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
37
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
38
38
|
}),
|
|
39
39
|
}));
|
|
40
40
|
|
|
@@ -263,7 +263,7 @@ describe("AssetMaterializeTool materialization", () => {
|
|
|
263
263
|
describe("AssetMaterializeTool size limit", () => {
|
|
264
264
|
beforeEach(resetTables);
|
|
265
265
|
|
|
266
|
-
test("rejects attachment exceeding
|
|
266
|
+
test("rejects attachment exceeding 100MB limit", async () => {
|
|
267
267
|
// Simulate a large attachment by inserting directly into the DB
|
|
268
268
|
// with a sizeBytes value over the limit
|
|
269
269
|
const db = getDb();
|
|
@@ -271,7 +271,7 @@ describe("AssetMaterializeTool size limit", () => {
|
|
|
271
271
|
db.run(
|
|
272
272
|
`INSERT INTO attachments (id, original_filename, mime_type, size_bytes, kind, data_base64, created_at)
|
|
273
273
|
VALUES ('${fakeId}', 'huge.bin', 'application/octet-stream', ${
|
|
274
|
-
|
|
274
|
+
101 * 1024 * 1024
|
|
275
275
|
}, 'document', 'AAAA', ${Date.now()})`,
|
|
276
276
|
);
|
|
277
277
|
|
|
@@ -314,8 +314,8 @@ describe("AssetMaterializeTool metadata", () => {
|
|
|
314
314
|
).toHaveProperty("destination_path");
|
|
315
315
|
});
|
|
316
316
|
|
|
317
|
-
test("tool has
|
|
318
|
-
expect(assetMaterializeTool.defaultRiskLevel).toBe(RiskLevel.
|
|
317
|
+
test("tool has LOW risk level", () => {
|
|
318
|
+
expect(assetMaterializeTool.defaultRiskLevel).toBe(RiskLevel.Low);
|
|
319
319
|
});
|
|
320
320
|
|
|
321
321
|
test("tool category is assets", () => {
|