@vellumai/assistant 0.8.12 → 0.9.0-staging.1
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/AGENTS.md +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/openapi.yaml +6935 -5545
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +125 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +75 -0
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +1 -1
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +26 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +122 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +48 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -45,12 +45,14 @@ import {
|
|
|
45
45
|
} from "../memory/canonical-guardian-store.js";
|
|
46
46
|
import { getDb } from "../memory/db-connection.js";
|
|
47
47
|
import { initializeDb } from "../memory/db-init.js";
|
|
48
|
+
import { processGuardianDecision } from "../runtime/guardian-action-service.js";
|
|
48
49
|
import { GUARDIAN_DECISION_ACTIONS } from "../runtime/guardian-decision-types.js";
|
|
49
50
|
import {
|
|
50
51
|
type GuardianReplyContext,
|
|
51
52
|
routeGuardianReply,
|
|
52
53
|
} from "../runtime/guardian-reply-router.js";
|
|
53
54
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
55
|
+
import { parseCallbackData } from "../runtime/routes/channel-route-shared.js";
|
|
54
56
|
import { listGuardianDecisionPrompts } from "../runtime/routes/guardian-action-routes.js";
|
|
55
57
|
|
|
56
58
|
initializeDb();
|
|
@@ -168,6 +170,12 @@ describe("routing invariant: all decision paths reference applyCanonicalGuardian
|
|
|
168
170
|
path: "runtime/routes/guardian-action-routes.ts",
|
|
169
171
|
symbols: ["processGuardianDecision"],
|
|
170
172
|
},
|
|
173
|
+
// Surface action route handler (Vellum in-app card buttons) — intercepts
|
|
174
|
+
// apr:* action IDs and routes through processGuardianDecision
|
|
175
|
+
{
|
|
176
|
+
path: "runtime/routes/surface-action-routes.ts",
|
|
177
|
+
symbols: ["processGuardianDecision"],
|
|
178
|
+
},
|
|
171
179
|
// Shared service where processGuardianDecision is defined — must route
|
|
172
180
|
// through the canonical primitive to complete the chain:
|
|
173
181
|
// entrypoint → processGuardianDecision → applyCanonicalGuardianDecision
|
|
@@ -1941,3 +1949,133 @@ describe("routing invariant: kind-specific action sets in prompt mapping", () =>
|
|
|
1941
1949
|
expect(actionIds).not.toContain("approve_conversation");
|
|
1942
1950
|
});
|
|
1943
1951
|
});
|
|
1952
|
+
|
|
1953
|
+
// ===========================================================================
|
|
1954
|
+
// SECTION 11: Surface action → access request approval routing
|
|
1955
|
+
// ===========================================================================
|
|
1956
|
+
|
|
1957
|
+
describe("routing invariant: surface action apr:* buttons route through canonical primitive", () => {
|
|
1958
|
+
beforeEach(() => resetTables());
|
|
1959
|
+
|
|
1960
|
+
test("parseCallbackData extracts requestId and action from apr: action IDs", () => {
|
|
1961
|
+
const result = parseCallbackData(
|
|
1962
|
+
"apr:access-req-test-slack-U123-1700000000:approve_once",
|
|
1963
|
+
"vellum",
|
|
1964
|
+
);
|
|
1965
|
+
expect(result).not.toBeNull();
|
|
1966
|
+
expect(result!.requestId).toBe("access-req-test-slack-U123-1700000000");
|
|
1967
|
+
expect(result!.action).toBe("approve_once");
|
|
1968
|
+
expect(result!.source).toBe("vellum_surface");
|
|
1969
|
+
});
|
|
1970
|
+
|
|
1971
|
+
test("parseCallbackData returns vellum_surface source for vellum channel", () => {
|
|
1972
|
+
const result = parseCallbackData("apr:req-1:reject", "vellum");
|
|
1973
|
+
expect(result).not.toBeNull();
|
|
1974
|
+
expect(result!.source).toBe("vellum_surface");
|
|
1975
|
+
});
|
|
1976
|
+
|
|
1977
|
+
test("processGuardianDecision approves access_request from vellum surface", async () => {
|
|
1978
|
+
const requestId = `access-req-test-slack-U123-${Date.now()}`;
|
|
1979
|
+
createCanonicalGuardianRequest({
|
|
1980
|
+
id: requestId,
|
|
1981
|
+
kind: "access_request",
|
|
1982
|
+
sourceType: "channel",
|
|
1983
|
+
sourceChannel: "slack",
|
|
1984
|
+
conversationId: "conv-surface-test",
|
|
1985
|
+
requesterExternalUserId: "U123",
|
|
1986
|
+
requesterChatId: "D456",
|
|
1987
|
+
guardianExternalUserId: "guardian-1",
|
|
1988
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
1989
|
+
toolName: "ingress_access_request",
|
|
1990
|
+
expiresAt: Date.now() + 60_000,
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
const result = await processGuardianDecision({
|
|
1994
|
+
requestId,
|
|
1995
|
+
action: "approve_once",
|
|
1996
|
+
channel: "vellum",
|
|
1997
|
+
actorContext: {
|
|
1998
|
+
actorPrincipalId: TEST_PRINCIPAL_ID,
|
|
1999
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
2000
|
+
},
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
expect(result.ok).toBe(true);
|
|
2004
|
+
if (result.ok && "applied" in result) {
|
|
2005
|
+
expect(result.applied).toBe(true);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
const resolved = getCanonicalGuardianRequest(requestId);
|
|
2009
|
+
expect(resolved!.status).toBe("approved");
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
test("processGuardianDecision denies access_request from vellum surface", async () => {
|
|
2013
|
+
const requestId = `access-req-test-slack-U789-${Date.now()}`;
|
|
2014
|
+
createCanonicalGuardianRequest({
|
|
2015
|
+
id: requestId,
|
|
2016
|
+
kind: "access_request",
|
|
2017
|
+
sourceType: "channel",
|
|
2018
|
+
sourceChannel: "slack",
|
|
2019
|
+
conversationId: "conv-surface-deny",
|
|
2020
|
+
requesterExternalUserId: "U789",
|
|
2021
|
+
requesterChatId: "D012",
|
|
2022
|
+
guardianExternalUserId: "guardian-1",
|
|
2023
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
2024
|
+
toolName: "ingress_access_request",
|
|
2025
|
+
expiresAt: Date.now() + 60_000,
|
|
2026
|
+
});
|
|
2027
|
+
|
|
2028
|
+
const result = await processGuardianDecision({
|
|
2029
|
+
requestId,
|
|
2030
|
+
action: "reject",
|
|
2031
|
+
channel: "vellum",
|
|
2032
|
+
actorContext: {
|
|
2033
|
+
actorPrincipalId: TEST_PRINCIPAL_ID,
|
|
2034
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
2035
|
+
},
|
|
2036
|
+
});
|
|
2037
|
+
|
|
2038
|
+
expect(result.ok).toBe(true);
|
|
2039
|
+
if (result.ok && "applied" in result) {
|
|
2040
|
+
expect(result.applied).toBe(true);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
const resolved = getCanonicalGuardianRequest(requestId);
|
|
2044
|
+
expect(resolved!.status).toBe("denied");
|
|
2045
|
+
});
|
|
2046
|
+
|
|
2047
|
+
test("principal mismatch rejects surface action decision", async () => {
|
|
2048
|
+
const requestId = `access-req-test-slack-U999-${Date.now()}`;
|
|
2049
|
+
createCanonicalGuardianRequest({
|
|
2050
|
+
id: requestId,
|
|
2051
|
+
kind: "access_request",
|
|
2052
|
+
sourceType: "channel",
|
|
2053
|
+
sourceChannel: "slack",
|
|
2054
|
+
conversationId: "conv-surface-mismatch",
|
|
2055
|
+
requesterExternalUserId: "U999",
|
|
2056
|
+
requesterChatId: "D345",
|
|
2057
|
+
guardianExternalUserId: "guardian-1",
|
|
2058
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
2059
|
+
toolName: "ingress_access_request",
|
|
2060
|
+
expiresAt: Date.now() + 60_000,
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
const result = await processGuardianDecision({
|
|
2064
|
+
requestId,
|
|
2065
|
+
action: "approve_once",
|
|
2066
|
+
channel: "vellum",
|
|
2067
|
+
actorContext: {
|
|
2068
|
+
actorPrincipalId: "wrong-principal",
|
|
2069
|
+
guardianPrincipalId: "wrong-principal",
|
|
2070
|
+
},
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
expect(result.ok).toBe(true);
|
|
2074
|
+
if (result.ok && "applied" in result) {
|
|
2075
|
+
expect(result.applied).toBe(false);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
const req = getCanonicalGuardianRequest(requestId);
|
|
2079
|
+
expect(req!.status).toBe("pending");
|
|
2080
|
+
});
|
|
2081
|
+
});
|
|
@@ -64,7 +64,6 @@ mock.module("../../daemon/approval-generators.js", () => ({
|
|
|
64
64
|
import type {
|
|
65
65
|
ApprovalConversationGenerator,
|
|
66
66
|
ApprovalCopyGenerator,
|
|
67
|
-
GuardianActionCopyGenerator,
|
|
68
67
|
MessageProcessor,
|
|
69
68
|
} from "../../runtime/http-types.js";
|
|
70
69
|
import {
|
|
@@ -111,7 +110,6 @@ export async function handleChannelInbound(
|
|
|
111
110
|
_assistantId?: string,
|
|
112
111
|
_approvalCopyGenerator?: ApprovalCopyGenerator,
|
|
113
112
|
_approvalConversationGenerator?: ApprovalConversationGenerator,
|
|
114
|
-
_guardianActionCopyGenerator?: GuardianActionCopyGenerator,
|
|
115
113
|
): Promise<Response> {
|
|
116
114
|
const body = await req.json();
|
|
117
115
|
return wrapHandler(() => _handleChannelInbound({ body }));
|
|
@@ -33,6 +33,10 @@ import {
|
|
|
33
33
|
} from "../memory/conversation-crud.js";
|
|
34
34
|
import { getDb } from "../memory/db-connection.js";
|
|
35
35
|
import { initializeDb } from "../memory/db-init.js";
|
|
36
|
+
import {
|
|
37
|
+
MEMORY_RETROSPECTIVE_FORK_SOURCE,
|
|
38
|
+
MEMORY_RETROSPECTIVE_INSTRUCTION_KIND,
|
|
39
|
+
} from "../memory/memory-retrospective-constants.js";
|
|
36
40
|
import { messages } from "../memory/schema.js";
|
|
37
41
|
import { handleListMessages } from "../runtime/routes/conversation-routes.js";
|
|
38
42
|
|
|
@@ -334,4 +338,99 @@ describe("handleListMessages metadata.hidden filtering", () => {
|
|
|
334
338
|
expect(latest.hasMore).toBe(false);
|
|
335
339
|
expect(latest.oldestTimestamp).not.toBeNull();
|
|
336
340
|
});
|
|
341
|
+
|
|
342
|
+
test("fork retrospective conversations expose the hidden instruction and keep the review as its own turn", async () => {
|
|
343
|
+
const conv = createConversation({
|
|
344
|
+
source: MEMORY_RETROSPECTIVE_FORK_SOURCE,
|
|
345
|
+
});
|
|
346
|
+
await addMessage(
|
|
347
|
+
conv.id,
|
|
348
|
+
"user",
|
|
349
|
+
JSON.stringify([{ type: "text", text: "source question" }]),
|
|
350
|
+
);
|
|
351
|
+
await addMessage(
|
|
352
|
+
conv.id,
|
|
353
|
+
"assistant",
|
|
354
|
+
JSON.stringify([{ type: "text", text: "source answer" }]),
|
|
355
|
+
);
|
|
356
|
+
await addMessage(
|
|
357
|
+
conv.id,
|
|
358
|
+
"user",
|
|
359
|
+
JSON.stringify([
|
|
360
|
+
{ type: "text", text: "background memory pass instruction" },
|
|
361
|
+
]),
|
|
362
|
+
{
|
|
363
|
+
metadata: { kind: MEMORY_RETROSPECTIVE_INSTRUCTION_KIND, hidden: true },
|
|
364
|
+
},
|
|
365
|
+
);
|
|
366
|
+
await addMessage(
|
|
367
|
+
conv.id,
|
|
368
|
+
"assistant",
|
|
369
|
+
JSON.stringify([{ type: "text", text: "retrospective review" }]),
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const response = handleListMessages({
|
|
373
|
+
queryParams: { conversationId: conv.id },
|
|
374
|
+
});
|
|
375
|
+
const body = response as { messages: MessagePayload[] };
|
|
376
|
+
|
|
377
|
+
// The hidden instruction is shown, and because it sits between the two
|
|
378
|
+
// assistant rows they do NOT merge — the review stands as its own turn.
|
|
379
|
+
expect(body.messages.map((m) => m.role)).toEqual([
|
|
380
|
+
"user",
|
|
381
|
+
"assistant",
|
|
382
|
+
"user",
|
|
383
|
+
"assistant",
|
|
384
|
+
]);
|
|
385
|
+
expect(plainText(body.messages[1])).toBe("source answer");
|
|
386
|
+
expect(plainText(body.messages[2])).toContain(
|
|
387
|
+
"background memory pass instruction",
|
|
388
|
+
);
|
|
389
|
+
expect(plainText(body.messages[3])).toBe("retrospective review");
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test("non-fork conversations still hide the retrospective instruction (relaxation is fork-scoped)", async () => {
|
|
393
|
+
const conv = createConversation();
|
|
394
|
+
await addMessage(
|
|
395
|
+
conv.id,
|
|
396
|
+
"user",
|
|
397
|
+
JSON.stringify([{ type: "text", text: "source question" }]),
|
|
398
|
+
);
|
|
399
|
+
await addMessage(
|
|
400
|
+
conv.id,
|
|
401
|
+
"assistant",
|
|
402
|
+
JSON.stringify([{ type: "text", text: "source answer" }]),
|
|
403
|
+
);
|
|
404
|
+
await addMessage(
|
|
405
|
+
conv.id,
|
|
406
|
+
"user",
|
|
407
|
+
JSON.stringify([
|
|
408
|
+
{ type: "text", text: "background memory pass instruction" },
|
|
409
|
+
]),
|
|
410
|
+
{
|
|
411
|
+
metadata: { kind: MEMORY_RETROSPECTIVE_INSTRUCTION_KIND, hidden: true },
|
|
412
|
+
},
|
|
413
|
+
);
|
|
414
|
+
await addMessage(
|
|
415
|
+
conv.id,
|
|
416
|
+
"assistant",
|
|
417
|
+
JSON.stringify([{ type: "text", text: "retrospective review" }]),
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const response = handleListMessages({
|
|
421
|
+
queryParams: { conversationId: conv.id },
|
|
422
|
+
});
|
|
423
|
+
const body = response as { messages: MessagePayload[] };
|
|
424
|
+
|
|
425
|
+
// Instruction stays hidden for non-retrospective conversations; the review
|
|
426
|
+
// (a normal assistant message) is still returned.
|
|
427
|
+
expect(
|
|
428
|
+
body.messages.some((m) =>
|
|
429
|
+
plainText(m).includes("background memory pass instruction"),
|
|
430
|
+
),
|
|
431
|
+
).toBe(false);
|
|
432
|
+
expect(
|
|
433
|
+
body.messages.some((m) => plainText(m).includes("retrospective review")),
|
|
434
|
+
).toBe(true);
|
|
435
|
+
});
|
|
337
436
|
});
|
|
@@ -134,7 +134,9 @@ describe("ClickHouseLlmRequestLogSource", () => {
|
|
|
134
134
|
const call = recorder[0]!;
|
|
135
135
|
const parsed = new URL(call.url);
|
|
136
136
|
expect(parsed.searchParams.get("database")).toBe("default");
|
|
137
|
-
expect(parsed.searchParams.get("param_assistant_id")).toBe(
|
|
137
|
+
expect(parsed.searchParams.get("param_assistant_id")).toBe(
|
|
138
|
+
"asst-fixture-001",
|
|
139
|
+
);
|
|
138
140
|
expect(parsed.searchParams.get("param_log_id")).toBe("log-1");
|
|
139
141
|
expect(call.init?.method).toBe("POST");
|
|
140
142
|
const auth = (call.init?.headers as Record<string, string>).Authorization;
|
|
@@ -224,6 +226,90 @@ describe("ClickHouseLlmRequestLogSource", () => {
|
|
|
224
226
|
expect(body).not.toContain(`'msg-b'`);
|
|
225
227
|
});
|
|
226
228
|
|
|
229
|
+
test("getRequestLogMetaById returns metadata without selecting either payload column", async () => {
|
|
230
|
+
// GIVEN a stored row in ClickHouse
|
|
231
|
+
const recorder: FakeFetchCall[] = [];
|
|
232
|
+
const metaRow = {
|
|
233
|
+
id: "log-1",
|
|
234
|
+
conversation_id: "conv-1",
|
|
235
|
+
message_id: "msg-1",
|
|
236
|
+
provider: "anthropic",
|
|
237
|
+
created_at: "1778465138786",
|
|
238
|
+
agent_loop_exit_reason: "no_tool_calls",
|
|
239
|
+
call_site: "compactionAgent",
|
|
240
|
+
};
|
|
241
|
+
const src = makeSource({
|
|
242
|
+
body: JSON.stringify(metaRow) + "\n",
|
|
243
|
+
recorder,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// WHEN looking it up by id
|
|
247
|
+
const row = await src.getRequestLogMetaById("log-1");
|
|
248
|
+
|
|
249
|
+
// THEN the metadata comes back without payload fields
|
|
250
|
+
expect(row).toEqual({
|
|
251
|
+
id: "log-1",
|
|
252
|
+
conversationId: "conv-1",
|
|
253
|
+
messageId: "msg-1",
|
|
254
|
+
provider: "anthropic",
|
|
255
|
+
createdAt: 1778465138786,
|
|
256
|
+
agentLoopExitReason: "no_tool_calls",
|
|
257
|
+
callSite: "compactionAgent",
|
|
258
|
+
});
|
|
259
|
+
// AND the query never selects the payload columns (a single request
|
|
260
|
+
// payload can be a full context window)
|
|
261
|
+
const body = String(recorder[0]!.init?.body ?? "");
|
|
262
|
+
expect(body).not.toContain("request_payload");
|
|
263
|
+
expect(body).not.toContain("response_payload");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("getCompactionLogsBetween computes message count in SQL and skips the request payload", async () => {
|
|
267
|
+
// GIVEN compaction rows whose message count ClickHouse computed via
|
|
268
|
+
// JSONLength (Int64 arrives as a quoted string under JSONEachRow)
|
|
269
|
+
const recorder: FakeFetchCall[] = [];
|
|
270
|
+
const compactionRow = {
|
|
271
|
+
id: "log-1",
|
|
272
|
+
conversation_id: "conv-1",
|
|
273
|
+
message_id: "",
|
|
274
|
+
provider: "anthropic",
|
|
275
|
+
response_payload: '{"bar":2}',
|
|
276
|
+
request_message_count: "42",
|
|
277
|
+
created_at: "1778465138786",
|
|
278
|
+
agent_loop_exit_reason: "",
|
|
279
|
+
call_site: "compactionAgent",
|
|
280
|
+
};
|
|
281
|
+
const src = makeSource({
|
|
282
|
+
body:
|
|
283
|
+
JSON.stringify(compactionRow) +
|
|
284
|
+
"\n" +
|
|
285
|
+
JSON.stringify({
|
|
286
|
+
...compactionRow,
|
|
287
|
+
id: "log-2",
|
|
288
|
+
request_message_count: null,
|
|
289
|
+
}) +
|
|
290
|
+
"\n",
|
|
291
|
+
recorder,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// WHEN querying the trail window
|
|
295
|
+
const rows = await src.getCompactionLogsBetween("conv-1", 100, 500);
|
|
296
|
+
|
|
297
|
+
// THEN each row carries the numeric message count (null when the
|
|
298
|
+
// payload had no known message array) and no request payload
|
|
299
|
+
expect(rows).toHaveLength(2);
|
|
300
|
+
expect(rows[0]!.requestMessageCount).toBe(42);
|
|
301
|
+
expect(rows[1]!.requestMessageCount).toBeNull();
|
|
302
|
+
expect(rows[0]!.responsePayload).toBe('{"bar":2}');
|
|
303
|
+
expect("requestPayload" in rows[0]!).toBe(false);
|
|
304
|
+
// AND the SQL only touches request_payload inside JSONLength —
|
|
305
|
+
// it is never selected as a column
|
|
306
|
+
const body = String(recorder[0]!.init?.body ?? "");
|
|
307
|
+
expect(body).toContain("JSONLength(request_payload");
|
|
308
|
+
expect(body.match(/request_payload/g)).toHaveLength(
|
|
309
|
+
body.match(/JSONLength\(request_payload/g)!.length,
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
|
|
227
313
|
test("missing clickhouse:url credential surfaces a clear error", async () => {
|
|
228
314
|
const src = new ClickHouseLlmRequestLogSource(DEFAULT_CONFIG, {
|
|
229
315
|
resolveUrl: async () => null,
|
|
@@ -341,6 +341,97 @@ describe("resolveCallSiteConfig", () => {
|
|
|
341
341
|
expect(resolved.verbosity).toBe("low");
|
|
342
342
|
});
|
|
343
343
|
|
|
344
|
+
test("forceOverrideProfile floats the override profile above site profile and callsite fields for non-main call sites", () => {
|
|
345
|
+
const llm = LLMSchema.parse({
|
|
346
|
+
default: fullDefault,
|
|
347
|
+
profiles: {
|
|
348
|
+
active: { verbosity: "low" },
|
|
349
|
+
sitep: { effort: "none", speed: "fast" },
|
|
350
|
+
forced: {
|
|
351
|
+
model: "claude-haiku-4-5",
|
|
352
|
+
effort: "high",
|
|
353
|
+
thinking: { enabled: false },
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
callSites: {
|
|
357
|
+
memoryExtraction: { profile: "sitep", maxTokens: 1000 },
|
|
358
|
+
},
|
|
359
|
+
activeProfile: "active",
|
|
360
|
+
});
|
|
361
|
+
const resolved = resolveCallSiteConfig("memoryExtraction", llm, {
|
|
362
|
+
overrideProfile: "forced",
|
|
363
|
+
forceOverrideProfile: true,
|
|
364
|
+
});
|
|
365
|
+
// Forced override wins over both the site profile and the call-site
|
|
366
|
+
// override fields it touches.
|
|
367
|
+
expect(resolved.model).toBe("claude-haiku-4-5");
|
|
368
|
+
expect(resolved.effort).toBe("high");
|
|
369
|
+
expect(resolved.thinking.enabled).toBe(false);
|
|
370
|
+
// Call-site layers still win where the forced profile is silent.
|
|
371
|
+
expect(resolved.maxTokens).toBe(1000);
|
|
372
|
+
expect(resolved.speed).toBe("fast");
|
|
373
|
+
// Active profile still applies under everything.
|
|
374
|
+
expect(resolved.verbosity).toBe("low");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("forceOverrideProfile absent leaves the override below callsite fields (unchanged precedence)", () => {
|
|
378
|
+
const llm = LLMSchema.parse({
|
|
379
|
+
default: fullDefault,
|
|
380
|
+
profiles: {
|
|
381
|
+
forced: { effort: "high" },
|
|
382
|
+
},
|
|
383
|
+
callSites: {
|
|
384
|
+
memoryExtraction: { effort: "none" },
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
const resolved = resolveCallSiteConfig("memoryExtraction", llm, {
|
|
388
|
+
overrideProfile: "forced",
|
|
389
|
+
});
|
|
390
|
+
expect(resolved.effort).toBe("none");
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("forceOverrideProfile with a missing profile reference falls through to unchanged precedence", () => {
|
|
394
|
+
const llm = LLMSchema.parse({
|
|
395
|
+
default: fullDefault,
|
|
396
|
+
profiles: {
|
|
397
|
+
sitep: { effort: "low", model: "claude-haiku-4-5" },
|
|
398
|
+
},
|
|
399
|
+
callSites: {
|
|
400
|
+
memoryExtraction: { profile: "sitep" },
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
const resolved = resolveCallSiteConfig("memoryExtraction", llm, {
|
|
404
|
+
overrideProfile: "nonexistent",
|
|
405
|
+
forceOverrideProfile: true,
|
|
406
|
+
});
|
|
407
|
+
// The missing reference is inert: site profile still wins.
|
|
408
|
+
expect(resolved.effort).toBe("low");
|
|
409
|
+
expect(resolved.model).toBe("claude-haiku-4-5");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test("forceOverrideProfile is a no-op for mainAgent (override already resolves on top)", () => {
|
|
413
|
+
const llm = LLMSchema.parse({
|
|
414
|
+
default: fullDefault,
|
|
415
|
+
profiles: {
|
|
416
|
+
active: { effort: "low" },
|
|
417
|
+
override: { effort: "high" },
|
|
418
|
+
},
|
|
419
|
+
callSites: {
|
|
420
|
+
mainAgent: { effort: "none" },
|
|
421
|
+
},
|
|
422
|
+
activeProfile: "active",
|
|
423
|
+
});
|
|
424
|
+
const withForce = resolveCallSiteConfig("mainAgent", llm, {
|
|
425
|
+
overrideProfile: "override",
|
|
426
|
+
forceOverrideProfile: true,
|
|
427
|
+
});
|
|
428
|
+
const withoutForce = resolveCallSiteConfig("mainAgent", llm, {
|
|
429
|
+
overrideProfile: "override",
|
|
430
|
+
});
|
|
431
|
+
expect(withForce).toEqual(withoutForce);
|
|
432
|
+
expect(withForce.effort).toBe("high");
|
|
433
|
+
});
|
|
434
|
+
|
|
344
435
|
test("overrideProfile absent leaves prior behavior intact", () => {
|
|
345
436
|
// No `opts` argument at all — the resolver must behave exactly as it did
|
|
346
437
|
// before this PR for configs without activeProfile/overrideProfile.
|
|
@@ -579,6 +670,30 @@ describe("resolveCallSiteConfig", () => {
|
|
|
579
670
|
expect(resolved.effort).toBe("medium");
|
|
580
671
|
});
|
|
581
672
|
|
|
673
|
+
test("workflowLeaf defaults to the cost-optimized profile", () => {
|
|
674
|
+
const llm = LLMSchema.parse({
|
|
675
|
+
default: fullDefault,
|
|
676
|
+
profiles: {
|
|
677
|
+
balanced: {
|
|
678
|
+
model: "claude-sonnet-4-7",
|
|
679
|
+
effort: "medium",
|
|
680
|
+
},
|
|
681
|
+
"cost-optimized": {
|
|
682
|
+
model: "claude-haiku-4-5-20251001",
|
|
683
|
+
effort: "low",
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
expect(resolveDefaultProfileKey("workflowLeaf", llm)).toBe(
|
|
689
|
+
"cost-optimized",
|
|
690
|
+
);
|
|
691
|
+
const resolved = resolveCallSiteConfig("workflowLeaf", llm);
|
|
692
|
+
expect(resolved.model).toBe("claude-haiku-4-5-20251001");
|
|
693
|
+
expect(resolved.effort).toBe("low");
|
|
694
|
+
expect(resolved.thinking.enabled).toBe(false);
|
|
695
|
+
});
|
|
696
|
+
|
|
582
697
|
test("explicit callSites config overrides CALL_SITE_DEFAULTS", () => {
|
|
583
698
|
const llm = LLMSchema.parse({
|
|
584
699
|
default: fullDefault,
|
|
@@ -66,6 +66,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
66
66
|
deepMergeForTest(target, overrides);
|
|
67
67
|
},
|
|
68
68
|
getConfig: () => rawConfig,
|
|
69
|
+
getDeploymentContextDefaults: () => ({}),
|
|
69
70
|
invalidateConfigCache: () => {},
|
|
70
71
|
withSuppressedConfigDiskWrites: async (fn: () => unknown) => fn(),
|
|
71
72
|
withSuppressedConfigDiskWritesSync: (fn: () => unknown) => fn(),
|
|
@@ -321,7 +322,7 @@ describe("PATCH /v1/config — managed profile deletion guard", () => {
|
|
|
321
322
|
const result = await patchRoute.handler({
|
|
322
323
|
body: { llm: { profiles: { "custom-balanced": null } } },
|
|
323
324
|
});
|
|
324
|
-
expect(result).
|
|
325
|
+
expect(result).toHaveProperty("llm");
|
|
325
326
|
});
|
|
326
327
|
|
|
327
328
|
test("allows deletion of a user-defined profile via null", async () => {
|
|
@@ -329,14 +330,14 @@ describe("PATCH /v1/config — managed profile deletion guard", () => {
|
|
|
329
330
|
const result = await patchRoute.handler({
|
|
330
331
|
body: { llm: { profiles: { "my-custom": null } } },
|
|
331
332
|
});
|
|
332
|
-
expect(result).
|
|
333
|
+
expect(result).toHaveProperty("llm");
|
|
333
334
|
});
|
|
334
335
|
|
|
335
336
|
test("allows non-profile config patches", async () => {
|
|
336
337
|
const result = await patchRoute.handler({
|
|
337
338
|
body: { someOtherKey: "value" },
|
|
338
339
|
});
|
|
339
|
-
expect(result).
|
|
340
|
+
expect(result).toHaveProperty("llm");
|
|
340
341
|
});
|
|
341
342
|
|
|
342
343
|
test("clears stale Velay ownership when manually patching public base URL", async () => {
|
|
@@ -353,7 +354,7 @@ describe("PATCH /v1/config — managed profile deletion guard", () => {
|
|
|
353
354
|
},
|
|
354
355
|
});
|
|
355
356
|
|
|
356
|
-
expect(result).
|
|
357
|
+
expect(result).toHaveProperty("ingress");
|
|
357
358
|
expect(savedRaw).toEqual({
|
|
358
359
|
ingress: {
|
|
359
360
|
publicBaseUrl: "https://manual.example.test",
|
|
@@ -371,7 +372,7 @@ describe("PATCH /v1/config — managed profile deletion guard", () => {
|
|
|
371
372
|
},
|
|
372
373
|
},
|
|
373
374
|
});
|
|
374
|
-
expect(result).
|
|
375
|
+
expect(result).toHaveProperty("llm");
|
|
375
376
|
});
|
|
376
377
|
|
|
377
378
|
test("rejects nulling the entire profiles map", async () => {
|