@vellumai/assistant 0.6.4 → 0.6.5
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/.prettierignore +5 -0
- package/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/openapi.yaml +123 -11
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/approval-cascade.test.ts +31 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +318 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/heartbeat-service.ts +76 -28
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +73 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +230 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +23 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +501 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +76 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +52 -1
- package/src/runtime/http-types.ts +23 -1
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +912 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/migration-routes.ts +720 -124
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +28 -6
- package/src/schedule/scheduler.ts +8 -0
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +26 -7
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +12 -3
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +2 -2
- package/src/util/pricing.ts +15 -5
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -157,12 +157,12 @@ describe("ConfigWatcher workspace file handlers", () => {
|
|
|
157
157
|
expect(evictCallCount).toBe(1);
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
-
test("UPDATES.md change
|
|
160
|
+
test("UPDATES.md change does NOT trigger onConversationEvict", async () => {
|
|
161
161
|
watcher.start(onConversationEvict);
|
|
162
162
|
simulateFileChange(WORKSPACE_DIR, "UPDATES.md");
|
|
163
163
|
|
|
164
164
|
await new Promise((r) => setTimeout(r, 300));
|
|
165
|
-
expect(evictCallCount).toBe(
|
|
165
|
+
expect(evictCallCount).toBe(0);
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
test("config.json change calls refreshConfigFromSources", async () => {
|
|
@@ -67,7 +67,7 @@ describe("upsertContact user_file selection", () => {
|
|
|
67
67
|
|
|
68
68
|
test("reuses an existing sibling's userFile when principalId matches", () => {
|
|
69
69
|
const primary = upsertContact({
|
|
70
|
-
displayName: "
|
|
70
|
+
displayName: "Chris",
|
|
71
71
|
role: "guardian",
|
|
72
72
|
principalId: "principal-abc",
|
|
73
73
|
channels: [
|
|
@@ -78,12 +78,12 @@ describe("upsertContact user_file selection", () => {
|
|
|
78
78
|
},
|
|
79
79
|
],
|
|
80
80
|
});
|
|
81
|
-
expect(primary.userFile).toBe("
|
|
81
|
+
expect(primary.userFile).toBe("chris.md");
|
|
82
82
|
|
|
83
83
|
// Second contact for the same principal on Slack — must inherit the
|
|
84
|
-
// first contact's userFile, NOT auto-increment to
|
|
84
|
+
// first contact's userFile, NOT auto-increment to chris-2.md.
|
|
85
85
|
const slack = upsertContact({
|
|
86
|
-
displayName: "
|
|
86
|
+
displayName: "chris",
|
|
87
87
|
role: "guardian",
|
|
88
88
|
principalId: "principal-abc",
|
|
89
89
|
channels: [
|
|
@@ -95,7 +95,7 @@ describe("upsertContact user_file selection", () => {
|
|
|
95
95
|
},
|
|
96
96
|
],
|
|
97
97
|
});
|
|
98
|
-
expect(slack.userFile).toBe("
|
|
98
|
+
expect(slack.userFile).toBe("chris.md");
|
|
99
99
|
expect(slack.id).not.toBe(primary.id);
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -118,31 +118,31 @@ describe("upsertContact user_file selection", () => {
|
|
|
118
118
|
|
|
119
119
|
test("still auto-increments when principalId is not set and displayName collides", () => {
|
|
120
120
|
const first = upsertContact({
|
|
121
|
-
displayName: "
|
|
121
|
+
displayName: "Bob",
|
|
122
122
|
role: "contact",
|
|
123
123
|
channels: [
|
|
124
124
|
{
|
|
125
125
|
type: "slack",
|
|
126
|
-
address: "
|
|
127
|
-
externalUserId: "
|
|
128
|
-
externalChatId: "
|
|
126
|
+
address: "ubob1",
|
|
127
|
+
externalUserId: "UBOB1",
|
|
128
|
+
externalChatId: "DBOB1",
|
|
129
129
|
},
|
|
130
130
|
],
|
|
131
131
|
});
|
|
132
132
|
const second = upsertContact({
|
|
133
|
-
displayName: "
|
|
133
|
+
displayName: "Bob",
|
|
134
134
|
role: "contact",
|
|
135
135
|
channels: [
|
|
136
136
|
{
|
|
137
137
|
type: "slack",
|
|
138
|
-
address: "
|
|
139
|
-
externalUserId: "
|
|
140
|
-
externalChatId: "
|
|
138
|
+
address: "ubob2",
|
|
139
|
+
externalUserId: "UBOB2",
|
|
140
|
+
externalChatId: "DBOB2",
|
|
141
141
|
},
|
|
142
142
|
],
|
|
143
143
|
});
|
|
144
|
-
expect(first.userFile).toBe("
|
|
145
|
-
expect(second.userFile).toBe("
|
|
144
|
+
expect(first.userFile).toBe("bob.md");
|
|
145
|
+
expect(second.userFile).toBe("bob-2.md");
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
test("ignores a sibling whose userFile is null and generates a new slug", () => {
|
|
@@ -203,18 +203,18 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
203
203
|
const now = Date.now();
|
|
204
204
|
insertContact({
|
|
205
205
|
id: "c1",
|
|
206
|
-
displayName: "
|
|
206
|
+
displayName: "chris",
|
|
207
207
|
role: "guardian",
|
|
208
208
|
principalId: "principal-x",
|
|
209
|
-
userFile: "
|
|
209
|
+
userFile: "chris.md",
|
|
210
210
|
createdAt: now - 1000,
|
|
211
211
|
});
|
|
212
212
|
insertContact({
|
|
213
213
|
id: "c2",
|
|
214
|
-
displayName: "
|
|
214
|
+
displayName: "chris",
|
|
215
215
|
role: "guardian",
|
|
216
216
|
principalId: "principal-x",
|
|
217
|
-
userFile: "
|
|
217
|
+
userFile: "chris-2.md",
|
|
218
218
|
createdAt: now,
|
|
219
219
|
});
|
|
220
220
|
|
|
@@ -222,23 +222,23 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
222
222
|
|
|
223
223
|
const rows = fetchUserFilesByPrincipal("principal-x");
|
|
224
224
|
expect(rows).toHaveLength(2);
|
|
225
|
-
expect(rows[0]?.user_file).toBe("
|
|
226
|
-
expect(rows[1]?.user_file).toBe("
|
|
225
|
+
expect(rows[0]?.user_file).toBe("chris.md");
|
|
226
|
+
expect(rows[1]?.user_file).toBe("chris.md");
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
test("propagates a sibling's user_file to NULL rows", () => {
|
|
230
230
|
const now = Date.now();
|
|
231
231
|
insertContact({
|
|
232
232
|
id: "c1",
|
|
233
|
-
displayName: "
|
|
233
|
+
displayName: "chris",
|
|
234
234
|
role: "guardian",
|
|
235
235
|
principalId: "principal-y",
|
|
236
|
-
userFile: "
|
|
236
|
+
userFile: "chris.md",
|
|
237
237
|
createdAt: now - 1000,
|
|
238
238
|
});
|
|
239
239
|
insertContact({
|
|
240
240
|
id: "c2",
|
|
241
|
-
displayName: "
|
|
241
|
+
displayName: "chris",
|
|
242
242
|
role: "guardian",
|
|
243
243
|
principalId: "principal-y",
|
|
244
244
|
userFile: null,
|
|
@@ -248,8 +248,8 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
248
248
|
migrateNormalizeUserFileByPrincipal(getDb());
|
|
249
249
|
|
|
250
250
|
const rows = fetchUserFilesByPrincipal("principal-y");
|
|
251
|
-
expect(rows[0]?.user_file).toBe("
|
|
252
|
-
expect(rows[1]?.user_file).toBe("
|
|
251
|
+
expect(rows[0]?.user_file).toBe("chris.md");
|
|
252
|
+
expect(rows[1]?.user_file).toBe("chris.md");
|
|
253
253
|
});
|
|
254
254
|
|
|
255
255
|
test("prefers non-auto-incremented candidate over auto-incremented older row", () => {
|
|
@@ -258,26 +258,26 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
258
258
|
const now = Date.now();
|
|
259
259
|
insertContact({
|
|
260
260
|
id: "c1",
|
|
261
|
-
displayName: "
|
|
261
|
+
displayName: "chris",
|
|
262
262
|
role: "guardian",
|
|
263
263
|
principalId: "principal-z",
|
|
264
|
-
userFile: "
|
|
264
|
+
userFile: "chris-3.md",
|
|
265
265
|
createdAt: now - 2000,
|
|
266
266
|
});
|
|
267
267
|
insertContact({
|
|
268
268
|
id: "c2",
|
|
269
|
-
displayName: "
|
|
269
|
+
displayName: "chris",
|
|
270
270
|
role: "guardian",
|
|
271
271
|
principalId: "principal-z",
|
|
272
|
-
userFile: "
|
|
272
|
+
userFile: "chris.md",
|
|
273
273
|
createdAt: now,
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
migrateNormalizeUserFileByPrincipal(getDb());
|
|
277
277
|
|
|
278
278
|
const rows = fetchUserFilesByPrincipal("principal-z");
|
|
279
|
-
expect(rows[0]?.user_file).toBe("
|
|
280
|
-
expect(rows[1]?.user_file).toBe("
|
|
279
|
+
expect(rows[0]?.user_file).toBe("chris.md");
|
|
280
|
+
expect(rows[1]?.user_file).toBe("chris.md");
|
|
281
281
|
});
|
|
282
282
|
|
|
283
283
|
test("leaves untouched when only one contact exists for a principal", () => {
|
|
@@ -298,25 +298,25 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
test("does not classify dated-slug filenames as auto-incremented", () => {
|
|
301
|
-
// A display name containing a 4-digit year (e.g. "
|
|
302
|
-
// `
|
|
303
|
-
// (starting at 2), so `
|
|
301
|
+
// A display name containing a 4-digit year (e.g. "Dana 2024") produces
|
|
302
|
+
// `dana-2024.md`. The auto-increment suffix only ever appends 1–3 digits
|
|
303
|
+
// (starting at 2), so `dana-2024.md` must be treated as a normal filename
|
|
304
304
|
// and not deprioritized in favor of an older sibling.
|
|
305
305
|
const now = Date.now();
|
|
306
306
|
insertContact({
|
|
307
307
|
id: "c1",
|
|
308
|
-
displayName: "
|
|
308
|
+
displayName: "Dana 2024",
|
|
309
309
|
role: "guardian",
|
|
310
310
|
principalId: "principal-dated",
|
|
311
|
-
userFile: "
|
|
311
|
+
userFile: "dana-2024.md",
|
|
312
312
|
createdAt: now,
|
|
313
313
|
});
|
|
314
314
|
insertContact({
|
|
315
315
|
id: "c2",
|
|
316
|
-
displayName: "
|
|
316
|
+
displayName: "Dana",
|
|
317
317
|
role: "guardian",
|
|
318
318
|
principalId: "principal-dated",
|
|
319
|
-
userFile: "
|
|
319
|
+
userFile: "dana.md",
|
|
320
320
|
createdAt: now - 1000,
|
|
321
321
|
});
|
|
322
322
|
|
|
@@ -324,9 +324,9 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
324
324
|
|
|
325
325
|
const rows = fetchUserFilesByPrincipal("principal-dated");
|
|
326
326
|
// Neither candidate looks auto-incremented, so tiebreaker is oldest
|
|
327
|
-
// created_at — c2 (`
|
|
327
|
+
// created_at — c2 (`dana.md`) wins. Crucially, `dana-2024.md` was NOT
|
|
328
328
|
// classified as auto-incremented and penalized.
|
|
329
|
-
expect(rows.map((r) => r.user_file).sort()).toEqual(["
|
|
329
|
+
expect(rows.map((r) => r.user_file).sort()).toEqual(["dana.md", "dana.md"]);
|
|
330
330
|
});
|
|
331
331
|
|
|
332
332
|
test("excludes year-like 4-digit tails from the auto-increment class", () => {
|
|
@@ -389,117 +389,116 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
389
389
|
});
|
|
390
390
|
|
|
391
391
|
test("excludes full date-shaped tails from the auto-increment class", () => {
|
|
392
|
-
// `
|
|
392
|
+
// `dana-2025-04-13.md` ends with `-13.md` (which otherwise looks like a
|
|
393
393
|
// small counter), but the preceding `-2025-04` marks the whole tail as a
|
|
394
394
|
// date. Must NOT be classified as auto-incremented.
|
|
395
395
|
const now = Date.now();
|
|
396
396
|
insertContact({
|
|
397
397
|
id: "c1",
|
|
398
|
-
displayName: "
|
|
398
|
+
displayName: "Dana 2025 04 13",
|
|
399
399
|
role: "guardian",
|
|
400
400
|
principalId: "principal-datefull",
|
|
401
|
-
userFile: "
|
|
401
|
+
userFile: "dana-2025-04-13.md",
|
|
402
402
|
createdAt: now - 2000,
|
|
403
403
|
});
|
|
404
404
|
insertContact({
|
|
405
405
|
id: "c2",
|
|
406
|
-
displayName: "
|
|
406
|
+
displayName: "Dana",
|
|
407
407
|
role: "guardian",
|
|
408
408
|
principalId: "principal-datefull",
|
|
409
|
-
userFile: "
|
|
409
|
+
userFile: "dana-2.md",
|
|
410
410
|
createdAt: now - 1000,
|
|
411
411
|
});
|
|
412
412
|
|
|
413
413
|
migrateNormalizeUserFileByPrincipal(getDb());
|
|
414
414
|
|
|
415
415
|
const rows = fetchUserFilesByPrincipal("principal-datefull");
|
|
416
|
-
// `
|
|
416
|
+
// `dana-2.md` is auto-incremented; `dana-2025-04-13.md` is a date-shaped
|
|
417
417
|
// slug and wins as canonical.
|
|
418
|
-
for (const row of rows)
|
|
419
|
-
expect(row.user_file).toBe("alex-2025-04-13.md");
|
|
418
|
+
for (const row of rows) expect(row.user_file).toBe("dana-2025-04-13.md");
|
|
420
419
|
});
|
|
421
420
|
|
|
422
421
|
test("treats year-prefixed single-digit counter slug as auto-incremented", () => {
|
|
423
|
-
// `
|
|
424
|
-
// `
|
|
422
|
+
// `dana-2025-2.md` is `generateUserFileSlug` output when the base
|
|
423
|
+
// `dana-2025.md` was already taken — it is a collision counter, NOT a
|
|
425
424
|
// date. Counters are emitted without leading zeros (`-2`, `-3`, ...)
|
|
426
425
|
// while ISO date components are always 2 digits (`-02`, `-04`), so
|
|
427
426
|
// single-digit trailing segments mark the tail as a counter.
|
|
428
427
|
const now = Date.now();
|
|
429
428
|
insertContact({
|
|
430
429
|
id: "c1",
|
|
431
|
-
displayName: "
|
|
430
|
+
displayName: "Dana 2025",
|
|
432
431
|
role: "guardian",
|
|
433
432
|
principalId: "principal-yc",
|
|
434
|
-
userFile: "
|
|
433
|
+
userFile: "dana-2025-2.md",
|
|
435
434
|
createdAt: now - 2000,
|
|
436
435
|
});
|
|
437
436
|
insertContact({
|
|
438
437
|
id: "c2",
|
|
439
|
-
displayName: "
|
|
438
|
+
displayName: "Dana 2025",
|
|
440
439
|
role: "guardian",
|
|
441
440
|
principalId: "principal-yc",
|
|
442
|
-
userFile: "
|
|
441
|
+
userFile: "dana-2025.md",
|
|
443
442
|
createdAt: now,
|
|
444
443
|
});
|
|
445
444
|
|
|
446
445
|
migrateNormalizeUserFileByPrincipal(getDb());
|
|
447
446
|
|
|
448
447
|
const rows = fetchUserFilesByPrincipal("principal-yc");
|
|
449
|
-
// Despite being older, `
|
|
450
|
-
// `
|
|
451
|
-
for (const row of rows) expect(row.user_file).toBe("
|
|
448
|
+
// Despite being older, `dana-2025-2.md` is auto-incremented, so
|
|
449
|
+
// `dana-2025.md` (the clean base) wins as canonical.
|
|
450
|
+
for (const row of rows) expect(row.user_file).toBe("dana-2025.md");
|
|
452
451
|
});
|
|
453
452
|
|
|
454
453
|
test("treats year-prefixed single-digit tail as base slug when display name generates it", () => {
|
|
455
|
-
// Ambiguous filename: `
|
|
456
|
-
// on base `
|
|
457
|
-
// "
|
|
454
|
+
// Ambiguous filename: `dana-2025-4.md` could be either a collision counter
|
|
455
|
+
// on base `dana-2025.md` OR the direct base slug from display name
|
|
456
|
+
// "Dana 2025 4". The classifier must cross-reference the row's display
|
|
458
457
|
// name — if `generateUserFileSlug`'s pure slug transform maps the name to
|
|
459
458
|
// the filename, treat it as a base slug, not an auto-incremented counter.
|
|
460
459
|
const now = Date.now();
|
|
461
460
|
insertContact({
|
|
462
461
|
id: "c1",
|
|
463
|
-
displayName: "
|
|
462
|
+
displayName: "Dana 2025 4",
|
|
464
463
|
role: "guardian",
|
|
465
464
|
principalId: "principal-yb",
|
|
466
|
-
userFile: "
|
|
465
|
+
userFile: "dana-2025-4.md",
|
|
467
466
|
createdAt: now,
|
|
468
467
|
});
|
|
469
468
|
insertContact({
|
|
470
469
|
id: "c2",
|
|
471
|
-
displayName: "
|
|
470
|
+
displayName: "Dana",
|
|
472
471
|
role: "guardian",
|
|
473
472
|
principalId: "principal-yb",
|
|
474
|
-
userFile: "
|
|
473
|
+
userFile: "dana-2.md",
|
|
475
474
|
createdAt: now - 1000,
|
|
476
475
|
});
|
|
477
476
|
|
|
478
477
|
migrateNormalizeUserFileByPrincipal(getDb());
|
|
479
478
|
|
|
480
479
|
const rows = fetchUserFilesByPrincipal("principal-yb");
|
|
481
|
-
// `
|
|
482
|
-
// name), while `
|
|
480
|
+
// `dana-2025-4.md` is a legitimate base slug (disambiguated by display
|
|
481
|
+
// name), while `dana-2.md` is auto-incremented. The base slug must win
|
|
483
482
|
// as canonical, otherwise canonical would point at a collision counter.
|
|
484
|
-
for (const row of rows) expect(row.user_file).toBe("
|
|
483
|
+
for (const row of rows) expect(row.user_file).toBe("dana-2025-4.md");
|
|
485
484
|
});
|
|
486
485
|
|
|
487
486
|
test("is idempotent", () => {
|
|
488
487
|
const now = Date.now();
|
|
489
488
|
insertContact({
|
|
490
489
|
id: "c1",
|
|
491
|
-
displayName: "
|
|
490
|
+
displayName: "chris",
|
|
492
491
|
role: "guardian",
|
|
493
492
|
principalId: "principal-i",
|
|
494
|
-
userFile: "
|
|
493
|
+
userFile: "chris.md",
|
|
495
494
|
createdAt: now - 1000,
|
|
496
495
|
});
|
|
497
496
|
insertContact({
|
|
498
497
|
id: "c2",
|
|
499
|
-
displayName: "
|
|
498
|
+
displayName: "chris",
|
|
500
499
|
role: "guardian",
|
|
501
500
|
principalId: "principal-i",
|
|
502
|
-
userFile: "
|
|
501
|
+
userFile: "chris-2.md",
|
|
503
502
|
createdAt: now,
|
|
504
503
|
});
|
|
505
504
|
|
|
@@ -507,6 +506,6 @@ describe("migrateNormalizeUserFileByPrincipal", () => {
|
|
|
507
506
|
migrateNormalizeUserFileByPrincipal(getDb());
|
|
508
507
|
|
|
509
508
|
const rows = fetchUserFilesByPrincipal("principal-i");
|
|
510
|
-
for (const row of rows) expect(row.user_file).toBe("
|
|
509
|
+
for (const row of rows) expect(row.user_file).toBe("chris.md");
|
|
511
510
|
});
|
|
512
511
|
});
|
|
@@ -70,13 +70,13 @@ describe("createGuardianBinding seeds users/<slug>.md", () => {
|
|
|
70
70
|
test("writes the persona template scaffold on first creation", () => {
|
|
71
71
|
createGuardianBinding({
|
|
72
72
|
channel: "telegram",
|
|
73
|
-
guardianExternalUserId: "
|
|
74
|
-
guardianDeliveryChatId: "chat-
|
|
75
|
-
guardianPrincipalId: "principal-
|
|
73
|
+
guardianExternalUserId: "Chris",
|
|
74
|
+
guardianDeliveryChatId: "chat-chris",
|
|
75
|
+
guardianPrincipalId: "principal-chris",
|
|
76
76
|
verifiedVia: "challenge",
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
const expectedPath = userFilePath("
|
|
79
|
+
const expectedPath = userFilePath("chris.md");
|
|
80
80
|
expect(existsSync(expectedPath)).toBe(true);
|
|
81
81
|
|
|
82
82
|
const content = readFileSync(expectedPath, "utf-8");
|
|
@@ -472,7 +472,9 @@ describe("token estimator", () => {
|
|
|
472
472
|
},
|
|
473
473
|
];
|
|
474
474
|
|
|
475
|
-
const total = estimateMessagesTokens(messages, {
|
|
475
|
+
const total = estimateMessagesTokens(messages, {
|
|
476
|
+
providerName: "anthropic",
|
|
477
|
+
});
|
|
476
478
|
|
|
477
479
|
// Each image: ~73 tokens. 100 images + message overhead ≈ 7,304
|
|
478
480
|
// Old behavior: 100 * ~1,043 = ~104,300 (14x overestimate)
|
|
@@ -522,3 +524,191 @@ describe("token estimator", () => {
|
|
|
522
524
|
expect(tallTokens).toBeLessThan(1_800);
|
|
523
525
|
});
|
|
524
526
|
});
|
|
527
|
+
|
|
528
|
+
describe("tool_result estimation mirrors Anthropic wire format", () => {
|
|
529
|
+
test("plain text tool_result counts overhead + id + content", () => {
|
|
530
|
+
const tokens = estimateContentBlockTokens({
|
|
531
|
+
type: "tool_result",
|
|
532
|
+
tool_use_id: "call_1",
|
|
533
|
+
content: "operation complete",
|
|
534
|
+
});
|
|
535
|
+
// Sanity bounds — small string, small overhead.
|
|
536
|
+
expect(tokens).toBeGreaterThan(estimateTextTokens("operation complete"));
|
|
537
|
+
expect(tokens).toBeLessThan(estimateTextTokens("operation complete") + 50);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test("image sub-block is counted when is_error is false", () => {
|
|
541
|
+
const pngBase64 = makePngBase64(512, 512);
|
|
542
|
+
const withImage = estimateContentBlockTokens(
|
|
543
|
+
{
|
|
544
|
+
type: "tool_result",
|
|
545
|
+
tool_use_id: "call_img",
|
|
546
|
+
content: "screenshot captured",
|
|
547
|
+
contentBlocks: [
|
|
548
|
+
{
|
|
549
|
+
type: "image",
|
|
550
|
+
source: {
|
|
551
|
+
type: "base64",
|
|
552
|
+
media_type: "image/png",
|
|
553
|
+
data: pngBase64,
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
},
|
|
558
|
+
{ providerName: "anthropic" },
|
|
559
|
+
);
|
|
560
|
+
const withoutImage = estimateContentBlockTokens(
|
|
561
|
+
{
|
|
562
|
+
type: "tool_result",
|
|
563
|
+
tool_use_id: "call_img",
|
|
564
|
+
content: "screenshot captured",
|
|
565
|
+
},
|
|
566
|
+
{ providerName: "anthropic" },
|
|
567
|
+
);
|
|
568
|
+
// 512x512 = 262144 pixels, tokens ≈ ceil(262144/750) ≈ 350.
|
|
569
|
+
expect(withImage - withoutImage).toBeGreaterThan(300);
|
|
570
|
+
expect(withImage - withoutImage).toBeLessThan(500);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
test("image sub-block is NOT counted when is_error is true", () => {
|
|
574
|
+
// The Anthropic serializer filters image parts out of error tool results
|
|
575
|
+
// (client.ts:1398), so the estimator must match.
|
|
576
|
+
const pngBase64 = makePngBase64(512, 512);
|
|
577
|
+
const errorWithImage = estimateContentBlockTokens(
|
|
578
|
+
{
|
|
579
|
+
type: "tool_result",
|
|
580
|
+
tool_use_id: "call_err",
|
|
581
|
+
content: "operation failed",
|
|
582
|
+
is_error: true,
|
|
583
|
+
contentBlocks: [
|
|
584
|
+
{
|
|
585
|
+
type: "image",
|
|
586
|
+
source: {
|
|
587
|
+
type: "base64",
|
|
588
|
+
media_type: "image/png",
|
|
589
|
+
data: pngBase64,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
},
|
|
594
|
+
{ providerName: "anthropic" },
|
|
595
|
+
);
|
|
596
|
+
const errorNoImage = estimateContentBlockTokens(
|
|
597
|
+
{
|
|
598
|
+
type: "tool_result",
|
|
599
|
+
tool_use_id: "call_err",
|
|
600
|
+
content: "operation failed",
|
|
601
|
+
is_error: true,
|
|
602
|
+
},
|
|
603
|
+
{ providerName: "anthropic" },
|
|
604
|
+
);
|
|
605
|
+
expect(errorWithImage).toBe(errorNoImage);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
test("image sub-block IS counted on error for non-Anthropic providers", () => {
|
|
609
|
+
// OpenAI and Gemini serializers forward error-result images (as a
|
|
610
|
+
// follow-up user message / parts entry), so the estimator must count
|
|
611
|
+
// them regardless of is_error under those providers.
|
|
612
|
+
const pngBase64 = makePngBase64(512, 512);
|
|
613
|
+
const build = (providerName: string) =>
|
|
614
|
+
estimateContentBlockTokens(
|
|
615
|
+
{
|
|
616
|
+
type: "tool_result",
|
|
617
|
+
tool_use_id: "call_err_img",
|
|
618
|
+
content: "operation failed",
|
|
619
|
+
is_error: true,
|
|
620
|
+
contentBlocks: [
|
|
621
|
+
{
|
|
622
|
+
type: "image",
|
|
623
|
+
source: {
|
|
624
|
+
type: "base64",
|
|
625
|
+
media_type: "image/png",
|
|
626
|
+
data: pngBase64,
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
],
|
|
630
|
+
},
|
|
631
|
+
{ providerName },
|
|
632
|
+
);
|
|
633
|
+
const buildPlain = (providerName: string) =>
|
|
634
|
+
estimateContentBlockTokens(
|
|
635
|
+
{
|
|
636
|
+
type: "tool_result",
|
|
637
|
+
tool_use_id: "call_err_img",
|
|
638
|
+
content: "operation failed",
|
|
639
|
+
is_error: true,
|
|
640
|
+
},
|
|
641
|
+
{ providerName },
|
|
642
|
+
);
|
|
643
|
+
for (const providerName of ["openai", "gemini"]) {
|
|
644
|
+
const withImage = build(providerName);
|
|
645
|
+
const withoutImage = buildPlain(providerName);
|
|
646
|
+
expect(withImage - withoutImage).toBeGreaterThan(0);
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test("unknown sub-block types are NOT counted", () => {
|
|
651
|
+
// The Anthropic serializer only forwards image/text sub-blocks. Anything
|
|
652
|
+
// else (thinking, tool_use, etc.) is dropped — the estimator must not
|
|
653
|
+
// add tokens for content that never reaches the wire.
|
|
654
|
+
const withThinking = estimateContentBlockTokens({
|
|
655
|
+
type: "tool_result",
|
|
656
|
+
tool_use_id: "call_think",
|
|
657
|
+
content: "done",
|
|
658
|
+
contentBlocks: [
|
|
659
|
+
{
|
|
660
|
+
type: "thinking",
|
|
661
|
+
thinking: "a".repeat(4000),
|
|
662
|
+
signature: "sig_stub",
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
});
|
|
666
|
+
const plain = estimateContentBlockTokens({
|
|
667
|
+
type: "tool_result",
|
|
668
|
+
tool_use_id: "call_think",
|
|
669
|
+
content: "done",
|
|
670
|
+
});
|
|
671
|
+
expect(withThinking).toBe(plain);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
test("text sub-block beyond block.content is counted once", () => {
|
|
675
|
+
// Handlers (e.g. secret-detection) may populate contentBlocks with an
|
|
676
|
+
// additional text entry distinct from block.content. The serializer
|
|
677
|
+
// forwards both, so the estimator counts block.content once and each
|
|
678
|
+
// text sub-block once — never doubling the content string against an
|
|
679
|
+
// echoing text sub-block.
|
|
680
|
+
const extraText = "x".repeat(4000);
|
|
681
|
+
const tokens = estimateContentBlockTokens({
|
|
682
|
+
type: "tool_result",
|
|
683
|
+
tool_use_id: "call_dual_text",
|
|
684
|
+
content: "short summary",
|
|
685
|
+
contentBlocks: [{ type: "text", text: extraText }],
|
|
686
|
+
});
|
|
687
|
+
// Estimate should be roughly:
|
|
688
|
+
// overhead + id + "short summary" + (text overhead + 1000 tokens for extraText)
|
|
689
|
+
// The extra text is ~1000 tokens on its own; overhead is small.
|
|
690
|
+
expect(tokens).toBeGreaterThan(1000);
|
|
691
|
+
expect(tokens).toBeLessThan(1100);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
test("regression: tool_result with thinking sub-block does not inflate estimate by 3x+", () => {
|
|
695
|
+
// A modest tool_result whose contentBlocks carry a large sub-block the
|
|
696
|
+
// serializer discards must not inflate the estimate: the estimator
|
|
697
|
+
// skips the thinking payload, matching the plain wire shape.
|
|
698
|
+
const content = "y".repeat(2000);
|
|
699
|
+
const inflated = estimateContentBlockTokens({
|
|
700
|
+
type: "tool_result",
|
|
701
|
+
tool_use_id: "call_regress",
|
|
702
|
+
content,
|
|
703
|
+
contentBlocks: [
|
|
704
|
+
{ type: "thinking", thinking: "z".repeat(8000), signature: "s" },
|
|
705
|
+
],
|
|
706
|
+
});
|
|
707
|
+
const wireShape = estimateContentBlockTokens({
|
|
708
|
+
type: "tool_result",
|
|
709
|
+
tool_use_id: "call_regress",
|
|
710
|
+
content,
|
|
711
|
+
});
|
|
712
|
+
expect(inflated).toBe(wireShape);
|
|
713
|
+
});
|
|
714
|
+
});
|