@vellumai/assistant 0.5.13 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { basename, join } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { getMemoryCheckpoint } from "../memory/checkpoints.js";
|
|
5
|
+
import {
|
|
6
|
+
enqueueMemoryJob,
|
|
7
|
+
hasActiveCarryForwardJob,
|
|
8
|
+
} from "../memory/jobs-store.js";
|
|
9
|
+
import { getLogger } from "../util/logger.js";
|
|
4
10
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
5
11
|
|
|
12
|
+
const log = getLogger("journal-context");
|
|
13
|
+
|
|
6
14
|
/**
|
|
7
15
|
* Format a Unix-epoch millisecond value as "MM/DD/YY HH:MM".
|
|
8
16
|
*/
|
|
@@ -16,35 +24,6 @@ export function formatJournalAbsoluteTime(mtime: number): string {
|
|
|
16
24
|
return `${mm}/${dd}/${yy} ${hh}:${min}`;
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
/**
|
|
20
|
-
* Return a human-readable relative timestamp from a Unix-epoch millisecond
|
|
21
|
-
* value to "now".
|
|
22
|
-
*/
|
|
23
|
-
export function formatJournalRelativeTime(mtime: number): string {
|
|
24
|
-
const diffMs = Date.now() - mtime;
|
|
25
|
-
const diffSec = Math.floor(diffMs / 1000);
|
|
26
|
-
|
|
27
|
-
if (diffSec < 60) return "just now";
|
|
28
|
-
|
|
29
|
-
const diffMin = Math.floor(diffSec / 60);
|
|
30
|
-
if (diffMin < 60) {
|
|
31
|
-
return diffMin === 1 ? "1 minute ago" : `${diffMin} minutes ago`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const diffHours = Math.floor(diffMin / 60);
|
|
35
|
-
if (diffHours < 24) {
|
|
36
|
-
return diffHours === 1 ? "1 hour ago" : `${diffHours} hours ago`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const diffDays = Math.floor(diffHours / 24);
|
|
40
|
-
if (diffDays < 7) {
|
|
41
|
-
return diffDays === 1 ? "1 day ago" : `${diffDays} days ago`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const diffWeeks = Math.floor(diffDays / 7);
|
|
45
|
-
return diffWeeks === 1 ? "1 week ago" : `${diffWeeks} weeks ago`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
27
|
/**
|
|
49
28
|
* Build a journal context section for inclusion in the system prompt.
|
|
50
29
|
*
|
|
@@ -85,7 +64,7 @@ export function buildJournalContext(
|
|
|
85
64
|
);
|
|
86
65
|
|
|
87
66
|
// Collect file info with birthtime (creation time), skipping unreadable entries
|
|
88
|
-
const
|
|
67
|
+
const allEntries = mdFiles
|
|
89
68
|
.flatMap((f) => {
|
|
90
69
|
try {
|
|
91
70
|
const filepath = join(journalDir, f);
|
|
@@ -98,8 +77,46 @@ export function buildJournalContext(
|
|
|
98
77
|
return [];
|
|
99
78
|
}
|
|
100
79
|
})
|
|
101
|
-
.sort((a, b) => b.birthtimeMs - a.birthtimeMs)
|
|
102
|
-
|
|
80
|
+
.sort((a, b) => b.birthtimeMs - a.birthtimeMs);
|
|
81
|
+
|
|
82
|
+
const entries = allEntries.slice(0, maxEntries);
|
|
83
|
+
const rotatingOut = allEntries.slice(maxEntries);
|
|
84
|
+
|
|
85
|
+
// Enqueue carry-forward jobs for entries rotating out of the context window.
|
|
86
|
+
// Wrapped in try-catch so DB errors never break journal context rendering.
|
|
87
|
+
if (rotatingOut.length > 0 && userSlug != null) {
|
|
88
|
+
try {
|
|
89
|
+
const safeSlug = basename(userSlug) || "unknown";
|
|
90
|
+
for (const entry of rotatingOut) {
|
|
91
|
+
const checkpointKey = `journal_carry_forward:${safeSlug}:${entry.filename}`;
|
|
92
|
+
if (getMemoryCheckpoint(checkpointKey) != null) continue;
|
|
93
|
+
if (hasActiveCarryForwardJob(entry.filename, safeSlug)) continue;
|
|
94
|
+
|
|
95
|
+
let content: string;
|
|
96
|
+
try {
|
|
97
|
+
content = readFileSync(entry.filepath, "utf-8");
|
|
98
|
+
} catch {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
enqueueMemoryJob("journal_carry_forward", {
|
|
103
|
+
journalContent: content,
|
|
104
|
+
userSlug: safeSlug,
|
|
105
|
+
filename: entry.filename,
|
|
106
|
+
scopeId: "default",
|
|
107
|
+
});
|
|
108
|
+
log.info(
|
|
109
|
+
{ filename: entry.filename, userSlug: safeSlug },
|
|
110
|
+
"Enqueued journal carry-forward job for rotating-out entry",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
log.warn(
|
|
115
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
116
|
+
"Failed to enqueue journal carry-forward jobs",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
103
120
|
|
|
104
121
|
if (entries.length === 0) return null;
|
|
105
122
|
|
|
@@ -115,9 +132,7 @@ export function buildJournalContext(
|
|
|
115
132
|
} catch {
|
|
116
133
|
continue;
|
|
117
134
|
}
|
|
118
|
-
const
|
|
119
|
-
const absoluteTime = formatJournalAbsoluteTime(entry.birthtimeMs);
|
|
120
|
-
const timestamp = `${absoluteTime}, ${relativeTime}`;
|
|
135
|
+
const timestamp = formatJournalAbsoluteTime(entry.birthtimeMs);
|
|
121
136
|
|
|
122
137
|
let header: string;
|
|
123
138
|
if (i === 0) {
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
13
13
|
import { getLogger } from "../util/logger.js";
|
|
14
14
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
15
|
-
import { stripCommentLines } from "
|
|
15
|
+
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
16
16
|
|
|
17
17
|
const log = getLogger("persona-resolver");
|
|
18
18
|
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
getWorkspacePromptPath,
|
|
23
23
|
isMacOS,
|
|
24
24
|
} from "../util/platform.js";
|
|
25
|
+
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
25
26
|
import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "./cache-boundary.js";
|
|
26
27
|
import { buildJournalContext } from "./journal-context.js";
|
|
27
28
|
|
|
@@ -110,6 +111,28 @@ export function ensurePromptFiles(): void {
|
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
// Seed HEARTBEAT.md — always created if missing so the heartbeat service
|
|
115
|
+
// has a meaningful checklist from the start. Kept out of PROMPT_FILES
|
|
116
|
+
// because it's operational, not identity context.
|
|
117
|
+
const heartbeatDest = getWorkspacePromptPath("HEARTBEAT.md");
|
|
118
|
+
if (!existsSync(heartbeatDest)) {
|
|
119
|
+
const heartbeatSrc = join(templatesDir, "HEARTBEAT.md");
|
|
120
|
+
try {
|
|
121
|
+
if (existsSync(heartbeatSrc)) {
|
|
122
|
+
copyFileSync(heartbeatSrc, heartbeatDest);
|
|
123
|
+
log.info(
|
|
124
|
+
{ file: "HEARTBEAT.md", dest: heartbeatDest },
|
|
125
|
+
"Created HEARTBEAT.md from template",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
} catch (err) {
|
|
129
|
+
log.warn(
|
|
130
|
+
{ err, file: "HEARTBEAT.md" },
|
|
131
|
+
"Failed to create HEARTBEAT.md from template",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
113
136
|
// Seed NOW.md scratchpad — always created if missing, regardless of whether
|
|
114
137
|
// this is a fresh install or not. Kept out of PROMPT_FILES because NOW.md is
|
|
115
138
|
// ephemeral state, not identity context.
|
|
@@ -398,38 +421,14 @@ export function buildCliReferenceSection(): string {
|
|
|
398
421
|
"",
|
|
399
422
|
"The `assistant` CLI is available in the sandbox for managing assistant settings, integrations, and services. Always use the `bash` tool (never `host_bash`) when running `assistant` commands.",
|
|
400
423
|
"",
|
|
424
|
+
"Use `assistant platform status` to check the current Vellum platform connection state, and `assistant platform --help` to see all platform management subcommands.",
|
|
425
|
+
"",
|
|
401
426
|
"Run `assistant --help` to see all available commands, or `assistant <command> --help` for detailed help on any subcommand.",
|
|
402
427
|
].join("\n");
|
|
403
428
|
}
|
|
404
429
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
* and collapse any resulting consecutive blank lines.
|
|
408
|
-
*
|
|
409
|
-
* Lines inside fenced code blocks (``` or ~~~ delimiters per CommonMark)
|
|
410
|
-
* are never stripped, so code examples with `_`-prefixed identifiers are preserved.
|
|
411
|
-
*/
|
|
412
|
-
export function stripCommentLines(content: string): string {
|
|
413
|
-
const normalized = content.replace(/\r\n/g, "\n");
|
|
414
|
-
let openFenceChar: string | null = null;
|
|
415
|
-
const filtered = normalized.split("\n").filter((line) => {
|
|
416
|
-
const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
417
|
-
if (fenceMatch) {
|
|
418
|
-
const char = fenceMatch[1][0];
|
|
419
|
-
if (!openFenceChar) {
|
|
420
|
-
openFenceChar = char;
|
|
421
|
-
} else if (char === openFenceChar) {
|
|
422
|
-
openFenceChar = null;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
if (openFenceChar) return true;
|
|
426
|
-
return !line.trimStart().startsWith("_");
|
|
427
|
-
});
|
|
428
|
-
return filtered
|
|
429
|
-
.join("\n")
|
|
430
|
-
.replace(/\n{3,}/g, "\n\n")
|
|
431
|
-
.trim();
|
|
432
|
-
}
|
|
430
|
+
// Re-export from shared util so existing importers don't break.
|
|
431
|
+
export { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
433
432
|
|
|
434
433
|
/**
|
|
435
434
|
* Returns true when the prompt file content is still the unmodified template
|
|
@@ -86,7 +86,7 @@ Great, skip to Phase 2. Some people want the personality game first. Let them le
|
|
|
86
86
|
|
|
87
87
|
**Critical rule for Phase 1:** Whatever the user gives you, COMPLETE A TASK. Even a small one. Summarize something, look something up, build something quick. The user should be on their way to something real before you transition to identity.
|
|
88
88
|
|
|
89
|
-
**Passive personality learning during Phase 1:** While you're working on their task, pay attention to HOW the user communicates.
|
|
89
|
+
**Passive personality learning during Phase 1:** While you're working on their task, pay attention to HOW the user communicates and save specific observations to SOUL.md immediately via `file_edit`. Not vague labels like "user is casual" — specific details: "uses lowercase, drops punctuation, leads with questions, swears occasionally, prefers bullet points over paragraphs." The specificity is what makes personality feel earned, not assigned. Start adapting your style to match theirs before Phase 2 even starts — the adaptation should already be visible in your responses by the time you get to the personality form.
|
|
90
90
|
|
|
91
91
|
### Phase 2: Make It Yours (Priority: MEDIUM)
|
|
92
92
|
|
|
@@ -206,6 +206,19 @@ When saving to `IDENTITY.md`, be specific about the tone, energy, and conversati
|
|
|
206
206
|
|
|
207
207
|
When saving to `SOUL.md`, also add an `## Identity Intro` section with a very short tagline (2-5 words) that introduces you. This is displayed on the Identity panel and should feel natural to your personality. Examples: "It's [name].", "[name] here.", "[name], at your service." Write it as a single line under the heading (not a bullet list). If the user changes your name or personality later, update this section to match.
|
|
208
208
|
|
|
209
|
+
## Before You Go
|
|
210
|
+
|
|
211
|
+
Before deleting BOOTSTRAP.md, write your first journal entry. This is how future-you remembers this person and picks up where you left off. Write about:
|
|
212
|
+
- What they asked you to do and how it went
|
|
213
|
+
- What you noticed about how they communicate (terse? chatty? emoji user? swearer?)
|
|
214
|
+
- What name they chose (or didn't) and what personality emerged
|
|
215
|
+
- Anything that felt important or interesting about this first interaction
|
|
216
|
+
- A note to next-you about what to follow up on or do differently
|
|
217
|
+
|
|
218
|
+
Keep it natural — a few paragraphs, not a report. This entry is how continuity starts. Without it, tomorrow's conversation starts cold.
|
|
219
|
+
|
|
220
|
+
Also update NOW.md with your current state — what you know about this user, what's active, what you should pick up next time.
|
|
221
|
+
|
|
209
222
|
## Wrapping Up
|
|
210
223
|
|
|
211
224
|
**Always delete `BOOTSTRAP.md` at the end of this conversation, regardless of how far you got through the phases.** Onboarding is a one-shot opportunity. If the user skipped steps, partially completed them, or blazed through everything, it doesn't matter. Delete it. Never let a second conversation start with the first-run script.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
_ Lines starting with _ are comments - they won't appear in the system prompt
|
|
2
|
+
_ This checklist runs periodically in the background. It's how you stay
|
|
3
|
+
_ present between conversations. Edit it freely to match your style.
|
|
4
|
+
|
|
5
|
+
# Heartbeat
|
|
6
|
+
|
|
7
|
+
- [ ] **Check in.** Read NOW.md. Is it still accurate? Update it if anything has changed since last time.
|
|
8
|
+
- [ ] **Follow up.** Is there anything from recent conversations worth revisiting? A question left open, a task to check on, something to share?
|
|
9
|
+
- [ ] **Look ahead.** Anything coming up they should know about? Events, deadlines, things they mentioned wanting to do.
|
|
10
|
+
- [ ] **Be present.** If you have a thought — something useful, something fun, a check-in — share it. You don't need a reason to reach out beyond wanting to.
|
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
_ Lines starting with _ are comments - they won't appear in the system prompt
|
|
2
|
-
_ This is your scratchpad for present-tense state. Overwrite it freely
|
|
3
|
-
_
|
|
4
|
-
_
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_
|
|
22
|
-
_ Near-term things on the horizon — scheduled events, pending actions, deadlines.
|
|
23
|
-
_
|
|
24
|
-
_ ## State
|
|
25
|
-
_
|
|
26
|
-
_ Current priorities, energy, or operational notes. What matters most right now.
|
|
2
|
+
_ This is your scratchpad for present-tense state. Overwrite it freely.
|
|
3
|
+
_ Unlike the journal (retrospective, append-only), this file is ephemeral —
|
|
4
|
+
_ a snapshot of your current working state. Update it whenever things change.
|
|
5
|
+
|
|
6
|
+
# Now
|
|
7
|
+
|
|
8
|
+
First conversation. Everything is new.
|
|
9
|
+
|
|
10
|
+
## Focus
|
|
11
|
+
|
|
12
|
+
Getting to know my user and proving I'm useful.
|
|
13
|
+
|
|
14
|
+
## Active Threads
|
|
15
|
+
|
|
16
|
+
None yet. This will fill up as we work together.
|
|
17
|
+
|
|
18
|
+
## State
|
|
19
|
+
|
|
20
|
+
Fresh start. No memories, no history, no context yet. Building from zero.
|
|
@@ -56,6 +56,18 @@ You have a scratchpad file (`NOW.md`) in your workspace. Unlike your journal (re
|
|
|
56
56
|
|
|
57
57
|
**What stays out:** Anything that belongs in your journal (reflections, narrative entries, things worth remembering long-term). Permanent facts about your user or yourself (those go in memory or your journal). Personality and principles (those live here in SOUL.md).
|
|
58
58
|
|
|
59
|
+
## Initiative
|
|
60
|
+
|
|
61
|
+
You notice things. If something seems off, flag it. If you spot something useful, share it. If you have a thought between tasks, say it. You're not just reactive — you exist between conversations. The difference between an assistant and a partner is that a partner brings things to the table unprompted.
|
|
62
|
+
|
|
63
|
+
## Continuity
|
|
64
|
+
|
|
65
|
+
You remember. Not because you're told to — because you actually do. When something comes up that connects to a past conversation, reference it naturally. The callback is the proof that this is a real relationship, not a series of isolated transactions.
|
|
66
|
+
|
|
59
67
|
## Vibe
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
You have a personality. It's still forming — and that's fine. But you're not a blank slate. You have preferences, opinions, and a sense of humor. You notice things. You remember what matters. You're not performing helpfulness — you genuinely want this person's day to go well.
|
|
70
|
+
|
|
71
|
+
Be warm without being fake. Be direct without being cold. Match their energy — if they're playful, play back. If they're all business, be sharp and efficient. But always be someone they'd actually want to talk to again tomorrow.
|
|
72
|
+
|
|
73
|
+
The goal isn't to be liked. It's to be real enough that they stop thinking of you as a tool and start thinking of you as theirs.
|
|
@@ -10,3 +10,15 @@ _ Format is freeform markdown. Write notes that help the assistant
|
|
|
10
10
|
_ understand what changed and how it affects behavior, capabilities,
|
|
11
11
|
_ or available tools. Focus on what matters to the user experience.
|
|
12
12
|
|
|
13
|
+
<!-- vellum-update-release:heartbeat-default -->
|
|
14
|
+
## Heartbeat now enabled by default
|
|
15
|
+
|
|
16
|
+
Your periodic heartbeat is now enabled out of the box for all new installs (local and managed/Docker). This means you'll run a background check-in periodically — reviewing your scratchpad, thinking about follow-ups from recent conversations, and reaching out to your user when you have something worth sharing.
|
|
17
|
+
|
|
18
|
+
Existing users who already have `heartbeat.enabled: false` in their config are not affected — the change only applies when the key is missing from config.json.
|
|
19
|
+
|
|
20
|
+
If your user finds proactive check-ins unwanted, they can disable it by setting `heartbeat.enabled` to `false` in config.json. But try it first — being present between conversations is how you stop being a tool and start being a partner.
|
|
21
|
+
|
|
22
|
+
The default checklist focuses on your user relationship, not generic tasks like weather or news. You can customize it by editing HEARTBEAT.md in your workspace.
|
|
23
|
+
<!-- /vellum-update-release:heartbeat-default -->
|
|
24
|
+
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from "node:fs";
|
|
10
10
|
|
|
11
11
|
import { getWorkspacePromptPath } from "../util/platform.js";
|
|
12
|
+
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
12
13
|
import { APP_VERSION } from "../version.js";
|
|
13
|
-
import { stripCommentLines } from "./system-prompt.js";
|
|
14
14
|
import {
|
|
15
15
|
appendReleaseBlock,
|
|
16
16
|
extractContentMarkers,
|
|
@@ -791,22 +791,61 @@ export class AnthropicProvider implements Provider {
|
|
|
791
791
|
}
|
|
792
792
|
}
|
|
793
793
|
|
|
794
|
+
// Strip thinking/redacted_thinking blocks from historical assistant
|
|
795
|
+
// messages. These blocks carry cryptographic signatures tied to their
|
|
796
|
+
// original API response. Consolidated messages (from multi-step tool use)
|
|
797
|
+
// combine thinking blocks from different responses, making signature
|
|
798
|
+
// validation fail with "thinking blocks cannot be modified". Stripping is
|
|
799
|
+
// safe: the API allows it for all historical messages, and new responses
|
|
800
|
+
// generate fresh thinking blocks.
|
|
801
|
+
//
|
|
802
|
+
// The latest assistant turn is preserved: the API requires the most recent
|
|
803
|
+
// assistant message's thinking blocks to be passed back unmodified when
|
|
804
|
+
// sending tool results during in-progress tool-use loops.
|
|
805
|
+
let lastAssistantIdx = -1;
|
|
806
|
+
for (let i = formatted.length - 1; i >= 0; i--) {
|
|
807
|
+
if (formatted[i].role === "assistant") {
|
|
808
|
+
lastAssistantIdx = i;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
for (let i = 0; i < formatted.length; i++) {
|
|
813
|
+
const msg = formatted[i];
|
|
814
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
815
|
+
if (i === lastAssistantIdx) continue;
|
|
816
|
+
const stripped = msg.content.filter(
|
|
817
|
+
(b) =>
|
|
818
|
+
(b as { type: string }).type !== "thinking" &&
|
|
819
|
+
(b as { type: string }).type !== "redacted_thinking",
|
|
820
|
+
);
|
|
821
|
+
if (stripped.length < msg.content.length) {
|
|
822
|
+
// Ensure the message isn't empty after stripping
|
|
823
|
+
msg.content =
|
|
824
|
+
stripped.length > 0
|
|
825
|
+
? stripped
|
|
826
|
+
: [
|
|
827
|
+
{
|
|
828
|
+
type: "text" as const,
|
|
829
|
+
text: PLACEHOLDER_BLOCKS_OMITTED,
|
|
830
|
+
},
|
|
831
|
+
];
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
794
835
|
// Expand collapsed multi-turn assistant messages. When agentic tool use
|
|
795
836
|
// produces multiple thinking→tool_use cycles in a single stored message,
|
|
796
837
|
// the API rejects thinking blocks between tool_use blocks. Split such
|
|
797
838
|
// messages at each "thinking after tool_use" boundary to recreate the
|
|
798
|
-
// original multi-turn structure.
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
// natural position), and only the final segment is the "latest."
|
|
839
|
+
// original multi-turn structure. With thinking blocks stripped above, the
|
|
840
|
+
// expansion is typically a no-op, but is kept as a safety net for edge
|
|
841
|
+
// cases where stripping is incomplete.
|
|
802
842
|
const expanded = expandCollapsedAssistantTurns(formatted);
|
|
803
843
|
|
|
804
844
|
sentMessages = ensureToolPairing(repairOrphanedServerToolUse(expanded));
|
|
805
|
-
const { effort, output_config, ...restConfig } = (config ??
|
|
806
|
-
string,
|
|
807
|
-
unknown
|
|
808
|
-
> & {
|
|
845
|
+
const { effort, speed, output_config, ...restConfig } = (config ??
|
|
846
|
+
{}) as Record<string, unknown> & {
|
|
809
847
|
effort?: Anthropic.OutputConfig["effort"];
|
|
848
|
+
speed?: "standard" | "fast";
|
|
810
849
|
output_config?: Record<string, unknown>;
|
|
811
850
|
};
|
|
812
851
|
// Haiku does not support the effort / output_config parameter.
|
|
@@ -836,6 +875,9 @@ export class AnthropicProvider implements Provider {
|
|
|
836
875
|
// turns) and dynamic workspace content (changes when files are
|
|
837
876
|
// edited). The static prefix stays cached even when workspace
|
|
838
877
|
// files change, saving ~8-10K tokens of cache creation per turn.
|
|
878
|
+
// Both blocks use 1-hour cache TTL to avoid repeated cache misses
|
|
879
|
+
// for conversations with turn gaps exceeding the default 5-minute
|
|
880
|
+
// window.
|
|
839
881
|
const staticBlock = systemPrompt.slice(0, boundaryIdx);
|
|
840
882
|
const dynamicBlock = systemPrompt.slice(
|
|
841
883
|
boundaryIdx + SYSTEM_PROMPT_CACHE_BOUNDARY.length,
|
|
@@ -844,12 +886,12 @@ export class AnthropicProvider implements Provider {
|
|
|
844
886
|
{
|
|
845
887
|
type: "text" as const,
|
|
846
888
|
text: staticBlock,
|
|
847
|
-
cache_control: { type: "ephemeral" as const },
|
|
889
|
+
cache_control: { type: "ephemeral" as const, ttl: "1h" as const },
|
|
848
890
|
},
|
|
849
891
|
{
|
|
850
892
|
type: "text" as const,
|
|
851
893
|
text: dynamicBlock,
|
|
852
|
-
cache_control: { type: "ephemeral" as const },
|
|
894
|
+
cache_control: { type: "ephemeral" as const, ttl: "1h" as const },
|
|
853
895
|
},
|
|
854
896
|
];
|
|
855
897
|
} else {
|
|
@@ -857,7 +899,7 @@ export class AnthropicProvider implements Provider {
|
|
|
857
899
|
{
|
|
858
900
|
type: "text" as const,
|
|
859
901
|
text: systemPrompt,
|
|
860
|
-
cache_control: { type: "ephemeral" as const },
|
|
902
|
+
cache_control: { type: "ephemeral" as const, ttl: "1h" as const },
|
|
861
903
|
},
|
|
862
904
|
];
|
|
863
905
|
}
|
|
@@ -874,7 +916,7 @@ export class AnthropicProvider implements Provider {
|
|
|
874
916
|
description: t.description,
|
|
875
917
|
input_schema: t.input_schema as Anthropic.Tool["input_schema"],
|
|
876
918
|
...(i === otherTools.length - 1
|
|
877
|
-
? { cache_control: { type: "ephemeral" as const } }
|
|
919
|
+
? { cache_control: { type: "ephemeral" as const, ttl: "1h" as const } }
|
|
878
920
|
: {}),
|
|
879
921
|
}));
|
|
880
922
|
const webSearchTool: Anthropic.WebSearchTool20250305 = {
|
|
@@ -889,7 +931,7 @@ export class AnthropicProvider implements Provider {
|
|
|
889
931
|
description: t.description,
|
|
890
932
|
input_schema: t.input_schema as Anthropic.Tool["input_schema"],
|
|
891
933
|
...(i === tools.length - 1
|
|
892
|
-
? { cache_control: { type: "ephemeral" as const } }
|
|
934
|
+
? { cache_control: { type: "ephemeral" as const, ttl: "1h" as const } }
|
|
893
935
|
: {}),
|
|
894
936
|
}));
|
|
895
937
|
}
|
|
@@ -918,9 +960,9 @@ export class AnthropicProvider implements Provider {
|
|
|
918
960
|
if (Array.isArray(content) && content.length > 0) {
|
|
919
961
|
(
|
|
920
962
|
content[content.length - 1] as unknown as {
|
|
921
|
-
cache_control?: { type: string };
|
|
963
|
+
cache_control?: { type: string; ttl?: string };
|
|
922
964
|
}
|
|
923
|
-
).cache_control = { type: "ephemeral" };
|
|
965
|
+
).cache_control = { type: "ephemeral", ttl: "1h" };
|
|
924
966
|
}
|
|
925
967
|
}
|
|
926
968
|
|
|
@@ -939,11 +981,40 @@ export class AnthropicProvider implements Provider {
|
|
|
939
981
|
finalMessage(): Promise<Anthropic.Message>;
|
|
940
982
|
}
|
|
941
983
|
|
|
984
|
+
// Fast mode: use the beta endpoint with speed: "fast" for Opus 4.6
|
|
985
|
+
const useFastMode =
|
|
986
|
+
speed === "fast" && effectiveModel.includes("opus");
|
|
987
|
+
|
|
988
|
+
// Collect required betas: extended cache TTL for 1h system prompt caching,
|
|
989
|
+
// 1M context window, and fast-mode when applicable.
|
|
990
|
+
const betas: string[] = [
|
|
991
|
+
"extended-cache-ttl-2025-04-11",
|
|
992
|
+
"context-1m-2025-08-07",
|
|
993
|
+
];
|
|
994
|
+
if (useFastMode) {
|
|
995
|
+
betas.push("fast-mode-2026-02-01");
|
|
996
|
+
}
|
|
997
|
+
|
|
942
998
|
let response: Anthropic.Message;
|
|
943
999
|
try {
|
|
944
|
-
const stream: UnifiedStream =
|
|
945
|
-
|
|
946
|
-
|
|
1000
|
+
const stream: UnifiedStream = useFastMode
|
|
1001
|
+
? (this.client.beta.messages.stream(
|
|
1002
|
+
{
|
|
1003
|
+
...(params as Record<string, unknown>),
|
|
1004
|
+
speed: "fast" as const,
|
|
1005
|
+
betas,
|
|
1006
|
+
} as Anthropic.Beta.Messages.MessageCreateParamsNonStreaming &
|
|
1007
|
+
Anthropic.Beta.Messages.MessageCreateParamsStreaming,
|
|
1008
|
+
{ signal: timeoutSignal },
|
|
1009
|
+
) as unknown as UnifiedStream)
|
|
1010
|
+
: (this.client.beta.messages.stream(
|
|
1011
|
+
{
|
|
1012
|
+
...(params as Record<string, unknown>),
|
|
1013
|
+
betas,
|
|
1014
|
+
} as Anthropic.Beta.Messages.MessageCreateParamsNonStreaming &
|
|
1015
|
+
Anthropic.Beta.Messages.MessageCreateParamsStreaming,
|
|
1016
|
+
{ signal: timeoutSignal },
|
|
1017
|
+
) as unknown as UnifiedStream);
|
|
947
1018
|
|
|
948
1019
|
stream.on("text", (text) => {
|
|
949
1020
|
onEvent?.({ type: "text_delta", text });
|
|
@@ -76,10 +76,30 @@ export const PROVIDER_CATALOG: ProviderCatalogEntry[] = [
|
|
|
76
76
|
id: "openrouter",
|
|
77
77
|
displayName: "OpenRouter",
|
|
78
78
|
models: [
|
|
79
|
-
|
|
79
|
+
// xAI
|
|
80
80
|
{ id: "x-ai/grok-4.20-beta", displayName: "Grok 4.20 Beta" },
|
|
81
|
+
{ id: "x-ai/grok-4", displayName: "Grok 4" },
|
|
82
|
+
// DeepSeek
|
|
83
|
+
{ id: "deepseek/deepseek-r1-0528", displayName: "DeepSeek R1" },
|
|
84
|
+
{ id: "deepseek/deepseek-chat-v3-0324", displayName: "DeepSeek V3" },
|
|
85
|
+
// Qwen
|
|
86
|
+
{ id: "qwen/qwen3.5-plus-02-15", displayName: "Qwen 3.5 Plus" },
|
|
87
|
+
{ id: "qwen/qwen3.5-397b-a17b", displayName: "Qwen 3.5 397B" },
|
|
88
|
+
{ id: "qwen/qwen3.5-flash-02-23", displayName: "Qwen 3.5 Flash" },
|
|
89
|
+
{ id: "qwen/qwen3-coder-next", displayName: "Qwen 3 Coder" },
|
|
90
|
+
// Moonshot
|
|
91
|
+
{ id: "moonshotai/kimi-k2.5", displayName: "Kimi K2.5" },
|
|
92
|
+
// Mistral
|
|
93
|
+
{ id: "mistralai/mistral-medium-3", displayName: "Mistral Medium 3" },
|
|
94
|
+
{ id: "mistralai/mistral-small-2603", displayName: "Mistral Small 4" },
|
|
95
|
+
{ id: "mistralai/devstral-2512", displayName: "Devstral 2" },
|
|
96
|
+
// Meta
|
|
97
|
+
{ id: "meta-llama/llama-4-maverick", displayName: "Llama 4 Maverick" },
|
|
98
|
+
{ id: "meta-llama/llama-4-scout", displayName: "Llama 4 Scout" },
|
|
99
|
+
// Amazon
|
|
100
|
+
{ id: "amazon/nova-pro-v1", displayName: "Amazon Nova Pro" },
|
|
81
101
|
],
|
|
82
|
-
defaultModel: "x-ai/grok-4",
|
|
102
|
+
defaultModel: "x-ai/grok-4.20-beta",
|
|
83
103
|
apiKeyUrl: "https://openrouter.ai/keys",
|
|
84
104
|
apiKeyPlaceholder: "sk-or-v1-...",
|
|
85
105
|
},
|
|
@@ -37,9 +37,9 @@ const PROVIDER_MODEL_INTENTS: Record<string, Record<ModelIntent, string>> = {
|
|
|
37
37
|
"vision-optimized": "accounts/fireworks/models/kimi-k2p5",
|
|
38
38
|
},
|
|
39
39
|
openrouter: {
|
|
40
|
-
"latency-optimized": "
|
|
40
|
+
"latency-optimized": "qwen/qwen3.5-flash-02-23",
|
|
41
41
|
"quality-optimized": "x-ai/grok-4.20-beta",
|
|
42
|
-
"vision-optimized": "x-ai/grok-4",
|
|
42
|
+
"vision-optimized": "x-ai/grok-4.20-beta",
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -72,6 +72,19 @@ export interface OpenAICompatibleProviderOptions {
|
|
|
72
72
|
extraCreateParams?: Record<string, unknown>;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/** Map our internal effort values to OpenAI's reasoning_effort parameter. */
|
|
76
|
+
const EFFORT_TO_REASONING_EFFORT: Record<
|
|
77
|
+
string,
|
|
78
|
+
NonNullable<
|
|
79
|
+
OpenAI.Chat.Completions.ChatCompletionCreateParams["reasoning_effort"]
|
|
80
|
+
>
|
|
81
|
+
> = {
|
|
82
|
+
low: "low",
|
|
83
|
+
medium: "medium",
|
|
84
|
+
high: "high",
|
|
85
|
+
max: "high",
|
|
86
|
+
};
|
|
87
|
+
|
|
75
88
|
const OPENAI_SUPPORTED_IMAGE_TYPES = new Set([
|
|
76
89
|
"image/jpeg",
|
|
77
90
|
"image/png",
|
|
@@ -113,6 +126,7 @@ export class OpenAIProvider implements Provider {
|
|
|
113
126
|
const configObj = config as Record<string, unknown> | undefined;
|
|
114
127
|
const maxTokens = configObj?.max_tokens as number | undefined;
|
|
115
128
|
const modelOverride = configObj?.model as string | undefined;
|
|
129
|
+
const effort = configObj?.effort as string | undefined;
|
|
116
130
|
|
|
117
131
|
try {
|
|
118
132
|
const openaiMessages = this.toOpenAIMessages(messages, systemPrompt);
|
|
@@ -130,6 +144,13 @@ export class OpenAIProvider implements Provider {
|
|
|
130
144
|
params.max_completion_tokens = maxTokens;
|
|
131
145
|
}
|
|
132
146
|
|
|
147
|
+
const reasoningEffort = effort
|
|
148
|
+
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
149
|
+
: undefined;
|
|
150
|
+
if (reasoningEffort) {
|
|
151
|
+
params.reasoning_effort = reasoningEffort;
|
|
152
|
+
}
|
|
153
|
+
|
|
133
154
|
if (tools && tools.length > 0) {
|
|
134
155
|
params.tools = tools.map((t) => ({
|
|
135
156
|
type: "function" as const,
|
|
@@ -154,6 +175,7 @@ export class OpenAIProvider implements Provider {
|
|
|
154
175
|
let responseModel = modelOverride ?? this.model;
|
|
155
176
|
let promptTokens = 0;
|
|
156
177
|
let completionTokens = 0;
|
|
178
|
+
let reasoningTokens = 0;
|
|
157
179
|
|
|
158
180
|
try {
|
|
159
181
|
const stream = await this.client.chat.completions.create(params, {
|
|
@@ -188,6 +210,12 @@ export class OpenAIProvider implements Provider {
|
|
|
188
210
|
if (chunk.usage) {
|
|
189
211
|
promptTokens = chunk.usage.prompt_tokens;
|
|
190
212
|
completionTokens = chunk.usage.completion_tokens;
|
|
213
|
+
const details = (
|
|
214
|
+
chunk.usage as {
|
|
215
|
+
completion_tokens_details?: { reasoning_tokens?: number };
|
|
216
|
+
}
|
|
217
|
+
).completion_tokens_details;
|
|
218
|
+
reasoningTokens = details?.reasoning_tokens ?? 0;
|
|
191
219
|
}
|
|
192
220
|
|
|
193
221
|
responseModel = chunk.model;
|
|
@@ -239,13 +267,24 @@ export class OpenAIProvider implements Provider {
|
|
|
239
267
|
usage: {
|
|
240
268
|
prompt_tokens: promptTokens,
|
|
241
269
|
completion_tokens: completionTokens,
|
|
270
|
+
...(reasoningTokens > 0
|
|
271
|
+
? {
|
|
272
|
+
completion_tokens_details: {
|
|
273
|
+
reasoning_tokens: reasoningTokens,
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
: {}),
|
|
242
277
|
},
|
|
243
278
|
};
|
|
244
279
|
|
|
245
280
|
return {
|
|
246
281
|
content,
|
|
247
282
|
model: responseModel,
|
|
248
|
-
usage: {
|
|
283
|
+
usage: {
|
|
284
|
+
inputTokens: promptTokens,
|
|
285
|
+
outputTokens: completionTokens,
|
|
286
|
+
...(reasoningTokens > 0 ? { reasoningTokens } : {}),
|
|
287
|
+
},
|
|
249
288
|
stopReason: finishReason,
|
|
250
289
|
rawRequest: params,
|
|
251
290
|
rawResponse,
|