@vellumai/assistant 0.8.10 → 0.8.11-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -2,10 +2,8 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { CompactionCircuit } from "../agent/compaction-circuit.js";
|
|
4
4
|
import type { AgentEvent, AgentLoopRunResult } from "../agent/loop.js";
|
|
5
|
-
import type { UserMessageAttachment } from "../daemon/message-protocol.js";
|
|
6
5
|
import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
|
|
7
6
|
import type { Message, ProviderResponse } from "../providers/types.js";
|
|
8
|
-
import { ProviderError } from "../util/errors.js";
|
|
9
7
|
|
|
10
8
|
mock.module("../util/logger.js", () => ({
|
|
11
9
|
getLogger: () =>
|
|
@@ -82,82 +80,16 @@ mock.module("../config/loader.js", () => ({
|
|
|
82
80
|
invalidateConfigCache: () => {},
|
|
83
81
|
}));
|
|
84
82
|
|
|
85
|
-
// Token estimator: return a small value (well within budget) so
|
|
86
|
-
// does not
|
|
87
|
-
// entry points
|
|
88
|
-
// mid-loop overflow checkpoints, and the raw estimate backs the pre-send
|
|
89
|
-
// calibration capture.
|
|
83
|
+
// Token estimator: return a small value (well within budget) so the loop's
|
|
84
|
+
// pre-call budget gate does not trip in these tests. Stub both the calibrated
|
|
85
|
+
// and raw entry points.
|
|
90
86
|
mock.module("../context/token-estimator.js", () => ({
|
|
91
87
|
estimatePromptTokens: () => 1000,
|
|
92
88
|
estimatePromptTokensRaw: () => 1000,
|
|
93
|
-
// The preflight overflow gate calls this calibrated wrapper directly; stub
|
|
94
|
-
// it alongside the others so it returns the same small value.
|
|
95
89
|
estimatePromptTokensWithTools: () => 1000,
|
|
96
90
|
estimateToolsTokens: () => 0,
|
|
97
91
|
}));
|
|
98
92
|
|
|
99
|
-
// Overflow recovery module mocks — the convergence loop delegates to these
|
|
100
|
-
// but these tests exercise the Conversation-level flow, not the reducer internals.
|
|
101
|
-
// The reducer mock delegates to the compactFn to simulate a forced compaction
|
|
102
|
-
// tier, matching the real reducer's behavior for Tier 1.
|
|
103
|
-
mock.module(
|
|
104
|
-
"../plugins/defaults/compaction/context-overflow-reducer.js",
|
|
105
|
-
() => ({
|
|
106
|
-
createInitialReducerState: () => ({
|
|
107
|
-
appliedTiers: [],
|
|
108
|
-
injectionMode: "full" as const,
|
|
109
|
-
exhausted: false,
|
|
110
|
-
}),
|
|
111
|
-
reduceContextOverflow: async (
|
|
112
|
-
msgs: Message[],
|
|
113
|
-
_cfg: unknown,
|
|
114
|
-
_state: unknown,
|
|
115
|
-
compactFn?: (
|
|
116
|
-
m: Message[],
|
|
117
|
-
s: AbortSignal | undefined,
|
|
118
|
-
o: Record<string, unknown>,
|
|
119
|
-
) => Promise<{
|
|
120
|
-
compacted: boolean;
|
|
121
|
-
messages: Message[];
|
|
122
|
-
compactedPersistedMessages?: number;
|
|
123
|
-
summaryText?: string;
|
|
124
|
-
[k: string]: unknown;
|
|
125
|
-
}>,
|
|
126
|
-
signal?: AbortSignal,
|
|
127
|
-
) => {
|
|
128
|
-
let resultMessages = msgs;
|
|
129
|
-
let compactionResult;
|
|
130
|
-
if (compactFn) {
|
|
131
|
-
const cr = await compactFn(msgs, signal, { force: true });
|
|
132
|
-
if (cr.compacted) {
|
|
133
|
-
resultMessages = cr.messages;
|
|
134
|
-
compactionResult = cr;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
messages: resultMessages,
|
|
139
|
-
tier: "forced_compaction",
|
|
140
|
-
state: {
|
|
141
|
-
appliedTiers: [
|
|
142
|
-
"forced_compaction",
|
|
143
|
-
"tool_result_truncation",
|
|
144
|
-
"media_stubbing",
|
|
145
|
-
"injection_downgrade",
|
|
146
|
-
],
|
|
147
|
-
injectionMode: "full",
|
|
148
|
-
exhausted: true,
|
|
149
|
-
},
|
|
150
|
-
estimatedTokens: 1000,
|
|
151
|
-
compactionResult,
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
}),
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
mock.module("../daemon/context-overflow-policy.js", () => ({
|
|
158
|
-
resolveOverflowAction: () => "fail_gracefully",
|
|
159
|
-
}));
|
|
160
|
-
|
|
161
93
|
mock.module("../prompts/system-prompt.js", () => ({
|
|
162
94
|
buildSystemPrompt: () => "system prompt",
|
|
163
95
|
}));
|
|
@@ -218,41 +150,20 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
218
150
|
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
219
151
|
}));
|
|
220
152
|
|
|
221
|
-
let maybeCompactCalls: Array<{ force: boolean }> = [];
|
|
222
|
-
let forceCompactionEnabled = false;
|
|
223
|
-
|
|
224
153
|
mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
225
154
|
ContextWindowManager: class {
|
|
226
|
-
|
|
155
|
+
conversationId: string | undefined;
|
|
156
|
+
constructor(options?: { conversationId?: string }) {
|
|
157
|
+
this.conversationId = options?.conversationId;
|
|
158
|
+
}
|
|
227
159
|
updateConfig() {}
|
|
228
160
|
shouldCompact() {
|
|
229
161
|
return { needed: false, estimatedTokens: 0 };
|
|
230
162
|
}
|
|
231
|
-
async maybeCompact(
|
|
232
|
-
messages: Message[],
|
|
233
|
-
_signal?: AbortSignal,
|
|
234
|
-
options?: { force?: boolean },
|
|
235
|
-
) {
|
|
236
|
-
maybeCompactCalls.push({ force: options?.force === true });
|
|
237
|
-
if (options?.force && forceCompactionEnabled) {
|
|
238
|
-
return {
|
|
239
|
-
compacted: true,
|
|
240
|
-
messages,
|
|
241
|
-
previousEstimatedInputTokens: 120000,
|
|
242
|
-
estimatedInputTokens: 50000,
|
|
243
|
-
maxInputTokens: 100000,
|
|
244
|
-
thresholdTokens: 80000,
|
|
245
|
-
compactedMessages: 2,
|
|
246
|
-
compactedPersistedMessages: 2,
|
|
247
|
-
summaryCalls: 1,
|
|
248
|
-
summaryInputTokens: 200,
|
|
249
|
-
summaryOutputTokens: 50,
|
|
250
|
-
summaryModel: "mock-summary-model",
|
|
251
|
-
summaryText: "## Goals\n- compacted",
|
|
252
|
-
};
|
|
253
|
-
}
|
|
163
|
+
async maybeCompact() {
|
|
254
164
|
return { compacted: false };
|
|
255
165
|
}
|
|
166
|
+
resetOverflowRecovery() {}
|
|
256
167
|
},
|
|
257
168
|
createContextSummaryMessage: () => ({
|
|
258
169
|
role: "user",
|
|
@@ -263,13 +174,7 @@ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
|
263
174
|
|
|
264
175
|
// Track how many times agentLoop.run was called
|
|
265
176
|
let agentLoopRunCount = 0;
|
|
266
|
-
let firstRunErrorMode:
|
|
267
|
-
| "none"
|
|
268
|
-
| "ordering"
|
|
269
|
-
| "context_too_large"
|
|
270
|
-
| "context_too_large_phrase"
|
|
271
|
-
| "context_too_large_413"
|
|
272
|
-
| "context_too_large_413_with_progress" = "ordering";
|
|
177
|
+
let firstRunErrorMode: "none" | "ordering" = "ordering";
|
|
273
178
|
|
|
274
179
|
mock.module("../agent/loop.js", () => ({
|
|
275
180
|
AgentLoop: class {
|
|
@@ -294,70 +199,11 @@ mock.module("../agent/loop.js", () => ({
|
|
|
294
199
|
await onEvent({ type: "llm_call_started" });
|
|
295
200
|
agentLoopRunCount++;
|
|
296
201
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
// hitting a 413 context-too-large error on the second LLM call.
|
|
303
|
-
onEvent({
|
|
304
|
-
type: "usage",
|
|
305
|
-
inputTokens: 10,
|
|
306
|
-
outputTokens: 20,
|
|
307
|
-
model: "mock",
|
|
308
|
-
providerDurationMs: 50,
|
|
309
|
-
});
|
|
310
|
-
const assistantMsg: Message = {
|
|
311
|
-
role: "assistant",
|
|
312
|
-
content: [
|
|
313
|
-
{
|
|
314
|
-
type: "tool_use",
|
|
315
|
-
id: "tu-1",
|
|
316
|
-
name: "bash",
|
|
317
|
-
input: { command: "echo hi" },
|
|
318
|
-
},
|
|
319
|
-
],
|
|
320
|
-
};
|
|
321
|
-
onEvent({ type: "message_complete", message: assistantMsg });
|
|
322
|
-
onEvent({
|
|
323
|
-
type: "tool_result",
|
|
324
|
-
toolUseId: "tu-1",
|
|
325
|
-
content: "hi",
|
|
326
|
-
isError: false,
|
|
327
|
-
});
|
|
328
|
-
// Now the second LLM call fails with 413
|
|
329
|
-
onEvent({
|
|
330
|
-
type: "error",
|
|
331
|
-
error: new ProviderError(
|
|
332
|
-
"request entity too large",
|
|
333
|
-
"mock-provider",
|
|
334
|
-
413,
|
|
335
|
-
),
|
|
336
|
-
});
|
|
337
|
-
const history = [...messages];
|
|
338
|
-
history.push(assistantMsg);
|
|
339
|
-
history.push({
|
|
340
|
-
role: "user",
|
|
341
|
-
content: [
|
|
342
|
-
{ type: "tool_result", tool_use_id: "tu-1", content: "hi" },
|
|
343
|
-
],
|
|
344
|
-
} as Message);
|
|
345
|
-
// Progress was made — history grew
|
|
346
|
-
return {
|
|
347
|
-
history,
|
|
348
|
-
exitReason: null,
|
|
349
|
-
appendedNewMessages: true,
|
|
350
|
-
newMessages: history.slice(messages.length),
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Context-too-large modes: keep failing when compaction can't help
|
|
355
|
-
const isContextTooLargeMode =
|
|
356
|
-
firstRunErrorMode !== "none" && firstRunErrorMode !== "ordering";
|
|
357
|
-
const shouldError =
|
|
358
|
-
firstRunErrorMode !== "none" &&
|
|
359
|
-
(agentLoopRunCount === 1 ||
|
|
360
|
-
(isContextTooLargeMode && !forceCompactionEnabled));
|
|
202
|
+
// Ordering-error recovery lives inside `AgentLoop.run`; with the loop
|
|
203
|
+
// mocked here, an unrecovered ordering error surfaces as a terminal
|
|
204
|
+
// `error` event so we can assert the wrapper's surface behavior — it
|
|
205
|
+
// routes the error to a durable conversation_error and does not re-run.
|
|
206
|
+
const shouldError = firstRunErrorMode === "ordering";
|
|
361
207
|
|
|
362
208
|
if (shouldError) {
|
|
363
209
|
onEvent({
|
|
@@ -367,34 +213,14 @@ mock.module("../agent/loop.js", () => ({
|
|
|
367
213
|
model: "mock",
|
|
368
214
|
providerDurationMs: 0,
|
|
369
215
|
});
|
|
370
|
-
const error = (
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
"tool_result blocks that are not immediately after a tool_use block",
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
if (firstRunErrorMode === "context_too_large_phrase") {
|
|
377
|
-
return new Error(
|
|
378
|
-
"The conversation is too long for the model to process.",
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
if (firstRunErrorMode === "context_too_large_413") {
|
|
382
|
-
return new ProviderError(
|
|
383
|
-
"request entity too large",
|
|
384
|
-
"mock-provider",
|
|
385
|
-
413,
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
return new Error(
|
|
389
|
-
"context_length_exceeded: request has too many input tokens",
|
|
390
|
-
);
|
|
391
|
-
})();
|
|
216
|
+
const error = new Error(
|
|
217
|
+
"tool_result blocks that are not immediately after a tool_use block",
|
|
218
|
+
);
|
|
392
219
|
onEvent({ type: "error", error });
|
|
393
220
|
// Return unchanged — no progress
|
|
394
221
|
return {
|
|
395
222
|
history: [...messages],
|
|
396
223
|
exitReason: null,
|
|
397
|
-
appendedNewMessages: false,
|
|
398
224
|
newMessages: [],
|
|
399
225
|
};
|
|
400
226
|
}
|
|
@@ -417,7 +243,6 @@ mock.module("../agent/loop.js", () => ({
|
|
|
417
243
|
return {
|
|
418
244
|
history,
|
|
419
245
|
exitReason: null,
|
|
420
|
-
appendedNewMessages: true,
|
|
421
246
|
newMessages: history.slice(messages.length),
|
|
422
247
|
};
|
|
423
248
|
}
|
|
@@ -474,23 +299,10 @@ function makeConversation(): Conversation {
|
|
|
474
299
|
);
|
|
475
300
|
}
|
|
476
301
|
|
|
477
|
-
|
|
478
|
-
count: number,
|
|
479
|
-
bytesPerImage = 20_000,
|
|
480
|
-
): UserMessageAttachment[] {
|
|
481
|
-
return Array.from({ length: count }, (_, i) => ({
|
|
482
|
-
filename: `shot-${i + 1}.png`,
|
|
483
|
-
mimeType: "image/png",
|
|
484
|
-
data: `${i}${"A".repeat(bytesPerImage)}`,
|
|
485
|
-
}));
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
describe("provider ordering error retry", () => {
|
|
302
|
+
describe("terminal provider ordering error", () => {
|
|
489
303
|
beforeEach(() => {
|
|
490
304
|
agentLoopRunCount = 0;
|
|
491
305
|
firstRunErrorMode = "ordering";
|
|
492
|
-
maybeCompactCalls = [];
|
|
493
|
-
forceCompactionEnabled = false;
|
|
494
306
|
// The compaction pipeline runs through the plugin registry; re-register
|
|
495
307
|
// every default so it has a middleware to dispatch to. Collaborators are
|
|
496
308
|
// mocked above, so the default plugins' delegates go through the mocked
|
|
@@ -498,12 +310,13 @@ describe("provider ordering error retry", () => {
|
|
|
498
310
|
resetPluginRegistryAndRegisterDefaults();
|
|
499
311
|
});
|
|
500
312
|
|
|
501
|
-
test("
|
|
313
|
+
test("surfaces a durable conversation error without re-running the loop", async () => {
|
|
314
|
+
// GIVEN the agent loop reports an unrecovered ordering error
|
|
502
315
|
firstRunErrorMode = "ordering";
|
|
503
|
-
|
|
504
316
|
const conversation = makeConversation();
|
|
505
317
|
await conversation.loadFromDb();
|
|
506
318
|
|
|
319
|
+
// WHEN the conversation processes a message
|
|
507
320
|
const events: Array<Record<string, unknown>> = [];
|
|
508
321
|
await conversation.processMessage({
|
|
509
322
|
content: "Hello",
|
|
@@ -511,43 +324,26 @@ describe("provider ordering error retry", () => {
|
|
|
511
324
|
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
512
325
|
});
|
|
513
326
|
|
|
514
|
-
//
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
test("[experimental] retry succeeds with repaired history and no spurious error event", async () => {
|
|
519
|
-
firstRunErrorMode = "ordering";
|
|
520
|
-
|
|
521
|
-
const conversation = makeConversation();
|
|
522
|
-
await conversation.loadFromDb();
|
|
523
|
-
|
|
524
|
-
const events: Array<Record<string, unknown>> = [];
|
|
525
|
-
await conversation.processMessage({
|
|
526
|
-
content: "Hello",
|
|
527
|
-
attachments: [],
|
|
528
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
// Should have a message_complete event (from successful retry)
|
|
532
|
-
const messageComplete = events.find((e) => e.type === "message_complete");
|
|
533
|
-
expect(messageComplete).toBeDefined();
|
|
534
|
-
|
|
535
|
-
// Ordering error should be suppressed when retry succeeds — no error events
|
|
536
|
-
const errorEvents = events.filter((e) => e.type === "error");
|
|
537
|
-
expect(errorEvents.length).toBe(0);
|
|
327
|
+
// THEN the wrapper invokes the loop exactly once — ordering recovery is
|
|
328
|
+
// the loop's responsibility, not a wrapper-level re-run
|
|
329
|
+
expect(agentLoopRunCount).toBe(1);
|
|
538
330
|
|
|
539
|
-
//
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
331
|
+
// AND the error is surfaced as a durable conversation_error consistent
|
|
332
|
+
// with every other terminal provider error
|
|
333
|
+
const conversationError = events.find(
|
|
334
|
+
(e) => e.type === "conversation_error",
|
|
335
|
+
);
|
|
336
|
+
expect(conversationError).toBeDefined();
|
|
337
|
+
expect(conversationError?.code).toBe("PROVIDER_ORDERING");
|
|
543
338
|
});
|
|
544
339
|
|
|
545
|
-
test("
|
|
340
|
+
test("runs the loop once and emits no error on a normal turn", async () => {
|
|
341
|
+
// GIVEN the agent loop completes the turn without error
|
|
546
342
|
firstRunErrorMode = "none";
|
|
547
|
-
|
|
548
343
|
const conversation = makeConversation();
|
|
549
344
|
await conversation.loadFromDb();
|
|
550
345
|
|
|
346
|
+
// WHEN the conversation processes a message
|
|
551
347
|
const events: Array<Record<string, unknown>> = [];
|
|
552
348
|
await conversation.processMessage({
|
|
553
349
|
content: "Hello",
|
|
@@ -555,145 +351,10 @@ describe("provider ordering error retry", () => {
|
|
|
555
351
|
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
556
352
|
});
|
|
557
353
|
|
|
558
|
-
//
|
|
354
|
+
// THEN the loop ran once and the assistant response was persisted
|
|
559
355
|
expect(agentLoopRunCount).toBe(1);
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
test("context-too-large triggers one forced-compaction retry for image-heavy input", async () => {
|
|
563
|
-
firstRunErrorMode = "context_too_large";
|
|
564
|
-
forceCompactionEnabled = true;
|
|
565
|
-
|
|
566
|
-
const conversation = makeConversation();
|
|
567
|
-
await conversation.loadFromDb();
|
|
568
|
-
|
|
569
|
-
const events: Array<Record<string, unknown>> = [];
|
|
570
|
-
await conversation.processMessage({
|
|
571
|
-
content: "Please compare these images.",
|
|
572
|
-
attachments: makeImageAttachments(8),
|
|
573
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
expect(agentLoopRunCount).toBe(2);
|
|
577
|
-
expect(maybeCompactCalls).toEqual([{ force: true }]);
|
|
578
|
-
expect(events.some((e) => e.type === "message_complete")).toBe(true);
|
|
579
356
|
expect(events.some((e) => e.type === "conversation_error")).toBe(false);
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
test("context-too-large exhausts reducer tiers without compaction — error surfaces after emergency attempt", async () => {
|
|
583
|
-
firstRunErrorMode = "context_too_large";
|
|
584
|
-
forceCompactionEnabled = false;
|
|
585
|
-
|
|
586
|
-
const conversation = makeConversation();
|
|
587
|
-
await conversation.loadFromDb();
|
|
588
|
-
|
|
589
|
-
const events: Array<Record<string, unknown>> = [];
|
|
590
|
-
await conversation.processMessage({
|
|
591
|
-
content: "Please compare these images.",
|
|
592
|
-
attachments: makeImageAttachments(8),
|
|
593
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
// The convergence loop enters and applies the reducer (which returns
|
|
597
|
-
// exhausted:true). With the early-break removed, the loop still re-runs
|
|
598
|
-
// the agent loop once after the reducer. Since compaction didn't help
|
|
599
|
-
// (forceCompactionEnabled=false), the second run also fails with
|
|
600
|
-
// context_too_large. The overflow policy (fail_gracefully) surfaces error.
|
|
601
|
-
expect(agentLoopRunCount).toBe(2);
|
|
602
|
-
// One maybeCompact call: the reducer's compactFn (force:true). The
|
|
603
|
-
// orchestrator's start-of-turn auto-compact no longer runs — the loop's
|
|
604
|
-
// pre-call budget gate owns turn-start compaction and doesn't trip here.
|
|
605
|
-
expect(maybeCompactCalls).toEqual([{ force: true }]);
|
|
606
|
-
|
|
607
|
-
expect(events.some((e) => e.type === "conversation_error")).toBe(true);
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
test("context-too-large still surfaces when no media payloads are available to trim", async () => {
|
|
611
|
-
firstRunErrorMode = "context_too_large";
|
|
612
|
-
forceCompactionEnabled = false;
|
|
613
|
-
|
|
614
|
-
const conversation = makeConversation();
|
|
615
|
-
await conversation.loadFromDb();
|
|
616
|
-
|
|
617
|
-
const events: Array<Record<string, unknown>> = [];
|
|
618
|
-
await conversation.processMessage({
|
|
619
|
-
content: "No attachments here.",
|
|
620
|
-
attachments: [],
|
|
621
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
// Same as above — convergence loop re-runs agent loop, which also fails.
|
|
625
|
-
expect(agentLoopRunCount).toBe(2);
|
|
626
|
-
expect(maybeCompactCalls).toEqual([{ force: true }]);
|
|
627
|
-
expect(events.some((e) => e.type === "conversation_error")).toBe(true);
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
test("context-too-large phrase also triggers one forced-compaction retry", async () => {
|
|
631
|
-
firstRunErrorMode = "context_too_large_phrase";
|
|
632
|
-
forceCompactionEnabled = true;
|
|
633
|
-
|
|
634
|
-
const conversation = makeConversation();
|
|
635
|
-
await conversation.loadFromDb();
|
|
636
|
-
|
|
637
|
-
const events: Array<Record<string, unknown>> = [];
|
|
638
|
-
await conversation.processMessage({
|
|
639
|
-
content: "Please compare these images.",
|
|
640
|
-
attachments: makeImageAttachments(4),
|
|
641
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
expect(agentLoopRunCount).toBe(2);
|
|
645
|
-
expect(maybeCompactCalls).toEqual([{ force: true }]);
|
|
646
|
-
expect(events.some((e) => e.type === "message_complete")).toBe(true);
|
|
647
|
-
expect(events.some((e) => e.type === "conversation_error")).toBe(false);
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
test("ProviderError with statusCode 413 triggers forced-compaction retry via classifyConversationError", async () => {
|
|
651
|
-
firstRunErrorMode = "context_too_large_413";
|
|
652
|
-
forceCompactionEnabled = true;
|
|
653
|
-
|
|
654
|
-
const conversation = makeConversation();
|
|
655
|
-
await conversation.loadFromDb();
|
|
656
|
-
|
|
657
|
-
const events: Array<Record<string, unknown>> = [];
|
|
658
|
-
await conversation.processMessage({
|
|
659
|
-
content: "Please compare these images.",
|
|
660
|
-
attachments: makeImageAttachments(8),
|
|
661
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
// The 413 ProviderError message "request entity too large" doesn't match
|
|
665
|
-
// the regex patterns, but classifyConversationError recognizes statusCode 413
|
|
666
|
-
// as CONTEXT_TOO_LARGE and sets contextTooLargeDetected = true.
|
|
667
|
-
expect(agentLoopRunCount).toBe(2);
|
|
668
|
-
expect(maybeCompactCalls).toEqual([{ force: true }]);
|
|
669
|
-
expect(events.some((e) => e.type === "message_complete")).toBe(true);
|
|
670
|
-
expect(events.some((e) => e.type === "conversation_error")).toBe(false);
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
test("context-too-large after progress surfaces error instead of silent failure", async () => {
|
|
674
|
-
firstRunErrorMode = "context_too_large_413_with_progress";
|
|
675
|
-
forceCompactionEnabled = false;
|
|
676
|
-
|
|
677
|
-
const conversation = makeConversation();
|
|
678
|
-
await conversation.loadFromDb();
|
|
679
|
-
|
|
680
|
-
const events: Array<Record<string, unknown>> = [];
|
|
681
|
-
await conversation.processMessage({
|
|
682
|
-
content: "Run some tools then hit the limit.",
|
|
683
|
-
attachments: [],
|
|
684
|
-
onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
// Two agent loop runs — first makes progress then 413, convergence loop
|
|
688
|
-
// re-runs after reducer (which returns exhausted). Second run also fails
|
|
689
|
-
// since compaction didn't help (forceCompactionEnabled=false).
|
|
690
|
-
expect(agentLoopRunCount).toBe(2);
|
|
691
|
-
|
|
692
|
-
// The error must be surfaced to clients via conversation_error, not silently swallowed.
|
|
693
|
-
const conversationError = events.find(
|
|
694
|
-
(e) => e.type === "conversation_error",
|
|
695
|
-
) as { code?: string } | undefined;
|
|
696
|
-
expect(conversationError).toBeDefined();
|
|
697
|
-
expect(conversationError?.code).toBe("CONTEXT_TOO_LARGE");
|
|
357
|
+
const messages = conversation.getMessages();
|
|
358
|
+
expect(messages[messages.length - 1].role).toBe("assistant");
|
|
698
359
|
});
|
|
699
360
|
});
|
|
@@ -272,6 +272,7 @@ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
|
272
272
|
async maybeCompact() {
|
|
273
273
|
return { compacted: false };
|
|
274
274
|
}
|
|
275
|
+
resetOverflowRecovery() {}
|
|
275
276
|
},
|
|
276
277
|
createContextSummaryMessage: () => ({
|
|
277
278
|
role: "user",
|
|
@@ -368,7 +369,6 @@ mock.module("../agent/loop.js", () => ({
|
|
|
368
369
|
resolveResult({
|
|
369
370
|
history,
|
|
370
371
|
exitReason: pending.exitReason,
|
|
371
|
-
appendedNewMessages: history.length > messages.length,
|
|
372
372
|
newMessages: history.slice(messages.length),
|
|
373
373
|
}),
|
|
374
374
|
reject,
|
|
@@ -2197,44 +2197,6 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2197
2197
|
// C should have completed successfully
|
|
2198
2198
|
expect(eventsC.some((e) => e.type === "message_complete")).toBe(true);
|
|
2199
2199
|
});
|
|
2200
|
-
|
|
2201
|
-
test("onCheckpoint callback is passed to both initial and retry runs", async () => {
|
|
2202
|
-
const conversation = makeConversation();
|
|
2203
|
-
await conversation.loadFromDb();
|
|
2204
|
-
|
|
2205
|
-
// Start processing
|
|
2206
|
-
const p1 = conversation.processMessage({
|
|
2207
|
-
content: "msg-1",
|
|
2208
|
-
attachments: [],
|
|
2209
|
-
requestId: "req-1",
|
|
2210
|
-
});
|
|
2211
|
-
await waitForPendingRun(1);
|
|
2212
|
-
|
|
2213
|
-
// The first run should have onCheckpoint
|
|
2214
|
-
expect(pendingRuns[0].onCheckpoint).toBeDefined();
|
|
2215
|
-
|
|
2216
|
-
// Simulate an ordering error: emit error + resolve with same length
|
|
2217
|
-
// to trigger the retry path
|
|
2218
|
-
const run0 = pendingRuns[0];
|
|
2219
|
-
run0.onEvent({
|
|
2220
|
-
type: "error",
|
|
2221
|
-
error: new Error(
|
|
2222
|
-
"tool_result block not immediately after tool_use block",
|
|
2223
|
-
),
|
|
2224
|
-
});
|
|
2225
|
-
// Resolve with the same messages (no new messages appended = ordering error)
|
|
2226
|
-
run0.resolve([...run0.messages]);
|
|
2227
|
-
|
|
2228
|
-
// Wait for the retry run
|
|
2229
|
-
await waitForPendingRun(2);
|
|
2230
|
-
|
|
2231
|
-
// The retry run should also have onCheckpoint
|
|
2232
|
-
expect(pendingRuns[1].onCheckpoint).toBeDefined();
|
|
2233
|
-
|
|
2234
|
-
// Complete retry cleanly
|
|
2235
|
-
await resolveRun(1);
|
|
2236
|
-
await p1;
|
|
2237
|
-
});
|
|
2238
2200
|
});
|
|
2239
2201
|
|
|
2240
2202
|
// ---------------------------------------------------------------------------
|