@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
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* double typed as `Conversation` (`makeWakeConversation`) that stubs only the
|
|
8
8
|
* handful of members the wake touches — `getMessages`, `messages.push`,
|
|
9
9
|
* `isProcessing`/`setProcessing`, `setTrustContext`, `setSubagentAllowedTools`,
|
|
10
|
-
* `drainQueue`,
|
|
10
|
+
* `drainQueue`, `maybeCompact`, `contextWindowManager.estimateInputTokens`,
|
|
11
|
+
* and a scripted `agentLoop.run()`.
|
|
11
12
|
*
|
|
12
13
|
* The wake's side effects flow through the daemon boundary, so the
|
|
13
14
|
* instrumentation is captured at that boundary: event emission and the
|
|
@@ -49,6 +50,12 @@ interface WakeConversationProbe {
|
|
|
49
50
|
requestId?: string;
|
|
50
51
|
trust?: unknown;
|
|
51
52
|
allowedTools?: string[];
|
|
53
|
+
/**
|
|
54
|
+
* `conversation.wakePersonaOverride` as observed at run start — the
|
|
55
|
+
* field the conversation's resolveSystemPrompt callback reads when
|
|
56
|
+
* building the wake's system prompt.
|
|
57
|
+
*/
|
|
58
|
+
personaOverride?: unknown;
|
|
52
59
|
order: number;
|
|
53
60
|
}>;
|
|
54
61
|
/** Every `setProcessing` value, in call order. */
|
|
@@ -76,8 +83,27 @@ interface WakeConversationProbe {
|
|
|
76
83
|
allowedToolSnapshots: Array<string[] | undefined>;
|
|
77
84
|
/** `setTrustContext` calls, with the value and a monotonic order tag. */
|
|
78
85
|
setTrustContextCalls: Array<{ ctx: unknown; order: number }>;
|
|
86
|
+
/**
|
|
87
|
+
* Every assignment to `conversation.wakePersonaOverride`, in order. A
|
|
88
|
+
* wake that applies an override records `[override, undefined]` — the
|
|
89
|
+
* trailing `undefined` proves the wake restored the field before
|
|
90
|
+
* releasing the conversation.
|
|
91
|
+
*/
|
|
92
|
+
personaOverrideSets: unknown[];
|
|
79
93
|
/** Number of persisted tail messages at the moment each frame emitted. */
|
|
80
94
|
persistedAtEachEmit: number[];
|
|
95
|
+
/**
|
|
96
|
+
* `conversation.maybeCompact()` invocations (the wake's pre-run
|
|
97
|
+
* auto-compaction gate), tagged with the same monotonic order counter as
|
|
98
|
+
* `runCalls` so tests can prove the gate ran before the agent loop.
|
|
99
|
+
*/
|
|
100
|
+
maybeCompactOrders: number[];
|
|
101
|
+
/**
|
|
102
|
+
* The sizing argument passed to each `conversation.maybeCompact()` call —
|
|
103
|
+
* the wake threads its resolved callSite/profile so the gate's threshold
|
|
104
|
+
* sizes against the wake's window instead of mainAgent's.
|
|
105
|
+
*/
|
|
106
|
+
maybeCompactSizings: unknown[];
|
|
81
107
|
}
|
|
82
108
|
|
|
83
109
|
const wakeConvRegistry = new Map<string, WakeConversationProbe>();
|
|
@@ -89,8 +115,15 @@ const wakeConvRegistry = new Map<string, WakeConversationProbe>();
|
|
|
89
115
|
// `persistWakeTailMessage` (createdAt for the disk-view sync). `addMessage`
|
|
90
116
|
// is the persistence boundary the wake's tail persistence flows through —
|
|
91
117
|
// it records into the originating conversation's probe.
|
|
118
|
+
// Overridable per test (e.g. to simulate a throwing DB read); reset in
|
|
119
|
+
// beforeEach.
|
|
120
|
+
let mockGetConversationOverrideProfile: (
|
|
121
|
+
conversationId: string,
|
|
122
|
+
) => string | undefined = () => undefined;
|
|
123
|
+
|
|
92
124
|
mock.module("../../memory/conversation-crud.js", () => ({
|
|
93
|
-
getConversationOverrideProfile: () =>
|
|
125
|
+
getConversationOverrideProfile: (conversationId: string) =>
|
|
126
|
+
mockGetConversationOverrideProfile(conversationId),
|
|
94
127
|
getConversation: () => ({
|
|
95
128
|
archivedAt: null,
|
|
96
129
|
createdAt: "2026-01-01T00:00:00.000Z",
|
|
@@ -209,6 +242,7 @@ const recordRequestLogCalls: Array<{
|
|
|
209
242
|
responsePayload: string;
|
|
210
243
|
messageId?: string;
|
|
211
244
|
provider?: string;
|
|
245
|
+
callSite?: string;
|
|
212
246
|
}> = [];
|
|
213
247
|
mock.module("../../memory/llm-request-log-store.js", () => ({
|
|
214
248
|
recordRequestLog: (
|
|
@@ -217,6 +251,7 @@ mock.module("../../memory/llm-request-log-store.js", () => ({
|
|
|
217
251
|
responsePayload: string,
|
|
218
252
|
messageId?: string,
|
|
219
253
|
provider?: string,
|
|
254
|
+
callSite?: string,
|
|
220
255
|
) => {
|
|
221
256
|
recordRequestLogCalls.push({
|
|
222
257
|
conversationId,
|
|
@@ -224,6 +259,7 @@ mock.module("../../memory/llm-request-log-store.js", () => ({
|
|
|
224
259
|
responsePayload,
|
|
225
260
|
messageId,
|
|
226
261
|
provider,
|
|
262
|
+
callSite,
|
|
227
263
|
});
|
|
228
264
|
return "log-id-test";
|
|
229
265
|
},
|
|
@@ -236,7 +272,7 @@ import type {
|
|
|
236
272
|
AgentLoopRunResult,
|
|
237
273
|
} from "../../agent/loop.js";
|
|
238
274
|
import type { Conversation } from "../../daemon/conversation.js";
|
|
239
|
-
import type
|
|
275
|
+
import { ContextOverflowError, type Message } from "../../providers/types.js";
|
|
240
276
|
import {
|
|
241
277
|
__resetWakeChainForTests,
|
|
242
278
|
wakeAgentForOpportunity,
|
|
@@ -276,6 +312,13 @@ function makeWakeConversation(options: {
|
|
|
276
312
|
initialAllowedTools?: Set<string>;
|
|
277
313
|
/** Replaces the default scripted `agentLoop.run` body entirely. */
|
|
278
314
|
runImpl?: ScriptedRun;
|
|
315
|
+
/**
|
|
316
|
+
* Token estimate returned by the double's
|
|
317
|
+
* `contextWindowManager.estimateInputTokens` (consumed by the wake's
|
|
318
|
+
* over-window pre-flight on compaction-suppressed wakes). Defaults to 0
|
|
319
|
+
* (always under the mocked 200k window).
|
|
320
|
+
*/
|
|
321
|
+
estimatedInputTokens?: number;
|
|
279
322
|
}): WakeConversation {
|
|
280
323
|
const conversationId = options.conversationId ?? "conv-test";
|
|
281
324
|
const probe: WakeConversationProbe = {
|
|
@@ -290,13 +333,17 @@ function makeWakeConversation(options: {
|
|
|
290
333
|
processingDuringDrain: [],
|
|
291
334
|
allowedToolSnapshots: [],
|
|
292
335
|
setTrustContextCalls: [],
|
|
336
|
+
personaOverrideSets: [],
|
|
293
337
|
persistedAtEachEmit: [],
|
|
338
|
+
maybeCompactOrders: [],
|
|
339
|
+
maybeCompactSizings: [],
|
|
294
340
|
};
|
|
295
341
|
wakeConvRegistry.set(conversationId, probe);
|
|
296
342
|
|
|
297
343
|
let processing = options.isProcessing ?? false;
|
|
298
344
|
let order = 0;
|
|
299
345
|
let activeAllowedTools = options.initialAllowedTools;
|
|
346
|
+
let wakePersonaOverride: unknown;
|
|
300
347
|
const snapshotAllowedTools = (): string[] | undefined =>
|
|
301
348
|
activeAllowedTools ? [...activeAllowedTools].sort() : undefined;
|
|
302
349
|
|
|
@@ -359,6 +406,13 @@ function makeWakeConversation(options: {
|
|
|
359
406
|
`tools:${snapshotAllowedTools()?.join(",") ?? "all"}`,
|
|
360
407
|
);
|
|
361
408
|
},
|
|
409
|
+
get wakePersonaOverride() {
|
|
410
|
+
return wakePersonaOverride;
|
|
411
|
+
},
|
|
412
|
+
set wakePersonaOverride(value: unknown) {
|
|
413
|
+
wakePersonaOverride = value;
|
|
414
|
+
probe.personaOverrideSets.push(value);
|
|
415
|
+
},
|
|
362
416
|
agentLoop: {
|
|
363
417
|
run: async (options: AgentLoopRunOptions) => {
|
|
364
418
|
const { messages: input, onEvent } = options;
|
|
@@ -367,6 +421,7 @@ function makeWakeConversation(options: {
|
|
|
367
421
|
requestId: options.requestId,
|
|
368
422
|
trust: options.trust,
|
|
369
423
|
allowedTools: snapshotAllowedTools(),
|
|
424
|
+
personaOverride: wakePersonaOverride,
|
|
370
425
|
order: order++,
|
|
371
426
|
});
|
|
372
427
|
return runBody(input, onEvent, options);
|
|
@@ -383,6 +438,19 @@ function makeWakeConversation(options: {
|
|
|
383
438
|
setTrustContext: (ctx: unknown) => {
|
|
384
439
|
probe.setTrustContextCalls.push({ ctx, order: order++ });
|
|
385
440
|
},
|
|
441
|
+
// Pre-run auto-compaction gate. The double only records the call (and
|
|
442
|
+
// the sizing argument) — compaction side effects are exercised in the
|
|
443
|
+
// Conversation tests.
|
|
444
|
+
maybeCompact: async (sizing?: unknown) => {
|
|
445
|
+
probe.maybeCompactOrders.push(order++);
|
|
446
|
+
probe.maybeCompactSizings.push(sizing);
|
|
447
|
+
probe.callSequence.push("maybeCompact");
|
|
448
|
+
return null;
|
|
449
|
+
},
|
|
450
|
+
// Consumed by the wake's over-window pre-flight (suppressed wakes only).
|
|
451
|
+
contextWindowManager: {
|
|
452
|
+
estimateInputTokens: () => options.estimatedInputTokens ?? 0,
|
|
453
|
+
},
|
|
386
454
|
getTurnChannelContext: () => null,
|
|
387
455
|
getTurnInterfaceContext: () => null,
|
|
388
456
|
trustContext: undefined,
|
|
@@ -398,6 +466,7 @@ beforeEach(() => {
|
|
|
398
466
|
recordRequestLogCalls.length = 0;
|
|
399
467
|
mockGetOrCreateConversationCalls.length = 0;
|
|
400
468
|
mockResolverTarget = null;
|
|
469
|
+
mockGetConversationOverrideProfile = () => undefined;
|
|
401
470
|
mockDiskPressureStatus = {
|
|
402
471
|
enabled: false,
|
|
403
472
|
state: "disabled",
|
|
@@ -553,6 +622,116 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
553
622
|
});
|
|
554
623
|
});
|
|
555
624
|
|
|
625
|
+
test("personaOverride is applied to the conversation for the run and restored after", async () => {
|
|
626
|
+
const conversation = makeWakeConversation({
|
|
627
|
+
scriptedAssistant: {
|
|
628
|
+
role: "assistant",
|
|
629
|
+
content: [{ type: "text", text: "reviewed." }],
|
|
630
|
+
},
|
|
631
|
+
});
|
|
632
|
+
const override = { userSlug: "alice", channelSlug: "telegram" };
|
|
633
|
+
|
|
634
|
+
const result = await wakeAgentForOpportunity(
|
|
635
|
+
{
|
|
636
|
+
conversationId: conversation.conversationId,
|
|
637
|
+
hint: "retrospective pass",
|
|
638
|
+
source: "memory-retrospective",
|
|
639
|
+
trustContext: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
640
|
+
personaOverride: override,
|
|
641
|
+
},
|
|
642
|
+
{ resolveTarget: async () => conversation },
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
expect(result.invoked).toBe(true);
|
|
646
|
+
// The override was live on the conversation when the loop ran — the
|
|
647
|
+
// resolveSystemPrompt callback reads this field at prompt-build time.
|
|
648
|
+
expect(conversation.runCalls[0]!.personaOverride).toEqual(override);
|
|
649
|
+
// Applied exactly once and cleared before the wake released the
|
|
650
|
+
// conversation, so a queued user turn can't build under it.
|
|
651
|
+
expect(conversation.personaOverrideSets).toEqual([override, undefined]);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
test("personaOverride is cleared even when the agent loop throws", async () => {
|
|
655
|
+
const conversation = makeWakeConversation({
|
|
656
|
+
runImpl: async () => {
|
|
657
|
+
throw new Error("loop exploded");
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
const override = { userSlug: "alice" };
|
|
661
|
+
|
|
662
|
+
const result = await wakeAgentForOpportunity(
|
|
663
|
+
{
|
|
664
|
+
conversationId: conversation.conversationId,
|
|
665
|
+
hint: "retrospective pass",
|
|
666
|
+
source: "memory-retrospective",
|
|
667
|
+
personaOverride: override,
|
|
668
|
+
},
|
|
669
|
+
{ resolveTarget: async () => conversation },
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
673
|
+
expect(conversation.personaOverrideSets).toEqual([override, undefined]);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
test("a throwing profile read never strands the persona override on the conversation", async () => {
|
|
677
|
+
// The override-profile lookup (and the config/window reads next to it)
|
|
678
|
+
// run BEFORE the try/finally that clears the wake's persona override. If
|
|
679
|
+
// the override were assigned before those reads, a throw there would
|
|
680
|
+
// strand it on the cached Conversation and corrupt every later prompt
|
|
681
|
+
// build. The wake must assign the override only after the reads succeed.
|
|
682
|
+
const conversation = makeWakeConversation({
|
|
683
|
+
scriptedAssistant: {
|
|
684
|
+
role: "assistant",
|
|
685
|
+
content: [{ type: "text", text: "never reached" }],
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
mockGetConversationOverrideProfile = () => {
|
|
689
|
+
throw new Error("profile read failed");
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
await expect(
|
|
693
|
+
wakeAgentForOpportunity(
|
|
694
|
+
{
|
|
695
|
+
conversationId: conversation.conversationId,
|
|
696
|
+
hint: "retrospective pass",
|
|
697
|
+
source: "memory-retrospective",
|
|
698
|
+
personaOverride: { userSlug: "alice" },
|
|
699
|
+
},
|
|
700
|
+
{ resolveTarget: async () => conversation },
|
|
701
|
+
),
|
|
702
|
+
).rejects.toThrow("profile read failed");
|
|
703
|
+
|
|
704
|
+
// The override was never assigned (not assigned-then-cleared) and the
|
|
705
|
+
// conversation field is clean for the next turn's prompt build.
|
|
706
|
+
expect(conversation.personaOverrideSets).toEqual([]);
|
|
707
|
+
expect(conversation.wakePersonaOverride).toBeUndefined();
|
|
708
|
+
// The processing flag was never stranded either — the reads run before
|
|
709
|
+
// setProcessing(true).
|
|
710
|
+
expect(conversation.processingToggles).toEqual([]);
|
|
711
|
+
expect(conversation.runCalls).toHaveLength(0);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
test("no personaOverride → the conversation's persona field is never touched", async () => {
|
|
715
|
+
const conversation = makeWakeConversation({
|
|
716
|
+
scriptedAssistant: {
|
|
717
|
+
role: "assistant",
|
|
718
|
+
content: [{ type: "text", text: "hello" }],
|
|
719
|
+
},
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
await wakeAgentForOpportunity(
|
|
723
|
+
{
|
|
724
|
+
conversationId: conversation.conversationId,
|
|
725
|
+
hint: "plain wake",
|
|
726
|
+
source: "background-tool",
|
|
727
|
+
},
|
|
728
|
+
{ resolveTarget: async () => conversation },
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
expect(conversation.runCalls[0]!.personaOverride).toBeUndefined();
|
|
732
|
+
expect(conversation.personaOverrideSets).toEqual([]);
|
|
733
|
+
});
|
|
734
|
+
|
|
556
735
|
test("silent no-op when agent produces no tool calls and no text", async () => {
|
|
557
736
|
const conversation = makeWakeConversation({
|
|
558
737
|
baseline: [
|
|
@@ -647,6 +826,99 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
647
826
|
expect(processingFalseIndex).toBeLessThan(drainIndex);
|
|
648
827
|
});
|
|
649
828
|
|
|
829
|
+
test("applies toolGateMode: 'execution' alongside the allowlist and restores it after the wake", async () => {
|
|
830
|
+
let gateModeDuringRun: string | undefined;
|
|
831
|
+
const conversation = makeWakeConversation({
|
|
832
|
+
runImpl: async (input) => {
|
|
833
|
+
gateModeDuringRun = conversation.subagentToolGateMode;
|
|
834
|
+
return runResult([
|
|
835
|
+
...input,
|
|
836
|
+
{ role: "assistant", content: [{ type: "text", text: "Saved." }] },
|
|
837
|
+
]);
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
const result = await wakeAgentForOpportunity(
|
|
842
|
+
{
|
|
843
|
+
conversationId: conversation.conversationId,
|
|
844
|
+
hint: "review for memories",
|
|
845
|
+
source: "memory-retrospective",
|
|
846
|
+
allowedTools: ["remember"],
|
|
847
|
+
toolGateMode: "execution",
|
|
848
|
+
},
|
|
849
|
+
{ resolveTarget: async () => conversation },
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
853
|
+
// The gate mode is live on the conversation for the duration of the run
|
|
854
|
+
// (the tool resolver and executor closures read it there)...
|
|
855
|
+
expect(gateModeDuringRun).toBe("execution");
|
|
856
|
+
expect(conversation.runCalls[0]!.allowedTools).toEqual(["remember"]);
|
|
857
|
+
// ...and restored alongside the allowlist after the wake.
|
|
858
|
+
expect(conversation.subagentToolGateMode).toBeUndefined();
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
test("applies toolContextPin alongside the allowlist and restores it after the wake", async () => {
|
|
862
|
+
let pinDuringRun: unknown;
|
|
863
|
+
const conversation = makeWakeConversation({
|
|
864
|
+
runImpl: async (input) => {
|
|
865
|
+
pinDuringRun = conversation.toolContextPin;
|
|
866
|
+
return runResult([
|
|
867
|
+
...input,
|
|
868
|
+
{ role: "assistant", content: [{ type: "text", text: "Saved." }] },
|
|
869
|
+
]);
|
|
870
|
+
},
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
const result = await wakeAgentForOpportunity(
|
|
874
|
+
{
|
|
875
|
+
conversationId: conversation.conversationId,
|
|
876
|
+
hint: "review for memories",
|
|
877
|
+
source: "memory-retrospective",
|
|
878
|
+
allowedTools: ["remember"],
|
|
879
|
+
toolGateMode: "execution",
|
|
880
|
+
toolContextPin: { hasNoClient: false, transportInterface: "macos" },
|
|
881
|
+
},
|
|
882
|
+
{ resolveTarget: async () => conversation },
|
|
883
|
+
);
|
|
884
|
+
|
|
885
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
886
|
+
// The pin is live on the conversation for the duration of the run (the
|
|
887
|
+
// tool resolver reads it there for wire-definition parity)...
|
|
888
|
+
expect(pinDuringRun).toEqual({
|
|
889
|
+
hasNoClient: false,
|
|
890
|
+
transportInterface: "macos",
|
|
891
|
+
});
|
|
892
|
+
// ...and restored alongside the allowlist + gate mode after the wake.
|
|
893
|
+
expect(conversation.toolContextPin).toBeUndefined();
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
test("defaults to the wire gate mode when toolGateMode is absent", async () => {
|
|
897
|
+
let gateModeDuringRun: string | undefined;
|
|
898
|
+
const conversation = makeWakeConversation({
|
|
899
|
+
runImpl: async (input) => {
|
|
900
|
+
gateModeDuringRun = conversation.subagentToolGateMode;
|
|
901
|
+
return runResult([
|
|
902
|
+
...input,
|
|
903
|
+
{ role: "assistant", content: [{ type: "text", text: "Saved." }] },
|
|
904
|
+
]);
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
await wakeAgentForOpportunity(
|
|
909
|
+
{
|
|
910
|
+
conversationId: conversation.conversationId,
|
|
911
|
+
hint: "review for memories",
|
|
912
|
+
source: "memory-retrospective",
|
|
913
|
+
allowedTools: ["remember"],
|
|
914
|
+
},
|
|
915
|
+
{ resolveTarget: async () => conversation },
|
|
916
|
+
);
|
|
917
|
+
|
|
918
|
+
expect(gateModeDuringRun).toBe("wire");
|
|
919
|
+
expect(conversation.subagentToolGateMode).toBeUndefined();
|
|
920
|
+
});
|
|
921
|
+
|
|
650
922
|
test("restores allowed tools before drain when the wake is a silent no-op", async () => {
|
|
651
923
|
const conversation = makeWakeConversation({
|
|
652
924
|
scriptedAssistant: {
|
|
@@ -1233,11 +1505,13 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1233
1505
|
{ resolveTarget: async () => conversation },
|
|
1234
1506
|
);
|
|
1235
1507
|
|
|
1236
|
-
// Full call sequence: processing toggled true →
|
|
1237
|
-
// 3 persists → processing toggled false → drain.
|
|
1238
|
-
// every push and every persist must precede the
|
|
1508
|
+
// Full call sequence: processing toggled true → compaction gate →
|
|
1509
|
+
// 3 pushes → 3 persists → processing toggled false → drain.
|
|
1510
|
+
// Specifically, every push and every persist must precede the
|
|
1511
|
+
// single drain.
|
|
1239
1512
|
expect(conversation.callSequence).toEqual([
|
|
1240
1513
|
"processing:true",
|
|
1514
|
+
"maybeCompact",
|
|
1241
1515
|
"push",
|
|
1242
1516
|
"push",
|
|
1243
1517
|
"push",
|
|
@@ -1294,9 +1568,11 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1294
1568
|
expect(conversation.emittedEvents).toHaveLength(0);
|
|
1295
1569
|
|
|
1296
1570
|
// But drain still ran exactly once, after processing flipped to
|
|
1297
|
-
// false. Sequence: toggle true → toggle false →
|
|
1571
|
+
// false. Sequence: toggle true → compaction gate → toggle false →
|
|
1572
|
+
// drain.
|
|
1298
1573
|
expect(conversation.callSequence).toEqual([
|
|
1299
1574
|
"processing:true",
|
|
1575
|
+
"maybeCompact",
|
|
1300
1576
|
"processing:false",
|
|
1301
1577
|
"drain",
|
|
1302
1578
|
]);
|
|
@@ -1578,7 +1854,46 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1578
1854
|
conversationId: conversation.conversationId,
|
|
1579
1855
|
provider: "test-provider",
|
|
1580
1856
|
messageId: undefined,
|
|
1857
|
+
callSite: "mainAgent",
|
|
1858
|
+
});
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
test("wake with an explicit callSite records logs under that call site", async () => {
|
|
1862
|
+
const usageEvent: AgentEvent = {
|
|
1863
|
+
type: "usage",
|
|
1864
|
+
inputTokens: 100,
|
|
1865
|
+
outputTokens: 5,
|
|
1866
|
+
model: "test-model",
|
|
1867
|
+
actualProvider: "test-provider",
|
|
1868
|
+
providerDurationMs: 10,
|
|
1869
|
+
rawRequest: { request: "retrospective wake" },
|
|
1870
|
+
rawResponse: { response: "real reply" },
|
|
1871
|
+
};
|
|
1872
|
+
const conversation = makeWakeConversation({
|
|
1873
|
+
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
1874
|
+
scriptedEvents: [usageEvent],
|
|
1875
|
+
scriptedAssistant: {
|
|
1876
|
+
role: "assistant",
|
|
1877
|
+
content: [{ type: "text", text: "real reply" }],
|
|
1878
|
+
},
|
|
1581
1879
|
});
|
|
1880
|
+
|
|
1881
|
+
const result = await wakeAgentForOpportunity(
|
|
1882
|
+
{
|
|
1883
|
+
conversationId: conversation.conversationId,
|
|
1884
|
+
hint: "do reply",
|
|
1885
|
+
source: "unit-test",
|
|
1886
|
+
callSite: "memoryRetrospective",
|
|
1887
|
+
},
|
|
1888
|
+
{ resolveTarget: async () => conversation },
|
|
1889
|
+
);
|
|
1890
|
+
|
|
1891
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
1892
|
+
expect(recordRequestLogCalls).toHaveLength(1);
|
|
1893
|
+
// Regression guard: persistLog used to hardcode "mainAgent", which
|
|
1894
|
+
// mislabeled retrospective/consolidation wake rows in llm_request_logs
|
|
1895
|
+
// and polluted call-site filtering during prompt diagnostics.
|
|
1896
|
+
expect(recordRequestLogCalls[0]?.callSite).toBe("memoryRetrospective");
|
|
1582
1897
|
});
|
|
1583
1898
|
|
|
1584
1899
|
test("non-serializable usage payload does not abort the wake", async () => {
|
|
@@ -1774,4 +2089,311 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1774
2089
|
},
|
|
1775
2090
|
);
|
|
1776
2091
|
});
|
|
2092
|
+
|
|
2093
|
+
describe("suppressAutoCompaction + over-window policy", () => {
|
|
2094
|
+
const reply: Message = {
|
|
2095
|
+
role: "assistant",
|
|
2096
|
+
content: [{ type: "text", text: "done" }],
|
|
2097
|
+
};
|
|
2098
|
+
|
|
2099
|
+
test("default wake runs the pre-run compaction gate before the agent loop", async () => {
|
|
2100
|
+
const conversation = makeWakeConversation({
|
|
2101
|
+
scriptedAssistant: reply,
|
|
2102
|
+
});
|
|
2103
|
+
|
|
2104
|
+
const result = await wakeAgentForOpportunity(
|
|
2105
|
+
{
|
|
2106
|
+
conversationId: conversation.conversationId,
|
|
2107
|
+
hint: "do work",
|
|
2108
|
+
source: "unit-test",
|
|
2109
|
+
},
|
|
2110
|
+
{ resolveTarget: async () => conversation },
|
|
2111
|
+
);
|
|
2112
|
+
|
|
2113
|
+
expect(result.invoked).toBe(true);
|
|
2114
|
+
expect(conversation.maybeCompactOrders).toHaveLength(1);
|
|
2115
|
+
expect(conversation.runCalls).toHaveLength(1);
|
|
2116
|
+
// The gate fired strictly before agentLoop.run (shared order counter).
|
|
2117
|
+
expect(conversation.maybeCompactOrders[0]!).toBeLessThan(
|
|
2118
|
+
conversation.runCalls[0]!.order,
|
|
2119
|
+
);
|
|
2120
|
+
// And under the processing marker, so a racing user send queues
|
|
2121
|
+
// instead of starting a concurrent turn during the summary call.
|
|
2122
|
+
expect(conversation.callSequence.indexOf("maybeCompact")).toBeGreaterThan(
|
|
2123
|
+
conversation.callSequence.indexOf("processing:true"),
|
|
2124
|
+
);
|
|
2125
|
+
});
|
|
2126
|
+
|
|
2127
|
+
test("suppressAutoCompaction: true skips the compaction gate on an otherwise identical wake", async () => {
|
|
2128
|
+
const conversation = makeWakeConversation({
|
|
2129
|
+
scriptedAssistant: reply,
|
|
2130
|
+
});
|
|
2131
|
+
|
|
2132
|
+
const result = await wakeAgentForOpportunity(
|
|
2133
|
+
{
|
|
2134
|
+
conversationId: conversation.conversationId,
|
|
2135
|
+
hint: "do work",
|
|
2136
|
+
source: "unit-test",
|
|
2137
|
+
suppressAutoCompaction: true,
|
|
2138
|
+
},
|
|
2139
|
+
{ resolveTarget: async () => conversation },
|
|
2140
|
+
);
|
|
2141
|
+
|
|
2142
|
+
expect(result.invoked).toBe(true);
|
|
2143
|
+
expect(conversation.maybeCompactOrders).toHaveLength(0);
|
|
2144
|
+
expect(conversation.runCalls).toHaveLength(1);
|
|
2145
|
+
});
|
|
2146
|
+
|
|
2147
|
+
test("over-window suppressed wake fails fast without invoking the loop or compacting", async () => {
|
|
2148
|
+
const conversation = makeWakeConversation({
|
|
2149
|
+
scriptedAssistant: reply,
|
|
2150
|
+
// Mocked resolveEffectiveContextWindow yields maxInputTokens 200k.
|
|
2151
|
+
estimatedInputTokens: 250_000,
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
const result = await wakeAgentForOpportunity(
|
|
2155
|
+
{
|
|
2156
|
+
conversationId: conversation.conversationId,
|
|
2157
|
+
hint: "do work",
|
|
2158
|
+
source: "unit-test",
|
|
2159
|
+
suppressAutoCompaction: true,
|
|
2160
|
+
},
|
|
2161
|
+
{ resolveTarget: async () => conversation },
|
|
2162
|
+
);
|
|
2163
|
+
|
|
2164
|
+
expect(result).toEqual({
|
|
2165
|
+
invoked: false,
|
|
2166
|
+
producedToolCalls: false,
|
|
2167
|
+
reason: "context_overflow",
|
|
2168
|
+
});
|
|
2169
|
+
expect(conversation.runCalls).toHaveLength(0);
|
|
2170
|
+
expect(conversation.maybeCompactOrders).toHaveLength(0);
|
|
2171
|
+
// The processing marker is released and the queue drained despite the
|
|
2172
|
+
// early failure.
|
|
2173
|
+
expect(conversation.processingToggles).toEqual([true, false]);
|
|
2174
|
+
expect(conversation.isProcessing()).toBe(false);
|
|
2175
|
+
expect(conversation.drainQueueCalls).toBe(1);
|
|
2176
|
+
});
|
|
2177
|
+
|
|
2178
|
+
test("identical over-window wake without suppression compacts and runs instead of failing", async () => {
|
|
2179
|
+
const conversation = makeWakeConversation({
|
|
2180
|
+
scriptedAssistant: reply,
|
|
2181
|
+
estimatedInputTokens: 250_000,
|
|
2182
|
+
});
|
|
2183
|
+
|
|
2184
|
+
const result = await wakeAgentForOpportunity(
|
|
2185
|
+
{
|
|
2186
|
+
conversationId: conversation.conversationId,
|
|
2187
|
+
hint: "do work",
|
|
2188
|
+
source: "unit-test",
|
|
2189
|
+
},
|
|
2190
|
+
{ resolveTarget: async () => conversation },
|
|
2191
|
+
);
|
|
2192
|
+
|
|
2193
|
+
expect(result.invoked).toBe(true);
|
|
2194
|
+
expect(conversation.maybeCompactOrders).toHaveLength(1);
|
|
2195
|
+
expect(conversation.runCalls).toHaveLength(1);
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
test("suppressed wake maps a provider context-overflow rejection to a failed result", async () => {
|
|
2199
|
+
// The estimator under-counted (pre-flight passed) but the provider
|
|
2200
|
+
// still rejected the call as over-window. The loop swallows provider
|
|
2201
|
+
// errors into a graceful no-output stop, which without the mapping
|
|
2202
|
+
// would read as a successful silent no-op.
|
|
2203
|
+
const conversation = makeWakeConversation({
|
|
2204
|
+
estimatedInputTokens: 0,
|
|
2205
|
+
runImpl: async (input, onEvent) => {
|
|
2206
|
+
await onEvent({
|
|
2207
|
+
type: "error",
|
|
2208
|
+
error: new ContextOverflowError(
|
|
2209
|
+
"prompt is too long: 250000 tokens > 200000 maximum",
|
|
2210
|
+
"test-provider",
|
|
2211
|
+
),
|
|
2212
|
+
});
|
|
2213
|
+
return runResult([...input]);
|
|
2214
|
+
},
|
|
2215
|
+
});
|
|
2216
|
+
|
|
2217
|
+
const result = await wakeAgentForOpportunity(
|
|
2218
|
+
{
|
|
2219
|
+
conversationId: conversation.conversationId,
|
|
2220
|
+
hint: "do work",
|
|
2221
|
+
source: "unit-test",
|
|
2222
|
+
suppressAutoCompaction: true,
|
|
2223
|
+
},
|
|
2224
|
+
{ resolveTarget: async () => conversation },
|
|
2225
|
+
);
|
|
2226
|
+
|
|
2227
|
+
expect(result).toEqual({
|
|
2228
|
+
invoked: false,
|
|
2229
|
+
producedToolCalls: false,
|
|
2230
|
+
reason: "context_overflow",
|
|
2231
|
+
});
|
|
2232
|
+
// Cleanup still ran.
|
|
2233
|
+
expect(conversation.processingToggles).toEqual([true, false]);
|
|
2234
|
+
expect(conversation.drainQueueCalls).toBe(1);
|
|
2235
|
+
});
|
|
2236
|
+
|
|
2237
|
+
test("suppressed wake maps a REWRAPPED (untyped) provider overflow error to a failed result", async () => {
|
|
2238
|
+
// Managed-proxy/adapter paths rewrap provider overflow rejections into
|
|
2239
|
+
// plain Errors, defeating the typed `instanceof` check. The wake's
|
|
2240
|
+
// capture must fall back to the message-level heuristic — otherwise
|
|
2241
|
+
// the overflow reads as a successful silent no-op and the
|
|
2242
|
+
// fork-retrospective job advances `lastProcessedMessageId` past a
|
|
2243
|
+
// slice that was never reviewed.
|
|
2244
|
+
const conversation = makeWakeConversation({
|
|
2245
|
+
estimatedInputTokens: 0,
|
|
2246
|
+
runImpl: async (input, onEvent) => {
|
|
2247
|
+
await onEvent({
|
|
2248
|
+
type: "error",
|
|
2249
|
+
error: new Error(
|
|
2250
|
+
"Provider API error (400): prompt is too long: 250000 tokens > 200000 maximum",
|
|
2251
|
+
),
|
|
2252
|
+
});
|
|
2253
|
+
return runResult([...input]);
|
|
2254
|
+
},
|
|
2255
|
+
});
|
|
2256
|
+
|
|
2257
|
+
const result = await wakeAgentForOpportunity(
|
|
2258
|
+
{
|
|
2259
|
+
conversationId: conversation.conversationId,
|
|
2260
|
+
hint: "do work",
|
|
2261
|
+
source: "unit-test",
|
|
2262
|
+
suppressAutoCompaction: true,
|
|
2263
|
+
},
|
|
2264
|
+
{ resolveTarget: async () => conversation },
|
|
2265
|
+
);
|
|
2266
|
+
|
|
2267
|
+
expect(result).toEqual({
|
|
2268
|
+
invoked: false,
|
|
2269
|
+
producedToolCalls: false,
|
|
2270
|
+
reason: "context_overflow",
|
|
2271
|
+
});
|
|
2272
|
+
});
|
|
2273
|
+
|
|
2274
|
+
test("suppressed wake maps a rewrapped overflow THROW to a failed result", async () => {
|
|
2275
|
+
const conversation = makeWakeConversation({
|
|
2276
|
+
estimatedInputTokens: 0,
|
|
2277
|
+
runImpl: async () => {
|
|
2278
|
+
throw new Error("too many input tokens: 250000 > 200000");
|
|
2279
|
+
},
|
|
2280
|
+
});
|
|
2281
|
+
|
|
2282
|
+
const result = await wakeAgentForOpportunity(
|
|
2283
|
+
{
|
|
2284
|
+
conversationId: conversation.conversationId,
|
|
2285
|
+
hint: "do work",
|
|
2286
|
+
source: "unit-test",
|
|
2287
|
+
suppressAutoCompaction: true,
|
|
2288
|
+
},
|
|
2289
|
+
{ resolveTarget: async () => conversation },
|
|
2290
|
+
);
|
|
2291
|
+
|
|
2292
|
+
expect(result).toEqual({
|
|
2293
|
+
invoked: false,
|
|
2294
|
+
producedToolCalls: false,
|
|
2295
|
+
reason: "context_overflow",
|
|
2296
|
+
});
|
|
2297
|
+
// Cleanup still ran.
|
|
2298
|
+
expect(conversation.processingToggles).toEqual([true, false]);
|
|
2299
|
+
expect(conversation.drainQueueCalls).toBe(1);
|
|
2300
|
+
});
|
|
2301
|
+
|
|
2302
|
+
test("suppressed wake treats an unrelated rewrapped error as a generic no-op, not an overflow", async () => {
|
|
2303
|
+
const conversation = makeWakeConversation({
|
|
2304
|
+
estimatedInputTokens: 0,
|
|
2305
|
+
runImpl: async () => {
|
|
2306
|
+
throw new Error("socket hang up");
|
|
2307
|
+
},
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2310
|
+
const result = await wakeAgentForOpportunity(
|
|
2311
|
+
{
|
|
2312
|
+
conversationId: conversation.conversationId,
|
|
2313
|
+
hint: "do work",
|
|
2314
|
+
source: "unit-test",
|
|
2315
|
+
suppressAutoCompaction: true,
|
|
2316
|
+
},
|
|
2317
|
+
{ resolveTarget: async () => conversation },
|
|
2318
|
+
);
|
|
2319
|
+
|
|
2320
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
2321
|
+
});
|
|
2322
|
+
|
|
2323
|
+
test("the compaction gate is sized with the wake's call site and forced profile", async () => {
|
|
2324
|
+
const conversation = makeWakeConversation({
|
|
2325
|
+
scriptedAssistant: reply,
|
|
2326
|
+
});
|
|
2327
|
+
|
|
2328
|
+
await wakeAgentForOpportunity(
|
|
2329
|
+
{
|
|
2330
|
+
conversationId: conversation.conversationId,
|
|
2331
|
+
hint: "do work",
|
|
2332
|
+
source: "memory-retrospective",
|
|
2333
|
+
callSite: "memoryRetrospective",
|
|
2334
|
+
forceOverrideProfile: "forced",
|
|
2335
|
+
},
|
|
2336
|
+
{ resolveTarget: async () => conversation },
|
|
2337
|
+
);
|
|
2338
|
+
|
|
2339
|
+
// The wake threads its own resolution inputs so the gate's threshold
|
|
2340
|
+
// sizes against the wake's window instead of mainAgent's.
|
|
2341
|
+
expect(conversation.maybeCompactSizings).toEqual([
|
|
2342
|
+
{
|
|
2343
|
+
callSite: "memoryRetrospective",
|
|
2344
|
+
overrideProfile: "forced",
|
|
2345
|
+
forceOverrideProfile: true,
|
|
2346
|
+
},
|
|
2347
|
+
]);
|
|
2348
|
+
});
|
|
2349
|
+
|
|
2350
|
+
test("a default wake sizes the compaction gate against mainAgent with no forced profile", async () => {
|
|
2351
|
+
const conversation = makeWakeConversation({
|
|
2352
|
+
scriptedAssistant: reply,
|
|
2353
|
+
});
|
|
2354
|
+
|
|
2355
|
+
await wakeAgentForOpportunity(
|
|
2356
|
+
{
|
|
2357
|
+
conversationId: conversation.conversationId,
|
|
2358
|
+
hint: "do work",
|
|
2359
|
+
source: "unit-test",
|
|
2360
|
+
},
|
|
2361
|
+
{ resolveTarget: async () => conversation },
|
|
2362
|
+
);
|
|
2363
|
+
|
|
2364
|
+
expect(conversation.maybeCompactSizings).toEqual([
|
|
2365
|
+
{
|
|
2366
|
+
callSite: "mainAgent",
|
|
2367
|
+
overrideProfile: undefined,
|
|
2368
|
+
forceOverrideProfile: false,
|
|
2369
|
+
},
|
|
2370
|
+
]);
|
|
2371
|
+
});
|
|
2372
|
+
|
|
2373
|
+
test("non-suppressed wake treats a provider context-overflow rejection as a silent no-op (existing behavior)", async () => {
|
|
2374
|
+
const conversation = makeWakeConversation({
|
|
2375
|
+
runImpl: async (input, onEvent) => {
|
|
2376
|
+
await onEvent({
|
|
2377
|
+
type: "error",
|
|
2378
|
+
error: new ContextOverflowError(
|
|
2379
|
+
"prompt is too long: 250000 tokens > 200000 maximum",
|
|
2380
|
+
"test-provider",
|
|
2381
|
+
),
|
|
2382
|
+
});
|
|
2383
|
+
return runResult([...input]);
|
|
2384
|
+
},
|
|
2385
|
+
});
|
|
2386
|
+
|
|
2387
|
+
const result = await wakeAgentForOpportunity(
|
|
2388
|
+
{
|
|
2389
|
+
conversationId: conversation.conversationId,
|
|
2390
|
+
hint: "do work",
|
|
2391
|
+
source: "unit-test",
|
|
2392
|
+
},
|
|
2393
|
+
{ resolveTarget: async () => conversation },
|
|
2394
|
+
);
|
|
2395
|
+
|
|
2396
|
+
expect(result).toEqual({ invoked: true, producedToolCalls: false });
|
|
2397
|
+
});
|
|
2398
|
+
});
|
|
1777
2399
|
});
|