@vellumai/assistant 0.8.12 → 0.9.0-staging.2
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/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- 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-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- 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-data-persist.test.ts +97 -0
- 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 +219 -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__/mock-gateway-ipc.ts +23 -0
- 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__/resolve-app-id.test.ts +56 -0
- 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/bundler/package-resolver.ts +0 -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/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- 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 +167 -3
- 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 +28 -11
- 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/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -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-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- 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-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -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 +152 -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/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- 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 +91 -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
|
@@ -89,6 +89,8 @@ function makeTarget(): Conversation {
|
|
|
89
89
|
getTurnChannelContext: () => null,
|
|
90
90
|
getTurnInterfaceContext: () => null,
|
|
91
91
|
drainQueue: async () => {},
|
|
92
|
+
// Pre-run auto-compaction gate — no-op for these tests.
|
|
93
|
+
maybeCompact: async () => null,
|
|
92
94
|
};
|
|
93
95
|
return target as unknown as Conversation;
|
|
94
96
|
}
|
|
@@ -101,6 +101,8 @@ function makeTarget(): {
|
|
|
101
101
|
getTurnChannelContext: () => null,
|
|
102
102
|
getTurnInterfaceContext: () => null,
|
|
103
103
|
drainQueue: async () => {},
|
|
104
|
+
// Pre-run auto-compaction gate — no-op for these tests.
|
|
105
|
+
maybeCompact: async () => null,
|
|
104
106
|
};
|
|
105
107
|
return { target: target as unknown as Conversation, runArgs };
|
|
106
108
|
}
|
|
@@ -159,6 +161,81 @@ describe("wakeAgentForOpportunity — overrideProfile forwarding", () => {
|
|
|
159
161
|
expect(runArgs[0]!.options?.requestId).toBe("wake:scheduler");
|
|
160
162
|
});
|
|
161
163
|
|
|
164
|
+
test("forceOverrideProfile replaces the pinned-profile lookup and forwards the force flag to agentLoop.run", async () => {
|
|
165
|
+
// The conversation's own pinned profile must be ignored — the caller's
|
|
166
|
+
// forced profile (e.g. a fork-based retrospective matching the source
|
|
167
|
+
// conversation) takes its place AND floats above the call-site layers
|
|
168
|
+
// via the resolver's `forceOverrideProfile` escape hatch.
|
|
169
|
+
mockOverrideProfile = "pinned-ignored";
|
|
170
|
+
mockLlmConfig = LLMSchema.parse({
|
|
171
|
+
default: {
|
|
172
|
+
provider: "anthropic",
|
|
173
|
+
model: "claude-sonnet-4-6",
|
|
174
|
+
maxTokens: 64000,
|
|
175
|
+
contextWindow: { maxInputTokens: 200000 },
|
|
176
|
+
},
|
|
177
|
+
profiles: {
|
|
178
|
+
forced: {
|
|
179
|
+
contextWindow: { maxInputTokens: 120000 },
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
callSites: {
|
|
183
|
+
memoryRetrospective: { contextWindow: { maxInputTokens: 180000 } },
|
|
184
|
+
},
|
|
185
|
+
}) as Record<string, unknown>;
|
|
186
|
+
const { target, runArgs } = makeTarget();
|
|
187
|
+
|
|
188
|
+
const result = await wakeAgentForOpportunity(
|
|
189
|
+
{
|
|
190
|
+
conversationId: target.conversationId,
|
|
191
|
+
hint: "test hint",
|
|
192
|
+
source: "memory-retrospective",
|
|
193
|
+
callSite: "memoryRetrospective",
|
|
194
|
+
forceOverrideProfile: "forced",
|
|
195
|
+
},
|
|
196
|
+
{ resolveTarget: async () => target },
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(result.invoked).toBe(true);
|
|
200
|
+
expect(runArgs).toHaveLength(1);
|
|
201
|
+
expect(runArgs[0]!.options?.overrideProfile).toBe("forced");
|
|
202
|
+
expect(runArgs[0]!.options?.forceOverrideProfile).toBe(true);
|
|
203
|
+
expect(runArgs[0]!.options?.callSite).toBe("memoryRetrospective");
|
|
204
|
+
// The effective context window resolves under the FORCED profile, above
|
|
205
|
+
// the explicit call-site override (120k beats the call site's 180k).
|
|
206
|
+
expect(runArgs[0]!.options?.resolveContextWindow?.().maxInputTokens).toBe(
|
|
207
|
+
120000,
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("without forceOverrideProfile the wake never sets the force flag", async () => {
|
|
212
|
+
mockOverrideProfile = "frontier";
|
|
213
|
+
mockLlmConfig = LLMSchema.parse({
|
|
214
|
+
default: {
|
|
215
|
+
provider: "anthropic",
|
|
216
|
+
model: "claude-sonnet-4-6",
|
|
217
|
+
maxTokens: 64000,
|
|
218
|
+
contextWindow: { maxInputTokens: 200000 },
|
|
219
|
+
},
|
|
220
|
+
profiles: { frontier: {} },
|
|
221
|
+
callSites: { mainAgent: {} },
|
|
222
|
+
}) as Record<string, unknown>;
|
|
223
|
+
const { target, runArgs } = makeTarget();
|
|
224
|
+
|
|
225
|
+
await wakeAgentForOpportunity(
|
|
226
|
+
{
|
|
227
|
+
conversationId: target.conversationId,
|
|
228
|
+
hint: "test hint",
|
|
229
|
+
source: "scheduler",
|
|
230
|
+
},
|
|
231
|
+
{ resolveTarget: async () => target },
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(runArgs).toHaveLength(1);
|
|
235
|
+
expect(runArgs[0]!.options?.overrideProfile).toBe("frontier");
|
|
236
|
+
expect(runArgs[0]!.options?.forceOverrideProfile).toBe(false);
|
|
237
|
+
});
|
|
238
|
+
|
|
162
239
|
test("passes undefined overrideProfile when the conversation has no pinned profile, but still forwards mainAgent callSite", async () => {
|
|
163
240
|
mockOverrideProfile = undefined;
|
|
164
241
|
const { target, runArgs } = makeTarget();
|
|
@@ -450,6 +450,12 @@ describe("package-resolver", () => {
|
|
|
450
450
|
expect(ALLOWED_PACKAGES).toContain("lodash-es");
|
|
451
451
|
expect(ALLOWED_PACKAGES).toContain("zod");
|
|
452
452
|
expect(ALLOWED_PACKAGES).toContain("clsx");
|
|
453
|
-
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// `lucide` (the vanilla package) unpacks to ~29 MB, far above the resolver's
|
|
456
|
+
// size cap, so it is installed then deleted and never resolves. It also
|
|
457
|
+
// exports icon data arrays, not Preact components. Apps use inline SVG.
|
|
458
|
+
test("ALLOWED_PACKAGES excludes lucide", () => {
|
|
459
|
+
expect(ALLOWED_PACKAGES).not.toContain("lucide");
|
|
454
460
|
});
|
|
455
461
|
});
|
|
@@ -20,6 +20,7 @@ const ALLOWLIST = new Set([
|
|
|
20
20
|
"assistant/src/memory/app-git-service.ts", // uses getAppsDir for git repo root, not per-app paths
|
|
21
21
|
"assistant/src/daemon/app-source-watcher.ts", // uses getAppsDir for recursive fs.watch root, not per-app paths
|
|
22
22
|
"assistant/src/tools/filesystem/write.ts", // uses getAppsDir as an exemption root for the artifact-HTML guard, not per-app paths
|
|
23
|
+
"assistant/src/memory/preloaded-apps.ts", // seeds fixed-slug apps (id === dirName by construction) and writes the definition JSON, which app-store has no fixed-id helper for
|
|
23
24
|
]);
|
|
24
25
|
|
|
25
26
|
function isTestFile(filePath: string): boolean {
|
|
@@ -37,12 +38,15 @@ function isMigrationFile(filePath: string): boolean {
|
|
|
37
38
|
|
|
38
39
|
describe("app directory path construction guard", () => {
|
|
39
40
|
test("no non-allowlisted production files import getAppsDir", () => {
|
|
40
|
-
//
|
|
41
|
+
// Match files that import getAppsDir or call it, but not files that only
|
|
42
|
+
// mention it in a comment (a doc comment referencing the helper by name is
|
|
43
|
+
// not a usage). `-n` yields `file:line:content` so comment-only lines can
|
|
44
|
+
// be filtered out before collapsing to the set of matching files.
|
|
41
45
|
const pattern = "import.*getAppsDir.*from|getAppsDir\\(\\)";
|
|
42
46
|
|
|
43
47
|
let grepOutput = "";
|
|
44
48
|
try {
|
|
45
|
-
grepOutput = execSync(`git grep -
|
|
49
|
+
grepOutput = execSync(`git grep -nE '${pattern}' -- '*.ts'`, {
|
|
46
50
|
encoding: "utf-8",
|
|
47
51
|
cwd: process.cwd() + "/..",
|
|
48
52
|
}).trim();
|
|
@@ -54,7 +58,27 @@ describe("app directory path construction guard", () => {
|
|
|
54
58
|
throw err;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
const
|
|
61
|
+
const isCommentLine = (line: string): boolean => {
|
|
62
|
+
const trimmed = line.trim();
|
|
63
|
+
return (
|
|
64
|
+
trimmed.startsWith("//") ||
|
|
65
|
+
trimmed.startsWith("*") ||
|
|
66
|
+
trimmed.startsWith("/*")
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
const files = Array.from(
|
|
70
|
+
new Set(
|
|
71
|
+
grepOutput
|
|
72
|
+
.split("\n")
|
|
73
|
+
.filter((line) => line.length > 0)
|
|
74
|
+
.map((line) => {
|
|
75
|
+
const [file, , ...rest] = line.split(":");
|
|
76
|
+
return { file, content: rest.join(":") };
|
|
77
|
+
})
|
|
78
|
+
.filter(({ content }) => !isCommentLine(content))
|
|
79
|
+
.map(({ file }) => file),
|
|
80
|
+
),
|
|
81
|
+
);
|
|
58
82
|
const violations = files.filter((f) => {
|
|
59
83
|
if (isTestFile(f)) return false;
|
|
60
84
|
if (isMigrationFile(f)) return false;
|
|
@@ -168,6 +168,49 @@ describe("executeAppCreate", () => {
|
|
|
168
168
|
expect(parsed.next_steps).toContain("app_refresh");
|
|
169
169
|
});
|
|
170
170
|
|
|
171
|
+
test("associates the new app with its conversation when a conversationId is given", async () => {
|
|
172
|
+
const app = makeMultifileApp({ id: "app-xyz", name: "Assoc App" });
|
|
173
|
+
const associated: Array<{ appId: string; conversationId: string }> = [];
|
|
174
|
+
const store: AppStore = {
|
|
175
|
+
...mockStore(app, {}),
|
|
176
|
+
addAppConversationId: (appId, conversationId) => {
|
|
177
|
+
associated.push({ appId, conversationId });
|
|
178
|
+
return true;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = await executeAppCreate(
|
|
183
|
+
{ name: "Assoc App" },
|
|
184
|
+
store,
|
|
185
|
+
undefined,
|
|
186
|
+
"conv-assoc-1",
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
expect(result.isError).toBe(false);
|
|
190
|
+
expect(associated).toEqual([
|
|
191
|
+
{ appId: "app-xyz", conversationId: "conv-assoc-1" },
|
|
192
|
+
]);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("a failed conversation association does not fail the create", async () => {
|
|
196
|
+
const app = makeMultifileApp({ id: "app-throw", name: "Throw App" });
|
|
197
|
+
const store: AppStore = {
|
|
198
|
+
...mockStore(app, {}),
|
|
199
|
+
addAppConversationId: () => {
|
|
200
|
+
throw new Error("disk gone");
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const result = await executeAppCreate(
|
|
205
|
+
{ name: "Throw App" },
|
|
206
|
+
store,
|
|
207
|
+
undefined,
|
|
208
|
+
"conv-assoc-2",
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
expect(result.isError).toBe(false);
|
|
212
|
+
});
|
|
213
|
+
|
|
171
214
|
test("skips auto_open on scaffold even when proxy resolver is available", async () => {
|
|
172
215
|
const files: Record<string, string> = {};
|
|
173
216
|
const app = makeMultifileApp({ name: "New App" });
|
|
@@ -40,11 +40,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
40
40
|
getLogger: () => makeLoggerStub(),
|
|
41
41
|
}));
|
|
42
42
|
|
|
43
|
-
mock.module("../memory/guardian-action-store.js", () => ({
|
|
44
|
-
getGuardianActionRequest: () => null,
|
|
45
|
-
resolveGuardianActionRequest: () => {},
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
43
|
mock.module("../providers/registry.js", () => ({
|
|
49
44
|
getProvider: () => ({ name: "mock-provider" }),
|
|
50
45
|
initializeProviders: async () => {},
|
|
@@ -8,6 +8,7 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
8
8
|
|
|
9
9
|
import type { Conversation } from "../daemon/conversation.js";
|
|
10
10
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
11
|
+
import type { SecretPromptResult } from "../permissions/secret-prompter.js";
|
|
11
12
|
|
|
12
13
|
mock.module("../util/logger.js", () => ({
|
|
13
14
|
getLogger: () =>
|
|
@@ -196,6 +197,7 @@ function makeIdleSession(opts?: {
|
|
|
196
197
|
pendingInteractions.resolve(requestId);
|
|
197
198
|
opts?.onConfirmation?.(requestId, decision);
|
|
198
199
|
},
|
|
200
|
+
hasPendingSecret: () => true,
|
|
199
201
|
handleSecretResponse: (
|
|
200
202
|
requestId: string,
|
|
201
203
|
value?: string,
|
|
@@ -593,6 +595,54 @@ describe("standalone approval endpoints — HTTP layer", () => {
|
|
|
593
595
|
await stopServer();
|
|
594
596
|
});
|
|
595
597
|
|
|
598
|
+
test("resolves a conversation-less secret request via its resolver", async () => {
|
|
599
|
+
/**
|
|
600
|
+
* The CLI `credentials prompt` command registers a conversation-less
|
|
601
|
+
* secret interaction whose resolver lives in pendingInteractions. POST
|
|
602
|
+
* /v1/secret must drive that resolver directly — with no owning
|
|
603
|
+
* conversation — so the standalone prompt completes instead of only
|
|
604
|
+
* timing out.
|
|
605
|
+
*/
|
|
606
|
+
// GIVEN a running server
|
|
607
|
+
await startServer(() => makeIdleSession());
|
|
608
|
+
|
|
609
|
+
// AND a conversation-less secret interaction whose resolver is captured
|
|
610
|
+
let resolved: SecretPromptResult | undefined;
|
|
611
|
+
pendingInteractions.register("standalone-secret-1", {
|
|
612
|
+
kind: "secret",
|
|
613
|
+
rpcResolve: (value: unknown) => {
|
|
614
|
+
resolved = value as SecretPromptResult;
|
|
615
|
+
},
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// WHEN the secret is submitted for that requestId
|
|
619
|
+
const res = await fetch(url("secret"), {
|
|
620
|
+
method: "POST",
|
|
621
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
622
|
+
body: JSON.stringify({
|
|
623
|
+
requestId: "standalone-secret-1",
|
|
624
|
+
value: "cli-secret-value",
|
|
625
|
+
delivery: "store",
|
|
626
|
+
}),
|
|
627
|
+
});
|
|
628
|
+
const body = (await res.json()) as { accepted: boolean };
|
|
629
|
+
|
|
630
|
+
// THEN the request is accepted
|
|
631
|
+
expect(res.status).toBe(200);
|
|
632
|
+
expect(body.accepted).toBe(true);
|
|
633
|
+
|
|
634
|
+
// AND the standalone resolver receives the value and delivery
|
|
635
|
+
expect(resolved).toEqual({
|
|
636
|
+
value: "cli-secret-value",
|
|
637
|
+
delivery: "store",
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
// AND the interaction is removed after resolution
|
|
641
|
+
expect(pendingInteractions.get("standalone-secret-1")).toBeUndefined();
|
|
642
|
+
|
|
643
|
+
await stopServer();
|
|
644
|
+
});
|
|
645
|
+
|
|
596
646
|
test("returns 404 for unknown requestId", async () => {
|
|
597
647
|
await startServer(() => makeIdleSession());
|
|
598
648
|
|
|
@@ -638,6 +688,47 @@ describe("standalone approval endpoints — HTTP layer", () => {
|
|
|
638
688
|
|
|
639
689
|
await stopServer();
|
|
640
690
|
});
|
|
691
|
+
|
|
692
|
+
test("rejects a non-secret requestId without consuming it", async () => {
|
|
693
|
+
/**
|
|
694
|
+
* /v1/secret only settles secret prompts. A confirmation (or any other
|
|
695
|
+
* interaction kind) posted here from stale client state must be rejected
|
|
696
|
+
* so its real approval endpoint still finds an intact pending interaction
|
|
697
|
+
* rather than one consumed and resolved with a SecretPromptResult.
|
|
698
|
+
*/
|
|
699
|
+
// GIVEN a running server
|
|
700
|
+
await startServer(() => makeIdleSession());
|
|
701
|
+
|
|
702
|
+
// AND a pending confirmation interaction whose resolver would be corrupted
|
|
703
|
+
let resolverCalled = false;
|
|
704
|
+
pendingInteractions.register("confirm-not-secret", {
|
|
705
|
+
conversationId: "conv-1",
|
|
706
|
+
kind: "confirmation",
|
|
707
|
+
rpcResolve: () => {
|
|
708
|
+
resolverCalled = true;
|
|
709
|
+
},
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
// WHEN that confirmation's requestId is posted to /v1/secret
|
|
713
|
+
const res = await fetch(url("secret"), {
|
|
714
|
+
method: "POST",
|
|
715
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
716
|
+
body: JSON.stringify({
|
|
717
|
+
requestId: "confirm-not-secret",
|
|
718
|
+
value: "leaked",
|
|
719
|
+
delivery: "store",
|
|
720
|
+
}),
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
// THEN the request is rejected
|
|
724
|
+
expect(res.status).toBe(404);
|
|
725
|
+
|
|
726
|
+
// AND the confirmation interaction is left intact and unresolved
|
|
727
|
+
expect(pendingInteractions.get("confirm-not-secret")).toBeDefined();
|
|
728
|
+
expect(resolverCalled).toBe(false);
|
|
729
|
+
|
|
730
|
+
await stopServer();
|
|
731
|
+
});
|
|
641
732
|
});
|
|
642
733
|
|
|
643
734
|
// ── POST /v1/trust-rules ─────────────────────────────────────────────
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
1
3
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
4
|
|
|
3
5
|
import type { AssistantEvent } from "../runtime/assistant-event.js";
|
|
@@ -8,6 +10,7 @@ import type {
|
|
|
8
10
|
import {
|
|
9
11
|
_peekStreamForTesting,
|
|
10
12
|
_resetStreamStateForTesting,
|
|
13
|
+
_simulateRestartForTesting,
|
|
11
14
|
getCurrentSeq,
|
|
12
15
|
getPersistedSeq,
|
|
13
16
|
getReplayWindow,
|
|
@@ -642,4 +645,108 @@ describe("assistant-stream-state", () => {
|
|
|
642
645
|
expect(getPersistedSeq("conv_1")).toBeNull();
|
|
643
646
|
});
|
|
644
647
|
});
|
|
648
|
+
|
|
649
|
+
describe("seq persistence across restarts", () => {
|
|
650
|
+
test("counter resumes above the persisted reservation after a restart", () => {
|
|
651
|
+
// GIVEN a process that stamped events (reserving a seq block on disk)
|
|
652
|
+
const a = mkEvent();
|
|
653
|
+
stampAndBuffer(a);
|
|
654
|
+
expect(a.seq).toBe(1);
|
|
655
|
+
|
|
656
|
+
// WHEN the daemon restarts
|
|
657
|
+
_simulateRestartForTesting();
|
|
658
|
+
|
|
659
|
+
// THEN the next stamp resumes above the reserved block instead of 1,
|
|
660
|
+
// so clients never observe the counter moving backwards.
|
|
661
|
+
const b = mkEvent();
|
|
662
|
+
stampAndBuffer(b);
|
|
663
|
+
expect(b.seq).toBe(1025);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
test("repeated restarts keep advancing monotonically", () => {
|
|
667
|
+
stampAndBuffer(mkEvent());
|
|
668
|
+
_simulateRestartForTesting();
|
|
669
|
+
const a = mkEvent();
|
|
670
|
+
stampAndBuffer(a);
|
|
671
|
+
_simulateRestartForTesting();
|
|
672
|
+
const b = mkEvent();
|
|
673
|
+
stampAndBuffer(b);
|
|
674
|
+
expect(a.seq).toBe(1025);
|
|
675
|
+
expect(b.seq).toBe(2049);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test("stamping within a reserved block does not advance the persisted ceiling", () => {
|
|
679
|
+
// GIVEN many stamps within one block
|
|
680
|
+
for (let i = 0; i < 100; i++) stampAndBuffer(mkEvent());
|
|
681
|
+
|
|
682
|
+
// WHEN the daemon restarts
|
|
683
|
+
_simulateRestartForTesting();
|
|
684
|
+
|
|
685
|
+
// THEN the resume point is the block ceiling, not per-event state.
|
|
686
|
+
const a = mkEvent();
|
|
687
|
+
stampAndBuffer(a);
|
|
688
|
+
expect(a.seq).toBe(1025);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
test("a corrupt reservation file degrades to a cold start", () => {
|
|
692
|
+
const path = join(
|
|
693
|
+
process.env.VELLUM_WORKSPACE_DIR!,
|
|
694
|
+
"data",
|
|
695
|
+
"stream-seq.json",
|
|
696
|
+
);
|
|
697
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
698
|
+
writeFileSync(path, "not json");
|
|
699
|
+
|
|
700
|
+
_simulateRestartForTesting();
|
|
701
|
+
const a = mkEvent();
|
|
702
|
+
stampAndBuffer(a);
|
|
703
|
+
expect(a.seq).toBe(1);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
test("a pre-restart cursor replays the post-restart events (reservation gap is not an eviction)", () => {
|
|
707
|
+
// GIVEN a client that saw seq 1 before the daemon restarted
|
|
708
|
+
stampAndBuffer(mkEvent());
|
|
709
|
+
_simulateRestartForTesting();
|
|
710
|
+
|
|
711
|
+
// AND the restarted daemon has buffered new events
|
|
712
|
+
const a = mkEvent();
|
|
713
|
+
const b = mkEvent();
|
|
714
|
+
stampAndBuffer(a);
|
|
715
|
+
stampAndBuffer(b);
|
|
716
|
+
expect(a.seq).toBe(1025);
|
|
717
|
+
|
|
718
|
+
// WHEN the client reconnects with its pre-restart cursor
|
|
719
|
+
const window = getReplayWindow(1);
|
|
720
|
+
|
|
721
|
+
// THEN the post-restart events are replayed rather than treated as
|
|
722
|
+
// an out-of-window gap (no events below the reservation ever existed
|
|
723
|
+
// in this process, so nothing replayable is missing).
|
|
724
|
+
expect(window).not.toBeNull();
|
|
725
|
+
expect(window?.map((e) => e.seq)).toEqual([1025, 1026]);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
test("a pre-restart cursor still gets the snapshot fallback once post-restart events evict", () => {
|
|
729
|
+
stampAndBuffer(mkEvent());
|
|
730
|
+
_simulateRestartForTesting();
|
|
731
|
+
|
|
732
|
+
// Stamp past the 200-event ring cap so the restarted process's
|
|
733
|
+
// earliest events are genuinely evicted.
|
|
734
|
+
for (let i = 0; i < 205; i++) stampAndBuffer(mkEvent());
|
|
735
|
+
|
|
736
|
+
// The gap now includes evicted post-restart events, so replay must
|
|
737
|
+
// signal the snapshot fallback.
|
|
738
|
+
expect(getReplayWindow(1)).toBeNull();
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
test("a missing reservation file is a cold start at 1", () => {
|
|
742
|
+
rmSync(
|
|
743
|
+
join(process.env.VELLUM_WORKSPACE_DIR!, "data", "stream-seq.json"),
|
|
744
|
+
{ force: true },
|
|
745
|
+
);
|
|
746
|
+
_simulateRestartForTesting();
|
|
747
|
+
const a = mkEvent();
|
|
748
|
+
stampAndBuffer(a);
|
|
749
|
+
expect(a.seq).toBe(1);
|
|
750
|
+
});
|
|
751
|
+
});
|
|
645
752
|
});
|
|
@@ -319,7 +319,7 @@ describe("executeBrowserFillCredential", () => {
|
|
|
319
319
|
);
|
|
320
320
|
expect(result.isError).toBe(true);
|
|
321
321
|
expect(result.content).toContain("No credential stored for slack/api_key");
|
|
322
|
-
expect(result.content).toContain("
|
|
322
|
+
expect(result.content).toContain("assistant credentials set");
|
|
323
323
|
// The broker short-circuits before DOM.focus is dispatched.
|
|
324
324
|
const methods = sendCalls.map((c) => c.method);
|
|
325
325
|
expect(methods).not.toContain("DOM.focus");
|
|
@@ -335,7 +335,7 @@ describe("executeBrowserFillCredential", () => {
|
|
|
335
335
|
);
|
|
336
336
|
expect(result.isError).toBe(true);
|
|
337
337
|
expect(result.content).toContain("No credential stored for slack/api_key");
|
|
338
|
-
expect(result.content).toContain("
|
|
338
|
+
expect(result.content).toContain("assistant credentials set");
|
|
339
339
|
const methods = sendCalls.map((c) => c.method);
|
|
340
340
|
expect(methods).not.toContain("Input.insertText");
|
|
341
341
|
});
|
|
@@ -465,7 +465,7 @@ describe("executeBrowserFillCredential", () => {
|
|
|
465
465
|
expect(result.isError).toBe(true);
|
|
466
466
|
expect(result.content).toContain("Policy denied");
|
|
467
467
|
expect(result.content).toContain("not allowed to use credential");
|
|
468
|
-
expect(result.content).toContain("
|
|
468
|
+
expect(result.content).toContain("assistant credentials set");
|
|
469
469
|
// The broker short-circuits before Input.insertText fires.
|
|
470
470
|
const methods = sendCalls.map((c) => c.method);
|
|
471
471
|
expect(methods).not.toContain("Input.insertText");
|
|
@@ -179,7 +179,7 @@ describe("bundled skill retrieval guard", () => {
|
|
|
179
179
|
if (violations.length > 0) {
|
|
180
180
|
const message = [
|
|
181
181
|
"Direct credential lookup instructions were found in skills.",
|
|
182
|
-
"Use
|
|
182
|
+
"Use the `assistant credentials` CLI and proxied flows instead.",
|
|
183
183
|
"",
|
|
184
184
|
"Violations:",
|
|
185
185
|
...violations.map((v) => ` - ${v}`),
|
|
@@ -41,11 +41,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
41
41
|
getLogger: () => makeLoggerStub(),
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
|
-
mock.module("../memory/guardian-action-store.js", () => ({
|
|
45
|
-
getGuardianActionRequest: () => null,
|
|
46
|
-
resolveGuardianActionRequest: () => {},
|
|
47
|
-
}));
|
|
48
|
-
|
|
49
44
|
mock.module("../providers/registry.js", () => ({
|
|
50
45
|
getProvider: () => ({ name: "mock-provider" }),
|
|
51
46
|
initializeProviders: async () => {},
|
|
@@ -79,7 +74,14 @@ mock.module("../config/loader.js", () => ({
|
|
|
79
74
|
},
|
|
80
75
|
},
|
|
81
76
|
profiles: {},
|
|
82
|
-
callSites: {
|
|
77
|
+
callSites: {
|
|
78
|
+
// Resolves a SMALLER window than mainAgent (which inherits
|
|
79
|
+
// llm.default's 100000) — exercised by the maybeCompact gate-sizing
|
|
80
|
+
// tests below.
|
|
81
|
+
memoryRetrospective: {
|
|
82
|
+
contextWindow: { maxInputTokens: 50000 },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
83
85
|
pricingOverrides: [],
|
|
84
86
|
},
|
|
85
87
|
rateLimit: { maxRequestsPerMinute: 0 },
|
|
@@ -187,11 +189,18 @@ let mockCompactResult: ContextWindowResult = {
|
|
|
187
189
|
summaryText: "",
|
|
188
190
|
};
|
|
189
191
|
|
|
192
|
+
// Config payloads handed to the manager's updateConfig — runCompaction calls
|
|
193
|
+
// it with the (possibly sizing-threaded) resolved context-window config
|
|
194
|
+
// before delegating to the manager.
|
|
195
|
+
const updateConfigCalls: Array<{ maxInputTokens?: number }> = [];
|
|
196
|
+
|
|
190
197
|
mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
191
198
|
ContextWindowManager: class {
|
|
192
199
|
nonPersistedPrefixCount = 0;
|
|
193
200
|
constructor() {}
|
|
194
|
-
updateConfig() {
|
|
201
|
+
updateConfig(cfg: { maxInputTokens?: number }) {
|
|
202
|
+
updateConfigCalls.push(cfg);
|
|
203
|
+
}
|
|
195
204
|
shouldCompact() {
|
|
196
205
|
return { needed: false, estimatedTokens: 0 };
|
|
197
206
|
}
|
|
@@ -433,6 +442,53 @@ describe("forceCompact event emission", () => {
|
|
|
433
442
|
});
|
|
434
443
|
});
|
|
435
444
|
|
|
445
|
+
describe("maybeCompact gate sizing", () => {
|
|
446
|
+
test("default sizing resolves mainAgent's window; wake sizing resolves the wake's call-site window", async () => {
|
|
447
|
+
// The auto-threshold gate derives its trip point from the
|
|
448
|
+
// context-window config pushed via updateConfig. Sized against
|
|
449
|
+
// mainAgent (100k here), a wake whose call site resolves a smaller
|
|
450
|
+
// window (memoryRetrospective → 50k) would pass the gate un-compacted
|
|
451
|
+
// and then overflow at the provider — threading the sizing makes the
|
|
452
|
+
// gate see the 50k window instead.
|
|
453
|
+
mockCompactResult = {
|
|
454
|
+
messages: [],
|
|
455
|
+
compacted: false,
|
|
456
|
+
previousEstimatedInputTokens: 0,
|
|
457
|
+
estimatedInputTokens: 0,
|
|
458
|
+
maxInputTokens: 0,
|
|
459
|
+
thresholdTokens: 0,
|
|
460
|
+
compactedMessages: 0,
|
|
461
|
+
compactedPersistedMessages: 0,
|
|
462
|
+
summaryCalls: 0,
|
|
463
|
+
summaryInputTokens: 0,
|
|
464
|
+
summaryOutputTokens: 0,
|
|
465
|
+
summaryModel: "",
|
|
466
|
+
summaryText: "",
|
|
467
|
+
};
|
|
468
|
+
const conversation = makeConversation([], "conv-compact-sizing");
|
|
469
|
+
|
|
470
|
+
updateConfigCalls.length = 0;
|
|
471
|
+
await conversation.maybeCompact();
|
|
472
|
+
await conversation.maybeCompact({ callSite: "memoryRetrospective" });
|
|
473
|
+
|
|
474
|
+
expect(updateConfigCalls.map((cfg) => cfg.maxInputTokens)).toEqual([
|
|
475
|
+
100000, 50000,
|
|
476
|
+
]);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test("forceCompact keeps mainAgent sizing", async () => {
|
|
480
|
+
mockCompactResult = { ...mockCompactResult, compacted: false };
|
|
481
|
+
const conversation = makeConversation([], "conv-compact-force-sizing");
|
|
482
|
+
|
|
483
|
+
updateConfigCalls.length = 0;
|
|
484
|
+
await conversation.forceCompact();
|
|
485
|
+
|
|
486
|
+
expect(updateConfigCalls.map((cfg) => cfg.maxInputTokens)).toEqual([
|
|
487
|
+
100000,
|
|
488
|
+
]);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
436
492
|
// ---------------------------------------------------------------------------
|
|
437
493
|
// computeSummaryQualitySignals — imported lazily after mocks are installed so
|
|
438
494
|
// the logger stub and other module replacements take effect first.
|