@vellumai/assistant 0.5.13 → 0.5.15
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
|
@@ -589,7 +589,7 @@ describe("injectTemporalContext", () => {
|
|
|
589
589
|
};
|
|
590
590
|
|
|
591
591
|
const sampleContext =
|
|
592
|
-
"<temporal_context>\nToday: 2026-02-18 (
|
|
592
|
+
"<temporal_context>\nToday: 2026-02-18 (Wed) 12:00 +00:00\nTZ: UTC\n</temporal_context>";
|
|
593
593
|
|
|
594
594
|
test("prepends temporal context block to user message", () => {
|
|
595
595
|
const result = injectTemporalContext(baseUserMessage, sampleContext);
|
|
@@ -729,7 +729,7 @@ describe("applyRuntimeInjections with temporalContext", () => {
|
|
|
729
729
|
];
|
|
730
730
|
|
|
731
731
|
const sampleContext =
|
|
732
|
-
"<temporal_context>\nToday: 2026-02-18 (
|
|
732
|
+
"<temporal_context>\nToday: 2026-02-18 (Wed) 12:00 +00:00\nTZ: UTC\n</temporal_context>";
|
|
733
733
|
|
|
734
734
|
test("injects temporal context when provided", () => {
|
|
735
735
|
const result = applyRuntimeInjections(baseMessages, {
|
|
@@ -1293,7 +1293,7 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1293
1293
|
workspaceTopLevelContext:
|
|
1294
1294
|
"<workspace_top_level>\nRoot: /sandbox\n</workspace_top_level>",
|
|
1295
1295
|
temporalContext:
|
|
1296
|
-
"<temporal_context>\nToday: 2026-03-04 (
|
|
1296
|
+
"<temporal_context>\nToday: 2026-03-04 (Tue) 12:00 +00:00\nTZ: UTC\n</temporal_context>",
|
|
1297
1297
|
channelCommandContext: { type: "start" } as const,
|
|
1298
1298
|
activeSurface: { surfaceId: "sf_1", html: "<div>test</div>" },
|
|
1299
1299
|
channelCapabilities: {
|
|
@@ -1341,7 +1341,7 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1341
1341
|
expect(allText).toContain("<turn_context>");
|
|
1342
1342
|
expect(allText).toContain("<inbound_actor_context>");
|
|
1343
1343
|
expect(allText).toContain("<non_interactive_context>");
|
|
1344
|
-
expect(allText).toContain("<
|
|
1344
|
+
expect(allText).toContain("<NOW.md");
|
|
1345
1345
|
});
|
|
1346
1346
|
|
|
1347
1347
|
test("explicit mode: 'full' behaves the same as default", () => {
|
|
@@ -1358,7 +1358,7 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1358
1358
|
expect(allText).toContain("<temporal_context>");
|
|
1359
1359
|
expect(allText).toContain("<channel_command_context>");
|
|
1360
1360
|
expect(allText).toContain("<active_workspace>");
|
|
1361
|
-
expect(allText).toContain("<
|
|
1361
|
+
expect(allText).toContain("<NOW.md");
|
|
1362
1362
|
});
|
|
1363
1363
|
|
|
1364
1364
|
test("minimal mode skips high-token optional blocks", () => {
|
|
@@ -1376,7 +1376,7 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
1376
1376
|
expect(allText).not.toContain("<temporal_context>");
|
|
1377
1377
|
expect(allText).not.toContain("<channel_command_context>");
|
|
1378
1378
|
expect(allText).not.toContain("<active_workspace>");
|
|
1379
|
-
expect(allText).not.toContain("<
|
|
1379
|
+
expect(allText).not.toContain("<NOW.md");
|
|
1380
1380
|
});
|
|
1381
1381
|
|
|
1382
1382
|
test("minimal mode preserves safety-critical blocks", () => {
|
|
@@ -1435,26 +1435,51 @@ describe("injectNowScratchpad", () => {
|
|
|
1435
1435
|
content: [{ type: "text", text: "What should I work on?" }],
|
|
1436
1436
|
};
|
|
1437
1437
|
|
|
1438
|
-
test("
|
|
1438
|
+
test("inserts NOW.md before user content", () => {
|
|
1439
1439
|
const result = injectNowScratchpad(
|
|
1440
1440
|
baseUserMessage,
|
|
1441
1441
|
"Current focus: shipping PR 3",
|
|
1442
1442
|
);
|
|
1443
1443
|
expect(result.content.length).toBe(2);
|
|
1444
|
-
//
|
|
1445
|
-
|
|
1446
|
-
"What should I work on?",
|
|
1447
|
-
);
|
|
1448
|
-
// Scratchpad is appended (not prepended)
|
|
1449
|
-
const injected = result.content[1];
|
|
1444
|
+
// Scratchpad comes first (before user content)
|
|
1445
|
+
const injected = result.content[0];
|
|
1450
1446
|
expect(injected.type).toBe("text");
|
|
1451
1447
|
const text = (injected as { type: "text"; text: string }).text;
|
|
1452
1448
|
expect(text).toBe(
|
|
1453
|
-
"<
|
|
1449
|
+
"<NOW.md Always keep this up to date>\nCurrent focus: shipping PR 3\n</NOW.md>",
|
|
1450
|
+
);
|
|
1451
|
+
// Original content comes last
|
|
1452
|
+
expect((result.content[1] as { type: "text"; text: string }).text).toBe(
|
|
1453
|
+
"What should I work on?",
|
|
1454
1454
|
);
|
|
1455
1455
|
});
|
|
1456
1456
|
|
|
1457
|
-
test("
|
|
1457
|
+
test("inserts after memory_context but before user content", () => {
|
|
1458
|
+
const messageWithMemory: Message = {
|
|
1459
|
+
role: "user",
|
|
1460
|
+
content: [
|
|
1461
|
+
{ type: "text", text: "<memory_context __injected>\nrecalled notes\n</memory_context>" },
|
|
1462
|
+
{ type: "text", text: "What should I work on?" },
|
|
1463
|
+
],
|
|
1464
|
+
};
|
|
1465
|
+
|
|
1466
|
+
const result = injectNowScratchpad(messageWithMemory, "scratchpad notes");
|
|
1467
|
+
expect(result.content.length).toBe(3);
|
|
1468
|
+
// Memory context stays first
|
|
1469
|
+
expect(
|
|
1470
|
+
(result.content[0] as { type: "text"; text: string }).text,
|
|
1471
|
+
).toContain("<memory_context");
|
|
1472
|
+
// Scratchpad inserted after memory
|
|
1473
|
+
expect(
|
|
1474
|
+
(result.content[1] as { type: "text"; text: string }).text,
|
|
1475
|
+
).toContain("<NOW.md");
|
|
1476
|
+
// User content is last
|
|
1477
|
+
expect((result.content[2] as { type: "text"; text: string }).text).toBe(
|
|
1478
|
+
"What should I work on?",
|
|
1479
|
+
);
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1482
|
+
test("preserves existing multi-block content with scratchpad before it", () => {
|
|
1458
1483
|
const multiBlockMessage: Message = {
|
|
1459
1484
|
role: "user",
|
|
1460
1485
|
content: [
|
|
@@ -1465,15 +1490,16 @@ describe("injectNowScratchpad", () => {
|
|
|
1465
1490
|
|
|
1466
1491
|
const result = injectNowScratchpad(multiBlockMessage, "scratchpad notes");
|
|
1467
1492
|
expect(result.content.length).toBe(3);
|
|
1468
|
-
|
|
1493
|
+
// Scratchpad is first (no memory_context to skip)
|
|
1494
|
+
expect(
|
|
1495
|
+
(result.content[0] as { type: "text"; text: string }).text,
|
|
1496
|
+
).toContain("<NOW.md");
|
|
1497
|
+
expect((result.content[1] as { type: "text"; text: string }).text).toBe(
|
|
1469
1498
|
"First block",
|
|
1470
1499
|
);
|
|
1471
|
-
expect((result.content[
|
|
1500
|
+
expect((result.content[2] as { type: "text"; text: string }).text).toBe(
|
|
1472
1501
|
"Second block",
|
|
1473
1502
|
);
|
|
1474
|
-
expect(
|
|
1475
|
-
(result.content[2] as { type: "text"; text: string }).text,
|
|
1476
|
-
).toContain("<now_scratchpad>");
|
|
1477
1503
|
});
|
|
1478
1504
|
});
|
|
1479
1505
|
|
|
@@ -1482,7 +1508,7 @@ describe("injectNowScratchpad", () => {
|
|
|
1482
1508
|
// ---------------------------------------------------------------------------
|
|
1483
1509
|
|
|
1484
1510
|
describe("stripNowScratchpad", () => {
|
|
1485
|
-
test("strips
|
|
1511
|
+
test("strips NOW.md blocks from user messages", () => {
|
|
1486
1512
|
const messages: Message[] = [
|
|
1487
1513
|
{
|
|
1488
1514
|
role: "user",
|
|
@@ -1490,7 +1516,7 @@ describe("stripNowScratchpad", () => {
|
|
|
1490
1516
|
{ type: "text", text: "Hello" },
|
|
1491
1517
|
{
|
|
1492
1518
|
type: "text",
|
|
1493
|
-
text: "<
|
|
1519
|
+
text: "<NOW.md Always keep this up to date>\nSome notes\n</NOW.md>",
|
|
1494
1520
|
},
|
|
1495
1521
|
],
|
|
1496
1522
|
},
|
|
@@ -1511,14 +1537,14 @@ describe("stripNowScratchpad", () => {
|
|
|
1511
1537
|
expect(result[1].content.length).toBe(1);
|
|
1512
1538
|
});
|
|
1513
1539
|
|
|
1514
|
-
test("removes user messages that only contain
|
|
1540
|
+
test("removes user messages that only contain NOW.md", () => {
|
|
1515
1541
|
const messages: Message[] = [
|
|
1516
1542
|
{
|
|
1517
1543
|
role: "user",
|
|
1518
1544
|
content: [
|
|
1519
1545
|
{
|
|
1520
1546
|
type: "text",
|
|
1521
|
-
text: "<
|
|
1547
|
+
text: "<NOW.md Always keep this up to date>\nSome notes\n</NOW.md>",
|
|
1522
1548
|
},
|
|
1523
1549
|
],
|
|
1524
1550
|
},
|
|
@@ -1528,7 +1554,7 @@ describe("stripNowScratchpad", () => {
|
|
|
1528
1554
|
expect(result.length).toBe(0);
|
|
1529
1555
|
});
|
|
1530
1556
|
|
|
1531
|
-
test("leaves messages without
|
|
1557
|
+
test("leaves messages without NOW.md untouched", () => {
|
|
1532
1558
|
const messages: Message[] = [
|
|
1533
1559
|
{
|
|
1534
1560
|
role: "user",
|
|
@@ -1543,11 +1569,11 @@ describe("stripNowScratchpad", () => {
|
|
|
1543
1569
|
});
|
|
1544
1570
|
|
|
1545
1571
|
// ---------------------------------------------------------------------------
|
|
1546
|
-
// stripInjectedContext removes
|
|
1572
|
+
// stripInjectedContext removes NOW.md blocks
|
|
1547
1573
|
// ---------------------------------------------------------------------------
|
|
1548
1574
|
|
|
1549
|
-
describe("stripInjectedContext with
|
|
1550
|
-
test("strips
|
|
1575
|
+
describe("stripInjectedContext with NOW.md", () => {
|
|
1576
|
+
test("strips NOW.md blocks alongside other injections", () => {
|
|
1551
1577
|
const messages: Message[] = [
|
|
1552
1578
|
{
|
|
1553
1579
|
role: "user",
|
|
@@ -1559,7 +1585,7 @@ describe("stripInjectedContext with now_scratchpad", () => {
|
|
|
1559
1585
|
{ type: "text", text: "Hello" },
|
|
1560
1586
|
{
|
|
1561
1587
|
type: "text",
|
|
1562
|
-
text: "<
|
|
1588
|
+
text: "<NOW.md Always keep this up to date>\nCurrent focus\n</NOW.md>",
|
|
1563
1589
|
},
|
|
1564
1590
|
],
|
|
1565
1591
|
},
|
|
@@ -1586,32 +1612,32 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
|
|
|
1586
1612
|
},
|
|
1587
1613
|
];
|
|
1588
1614
|
|
|
1589
|
-
test("injects
|
|
1615
|
+
test("injects NOW.md block when provided", () => {
|
|
1590
1616
|
const result = applyRuntimeInjections(baseMessages, {
|
|
1591
1617
|
nowScratchpad: "Current focus: fix the bug",
|
|
1592
1618
|
});
|
|
1593
1619
|
|
|
1594
1620
|
expect(result.length).toBe(1);
|
|
1595
1621
|
expect(result[0].content.length).toBe(2);
|
|
1596
|
-
const injected = result[0].content[
|
|
1622
|
+
const injected = result[0].content[0];
|
|
1597
1623
|
const text = (injected as { type: "text"; text: string }).text;
|
|
1598
|
-
expect(text).toContain("<
|
|
1624
|
+
expect(text).toContain("<NOW.md");
|
|
1599
1625
|
expect(text).toContain("Current focus: fix the bug");
|
|
1600
1626
|
});
|
|
1601
1627
|
|
|
1602
|
-
test("
|
|
1628
|
+
test("scratchpad appears before user's original text content", () => {
|
|
1603
1629
|
const result = applyRuntimeInjections(baseMessages, {
|
|
1604
1630
|
nowScratchpad: "scratchpad notes",
|
|
1605
1631
|
});
|
|
1606
1632
|
|
|
1607
|
-
//
|
|
1633
|
+
// Scratchpad comes first (before user content)
|
|
1608
1634
|
expect(
|
|
1609
1635
|
(result[0].content[0] as { type: "text"; text: string }).text,
|
|
1610
|
-
).
|
|
1611
|
-
//
|
|
1636
|
+
).toContain("<NOW.md");
|
|
1637
|
+
// Original text is last
|
|
1612
1638
|
expect(
|
|
1613
1639
|
(result[0].content[1] as { type: "text"; text: string }).text,
|
|
1614
|
-
).
|
|
1640
|
+
).toBe("What should I do?");
|
|
1615
1641
|
});
|
|
1616
1642
|
|
|
1617
1643
|
test("does not inject when nowScratchpad is null", () => {
|
|
@@ -1641,6 +1667,6 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
|
|
|
1641
1667
|
.map((b) => b.text)
|
|
1642
1668
|
.join("\n");
|
|
1643
1669
|
|
|
1644
|
-
expect(allText).not.toContain("<
|
|
1670
|
+
expect(allText).not.toContain("<NOW.md");
|
|
1645
1671
|
});
|
|
1646
1672
|
});
|
|
@@ -36,6 +36,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
36
36
|
);
|
|
37
37
|
expect(lines).toEqual([
|
|
38
38
|
"/commands — List all available commands",
|
|
39
|
+
"/compact — Force context compaction immediately",
|
|
39
40
|
"/models — List all available models",
|
|
40
41
|
"/status — Show conversation status and context usage",
|
|
41
42
|
"/btw — Ask a side question while the assistant is working",
|
|
@@ -53,6 +54,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
53
54
|
);
|
|
54
55
|
expect(lines).toEqual([
|
|
55
56
|
"/commands — List all available commands",
|
|
57
|
+
"/compact — Force context compaction immediately",
|
|
56
58
|
"/models — List all available models",
|
|
57
59
|
"/status — Show conversation status and context usage",
|
|
58
60
|
"/btw — Ask a side question while the assistant is working",
|
|
@@ -66,6 +68,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
66
68
|
);
|
|
67
69
|
expect(lines).toEqual([
|
|
68
70
|
"/commands — List all available commands",
|
|
71
|
+
"/compact — Force context compaction immediately",
|
|
69
72
|
"/models — List all available models",
|
|
70
73
|
"/status — Show conversation status and context usage",
|
|
71
74
|
"/btw — Ask a side question while the assistant is working",
|
|
@@ -76,6 +79,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
76
79
|
const lines = await resolveCommandsLines(makeSlashContext());
|
|
77
80
|
expect(lines).toEqual([
|
|
78
81
|
"/commands — List all available commands",
|
|
82
|
+
"/compact — Force context compaction immediately",
|
|
79
83
|
"/models — List all available models",
|
|
80
84
|
"/pair — Generate pairing info for connecting a mobile device",
|
|
81
85
|
"/status — Show conversation status and context usage",
|
|
@@ -86,6 +90,7 @@ describe("resolveSlash /commands interface-aware help", () => {
|
|
|
86
90
|
const lines = await resolveCommandsLines();
|
|
87
91
|
expect(lines).toEqual([
|
|
88
92
|
"/commands — List all available commands",
|
|
93
|
+
"/compact — Force context compaction immediately",
|
|
89
94
|
"/models — List all available models",
|
|
90
95
|
"/pair — Generate pairing info for connecting a mobile device",
|
|
91
96
|
]);
|
|
@@ -17,10 +17,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
17
17
|
new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
mock.module("../util/platform.js", () => ({
|
|
21
|
-
getDataDir: () => "/tmp",
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
20
|
mock.module("../memory/guardian-action-store.js", () => ({
|
|
25
21
|
getGuardianActionRequest: () => null,
|
|
26
22
|
resolveGuardianActionRequest: () => {},
|
|
@@ -17,10 +17,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
17
17
|
new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
mock.module("../util/platform.js", () => ({
|
|
21
|
-
getDataDir: () => "/tmp",
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
20
|
mock.module("../memory/guardian-action-store.js", () => ({
|
|
25
21
|
getGuardianActionRequest: () => null,
|
|
26
22
|
resolveGuardianActionRequest: () => {},
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for per-conversation speed override.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that the Conversation constructor resolves speed from the
|
|
5
|
+
* per-conversation speedOverride parameter first, falling back to the
|
|
6
|
+
* global config speed setting.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
AgentEvent,
|
|
12
|
+
AgentLoopConfig,
|
|
13
|
+
CheckpointDecision,
|
|
14
|
+
CheckpointInfo,
|
|
15
|
+
} from "../agent/loop.js";
|
|
16
|
+
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
17
|
+
import type { Message, ProviderResponse } from "../providers/types.js";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Mocks — must precede Conversation import
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
function makeLoggerStub(): Record<string, unknown> {
|
|
24
|
+
const stub: Record<string, unknown> = {};
|
|
25
|
+
for (const m of [
|
|
26
|
+
"info",
|
|
27
|
+
"warn",
|
|
28
|
+
"error",
|
|
29
|
+
"debug",
|
|
30
|
+
"trace",
|
|
31
|
+
"fatal",
|
|
32
|
+
"silent",
|
|
33
|
+
"child",
|
|
34
|
+
]) {
|
|
35
|
+
stub[m] = m === "child" ? () => makeLoggerStub() : () => {};
|
|
36
|
+
}
|
|
37
|
+
return stub;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
mock.module("../util/logger.js", () => ({
|
|
41
|
+
getLogger: () => makeLoggerStub(),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
mock.module("../memory/guardian-action-store.js", () => ({
|
|
45
|
+
getGuardianActionRequest: () => null,
|
|
46
|
+
resolveGuardianActionRequest: () => {},
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
mock.module("../providers/registry.js", () => ({
|
|
50
|
+
getProvider: () => ({ name: "mock-provider" }),
|
|
51
|
+
initializeProviders: () => {},
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
// Controllable config mock — speed and feature flag behavior are test-specific.
|
|
55
|
+
let mockConfigSpeed: "standard" | "fast" = "fast";
|
|
56
|
+
|
|
57
|
+
mock.module("../config/loader.js", () => ({
|
|
58
|
+
getConfig: () => ({
|
|
59
|
+
ui: {},
|
|
60
|
+
provider: "mock-provider",
|
|
61
|
+
maxTokens: 4096,
|
|
62
|
+
thinking: false,
|
|
63
|
+
speed: mockConfigSpeed,
|
|
64
|
+
effort: "high",
|
|
65
|
+
contextWindow: {
|
|
66
|
+
maxInputTokens: 100000,
|
|
67
|
+
thresholdTokens: 80000,
|
|
68
|
+
preserveRecentMessages: 6,
|
|
69
|
+
summaryModel: "mock-model",
|
|
70
|
+
maxSummaryTokens: 512,
|
|
71
|
+
},
|
|
72
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
73
|
+
timeouts: { permissionTimeoutSec: 1 },
|
|
74
|
+
skills: { entries: {}, allowBundled: true },
|
|
75
|
+
permissions: { mode: "workspace" },
|
|
76
|
+
}),
|
|
77
|
+
loadRawConfig: () => ({}),
|
|
78
|
+
saveRawConfig: () => {},
|
|
79
|
+
invalidateConfigCache: () => {},
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
// Feature flag mock — fast-mode enabled for all tests in this file.
|
|
83
|
+
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
84
|
+
isAssistantFeatureFlagEnabled: (key: string) => {
|
|
85
|
+
if (key === "fast-mode") return true;
|
|
86
|
+
return true;
|
|
87
|
+
},
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
mock.module("../prompts/system-prompt.js", () => ({
|
|
91
|
+
buildSystemPrompt: () => "system prompt",
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
mock.module("../config/skills.js", () => ({
|
|
95
|
+
loadSkillCatalog: () => [],
|
|
96
|
+
loadSkillBySelector: () => ({ skill: null }),
|
|
97
|
+
ensureSkillIcon: async () => null,
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
mock.module("../config/skill-state.js", () => ({
|
|
101
|
+
resolveSkillStates: () => [],
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
mock.module("../permissions/trust-store.js", () => ({
|
|
105
|
+
addRule: () => {},
|
|
106
|
+
findHighestPriorityRule: () => null,
|
|
107
|
+
clearCache: () => {},
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
mock.module("../security/secret-allowlist.js", () => ({
|
|
111
|
+
resetAllowlist: () => {},
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
mock.module("../memory/conversation-crud.js", () => ({
|
|
115
|
+
getConversationType: () => "default",
|
|
116
|
+
setConversationOriginChannelIfUnset: () => {},
|
|
117
|
+
updateConversationContextWindow: () => {},
|
|
118
|
+
deleteMessageById: () => {},
|
|
119
|
+
provenanceFromTrustContext: () => ({
|
|
120
|
+
source: "user",
|
|
121
|
+
trustContext: undefined,
|
|
122
|
+
}),
|
|
123
|
+
getConversationOriginInterface: () => null,
|
|
124
|
+
getConversationOriginChannel: () => null,
|
|
125
|
+
getMessages: () => [],
|
|
126
|
+
getConversation: () => ({
|
|
127
|
+
id: "conv-1",
|
|
128
|
+
contextSummary: null,
|
|
129
|
+
contextCompactedMessageCount: 0,
|
|
130
|
+
totalInputTokens: 0,
|
|
131
|
+
totalOutputTokens: 0,
|
|
132
|
+
totalEstimatedCost: 0,
|
|
133
|
+
}),
|
|
134
|
+
createConversation: () => ({ id: "conv-1" }),
|
|
135
|
+
addMessage: () => ({ id: `msg-${Date.now()}` }),
|
|
136
|
+
updateConversationUsage: () => {},
|
|
137
|
+
updateConversationTitle: () => {},
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
mock.module("../memory/conversation-queries.js", () => ({
|
|
141
|
+
listConversations: () => [],
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
mock.module("../memory/attachments-store.js", () => ({
|
|
145
|
+
uploadAttachment: () => ({ id: `att-${Date.now()}` }),
|
|
146
|
+
linkAttachmentToMessage: () => {},
|
|
147
|
+
}));
|
|
148
|
+
|
|
149
|
+
mock.module("../memory/retriever.js", () => ({
|
|
150
|
+
buildMemoryRecall: async () => ({
|
|
151
|
+
enabled: false,
|
|
152
|
+
degraded: false,
|
|
153
|
+
injectedText: "",
|
|
154
|
+
semanticHits: 0,
|
|
155
|
+
injectedTokens: 0,
|
|
156
|
+
latencyMs: 0,
|
|
157
|
+
}),
|
|
158
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
mock.module("../context/window-manager.js", () => ({
|
|
162
|
+
ContextWindowManager: class {
|
|
163
|
+
constructor() {}
|
|
164
|
+
shouldCompact() {
|
|
165
|
+
return { needed: false, estimatedTokens: 0 };
|
|
166
|
+
}
|
|
167
|
+
async maybeCompact() {
|
|
168
|
+
return { compacted: false };
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
createContextSummaryMessage: () => ({
|
|
172
|
+
role: "user",
|
|
173
|
+
content: [{ type: "text", text: "summary" }],
|
|
174
|
+
}),
|
|
175
|
+
getSummaryFromContextMessage: () => null,
|
|
176
|
+
}));
|
|
177
|
+
|
|
178
|
+
mock.module("../memory/llm-usage-store.js", () => ({
|
|
179
|
+
recordUsageEvent: () => ({ id: "mock-id", createdAt: Date.now() }),
|
|
180
|
+
listUsageEvents: () => [],
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
// Capture AgentLoop constructor config for assertions.
|
|
184
|
+
let lastAgentLoopConfig: Partial<AgentLoopConfig> | undefined;
|
|
185
|
+
|
|
186
|
+
mock.module("../agent/loop.js", () => ({
|
|
187
|
+
AgentLoop: class {
|
|
188
|
+
constructor(
|
|
189
|
+
_provider: unknown,
|
|
190
|
+
_systemPrompt: string,
|
|
191
|
+
config?: Partial<AgentLoopConfig>,
|
|
192
|
+
) {
|
|
193
|
+
lastAgentLoopConfig = config;
|
|
194
|
+
}
|
|
195
|
+
getToolTokenBudget() {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
async run(
|
|
199
|
+
_messages: Message[],
|
|
200
|
+
_onEvent: (event: AgentEvent) => void,
|
|
201
|
+
_signal?: AbortSignal,
|
|
202
|
+
_requestId?: string,
|
|
203
|
+
_onCheckpoint?: (checkpoint: CheckpointInfo) => CheckpointDecision,
|
|
204
|
+
): Promise<Message[]> {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
}));
|
|
209
|
+
|
|
210
|
+
mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
211
|
+
listPendingCanonicalGuardianRequestsByDestinationConversation: () => [],
|
|
212
|
+
listCanonicalGuardianRequests: () => [],
|
|
213
|
+
listPendingRequestsByConversationScope: () => [],
|
|
214
|
+
createCanonicalGuardianRequest: () => ({
|
|
215
|
+
id: "mock-cg-id",
|
|
216
|
+
code: "MOCK",
|
|
217
|
+
status: "pending",
|
|
218
|
+
}),
|
|
219
|
+
getCanonicalGuardianRequest: () => null,
|
|
220
|
+
getCanonicalGuardianRequestByCode: () => null,
|
|
221
|
+
updateCanonicalGuardianRequest: () => {},
|
|
222
|
+
resolveCanonicalGuardianRequest: () => {},
|
|
223
|
+
createCanonicalGuardianDelivery: () => ({ id: "mock-cgd-id" }),
|
|
224
|
+
listCanonicalGuardianDeliveries: () => [],
|
|
225
|
+
listPendingCanonicalGuardianRequestsByDestinationChat: () => [],
|
|
226
|
+
updateCanonicalGuardianDelivery: () => {},
|
|
227
|
+
generateCanonicalRequestCode: () => "MOCK-CODE",
|
|
228
|
+
}));
|
|
229
|
+
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Import Conversation AFTER mocks
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
import { Conversation } from "../daemon/conversation.js";
|
|
235
|
+
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
// Helpers
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
function makeProvider() {
|
|
241
|
+
return {
|
|
242
|
+
name: "mock",
|
|
243
|
+
async sendMessage(): Promise<ProviderResponse> {
|
|
244
|
+
return {
|
|
245
|
+
content: [],
|
|
246
|
+
model: "mock",
|
|
247
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
248
|
+
stopReason: "end_turn",
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function makeSendToClient(): (msg: ServerMessage) => void {
|
|
255
|
+
return () => {};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// Tests
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
|
|
262
|
+
describe("per-conversation speed override", () => {
|
|
263
|
+
test("speedOverride 'standard' prevents fast mode even when global config is 'fast'", () => {
|
|
264
|
+
mockConfigSpeed = "fast";
|
|
265
|
+
lastAgentLoopConfig = undefined;
|
|
266
|
+
|
|
267
|
+
new Conversation(
|
|
268
|
+
"conv-speed-override-1",
|
|
269
|
+
makeProvider(),
|
|
270
|
+
"system prompt",
|
|
271
|
+
4096,
|
|
272
|
+
makeSendToClient(),
|
|
273
|
+
"/tmp",
|
|
274
|
+
undefined, // broadcastToAllClients
|
|
275
|
+
undefined, // memoryPolicy
|
|
276
|
+
undefined, // sharedCesClient
|
|
277
|
+
"standard", // speedOverride
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
expect(lastAgentLoopConfig).toBeDefined();
|
|
281
|
+
// When speedOverride is "standard", the AgentLoop should NOT receive speed: "fast"
|
|
282
|
+
expect(lastAgentLoopConfig!.speed).toBeUndefined();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("no speedOverride uses global config speed", () => {
|
|
286
|
+
mockConfigSpeed = "fast";
|
|
287
|
+
lastAgentLoopConfig = undefined;
|
|
288
|
+
|
|
289
|
+
new Conversation(
|
|
290
|
+
"conv-speed-global-1",
|
|
291
|
+
makeProvider(),
|
|
292
|
+
"system prompt",
|
|
293
|
+
4096,
|
|
294
|
+
makeSendToClient(),
|
|
295
|
+
"/tmp",
|
|
296
|
+
undefined, // broadcastToAllClients
|
|
297
|
+
undefined, // memoryPolicy
|
|
298
|
+
undefined, // sharedCesClient
|
|
299
|
+
// no speedOverride — should fall back to global config "fast"
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
expect(lastAgentLoopConfig).toBeDefined();
|
|
303
|
+
expect(lastAgentLoopConfig!.speed).toBe("fast");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("speedOverride 'fast' enables fast mode even when global config is 'standard'", () => {
|
|
307
|
+
mockConfigSpeed = "standard";
|
|
308
|
+
lastAgentLoopConfig = undefined;
|
|
309
|
+
|
|
310
|
+
new Conversation(
|
|
311
|
+
"conv-speed-override-fast-1",
|
|
312
|
+
makeProvider(),
|
|
313
|
+
"system prompt",
|
|
314
|
+
4096,
|
|
315
|
+
makeSendToClient(),
|
|
316
|
+
"/tmp",
|
|
317
|
+
undefined, // broadcastToAllClients
|
|
318
|
+
undefined, // memoryPolicy
|
|
319
|
+
undefined, // sharedCesClient
|
|
320
|
+
"fast", // speedOverride
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
expect(lastAgentLoopConfig).toBeDefined();
|
|
324
|
+
expect(lastAgentLoopConfig!.speed).toBe("fast");
|
|
325
|
+
});
|
|
326
|
+
});
|
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
2
|
|
|
6
3
|
import { v4 as uuid } from "uuid";
|
|
7
4
|
|
|
8
|
-
const testDir = mkdtempSync(
|
|
9
|
-
join(tmpdir(), "conversation-starter-routes-test-"),
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
mock.module("../util/platform.js", () => ({
|
|
13
|
-
getDataDir: () => testDir,
|
|
14
|
-
isMacOS: () => process.platform === "darwin",
|
|
15
|
-
isLinux: () => process.platform === "linux",
|
|
16
|
-
isWindows: () => process.platform === "win32",
|
|
17
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
18
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
19
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
20
|
-
ensureDataDir: () => {},
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
5
|
mock.module("../util/logger.js", () => ({
|
|
24
6
|
getLogger: () =>
|
|
25
7
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -37,11 +19,6 @@ initializeDb();
|
|
|
37
19
|
|
|
38
20
|
afterAll(() => {
|
|
39
21
|
resetDb();
|
|
40
|
-
try {
|
|
41
|
-
rmSync(testDir, { recursive: true });
|
|
42
|
-
} catch {
|
|
43
|
-
/* best effort */
|
|
44
|
-
}
|
|
45
22
|
});
|
|
46
23
|
|
|
47
24
|
const routes = conversationStarterRouteDefinitions();
|