@vellumai/assistant 0.4.56 → 0.5.0
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 +204 -185
- 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 +249 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/file-read-tool.test.ts +40 -0
- 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-file-read-tool.test.ts +87 -0
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +209 -0
- 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 +2 -2
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +3 -3
- 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 +549 -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 +141 -275
- 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 +26 -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 +5 -1
- 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 +45 -4
- package/src/notifications/emit-signal.ts +5 -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 +172 -33
- package/src/prompts/templates/IDENTITY.md +8 -24
- 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 +51 -19
- 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 +16 -2
- 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 +61 -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 +93 -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/identity-intro-cache.ts +105 -0
- package/src/runtime/routes/identity-routes.ts +51 -0
- 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 +9 -9
- 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 +262 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +222 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
- package/src/telemetry/usage-telemetry-reporter.ts +53 -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 +15 -4
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +18 -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/pricing.ts +4 -0
- 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
|
@@ -69,11 +69,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
69
69
|
nonInteractiveLatestTurnCompression: "truncate",
|
|
70
70
|
},
|
|
71
71
|
},
|
|
72
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
72
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
73
73
|
timeouts: { permissionTimeoutSec: 1 },
|
|
74
74
|
skills: { entries: {}, allowBundled: true },
|
|
75
75
|
permissions: { mode: "workspace" },
|
|
76
|
-
sandbox: { enabled: false },
|
|
77
76
|
daemon: {
|
|
78
77
|
startupSocketWaitMs: 5000,
|
|
79
78
|
stopTimeoutMs: 5000,
|
|
@@ -90,9 +89,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
90
89
|
"image-generation": {
|
|
91
90
|
mode: "your-own",
|
|
92
91
|
provider: "gemini",
|
|
93
|
-
model: "gemini-
|
|
92
|
+
model: "gemini-3.1-flash-image-preview",
|
|
94
93
|
},
|
|
95
|
-
"web-search": { mode: "your-own", provider: "
|
|
94
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
96
95
|
},
|
|
97
96
|
}),
|
|
98
97
|
loadRawConfig: () => ({}),
|
|
@@ -178,7 +177,7 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
178
177
|
injectedTokens: 0,
|
|
179
178
|
latencyMs: 0,
|
|
180
179
|
}),
|
|
181
|
-
|
|
180
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
182
181
|
}));
|
|
183
182
|
|
|
184
183
|
mock.module("../context/window-manager.js", () => ({
|
|
@@ -1735,12 +1734,23 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
1735
1734
|
});
|
|
1736
1735
|
|
|
1737
1736
|
test("commitTurnChanges never resolving within budget -> turn still completes and drains queue", async () => {
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1737
|
+
// Replace setTimeout with a zero-delay version so the 4000ms
|
|
1738
|
+
// raceWithTimeout fires instantly instead of waiting real time.
|
|
1739
|
+
const origSetTimeout = globalThis.setTimeout;
|
|
1740
|
+
globalThis.setTimeout = ((
|
|
1741
|
+
fn: TimerHandler,
|
|
1742
|
+
_ms?: number,
|
|
1743
|
+
...args: unknown[]
|
|
1744
|
+
) => {
|
|
1745
|
+
return origSetTimeout(fn, 0, ...args);
|
|
1746
|
+
}) as typeof setTimeout;
|
|
1742
1747
|
|
|
1743
1748
|
try {
|
|
1749
|
+
const conversation = makeConversation();
|
|
1750
|
+
await conversation.loadFromDb();
|
|
1751
|
+
|
|
1752
|
+
turnCommitHangForever = true;
|
|
1753
|
+
|
|
1744
1754
|
const events1: ServerMessage[] = [];
|
|
1745
1755
|
const events2: ServerMessage[] = [];
|
|
1746
1756
|
|
|
@@ -1761,10 +1771,8 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
1761
1771
|
|
|
1762
1772
|
// The turn should still complete (timeout fires) and drain the queue
|
|
1763
1773
|
// even though commitTurnChanges never resolves.
|
|
1764
|
-
//
|
|
1765
|
-
//
|
|
1766
|
-
// We wait for the second run to be registered, which proves the
|
|
1767
|
-
// turn completed and the queue drained despite the hanging commit.
|
|
1774
|
+
// With the zero-delay setTimeout wrapper the 4000ms budget fires
|
|
1775
|
+
// instantly, so we only need a short wait for the second run.
|
|
1768
1776
|
await waitForPendingRun(2, 10_000);
|
|
1769
1777
|
|
|
1770
1778
|
// First message should have completed
|
|
@@ -1781,9 +1789,10 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
1781
1789
|
// Complete the second run so the test can clean up
|
|
1782
1790
|
turnCommitHangForever = false;
|
|
1783
1791
|
resolveRun(1);
|
|
1784
|
-
await new Promise((r) =>
|
|
1792
|
+
await new Promise((r) => origSetTimeout(r, 10));
|
|
1785
1793
|
} finally {
|
|
1786
1794
|
turnCommitHangForever = false;
|
|
1795
|
+
globalThis.setTimeout = origSetTimeout;
|
|
1787
1796
|
}
|
|
1788
1797
|
}, 15_000);
|
|
1789
1798
|
});
|
|
@@ -29,7 +29,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
29
29
|
model: "claude-opus-4-6",
|
|
30
30
|
provider: "anthropic",
|
|
31
31
|
memory: { enabled: false },
|
|
32
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
32
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
33
33
|
secretDetection: { enabled: false },
|
|
34
34
|
contextWindow: { maxInputTokens: 200000 },
|
|
35
35
|
services: {
|
|
@@ -41,9 +41,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
41
41
|
"image-generation": {
|
|
42
42
|
mode: "your-own",
|
|
43
43
|
provider: "gemini",
|
|
44
|
-
model: "gemini-
|
|
44
|
+
model: "gemini-3.1-flash-image-preview",
|
|
45
45
|
},
|
|
46
|
-
"web-search": { mode: "your-own", provider: "
|
|
46
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
47
47
|
},
|
|
48
48
|
}),
|
|
49
49
|
}));
|
|
@@ -7,20 +7,19 @@ import type {
|
|
|
7
7
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
8
8
|
import {
|
|
9
9
|
applyRuntimeInjections,
|
|
10
|
-
|
|
10
|
+
buildTurnContextBlock,
|
|
11
11
|
injectChannelCapabilityContext,
|
|
12
|
-
|
|
12
|
+
injectChannelCommandContext,
|
|
13
13
|
injectInboundActorContext,
|
|
14
14
|
injectTemporalContext,
|
|
15
|
+
injectTurnContext,
|
|
15
16
|
isGroupChatType,
|
|
16
17
|
resolveChannelCapabilities,
|
|
17
|
-
sanitizePttActivationKey,
|
|
18
18
|
stripChannelCapabilityContext,
|
|
19
19
|
stripChannelTurnContext,
|
|
20
20
|
stripInboundActorContext,
|
|
21
21
|
stripTemporalContext,
|
|
22
22
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
23
|
-
import { buildChannelAwarenessSection } from "../prompts/system-prompt.js";
|
|
24
23
|
import type { Message } from "../providers/types.js";
|
|
25
24
|
|
|
26
25
|
// ---------------------------------------------------------------------------
|
|
@@ -135,7 +134,7 @@ describe("resolveChannelCapabilities", () => {
|
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
test("propagates chatType when provided", () => {
|
|
138
|
-
const caps = resolveChannelCapabilities("telegram", null,
|
|
137
|
+
const caps = resolveChannelCapabilities("telegram", null, "group");
|
|
139
138
|
expect(caps.chatType).toBe("group");
|
|
140
139
|
});
|
|
141
140
|
|
|
@@ -155,7 +154,7 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
155
154
|
content: [{ type: "text", text: "Hello" }],
|
|
156
155
|
};
|
|
157
156
|
|
|
158
|
-
test("
|
|
157
|
+
test("skips injection entirely for desktop happy path (all capabilities true)", () => {
|
|
159
158
|
const caps: ChannelCapabilities = {
|
|
160
159
|
channel: "vellum",
|
|
161
160
|
dashboardCapable: true,
|
|
@@ -165,26 +164,9 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
165
164
|
|
|
166
165
|
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
167
166
|
|
|
168
|
-
//
|
|
169
|
-
expect(result
|
|
170
|
-
|
|
171
|
-
expect(injected.type).toBe("text");
|
|
172
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
173
|
-
"<channel_capabilities>",
|
|
174
|
-
);
|
|
175
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
176
|
-
"dashboard_capable: true",
|
|
177
|
-
);
|
|
178
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
179
|
-
"supports_dynamic_ui: true",
|
|
180
|
-
);
|
|
181
|
-
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
182
|
-
"</channel_capabilities>",
|
|
183
|
-
);
|
|
184
|
-
// Should NOT contain constraint rules for dashboard
|
|
185
|
-
expect((injected as { type: "text"; text: string }).text).not.toContain(
|
|
186
|
-
"CHANNEL CONSTRAINTS",
|
|
187
|
-
);
|
|
167
|
+
// Message returned unchanged — no injection at all
|
|
168
|
+
expect(result).toBe(baseUserMessage);
|
|
169
|
+
expect(result.content.length).toBe(1);
|
|
188
170
|
});
|
|
189
171
|
|
|
190
172
|
test("injects constraint rules for non-dashboard channel", () => {
|
|
@@ -293,6 +275,50 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
293
275
|
expect(text).toContain("GROUP CHAT ETIQUETTE");
|
|
294
276
|
expect(text).toContain("emoji reactions");
|
|
295
277
|
});
|
|
278
|
+
|
|
279
|
+
test("still injects for group chats even when all capabilities are true", () => {
|
|
280
|
+
const caps: ChannelCapabilities = {
|
|
281
|
+
channel: "slack",
|
|
282
|
+
dashboardCapable: true,
|
|
283
|
+
supportsDynamicUi: true,
|
|
284
|
+
supportsVoiceInput: true,
|
|
285
|
+
chatType: "channel",
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
289
|
+
// Not the happy path because chatType is a group type
|
|
290
|
+
expect(result).not.toBe(baseUserMessage);
|
|
291
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
292
|
+
expect(text).toContain("GROUP CHAT ETIQUETTE");
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("injects WhatsApp formatting constraint for whatsapp channel", () => {
|
|
296
|
+
const caps: ChannelCapabilities = {
|
|
297
|
+
channel: "whatsapp",
|
|
298
|
+
dashboardCapable: false,
|
|
299
|
+
supportsDynamicUi: false,
|
|
300
|
+
supportsVoiceInput: false,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
304
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
305
|
+
expect(text).toContain("Do NOT use markdown tables");
|
|
306
|
+
expect(text).toContain("bullet lists");
|
|
307
|
+
expect(text).toContain("CAPS for emphasis");
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("does NOT inject WhatsApp formatting for non-whatsapp channels", () => {
|
|
311
|
+
const caps: ChannelCapabilities = {
|
|
312
|
+
channel: "telegram",
|
|
313
|
+
dashboardCapable: false,
|
|
314
|
+
supportsDynamicUi: false,
|
|
315
|
+
supportsVoiceInput: false,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
319
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
320
|
+
expect(text).not.toContain("Do NOT use markdown tables");
|
|
321
|
+
});
|
|
296
322
|
});
|
|
297
323
|
|
|
298
324
|
// ---------------------------------------------------------------------------
|
|
@@ -449,60 +475,12 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
|
|
|
449
475
|
});
|
|
450
476
|
});
|
|
451
477
|
|
|
452
|
-
// ---------------------------------------------------------------------------
|
|
453
|
-
// buildChannelAwarenessSection
|
|
454
|
-
// ---------------------------------------------------------------------------
|
|
455
|
-
|
|
456
|
-
describe("buildChannelAwarenessSection", () => {
|
|
457
|
-
test("includes channel awareness heading", () => {
|
|
458
|
-
const section = buildChannelAwarenessSection();
|
|
459
|
-
expect(section).toContain("## Channel Awareness & Trust Gating");
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
test("includes channel-specific rules", () => {
|
|
463
|
-
const section = buildChannelAwarenessSection();
|
|
464
|
-
expect(section).toContain("dashboard_capable");
|
|
465
|
-
expect(section).toContain("supports_dynamic_ui");
|
|
466
|
-
expect(section).toContain("supports_voice_input");
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test("includes trust gating rules for permission asks", () => {
|
|
470
|
-
const section = buildChannelAwarenessSection();
|
|
471
|
-
expect(section).toContain("firstConversationComplete");
|
|
472
|
-
expect(section).toContain("Permission ask trust gating");
|
|
473
|
-
expect(section).toContain(
|
|
474
|
-
"Do NOT proactively ask for elevated permissions",
|
|
475
|
-
);
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
test("gates microphone permissions on voice capability", () => {
|
|
479
|
-
const section = buildChannelAwarenessSection();
|
|
480
|
-
expect(section).toContain("Do not ask for microphone permissions");
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
test("gates computer-control on dashboard channel", () => {
|
|
484
|
-
const section = buildChannelAwarenessSection();
|
|
485
|
-
expect(section).toContain("computer-control permissions on non-dashboard");
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
test("does NOT include group chat etiquette (gated per-turn instead)", () => {
|
|
489
|
-
const section = buildChannelAwarenessSection();
|
|
490
|
-
expect(section).not.toContain("Group chat etiquette");
|
|
491
|
-
expect(section).not.toContain("Stay silent when");
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
test("does NOT include Discord references (not a supported channel)", () => {
|
|
495
|
-
const section = buildChannelAwarenessSection();
|
|
496
|
-
expect(section).not.toContain("Discord");
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
478
|
// ---------------------------------------------------------------------------
|
|
501
479
|
// Trust-gating behavior: channel constraints for permission asks
|
|
502
480
|
// ---------------------------------------------------------------------------
|
|
503
481
|
|
|
504
482
|
describe("trust-gating via channel capabilities", () => {
|
|
505
|
-
test("vellum channel with macos interface
|
|
483
|
+
test("vellum channel with macos interface skips injection (happy path)", () => {
|
|
506
484
|
const caps = resolveChannelCapabilities("vellum", "macos");
|
|
507
485
|
const message: Message = {
|
|
508
486
|
role: "user",
|
|
@@ -510,10 +488,9 @@ describe("trust-gating via channel capabilities", () => {
|
|
|
510
488
|
};
|
|
511
489
|
|
|
512
490
|
const result = injectChannelCapabilityContext(message, caps);
|
|
513
|
-
const injected = (result.content[0] as { type: "text"; text: string }).text;
|
|
514
491
|
|
|
515
|
-
|
|
516
|
-
expect(
|
|
492
|
+
// Happy path: message returned unchanged
|
|
493
|
+
expect(result).toBe(message);
|
|
517
494
|
});
|
|
518
495
|
|
|
519
496
|
test("non-dashboard channel adds constraint rules preventing UI references", () => {
|
|
@@ -554,6 +531,50 @@ describe("trust-gating via channel capabilities", () => {
|
|
|
554
531
|
});
|
|
555
532
|
});
|
|
556
533
|
|
|
534
|
+
// ---------------------------------------------------------------------------
|
|
535
|
+
// injectChannelCommandContext
|
|
536
|
+
// ---------------------------------------------------------------------------
|
|
537
|
+
|
|
538
|
+
describe("injectChannelCommandContext", () => {
|
|
539
|
+
const baseUserMessage: Message = {
|
|
540
|
+
role: "user",
|
|
541
|
+
content: [{ type: "text", text: "Hello" }],
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
test("injects start command instructions when type is start", () => {
|
|
545
|
+
const result = injectChannelCommandContext(baseUserMessage, {
|
|
546
|
+
type: "start",
|
|
547
|
+
});
|
|
548
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
549
|
+
expect(text).toContain("command_type: start");
|
|
550
|
+
expect(text).toContain("warm, brief greeting");
|
|
551
|
+
expect(text).toContain("Treat /start as a hello");
|
|
552
|
+
expect(text).toContain("Do NOT reset conversation");
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test("includes language code and payload when provided", () => {
|
|
556
|
+
const result = injectChannelCommandContext(baseUserMessage, {
|
|
557
|
+
type: "start",
|
|
558
|
+
payload: "ref123",
|
|
559
|
+
languageCode: "es",
|
|
560
|
+
});
|
|
561
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
562
|
+
expect(text).toContain("payload: ref123");
|
|
563
|
+
expect(text).toContain("language_code: es");
|
|
564
|
+
expect(text).toContain("warm, brief greeting");
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test("does NOT inject start instructions for non-start commands", () => {
|
|
568
|
+
const result = injectChannelCommandContext(baseUserMessage, {
|
|
569
|
+
type: "help",
|
|
570
|
+
});
|
|
571
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
572
|
+
expect(text).toContain("command_type: help");
|
|
573
|
+
expect(text).not.toContain("warm, brief greeting");
|
|
574
|
+
expect(text).not.toContain("Treat /start as a hello");
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
557
578
|
// ---------------------------------------------------------------------------
|
|
558
579
|
// injectTemporalContext
|
|
559
580
|
// ---------------------------------------------------------------------------
|
|
@@ -768,9 +789,13 @@ describe("injectInboundActorContext", () => {
|
|
|
768
789
|
expect(text).toContain("trust_class: guardian");
|
|
769
790
|
expect(text).toContain("source_channel: phone");
|
|
770
791
|
expect(text).toContain("canonical_actor_identity: guardian-user-1");
|
|
792
|
+
// Display names differ from canonical, so they should appear
|
|
793
|
+
expect(text).toContain("actor_identifier: +15550001111");
|
|
771
794
|
expect(text).toContain("actor_display_name: Guardian Name");
|
|
772
795
|
expect(text).toContain("actor_sender_display_name: Guardian Name");
|
|
773
796
|
expect(text).toContain("actor_member_display_name: Guardian Name");
|
|
797
|
+
// guardian_identity matches canonical, so it should be omitted
|
|
798
|
+
expect(text).not.toContain("guardian_identity:");
|
|
774
799
|
expect(text).toContain("</inbound_actor_context>");
|
|
775
800
|
});
|
|
776
801
|
|
|
@@ -798,6 +823,30 @@ describe("injectInboundActorContext", () => {
|
|
|
798
823
|
);
|
|
799
824
|
});
|
|
800
825
|
|
|
826
|
+
test("omits name_preference_note when member name matches canonical and is suppressed", () => {
|
|
827
|
+
const ctx: InboundActorContext = {
|
|
828
|
+
sourceChannel: "telegram",
|
|
829
|
+
canonicalActorIdentity: "Jeff",
|
|
830
|
+
actorIdentifier: "@jeff_handle",
|
|
831
|
+
actorDisplayName: "Jeff",
|
|
832
|
+
actorSenderDisplayName: "Jeffrey",
|
|
833
|
+
actorMemberDisplayName: "Jeff",
|
|
834
|
+
trustClass: "trusted_contact",
|
|
835
|
+
guardianIdentity: "guardian-user-1",
|
|
836
|
+
memberStatus: "active",
|
|
837
|
+
memberPolicy: "allow",
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
const result = injectInboundActorContext(baseUserMessage, ctx);
|
|
841
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
842
|
+
// actor_member_display_name matches canonical → omitted by differs() guard
|
|
843
|
+
expect(text).not.toContain("actor_member_display_name:");
|
|
844
|
+
// actor_sender_display_name differs from canonical → emitted
|
|
845
|
+
expect(text).toContain("actor_sender_display_name: Jeffrey");
|
|
846
|
+
// name_preference_note must NOT appear since actor_member_display_name was omitted
|
|
847
|
+
expect(text).not.toContain("name_preference_note:");
|
|
848
|
+
});
|
|
849
|
+
|
|
801
850
|
test("sanitizes inline actor context values to prevent line injection", () => {
|
|
802
851
|
const ctx: InboundActorContext = {
|
|
803
852
|
sourceChannel: "telegram",
|
|
@@ -903,6 +952,38 @@ describe("injectInboundActorContext", () => {
|
|
|
903
952
|
expect(text).not.toContain("non-guardian account");
|
|
904
953
|
});
|
|
905
954
|
|
|
955
|
+
test("omits redundant fields when they match canonical_actor_identity", () => {
|
|
956
|
+
const uuid = "vellum-principal-b77e94f5-67c0-4599-8baa-871b925b3da8";
|
|
957
|
+
const ctx: InboundActorContext = {
|
|
958
|
+
sourceChannel: "vellum",
|
|
959
|
+
canonicalActorIdentity: uuid,
|
|
960
|
+
actorIdentifier: uuid,
|
|
961
|
+
actorDisplayName: uuid,
|
|
962
|
+
actorSenderDisplayName: undefined,
|
|
963
|
+
actorMemberDisplayName: uuid,
|
|
964
|
+
trustClass: "guardian",
|
|
965
|
+
guardianIdentity: uuid,
|
|
966
|
+
memberStatus: "active",
|
|
967
|
+
memberPolicy: "allow",
|
|
968
|
+
contactNotes: "guardian",
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
const result = injectInboundActorContext(baseUserMessage, ctx);
|
|
972
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
973
|
+
// Only essential fields should remain
|
|
974
|
+
expect(text).toContain("source_channel: vellum");
|
|
975
|
+
expect(text).toContain(`canonical_actor_identity: ${uuid}`);
|
|
976
|
+
expect(text).toContain("trust_class: guardian");
|
|
977
|
+
// Redundant fields should be omitted
|
|
978
|
+
expect(text).not.toContain("actor_identifier:");
|
|
979
|
+
expect(text).not.toContain("actor_display_name:");
|
|
980
|
+
expect(text).not.toContain("actor_sender_display_name:");
|
|
981
|
+
expect(text).not.toContain("actor_member_display_name:");
|
|
982
|
+
expect(text).not.toContain("guardian_identity:");
|
|
983
|
+
// contact_notes: "guardian" matches trust_class, should be omitted
|
|
984
|
+
expect(text).not.toContain("contact_notes:");
|
|
985
|
+
});
|
|
986
|
+
|
|
906
987
|
test("omits member_status and member_policy when not provided", () => {
|
|
907
988
|
const ctx: InboundActorContext = {
|
|
908
989
|
sourceChannel: "phone",
|
|
@@ -969,46 +1050,51 @@ describe("applyRuntimeInjections with inboundActorContext", () => {
|
|
|
969
1050
|
});
|
|
970
1051
|
|
|
971
1052
|
// ---------------------------------------------------------------------------
|
|
972
|
-
//
|
|
1053
|
+
// buildTurnContextBlock (channel-only)
|
|
973
1054
|
// ---------------------------------------------------------------------------
|
|
974
1055
|
|
|
975
|
-
describe("
|
|
976
|
-
test("
|
|
977
|
-
const block =
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1056
|
+
describe("buildTurnContextBlock (channel-only)", () => {
|
|
1057
|
+
test("collapses to single field when all channels match", () => {
|
|
1058
|
+
const block = buildTurnContextBlock(
|
|
1059
|
+
{
|
|
1060
|
+
turnContext: {
|
|
1061
|
+
userMessageChannel: "telegram",
|
|
1062
|
+
assistantMessageChannel: "telegram",
|
|
1063
|
+
},
|
|
1064
|
+
conversationOriginChannel: "telegram",
|
|
981
1065
|
},
|
|
982
|
-
|
|
983
|
-
|
|
1066
|
+
undefined,
|
|
1067
|
+
);
|
|
984
1068
|
expect(block).toBe(
|
|
985
|
-
"<
|
|
986
|
-
"user_message_channel: telegram\n" +
|
|
987
|
-
"assistant_message_channel: telegram\n" +
|
|
988
|
-
"conversation_origin_channel: telegram\n" +
|
|
989
|
-
"</channel_turn_context>",
|
|
1069
|
+
"<turn_context>\n" + "channel: telegram\n" + "</turn_context>",
|
|
990
1070
|
);
|
|
991
1071
|
});
|
|
992
1072
|
|
|
993
1073
|
test('uses "unknown" when conversationOriginChannel is null', () => {
|
|
994
|
-
const block =
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1074
|
+
const block = buildTurnContextBlock(
|
|
1075
|
+
{
|
|
1076
|
+
turnContext: {
|
|
1077
|
+
userMessageChannel: "vellum",
|
|
1078
|
+
assistantMessageChannel: "vellum",
|
|
1079
|
+
},
|
|
1080
|
+
conversationOriginChannel: null,
|
|
998
1081
|
},
|
|
999
|
-
|
|
1000
|
-
|
|
1082
|
+
undefined,
|
|
1083
|
+
);
|
|
1001
1084
|
expect(block).toContain("conversation_origin_channel: unknown");
|
|
1002
1085
|
});
|
|
1003
1086
|
|
|
1004
1087
|
test("handles mixed channels", () => {
|
|
1005
|
-
const block =
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1088
|
+
const block = buildTurnContextBlock(
|
|
1089
|
+
{
|
|
1090
|
+
turnContext: {
|
|
1091
|
+
userMessageChannel: "telegram",
|
|
1092
|
+
assistantMessageChannel: "vellum",
|
|
1093
|
+
},
|
|
1094
|
+
conversationOriginChannel: "vellum",
|
|
1009
1095
|
},
|
|
1010
|
-
|
|
1011
|
-
|
|
1096
|
+
undefined,
|
|
1097
|
+
);
|
|
1012
1098
|
expect(block).toContain("user_message_channel: telegram");
|
|
1013
1099
|
expect(block).toContain("assistant_message_channel: vellum");
|
|
1014
1100
|
expect(block).toContain("conversation_origin_channel: vellum");
|
|
@@ -1016,10 +1102,10 @@ describe("buildChannelTurnContextBlock", () => {
|
|
|
1016
1102
|
});
|
|
1017
1103
|
|
|
1018
1104
|
// ---------------------------------------------------------------------------
|
|
1019
|
-
//
|
|
1105
|
+
// injectTurnContext (channel-only)
|
|
1020
1106
|
// ---------------------------------------------------------------------------
|
|
1021
1107
|
|
|
1022
|
-
describe("
|
|
1108
|
+
describe("injectTurnContext (channel-only)", () => {
|
|
1023
1109
|
const baseUserMessage: Message = {
|
|
1024
1110
|
role: "user",
|
|
1025
1111
|
content: [{ type: "text", text: "Hello from telegram" }],
|
|
@@ -1033,14 +1119,14 @@ describe("injectChannelTurnContext", () => {
|
|
|
1033
1119
|
},
|
|
1034
1120
|
conversationOriginChannel: "telegram",
|
|
1035
1121
|
};
|
|
1036
|
-
const result =
|
|
1122
|
+
const result = injectTurnContext(baseUserMessage, params, undefined);
|
|
1037
1123
|
expect(result.content.length).toBe(2);
|
|
1038
1124
|
const injected = result.content[0];
|
|
1039
1125
|
expect(injected.type).toBe("text");
|
|
1040
1126
|
const text = (injected as { type: "text"; text: string }).text;
|
|
1041
|
-
expect(text).toContain("<
|
|
1042
|
-
expect(text).toContain("
|
|
1043
|
-
expect(text).toContain("</
|
|
1127
|
+
expect(text).toContain("<turn_context>");
|
|
1128
|
+
expect(text).toContain("channel: telegram");
|
|
1129
|
+
expect(text).toContain("</turn_context>");
|
|
1044
1130
|
});
|
|
1045
1131
|
|
|
1046
1132
|
test("preserves original message content", () => {
|
|
@@ -1051,7 +1137,7 @@ describe("injectChannelTurnContext", () => {
|
|
|
1051
1137
|
},
|
|
1052
1138
|
conversationOriginChannel: "vellum",
|
|
1053
1139
|
};
|
|
1054
|
-
const result =
|
|
1140
|
+
const result = injectTurnContext(baseUserMessage, params, undefined);
|
|
1055
1141
|
const lastBlock = result.content[result.content.length - 1];
|
|
1056
1142
|
expect((lastBlock as { type: "text"; text: string }).text).toBe(
|
|
1057
1143
|
"Hello from telegram",
|
|
@@ -1071,7 +1157,7 @@ describe("stripChannelTurnContext", () => {
|
|
|
1071
1157
|
content: [
|
|
1072
1158
|
{
|
|
1073
1159
|
type: "text",
|
|
1074
|
-
text: "<
|
|
1160
|
+
text: "<turn_context>\nuser_message_channel: telegram\n</turn_context>",
|
|
1075
1161
|
},
|
|
1076
1162
|
{ type: "text", text: "Hello" },
|
|
1077
1163
|
],
|
|
@@ -1099,7 +1185,7 @@ describe("stripChannelTurnContext", () => {
|
|
|
1099
1185
|
content: [
|
|
1100
1186
|
{
|
|
1101
1187
|
type: "text",
|
|
1102
|
-
text: "<
|
|
1188
|
+
text: "<turn_context>\nuser_message_channel: macos\n</turn_context>",
|
|
1103
1189
|
},
|
|
1104
1190
|
],
|
|
1105
1191
|
},
|
|
@@ -1152,7 +1238,7 @@ describe("applyRuntimeInjections with channelTurnContext", () => {
|
|
|
1152
1238
|
expect(result[0].content.length).toBe(2);
|
|
1153
1239
|
const injected = result[0].content[0];
|
|
1154
1240
|
expect((injected as { type: "text"; text: string }).text).toContain(
|
|
1155
|
-
"<
|
|
1241
|
+
"<turn_context>",
|
|
1156
1242
|
);
|
|
1157
1243
|
});
|
|
1158
1244
|
|
|
@@ -1173,73 +1259,6 @@ describe("applyRuntimeInjections with channelTurnContext", () => {
|
|
|
1173
1259
|
});
|
|
1174
1260
|
});
|
|
1175
1261
|
|
|
1176
|
-
// ---------------------------------------------------------------------------
|
|
1177
|
-
// sanitizePttActivationKey
|
|
1178
|
-
// ---------------------------------------------------------------------------
|
|
1179
|
-
|
|
1180
|
-
describe("sanitizePttActivationKey", () => {
|
|
1181
|
-
test("returns undefined for null/undefined input", () => {
|
|
1182
|
-
expect(sanitizePttActivationKey(null)).toBeUndefined();
|
|
1183
|
-
expect(sanitizePttActivationKey(undefined)).toBeUndefined();
|
|
1184
|
-
});
|
|
1185
|
-
|
|
1186
|
-
test("passes through valid JSON PTTActivator payloads", () => {
|
|
1187
|
-
const modifierOnly = JSON.stringify({
|
|
1188
|
-
kind: "modifierOnly",
|
|
1189
|
-
modifierFlags: 8388608,
|
|
1190
|
-
});
|
|
1191
|
-
expect(sanitizePttActivationKey(modifierOnly)).toBe(modifierOnly);
|
|
1192
|
-
const keyPayload = JSON.stringify({ kind: "key", keyCode: 49 });
|
|
1193
|
-
expect(sanitizePttActivationKey(keyPayload)).toBe(keyPayload);
|
|
1194
|
-
const nonePayload = JSON.stringify({ kind: "none" });
|
|
1195
|
-
expect(sanitizePttActivationKey(nonePayload)).toBe(nonePayload);
|
|
1196
|
-
});
|
|
1197
|
-
|
|
1198
|
-
test("returns undefined for invalid keys", () => {
|
|
1199
|
-
expect(
|
|
1200
|
-
sanitizePttActivationKey("malicious\nprompt injection"),
|
|
1201
|
-
).toBeUndefined();
|
|
1202
|
-
expect(sanitizePttActivationKey("arbitrary_value")).toBeUndefined();
|
|
1203
|
-
expect(sanitizePttActivationKey("")).toBeUndefined();
|
|
1204
|
-
});
|
|
1205
|
-
});
|
|
1206
|
-
|
|
1207
|
-
// ---------------------------------------------------------------------------
|
|
1208
|
-
// resolveChannelCapabilities sanitizes pttActivationKey
|
|
1209
|
-
// ---------------------------------------------------------------------------
|
|
1210
|
-
|
|
1211
|
-
describe("resolveChannelCapabilities with PTT metadata", () => {
|
|
1212
|
-
test("sanitizes valid JSON PTTActivator pttActivationKey", () => {
|
|
1213
|
-
const key = JSON.stringify({
|
|
1214
|
-
kind: "modifierOnly",
|
|
1215
|
-
modifierFlags: 8388608,
|
|
1216
|
-
});
|
|
1217
|
-
const caps = resolveChannelCapabilities("macos", "macos", {
|
|
1218
|
-
pttActivationKey: key,
|
|
1219
|
-
});
|
|
1220
|
-
expect(caps.pttActivationKey).toBe(key);
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
test("sanitizes invalid pttActivationKey to undefined", () => {
|
|
1224
|
-
const caps = resolveChannelCapabilities("macos", "macos", {
|
|
1225
|
-
pttActivationKey: "evil\nprompt",
|
|
1226
|
-
});
|
|
1227
|
-
expect(caps.pttActivationKey).toBeUndefined();
|
|
1228
|
-
});
|
|
1229
|
-
|
|
1230
|
-
test("passes through microphonePermissionGranted", () => {
|
|
1231
|
-
const key = JSON.stringify({
|
|
1232
|
-
kind: "modifierOnly",
|
|
1233
|
-
modifierFlags: 8388608,
|
|
1234
|
-
});
|
|
1235
|
-
const caps = resolveChannelCapabilities("macos", "macos", {
|
|
1236
|
-
pttActivationKey: key,
|
|
1237
|
-
microphonePermissionGranted: true,
|
|
1238
|
-
});
|
|
1239
|
-
expect(caps.microphonePermissionGranted).toBe(true);
|
|
1240
|
-
});
|
|
1241
|
-
});
|
|
1242
|
-
|
|
1243
1262
|
// ---------------------------------------------------------------------------
|
|
1244
1263
|
// applyRuntimeInjections — injection mode
|
|
1245
1264
|
// ---------------------------------------------------------------------------
|
|
@@ -1299,8 +1318,8 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1299
1318
|
expect(allText).toContain("<channel_command_context>");
|
|
1300
1319
|
expect(allText).toContain("<active_workspace>");
|
|
1301
1320
|
expect(allText).toContain("<channel_capabilities>");
|
|
1302
|
-
expect(allText).toContain("<
|
|
1303
|
-
expect(allText).toContain("<
|
|
1321
|
+
expect(allText).toContain("<turn_context>");
|
|
1322
|
+
expect(allText).toContain("<turn_context>");
|
|
1304
1323
|
expect(allText).toContain("<inbound_actor_context>");
|
|
1305
1324
|
expect(allText).toContain("<non_interactive_context>");
|
|
1306
1325
|
});
|
|
@@ -1349,8 +1368,8 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1349
1368
|
.join("\n");
|
|
1350
1369
|
|
|
1351
1370
|
// Kept in minimal mode
|
|
1352
|
-
expect(allText).toContain("<
|
|
1353
|
-
expect(allText).toContain("<
|
|
1371
|
+
expect(allText).toContain("<turn_context>");
|
|
1372
|
+
expect(allText).toContain("<turn_context>");
|
|
1354
1373
|
expect(allText).toContain("<inbound_actor_context>");
|
|
1355
1374
|
expect(allText).toContain("<non_interactive_context>");
|
|
1356
1375
|
expect(allText).toContain("<channel_capabilities>");
|