@vellumai/assistant 0.6.5 → 0.6.6
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 +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -31,13 +31,13 @@ describe("resolveOverflowAction", () => {
|
|
|
31
31
|
|
|
32
32
|
// ── Interactive defaults ──
|
|
33
33
|
|
|
34
|
-
test("interactive session with default config
|
|
34
|
+
test("interactive session with default config auto-compresses", () => {
|
|
35
35
|
expect(
|
|
36
36
|
resolveOverflowAction({
|
|
37
37
|
overflowRecovery: DEFAULTS,
|
|
38
38
|
isInteractive: true,
|
|
39
39
|
}),
|
|
40
|
-
).toBe("
|
|
40
|
+
).toBe("auto_compress_latest_turn");
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
// ── Non-interactive defaults ──
|
|
@@ -53,7 +53,7 @@ describe("resolveOverflowAction", () => {
|
|
|
53
53
|
|
|
54
54
|
// ── Interactive with explicit policies ──
|
|
55
55
|
|
|
56
|
-
test("interactive + truncate policy
|
|
56
|
+
test("interactive + truncate policy auto-compresses", () => {
|
|
57
57
|
expect(
|
|
58
58
|
resolveOverflowAction({
|
|
59
59
|
overflowRecovery: {
|
|
@@ -62,10 +62,10 @@ describe("resolveOverflowAction", () => {
|
|
|
62
62
|
},
|
|
63
63
|
isInteractive: true,
|
|
64
64
|
}),
|
|
65
|
-
).toBe("
|
|
65
|
+
).toBe("auto_compress_latest_turn");
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
test("interactive + summarize policy
|
|
68
|
+
test("interactive + summarize policy auto-compresses", () => {
|
|
69
69
|
expect(
|
|
70
70
|
resolveOverflowAction({
|
|
71
71
|
overflowRecovery: {
|
|
@@ -74,7 +74,7 @@ describe("resolveOverflowAction", () => {
|
|
|
74
74
|
},
|
|
75
75
|
isInteractive: true,
|
|
76
76
|
}),
|
|
77
|
-
).toBe("
|
|
77
|
+
).toBe("auto_compress_latest_turn");
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
test("interactive + drop policy fails gracefully", () => {
|
|
@@ -139,7 +139,7 @@ describe("resolveOverflowAction", () => {
|
|
|
139
139
|
},
|
|
140
140
|
isInteractive: true,
|
|
141
141
|
}),
|
|
142
|
-
).toBe("
|
|
142
|
+
).toBe("auto_compress_latest_turn");
|
|
143
143
|
});
|
|
144
144
|
|
|
145
145
|
test("non-interactive policy is independent of interactive setting", () => {
|
|
@@ -3,16 +3,19 @@ import { describe, expect, test } from "bun:test";
|
|
|
3
3
|
import type { ContextWindowConfig } from "../config/types.js";
|
|
4
4
|
import { estimateTextTokens } from "../context/token-estimator.js";
|
|
5
5
|
import {
|
|
6
|
+
clampSummaryAtSectionBoundary,
|
|
6
7
|
CONTEXT_SUMMARY_MARKER,
|
|
7
8
|
ContextWindowManager,
|
|
8
9
|
createContextSummaryMessage,
|
|
9
10
|
getSummaryFromContextMessage,
|
|
11
|
+
stripCompactionOnlyInjections,
|
|
10
12
|
} from "../context/window-manager.js";
|
|
11
13
|
import type {
|
|
12
14
|
ContentBlock,
|
|
13
15
|
Message,
|
|
14
16
|
Provider,
|
|
15
17
|
ProviderResponse,
|
|
18
|
+
SendMessageOptions,
|
|
16
19
|
} from "../providers/types.js";
|
|
17
20
|
|
|
18
21
|
function makeConfig(
|
|
@@ -320,12 +323,12 @@ describe("ContextWindowManager", () => {
|
|
|
320
323
|
provider,
|
|
321
324
|
systemPrompt: "system prompt",
|
|
322
325
|
config: makeConfig({
|
|
323
|
-
maxInputTokens:
|
|
324
|
-
targetBudgetRatio: 0.
|
|
325
|
-
compactThreshold: 0.
|
|
326
|
+
maxInputTokens: 2000,
|
|
327
|
+
targetBudgetRatio: 0.4,
|
|
328
|
+
compactThreshold: 0.35,
|
|
326
329
|
}),
|
|
327
330
|
});
|
|
328
|
-
const long = "f".repeat(
|
|
331
|
+
const long = "f".repeat(1500);
|
|
329
332
|
const history: Message[] = [
|
|
330
333
|
{
|
|
331
334
|
role: "user",
|
|
@@ -1668,4 +1671,352 @@ describe("ContextWindowManager", () => {
|
|
|
1668
1671
|
expect(result.compactedMessages).toBe(3);
|
|
1669
1672
|
expect(result.messages).toHaveLength(1);
|
|
1670
1673
|
});
|
|
1674
|
+
|
|
1675
|
+
test("summary provider call includes callSite: conversationSummarization", async () => {
|
|
1676
|
+
// Regression guard for JARVIS-587: without the callSite, the summary
|
|
1677
|
+
// call fell through to `llm.default` (opus + effort=max + thinking
|
|
1678
|
+
// enabled) and exceeded the 30s plugin pipeline budget on ~150k-token
|
|
1679
|
+
// transcripts. The fix is to route the summary call through the
|
|
1680
|
+
// dedicated `conversationSummarization` call-site config.
|
|
1681
|
+
const capturedOptions: (SendMessageOptions | undefined)[] = [];
|
|
1682
|
+
const provider: Provider = {
|
|
1683
|
+
name: "mock",
|
|
1684
|
+
async sendMessage(
|
|
1685
|
+
_messages: Message[],
|
|
1686
|
+
_tools: unknown,
|
|
1687
|
+
_systemPrompt: unknown,
|
|
1688
|
+
options?: SendMessageOptions,
|
|
1689
|
+
): Promise<ProviderResponse> {
|
|
1690
|
+
capturedOptions.push(options);
|
|
1691
|
+
return {
|
|
1692
|
+
content: [{ type: "text", text: "## Goals\n- summary" }],
|
|
1693
|
+
model: "mock-model",
|
|
1694
|
+
usage: { inputTokens: 50, outputTokens: 10 },
|
|
1695
|
+
stopReason: "end_turn",
|
|
1696
|
+
};
|
|
1697
|
+
},
|
|
1698
|
+
};
|
|
1699
|
+
const manager = new ContextWindowManager({
|
|
1700
|
+
provider,
|
|
1701
|
+
systemPrompt: "system prompt",
|
|
1702
|
+
config: makeConfig({ maxInputTokens: 600 }),
|
|
1703
|
+
});
|
|
1704
|
+
const long = "x".repeat(240);
|
|
1705
|
+
const history: Message[] = [
|
|
1706
|
+
message("user", `u1 ${long}`),
|
|
1707
|
+
message("assistant", `a1 ${long}`),
|
|
1708
|
+
message("user", `u2 ${long}`),
|
|
1709
|
+
message("assistant", `a2 ${long}`),
|
|
1710
|
+
message("user", `u3 ${long}`),
|
|
1711
|
+
message("assistant", `a3 ${long}`),
|
|
1712
|
+
];
|
|
1713
|
+
|
|
1714
|
+
const result = await manager.maybeCompact(history);
|
|
1715
|
+
expect(result.compacted).toBe(true);
|
|
1716
|
+
expect(capturedOptions.length).toBeGreaterThan(0);
|
|
1717
|
+
for (const options of capturedOptions) {
|
|
1718
|
+
expect(options?.config?.callSite).toBe("conversationSummarization");
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
});
|
|
1722
|
+
|
|
1723
|
+
describe("stripCompactionOnlyInjections", () => {
|
|
1724
|
+
test("removes memory, turn_context, and workspace text blocks from user messages", () => {
|
|
1725
|
+
const messages: Message[] = [
|
|
1726
|
+
{
|
|
1727
|
+
role: "user",
|
|
1728
|
+
content: [
|
|
1729
|
+
{
|
|
1730
|
+
type: "text",
|
|
1731
|
+
text: "<memory __injected>\nrecall notes\n</memory>",
|
|
1732
|
+
},
|
|
1733
|
+
{
|
|
1734
|
+
type: "text",
|
|
1735
|
+
text: "<turn_context>\nActor: Alice\n</turn_context>",
|
|
1736
|
+
},
|
|
1737
|
+
{ type: "text", text: "real user content" },
|
|
1738
|
+
],
|
|
1739
|
+
},
|
|
1740
|
+
{
|
|
1741
|
+
role: "assistant",
|
|
1742
|
+
content: [{ type: "text", text: "assistant reply" }],
|
|
1743
|
+
},
|
|
1744
|
+
];
|
|
1745
|
+
const stripped = stripCompactionOnlyInjections(messages);
|
|
1746
|
+
expect(stripped).toHaveLength(2);
|
|
1747
|
+
const firstText = (stripped[0].content[0] as { text: string }).text;
|
|
1748
|
+
expect(firstText).toBe("real user content");
|
|
1749
|
+
expect(stripped[0].content).toHaveLength(1);
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1752
|
+
test("drops user messages that become empty after stripping", () => {
|
|
1753
|
+
const messages: Message[] = [
|
|
1754
|
+
{
|
|
1755
|
+
role: "user",
|
|
1756
|
+
content: [
|
|
1757
|
+
{ type: "text", text: "<memory __injected>\nonly memory\n</memory>" },
|
|
1758
|
+
],
|
|
1759
|
+
},
|
|
1760
|
+
{ role: "user", content: [{ type: "text", text: "real content" }] },
|
|
1761
|
+
];
|
|
1762
|
+
const stripped = stripCompactionOnlyInjections(messages);
|
|
1763
|
+
expect(stripped).toHaveLength(1);
|
|
1764
|
+
expect((stripped[0].content[0] as { text: string }).text).toBe(
|
|
1765
|
+
"real content",
|
|
1766
|
+
);
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
test("leaves assistant messages and non-text blocks untouched", () => {
|
|
1770
|
+
const messages: Message[] = [
|
|
1771
|
+
{
|
|
1772
|
+
role: "assistant",
|
|
1773
|
+
content: [
|
|
1774
|
+
{
|
|
1775
|
+
type: "text",
|
|
1776
|
+
text: "<turn_context>\nnot really injected\n</turn_context>",
|
|
1777
|
+
},
|
|
1778
|
+
],
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
role: "user",
|
|
1782
|
+
content: [
|
|
1783
|
+
{
|
|
1784
|
+
type: "tool_result",
|
|
1785
|
+
tool_use_id: "t1",
|
|
1786
|
+
content: "<memory>fake</memory>",
|
|
1787
|
+
},
|
|
1788
|
+
{ type: "text", text: "user reply" },
|
|
1789
|
+
],
|
|
1790
|
+
},
|
|
1791
|
+
];
|
|
1792
|
+
const stripped = stripCompactionOnlyInjections(messages);
|
|
1793
|
+
expect(stripped).toHaveLength(2);
|
|
1794
|
+
expect((stripped[0].content[0] as { text: string }).text).toContain(
|
|
1795
|
+
"turn_context",
|
|
1796
|
+
);
|
|
1797
|
+
expect(stripped[1].content).toHaveLength(2);
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1800
|
+
test("preserves user prose that merely mentions ambiguous tag names", () => {
|
|
1801
|
+
// Common-word bare tags embedded in legitimate user prose (discussions of
|
|
1802
|
+
// XML, system terminology, etc.) must survive stripping because they are
|
|
1803
|
+
// not shaped like a runtime injection — no leading newline after the
|
|
1804
|
+
// open tag, or other prose surrounds the tag.
|
|
1805
|
+
const messages: Message[] = [
|
|
1806
|
+
{
|
|
1807
|
+
role: "user",
|
|
1808
|
+
content: [
|
|
1809
|
+
{
|
|
1810
|
+
type: "text",
|
|
1811
|
+
text: "<memory> is a tag I'd like to add to my parser",
|
|
1812
|
+
},
|
|
1813
|
+
],
|
|
1814
|
+
},
|
|
1815
|
+
{
|
|
1816
|
+
role: "user",
|
|
1817
|
+
content: [
|
|
1818
|
+
{
|
|
1819
|
+
type: "text",
|
|
1820
|
+
text: "checking <workspace> usage across the repo, any thoughts?",
|
|
1821
|
+
},
|
|
1822
|
+
],
|
|
1823
|
+
},
|
|
1824
|
+
{
|
|
1825
|
+
role: "user",
|
|
1826
|
+
content: [
|
|
1827
|
+
{
|
|
1828
|
+
type: "text",
|
|
1829
|
+
text: "what is <knowledge_base> in this context?",
|
|
1830
|
+
},
|
|
1831
|
+
],
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
role: "user",
|
|
1835
|
+
content: [
|
|
1836
|
+
{ type: "text", text: "<pkb> sounds like a short name — wrong?" },
|
|
1837
|
+
],
|
|
1838
|
+
},
|
|
1839
|
+
{
|
|
1840
|
+
role: "user",
|
|
1841
|
+
content: [
|
|
1842
|
+
{
|
|
1843
|
+
type: "text",
|
|
1844
|
+
text: "when the model hits a <system_reminder>, what happens next?",
|
|
1845
|
+
},
|
|
1846
|
+
],
|
|
1847
|
+
},
|
|
1848
|
+
];
|
|
1849
|
+
const stripped = stripCompactionOnlyInjections(messages);
|
|
1850
|
+
expect(stripped).toHaveLength(messages.length);
|
|
1851
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1852
|
+
expect(stripped[i].content).toHaveLength(1);
|
|
1853
|
+
expect((stripped[i].content[0] as { text: string }).text).toBe(
|
|
1854
|
+
(messages[i].content[0] as { text: string }).text,
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
test("still strips runtime-shaped wrapped blocks for ambiguous tag names", () => {
|
|
1860
|
+
// Bare-tag blocks with a newline after the open tag and a matching close
|
|
1861
|
+
// tag (e.g. `<memory>\n...\n</memory>`) match the wrapped-strip path.
|
|
1862
|
+
// This covers both the current runtime emission shape and blocks
|
|
1863
|
+
// persisted before the `__injected` attribute existed — the prefix list
|
|
1864
|
+
// handles `__injected`-attributed tags, and the wrapped matcher handles
|
|
1865
|
+
// the bare-tag wrap shape.
|
|
1866
|
+
const messages: Message[] = [
|
|
1867
|
+
{
|
|
1868
|
+
role: "user",
|
|
1869
|
+
content: [
|
|
1870
|
+
{ type: "text", text: "<memory>\nlegacy recall blob\n</memory>" },
|
|
1871
|
+
{ type: "text", text: "actual user content" },
|
|
1872
|
+
],
|
|
1873
|
+
},
|
|
1874
|
+
{
|
|
1875
|
+
role: "user",
|
|
1876
|
+
content: [
|
|
1877
|
+
{
|
|
1878
|
+
type: "text",
|
|
1879
|
+
text: "<workspace>\nRoot: /home\nFiles: a, b\n</workspace>",
|
|
1880
|
+
},
|
|
1881
|
+
{ type: "text", text: "more prose" },
|
|
1882
|
+
],
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
role: "user",
|
|
1886
|
+
content: [
|
|
1887
|
+
{
|
|
1888
|
+
type: "text",
|
|
1889
|
+
text: "<system_reminder>\nread your PKB\n</system_reminder>",
|
|
1890
|
+
},
|
|
1891
|
+
{ type: "text", text: "ok" },
|
|
1892
|
+
],
|
|
1893
|
+
},
|
|
1894
|
+
];
|
|
1895
|
+
const stripped = stripCompactionOnlyInjections(messages);
|
|
1896
|
+
expect(stripped).toHaveLength(3);
|
|
1897
|
+
for (const msg of stripped) {
|
|
1898
|
+
expect(msg.content).toHaveLength(1);
|
|
1899
|
+
}
|
|
1900
|
+
expect((stripped[0].content[0] as { text: string }).text).toBe(
|
|
1901
|
+
"actual user content",
|
|
1902
|
+
);
|
|
1903
|
+
expect((stripped[1].content[0] as { text: string }).text).toBe(
|
|
1904
|
+
"more prose",
|
|
1905
|
+
);
|
|
1906
|
+
expect((stripped[2].content[0] as { text: string }).text).toBe("ok");
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
test("does not strip a user's inline snippet that is not shaped like an injection", () => {
|
|
1910
|
+
// A user quoting a `<memory>...</memory>` snippet alongside prose in the
|
|
1911
|
+
// SAME text block should survive — the block does not start with
|
|
1912
|
+
// `<memory>\n` (there's surrounding prose) so the wrapped-tag match
|
|
1913
|
+
// does not trigger.
|
|
1914
|
+
const messages: Message[] = [
|
|
1915
|
+
{
|
|
1916
|
+
role: "user",
|
|
1917
|
+
content: [
|
|
1918
|
+
{
|
|
1919
|
+
type: "text",
|
|
1920
|
+
text: "Here's the XML I'm working with: <memory>x</memory> — what do you think?",
|
|
1921
|
+
},
|
|
1922
|
+
],
|
|
1923
|
+
},
|
|
1924
|
+
];
|
|
1925
|
+
const stripped = stripCompactionOnlyInjections(messages);
|
|
1926
|
+
expect(stripped).toHaveLength(1);
|
|
1927
|
+
expect((stripped[0].content[0] as { text: string }).text).toContain(
|
|
1928
|
+
"<memory>x</memory>",
|
|
1929
|
+
);
|
|
1930
|
+
});
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
describe("summarizer input excludes runtime injections", () => {
|
|
1934
|
+
test("maybeCompact does not pass memory/turn_context text to the summarizer", async () => {
|
|
1935
|
+
const seenPrompts: string[] = [];
|
|
1936
|
+
const provider = createProvider((messages) => {
|
|
1937
|
+
for (const msg of messages) {
|
|
1938
|
+
for (const block of msg.content) {
|
|
1939
|
+
if (block.type === "text") seenPrompts.push(block.text);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
return {
|
|
1943
|
+
content: [
|
|
1944
|
+
{
|
|
1945
|
+
type: "text",
|
|
1946
|
+
text: "## Facts Worth Remembering\n- summary produced",
|
|
1947
|
+
},
|
|
1948
|
+
],
|
|
1949
|
+
model: "mock",
|
|
1950
|
+
usage: { inputTokens: 100, outputTokens: 25 },
|
|
1951
|
+
stopReason: "end_turn",
|
|
1952
|
+
};
|
|
1953
|
+
});
|
|
1954
|
+
const manager = new ContextWindowManager({
|
|
1955
|
+
provider,
|
|
1956
|
+
systemPrompt: "system prompt",
|
|
1957
|
+
config: makeConfig({
|
|
1958
|
+
maxInputTokens: 2000,
|
|
1959
|
+
targetBudgetRatio: 0.4,
|
|
1960
|
+
compactThreshold: 0.35,
|
|
1961
|
+
}),
|
|
1962
|
+
});
|
|
1963
|
+
const long = "x".repeat(1500);
|
|
1964
|
+
const memoryBlob =
|
|
1965
|
+
"<memory __injected>\nBOB_ATTENDED_STANDUP_YESTERDAY\n</memory>";
|
|
1966
|
+
const turnCtx =
|
|
1967
|
+
"<turn_context>\nACTOR_METADATA_THAT_SHOULD_NOT_LEAK\n</turn_context>";
|
|
1968
|
+
const history: Message[] = [
|
|
1969
|
+
{
|
|
1970
|
+
role: "user",
|
|
1971
|
+
content: [
|
|
1972
|
+
{ type: "text", text: memoryBlob },
|
|
1973
|
+
{ type: "text", text: turnCtx },
|
|
1974
|
+
{ type: "text", text: `u1 ${long}` },
|
|
1975
|
+
],
|
|
1976
|
+
},
|
|
1977
|
+
message("assistant", `a1 ${long}`),
|
|
1978
|
+
message("user", `u2 ${long}`),
|
|
1979
|
+
message("assistant", `a2 ${long}`),
|
|
1980
|
+
message("user", `u3 ${long}`),
|
|
1981
|
+
];
|
|
1982
|
+
|
|
1983
|
+
const result = await manager.maybeCompact(history);
|
|
1984
|
+
expect(result.compacted).toBe(true);
|
|
1985
|
+
const joined = seenPrompts.join("\n");
|
|
1986
|
+
expect(joined).not.toContain("BOB_ATTENDED_STANDUP_YESTERDAY");
|
|
1987
|
+
expect(joined).not.toContain("ACTOR_METADATA_THAT_SHOULD_NOT_LEAK");
|
|
1988
|
+
expect(joined).not.toContain("<memory __injected>");
|
|
1989
|
+
expect(joined).not.toContain("<turn_context>");
|
|
1990
|
+
// Real conversation content should survive — at least one of the
|
|
1991
|
+
// middle turns (whose header/body is short enough to fit within the
|
|
1992
|
+
// capped transcript budget) should appear in the summarizer input.
|
|
1993
|
+
expect(joined).toMatch(/u2 |a1 /);
|
|
1994
|
+
});
|
|
1995
|
+
});
|
|
1996
|
+
|
|
1997
|
+
describe("clampSummaryAtSectionBoundary", () => {
|
|
1998
|
+
test("returns the input unchanged when under the limit", () => {
|
|
1999
|
+
const summary = "## Decisions\nWe decided to ship.";
|
|
2000
|
+
expect(clampSummaryAtSectionBoundary(summary, 1000)).toBe(summary);
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
test("truncates at a `## ` boundary when one exists in the allowed region", () => {
|
|
2004
|
+
const keeper = "## Facts\n" + "a".repeat(200);
|
|
2005
|
+
const dropped = "## Open Threads\n" + "b".repeat(500);
|
|
2006
|
+
const summary = `${keeper}\n${dropped}`;
|
|
2007
|
+
const maxChars = keeper.length + 20;
|
|
2008
|
+
const clamped = clampSummaryAtSectionBoundary(summary, maxChars);
|
|
2009
|
+
expect(clamped.endsWith("...")).toBe(true);
|
|
2010
|
+
expect(clamped).not.toContain("## Open Threads");
|
|
2011
|
+
expect(clamped).toContain("## Facts");
|
|
2012
|
+
// No mid-header cut: nothing that looks like a partial heading.
|
|
2013
|
+
expect(/##\s*$/.test(clamped)).toBe(false);
|
|
2014
|
+
});
|
|
2015
|
+
|
|
2016
|
+
test("falls back to a hard cut when no section boundary is past the midpoint", () => {
|
|
2017
|
+
const body = "no section headers in this output " + "z".repeat(1000);
|
|
2018
|
+
const clamped = clampSummaryAtSectionBoundary(body, 100);
|
|
2019
|
+
expect(clamped.endsWith("...")).toBe(true);
|
|
2020
|
+
expect(clamped.length).toBeLessThanOrEqual(100);
|
|
2021
|
+
});
|
|
1671
2022
|
});
|
|
@@ -25,7 +25,7 @@ mock.module("../providers/registry.js", () => ({
|
|
|
25
25
|
mock.module("../config/loader.js", () => ({
|
|
26
26
|
getConfig: () => ({
|
|
27
27
|
ui: {},
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
llm: {
|
|
30
30
|
default: {
|
|
31
31
|
provider: "mock-provider",
|
|
@@ -168,6 +168,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
168
168
|
getToolTokenBudget() {
|
|
169
169
|
return 0;
|
|
170
170
|
}
|
|
171
|
+
getResolvedTools() {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
171
174
|
getActiveModel() {
|
|
172
175
|
return undefined;
|
|
173
176
|
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
CheckpointInfo,
|
|
19
19
|
} from "../agent/loop.js";
|
|
20
20
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
21
|
+
import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
|
|
21
22
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
22
23
|
|
|
23
24
|
// ── Module mocks (must precede imports of the module under test) ─────
|
|
@@ -71,12 +72,23 @@ mock.module("../config/loader.js", () => ({
|
|
|
71
72
|
// Token estimator — controllable per-test via mockEstimateTokens.
|
|
72
73
|
// Can be a number (constant), a no-arg function, or a function that
|
|
73
74
|
// receives the messages array for dynamic behavior based on content.
|
|
75
|
+
// Both the calibrated entry point (`estimatePromptTokens`, used in the
|
|
76
|
+
// convergence path) and the raw entry point (`estimatePromptTokensRaw`,
|
|
77
|
+
// used by the default `tokenEstimate` plugin pipeline for preflight/mid-
|
|
78
|
+
// loop) are stubbed so either call site can drive the test.
|
|
74
79
|
let mockEstimateTokens: number | ((msgs?: Message[]) => number) = 1000;
|
|
75
80
|
mock.module("../context/token-estimator.js", () => ({
|
|
76
81
|
estimatePromptTokens: (msgs: Message[]) =>
|
|
77
82
|
typeof mockEstimateTokens === "function"
|
|
78
83
|
? mockEstimateTokens(msgs)
|
|
79
84
|
: mockEstimateTokens,
|
|
85
|
+
estimatePromptTokensRaw: (msgs: Message[]) =>
|
|
86
|
+
typeof mockEstimateTokens === "function"
|
|
87
|
+
? mockEstimateTokens(msgs)
|
|
88
|
+
: mockEstimateTokens,
|
|
89
|
+
// Default plugin multiplies-in tool tokens via this helper; 0 keeps the
|
|
90
|
+
// stubbed raw value unchanged.
|
|
91
|
+
estimateToolsTokens: () => 0,
|
|
80
92
|
// Conversation agent loop now calls this helper to canonicalize the
|
|
81
93
|
// provider key shared with the calibration system. The tests here
|
|
82
94
|
// don't exercise that path, so a passthrough mock is fine.
|
|
@@ -126,27 +138,6 @@ mock.module("../daemon/context-overflow-policy.js", () => ({
|
|
|
126
138
|
resolveOverflowAction: () => mockOverflowAction,
|
|
127
139
|
}));
|
|
128
140
|
|
|
129
|
-
// Approval: default to denied
|
|
130
|
-
let mockApprovalResult = { approved: false };
|
|
131
|
-
mock.module("../daemon/context-overflow-approval.js", () => ({
|
|
132
|
-
requestCompressionApproval: async () => mockApprovalResult,
|
|
133
|
-
CONTEXT_OVERFLOW_TOOL_NAME: "context_overflow_compression",
|
|
134
|
-
}));
|
|
135
|
-
|
|
136
|
-
let hookBlocked = false;
|
|
137
|
-
let hookBlockedBy = "";
|
|
138
|
-
|
|
139
|
-
mock.module("../hooks/manager.js", () => ({
|
|
140
|
-
getHookManager: () => ({
|
|
141
|
-
trigger: async (hookName: string) => {
|
|
142
|
-
if (hookName === "pre-message" && hookBlocked) {
|
|
143
|
-
return { blocked: true, blockedBy: hookBlockedBy };
|
|
144
|
-
}
|
|
145
|
-
return { blocked: false };
|
|
146
|
-
},
|
|
147
|
-
}),
|
|
148
|
-
}));
|
|
149
|
-
|
|
150
141
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
151
142
|
getConversationType: () => "default",
|
|
152
143
|
setConversationOriginChannelIfUnset: () => {},
|
|
@@ -174,7 +165,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
174
165
|
getMessageById: () => null,
|
|
175
166
|
updateMessageContent: () => {},
|
|
176
167
|
updateMessageMetadata: () => {},
|
|
177
|
-
|
|
168
|
+
clearStrippedInjectionMetadataForConversation: () => {},
|
|
178
169
|
}));
|
|
179
170
|
|
|
180
171
|
mock.module("../memory/retriever.js", () => ({
|
|
@@ -380,7 +371,9 @@ type AgentLoopRun = (
|
|
|
380
371
|
onEvent: (event: AgentEvent) => void,
|
|
381
372
|
signal?: AbortSignal,
|
|
382
373
|
requestId?: string,
|
|
383
|
-
onCheckpoint?: (
|
|
374
|
+
onCheckpoint?: (
|
|
375
|
+
checkpoint: CheckpointInfo,
|
|
376
|
+
) => CheckpointDecision | Promise<CheckpointDecision>,
|
|
384
377
|
) => Promise<Message[]>;
|
|
385
378
|
|
|
386
379
|
function makeCtx(
|
|
@@ -410,6 +403,7 @@ function makeCtx(
|
|
|
410
403
|
agentLoop: {
|
|
411
404
|
run: agentLoopRun,
|
|
412
405
|
getToolTokenBudget: () => 0,
|
|
406
|
+
getResolvedTools: () => [],
|
|
413
407
|
// Tests in this file don't exercise calibration, so returning
|
|
414
408
|
// undefined is fine — the estimator falls back to the per-provider
|
|
415
409
|
// aggregate key.
|
|
@@ -570,14 +564,16 @@ function buildLongConversation(messageCount: number): Message[] {
|
|
|
570
564
|
// ── Tests ────────────────────────────────────────────────────────────
|
|
571
565
|
|
|
572
566
|
beforeEach(() => {
|
|
573
|
-
hookBlocked = false;
|
|
574
|
-
hookBlockedBy = "";
|
|
575
567
|
mockEstimateTokens = 1000;
|
|
576
568
|
mockReducerStepFn = null;
|
|
577
569
|
mockOverflowAction = "fail_gracefully";
|
|
578
|
-
mockApprovalResult = { approved: false };
|
|
579
570
|
mockApplyRuntimeInjections = (msgs) => msgs;
|
|
580
571
|
recordUsageMock.mockClear();
|
|
572
|
+
// Reset the plugin registry and re-register every default so the
|
|
573
|
+
// orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
|
|
574
|
+
// the default middleware, which in turn hits the mocked collaborators
|
|
575
|
+
// (`reduceContextOverflow`, `syncMessageToDisk`, …) these tests install.
|
|
576
|
+
resetPluginRegistryAndRegisterDefaults();
|
|
581
577
|
});
|
|
582
578
|
|
|
583
579
|
describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
@@ -1393,7 +1389,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1393
1389
|
// Call onCheckpoint — this should trigger the mid-loop budget check
|
|
1394
1390
|
// which sees 170_000 > 161_500 and returns "yield"
|
|
1395
1391
|
if (onCheckpoint) {
|
|
1396
|
-
const decision = onCheckpoint({
|
|
1392
|
+
const decision = await onCheckpoint({
|
|
1397
1393
|
turnIndex: 0,
|
|
1398
1394
|
toolCount: 1,
|
|
1399
1395
|
hasToolUse: true,
|
|
@@ -1567,7 +1563,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1567
1563
|
});
|
|
1568
1564
|
|
|
1569
1565
|
if (onCheckpoint) {
|
|
1570
|
-
const decision = onCheckpoint({
|
|
1566
|
+
const decision = await onCheckpoint({
|
|
1571
1567
|
turnIndex: i,
|
|
1572
1568
|
toolCount: 1,
|
|
1573
1569
|
hasToolUse: true,
|
|
@@ -1751,7 +1747,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1751
1747
|
|
|
1752
1748
|
// Always yield at checkpoint — simulates compaction not helping
|
|
1753
1749
|
if (onCheckpoint) {
|
|
1754
|
-
const decision = onCheckpoint({
|
|
1750
|
+
const decision = await onCheckpoint({
|
|
1755
1751
|
turnIndex: 0,
|
|
1756
1752
|
toolCount: 1,
|
|
1757
1753
|
hasToolUse: true,
|
|
@@ -1907,7 +1903,7 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1907
1903
|
|
|
1908
1904
|
// Always yield at checkpoint — simulates reduction not helping enough
|
|
1909
1905
|
if (onCheckpoint) {
|
|
1910
|
-
const decision = onCheckpoint({
|
|
1906
|
+
const decision = await onCheckpoint({
|
|
1911
1907
|
turnIndex: 0,
|
|
1912
1908
|
toolCount: 1,
|
|
1913
1909
|
hasToolUse: true,
|