@vellumai/assistant 0.8.6 → 0.8.7
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 +4 -4
- package/Dockerfile +1 -0
- package/bun.lock +11 -2
- package/docker-entrypoint.sh +8 -6
- package/docs/plugins.md +63 -28
- package/examples/plugins/echo/register.ts +4 -7
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -2
- package/openapi.yaml +3735 -353
- package/package.json +7 -3
- package/scripts/generate-openapi.ts +20 -13
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +240 -39
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +207 -341
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +4 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
- package/src/__tests__/anthropic-provider.test.ts +201 -55
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +5 -0
- package/src/__tests__/approval-cascade.test.ts +4 -11
- package/src/__tests__/approval-routes-http.test.ts +4 -2
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/btw-routes.test.ts +7 -12
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +48 -20
- package/src/__tests__/channel-approvals.test.ts +3 -1
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
- package/src/__tests__/channel-retry-sweep.test.ts +71 -79
- package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-events.test.ts +5 -17
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
- package/src/__tests__/compaction-trail-store.test.ts +1 -79
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/computer-use-tools.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +34 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +14 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +12 -27
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +430 -90
- package/src/__tests__/conversation-agent-loop.test.ts +581 -62
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-fork-crud.test.ts +86 -172
- package/src/__tests__/conversation-fork-route.test.ts +16 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +34 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -0
- package/src/__tests__/conversation-process-callsite.test.ts +27 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +53 -44
- package/src/__tests__/conversation-queue.test.ts +270 -164
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +20 -22
- package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
- package/src/__tests__/conversation-slash-queue.test.ts +37 -31
- package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
- package/src/__tests__/conversation-speed-override.test.ts +8 -22
- package/src/__tests__/conversation-stream-state.test.ts +484 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +6 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +48 -22
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +27 -7
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/cross-provider-web-search.test.ts +6 -2
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
- package/src/__tests__/disk-pressure-guard.test.ts +66 -0
- package/src/__tests__/disk-pressure-routes.test.ts +9 -2
- package/src/__tests__/dm-persistence.test.ts +7 -2
- package/src/__tests__/dynamic-page-surface.test.ts +68 -0
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-pipeline.test.ts +127 -5
- package/src/__tests__/filing-service.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +55 -14
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +351 -28
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +9 -7
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -1
- package/src/__tests__/history-repair-hook.test.ts +161 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +9 -4
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +35 -14
- package/src/__tests__/inbound-slack-persistence.test.ts +7 -2
- package/src/__tests__/injector-background-turn.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-disk-pressure.test.ts +1 -1
- package/src/__tests__/injector-document-comments.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
- package/src/__tests__/injector-v3-suppression.test.ts +220 -0
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +17 -15
- package/src/__tests__/list-messages-page-latest.test.ts +0 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
- package/src/__tests__/llm-call-pipeline.test.ts +21 -15
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +23 -47
- package/src/__tests__/llm-usage-store.test.ts +45 -0
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
- package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +14 -20
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/oauth-commands-routes.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +10 -0
- package/src/__tests__/openai-provider.test.ts +66 -70
- package/src/__tests__/openai-responses-provider.test.ts +21 -77
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
- package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persistence-pipeline.test.ts +15 -26
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -1
- package/src/__tests__/pipeline-runner.test.ts +2 -3
- package/src/__tests__/plugin-bootstrap.test.ts +51 -25
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
- package/src/__tests__/plugin-types.test.ts +7 -14
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +17 -16
- package/src/__tests__/process-message-display-content.test.ts +30 -42
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/relay-server.test.ts +20 -13
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
- package/src/__tests__/schedule-routes.test.ts +80 -10
- package/src/__tests__/schedule-store.test.ts +67 -0
- package/src/__tests__/schedule-tools.test.ts +125 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/send-endpoint-busy.test.ts +2 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +11 -11
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +54 -0
- package/src/__tests__/terminal-tools.test.ts +3 -24
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-pipeline.test.ts +1 -1
- package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
- package/src/__tests__/tool-error-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
- package/src/__tests__/tool-preview-lifecycle.test.ts +13 -11
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
- package/src/__tests__/tool-result-truncation.test.ts +3 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
- package/src/__tests__/voice-session-bridge.test.ts +13 -7
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/agent/compaction-circuit.ts +140 -0
- package/src/agent/loop.ts +409 -85
- package/src/api/README.md +19 -17
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +25 -27
- package/src/api/events/assistant-text-delta.ts +6 -8
- package/src/api/events/assistant-turn-start.ts +5 -7
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +24 -28
- package/src/api/events/document-comment-deleted.ts +6 -8
- package/src/api/events/document-comment-reopened.ts +6 -8
- package/src/api/events/document-comment-resolved.ts +8 -10
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +4 -6
- package/src/api/events/generation-handoff.ts +13 -15
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +10 -12
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +6 -8
- package/src/api/events/question-request.ts +67 -0
- package/src/api/events/relationship-state-updated.ts +4 -6
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-start.ts +8 -10
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +354 -0
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +2 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +6 -9
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +193 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +4 -1
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +13 -27
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +159 -445
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/program.ts +4 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/assistant-feature-flags.ts +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/call-site-defaults.ts +2 -7
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -226
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +0 -253
- package/src/config/schemas/memory-v3.ts +39 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/context/compactor.ts +54 -31
- package/src/context/token-estimator.ts +19 -0
- package/src/context/tool-result-truncation.ts +1 -43
- package/src/context/window-manager.ts +138 -20
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
- package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +225 -88
- package/src/daemon/conversation-agent-loop.ts +284 -584
- package/src/daemon/conversation-error.ts +7 -7
- package/src/daemon/conversation-history.ts +22 -6
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +10 -38
- package/src/daemon/conversation-messaging.ts +1 -3
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +100 -79
- package/src/daemon/conversation-runtime-assembly.ts +47 -21
- package/src/daemon/conversation-store.ts +6 -5
- package/src/daemon/conversation-surfaces.ts +55 -69
- package/src/daemon/conversation-tool-setup.ts +3 -0
- package/src/daemon/conversation.ts +91 -126
- package/src/daemon/daemon-skill-host.ts +2 -6
- package/src/daemon/disk-pressure-guard.ts +35 -29
- package/src/daemon/external-plugins-bootstrap.ts +46 -24
- package/src/daemon/first-greeting.ts +26 -4
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/conversations.ts +6 -22
- package/src/daemon/handlers/shared.ts +4 -0
- package/src/daemon/handlers/skills.ts +15 -14
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +28 -55
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +13 -111
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +4 -17
- package/src/daemon/message-types/integrations.ts +2 -6
- package/src/daemon/message-types/messages.ts +28 -343
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/process-message.ts +49 -46
- package/src/daemon/server.ts +12 -0
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +11 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -1
- package/src/heartbeat/heartbeat-run-store.ts +31 -0
- package/src/heartbeat/heartbeat-service.ts +16 -0
- package/src/home/feature-gate.ts +22 -0
- package/src/home/feed-types.ts +36 -221
- package/src/ipc/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +18 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
- package/src/ipc/skill-routes/memory.ts +26 -13
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +13 -61
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-crud.ts +39 -8
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/db-init.ts +8 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +41 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +32 -32
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs-store.ts +3 -1
- package/src/memory/jobs-worker.ts +51 -39
- package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
- package/src/memory/llm-request-log-source-local.ts +0 -11
- package/src/memory/llm-request-log-source.ts +9 -25
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +10 -0
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +1 -83
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/schema/infrastructure.ts +11 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +89 -9
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/memory/v3/__tests__/assign.test.ts +242 -0
- package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
- package/src/memory/v3/__tests__/core.test.ts +39 -0
- package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
- package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
- package/src/memory/v3/__tests__/health.test.ts +203 -0
- package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
- package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
- package/src/memory/v3/__tests__/needle.test.ts +107 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
- package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
- package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
- package/src/memory/v3/__tests__/router.test.ts +260 -0
- package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
- package/src/memory/v3/__tests__/selector.test.ts +404 -0
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
- package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
- package/src/memory/v3/__tests__/tree.test.ts +192 -0
- package/src/memory/v3/__tests__/types.test.ts +54 -0
- package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
- package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/memory/v3/assign.ts +268 -0
- package/src/memory/v3/capabilities.ts +124 -0
- package/src/memory/v3/core.ts +26 -0
- package/src/memory/v3/data/README.md +84 -0
- package/src/memory/v3/data/assignments.json +5 -0
- package/src/memory/v3/data/core.json +1 -0
- package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
- package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
- package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +314 -0
- package/src/memory/v3/needle.ts +115 -0
- package/src/memory/v3/orchestrate.ts +114 -0
- package/src/memory/v3/page-content.ts +34 -0
- package/src/memory/v3/provider-blocks.ts +16 -0
- package/src/memory/v3/reconcile.ts +523 -0
- package/src/memory/v3/render-injection.ts +32 -0
- package/src/memory/v3/router.ts +184 -0
- package/src/memory/v3/selection-log-store.ts +84 -0
- package/src/memory/v3/selector.ts +211 -0
- package/src/memory/v3/shadow-plugin.ts +379 -0
- package/src/memory/v3/snapshot.ts +209 -0
- package/src/memory/v3/tree.ts +174 -0
- package/src/memory/v3/types.ts +46 -60
- package/src/memory/v3/working-set.ts +88 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/decision-engine.ts +10 -13
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/permissions/prompter.ts +42 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/plugin-api/index.ts +2 -0
- package/src/plugin-api/types.ts +25 -3
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
- package/src/plugins/defaults/circuit-breaker/package.json +15 -0
- package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +35 -0
- package/src/plugins/defaults/compaction/terminal.ts +73 -0
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +28 -0
- package/src/plugins/defaults/empty-response/terminal.ts +106 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +29 -40
- package/src/plugins/defaults/injectors/package.json +15 -0
- package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +14 -38
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
- package/src/plugins/defaults/llm-call/package.json +15 -0
- package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
- package/src/plugins/defaults/memory-retrieval/package.json +15 -0
- package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
- package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
- package/src/plugins/defaults/overflow-reduce/package.json +15 -0
- package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
- package/src/plugins/defaults/persistence/package.json +15 -0
- package/src/plugins/defaults/persistence/register.ts +38 -0
- package/src/plugins/defaults/persistence/terminal.ts +83 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/title-generate/terminal.ts +31 -0
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
- package/src/plugins/defaults/token-estimate/package.json +15 -0
- package/src/plugins/defaults/token-estimate/register.ts +34 -0
- package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +35 -0
- package/src/plugins/defaults/tool-error/terminal.ts +47 -0
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
- package/src/plugins/defaults/tool-execute/package.json +15 -0
- package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
- package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +0 -12
- package/src/plugins/types.ts +51 -90
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +0 -1
- package/src/proactive-artifact/job.test.ts +20 -8
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +5 -1
- package/src/prompts/templates/system-sections.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +24 -5
- package/src/providers/call-site-routing.ts +1 -9
- package/src/providers/gemini/client.ts +152 -34
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +0 -2
- package/src/providers/openai/chat-completions-provider.ts +1 -4
- package/src/providers/openai/responses-provider.ts +1 -4
- package/src/providers/openrouter/client.ts +1 -6
- package/src/providers/provider-send-message.ts +6 -6
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/retry.ts +0 -5
- package/src/providers/types.ts +11 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +131 -26
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
- package/src/runtime/agent-wake.ts +93 -18
- package/src/runtime/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1079
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +3 -14
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +2 -5
- package/src/runtime/channel-retry-sweep.ts +12 -16
- package/src/runtime/conversation-stream-state.ts +294 -0
- package/src/runtime/http-router.ts +19 -22
- package/src/runtime/http-types.ts +12 -6
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
- package/src/runtime/routes/acp-routes.test.ts +97 -75
- package/src/runtime/routes/acp-routes.ts +29 -6
- package/src/runtime/routes/app-management-routes.ts +97 -24
- package/src/runtime/routes/app-routes.ts +25 -5
- package/src/runtime/routes/approval-routes.ts +16 -4
- package/src/runtime/routes/attachment-routes.ts +25 -1
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +238 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +13 -3
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +5 -0
- package/src/runtime/routes/browser-tabs-routes.ts +5 -0
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +13 -5
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
- package/src/runtime/routes/conversation-list-routes.ts +81 -12
- package/src/runtime/routes/conversation-management-routes.ts +57 -14
- package/src/runtime/routes/conversation-query-routes.ts +88 -41
- package/src/runtime/routes/conversation-routes.ts +74 -19
- package/src/runtime/routes/conversation-starter-routes.ts +22 -13
- package/src/runtime/routes/conversations-import-routes.ts +6 -1
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +21 -10
- package/src/runtime/routes/domain-routes.ts +61 -28
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/events-routes.ts +114 -9
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +5 -0
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +21 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +25 -9
- package/src/runtime/routes/home-feed-routes.ts +23 -19
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +5 -0
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +5 -0
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +17 -6
- package/src/runtime/routes/identity-routes.ts +12 -2
- package/src/runtime/routes/image-generation-routes.ts +5 -0
- package/src/runtime/routes/inbound-message-handler.ts +15 -11
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +30 -7
- package/src/runtime/routes/integrations/slack/channel.ts +19 -3
- package/src/runtime/routes/integrations/slack/share.ts +9 -2
- package/src/runtime/routes/integrations/telegram.ts +28 -9
- package/src/runtime/routes/integrations/twilio.ts +35 -7
- package/src/runtime/routes/integrations/vercel.ts +3 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/log-export-routes.ts +28 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +90 -36
- package/src/runtime/routes/memory-v3-routes.ts +273 -407
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +29 -0
- package/src/runtime/routes/notification-routes.ts +17 -1
- package/src/runtime/routes/oauth-apps.ts +33 -11
- package/src/runtime/routes/oauth-commands-routes.ts +37 -14
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +35 -10
- package/src/runtime/routes/platform-routes.ts +21 -0
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +0 -1
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +137 -47
- package/src/runtime/routes/secret-routes.ts +17 -4
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +133 -69
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +9 -0
- package/src/runtime/routes/surface-content-routes.ts +10 -2
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +5 -0
- package/src/runtime/routes/tts-routes.ts +13 -6
- package/src/runtime/routes/types.ts +17 -8
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +71 -3
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +9 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
- package/src/runtime/routes/work-items-routes.ts +47 -19
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +120 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -4
- package/src/runtime/services/analyze-conversation.ts +3 -6
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/sync/resource-sync-events.ts +16 -2
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +8 -0
- package/src/schedule/scheduler.ts +3 -1
- package/src/signals/user-message.ts +5 -8
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/clawhub-files.ts +2 -0
- package/src/skills/skillssh-files.ts +2 -0
- package/src/subagent/manager.ts +3 -6
- package/src/telemetry/types.ts +26 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
- package/src/telemetry/usage-telemetry-reporter.ts +31 -0
- package/src/tools/acp/spawn.test.ts +88 -38
- package/src/tools/apps/definitions.ts +8 -4
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/computer-use/definitions.ts +28 -24
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/execution-target.ts +1 -1
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +34 -31
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search.ts +16 -13
- package/src/tools/registry.ts +39 -16
- package/src/tools/schedule/create.ts +11 -0
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +74 -23
- package/src/tools/ui-surface/definitions.ts +69 -9
- package/src/usage/types.ts +10 -0
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -491
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -706
- package/src/memory/v3/__tests__/filter.test.ts +0 -560
- package/src/memory/v3/__tests__/gate.test.ts +0 -637
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
- package/src/memory/v3/__tests__/loop.test.ts +0 -775
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -489
- package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -508
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
- package/src/memory/v3/__tests__/validate.test.ts +0 -277
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/coretrieval-seed.ts +0 -240
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -286
- package/src/memory/v3/filter.ts +0 -286
- package/src/memory/v3/gate.ts +0 -349
- package/src/memory/v3/index-composition.ts +0 -126
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -430
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -431
- package/src/memory/v3/shadow-diff.ts +0 -287
- package/src/memory/v3/shadow-middleware.ts +0 -347
- package/src/memory/v3/traversal.ts +0 -211
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -356
- package/src/memory/v3/validate.ts +0 -323
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/persistence.ts +0 -146
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
|
@@ -40,6 +40,8 @@ const {
|
|
|
40
40
|
DISK_PRESSURE_CLEAR_THRESHOLD_PERCENT,
|
|
41
41
|
DISK_PRESSURE_OVERRIDE_CONFIRMATION,
|
|
42
42
|
DISK_PRESSURE_THRESHOLD_PERCENT,
|
|
43
|
+
DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT,
|
|
44
|
+
DISK_PRESSURE_WARNING_THRESHOLD_PERCENT,
|
|
43
45
|
__getDiskPressureGuardTimerForTests,
|
|
44
46
|
__resetDiskPressureGuardForTests,
|
|
45
47
|
acknowledgeDiskPressureLock,
|
|
@@ -276,4 +278,68 @@ describe("disk pressure guard", () => {
|
|
|
276
278
|
stopDiskPressureGuard();
|
|
277
279
|
expect(__getDiskPressureGuardTimerForTests()).toBeNull();
|
|
278
280
|
});
|
|
281
|
+
|
|
282
|
+
test("does not enter warning until usage reaches the warning threshold", () => {
|
|
283
|
+
// Below 80% and never previously in a pressure state.
|
|
284
|
+
setDiskUsage(DISK_PRESSURE_WARNING_THRESHOLD_PERCENT - 2);
|
|
285
|
+
|
|
286
|
+
const status = evaluateDiskPressureNow();
|
|
287
|
+
|
|
288
|
+
expect(status.state).toBe("ok");
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("holds the warning state across a dip within the warning clear deadband", () => {
|
|
292
|
+
setDiskUsage(DISK_PRESSURE_WARNING_THRESHOLD_PERCENT + 2);
|
|
293
|
+
expect(evaluateDiskPressureNow().state).toBe("warning");
|
|
294
|
+
|
|
295
|
+
// Below the 80% warning threshold but at/above the 77% clear threshold.
|
|
296
|
+
setDiskUsage(DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT + 1);
|
|
297
|
+
expect(evaluateDiskPressureNow().state).toBe("warning");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("clears the warning state once usage falls below the warning clear threshold", () => {
|
|
301
|
+
setDiskUsage(DISK_PRESSURE_WARNING_THRESHOLD_PERCENT + 2);
|
|
302
|
+
expect(evaluateDiskPressureNow().state).toBe("warning");
|
|
303
|
+
|
|
304
|
+
setDiskUsage(DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT - 1);
|
|
305
|
+
expect(evaluateDiskPressureNow().state).toBe("ok");
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test("steps a critical lock down into a held warning state", () => {
|
|
309
|
+
setDiskUsage(DISK_PRESSURE_THRESHOLD_PERCENT + 1);
|
|
310
|
+
expect(evaluateDiskPressureNow().state).toBe("critical");
|
|
311
|
+
|
|
312
|
+
// Below the 90% critical clear but above the 80% warning threshold.
|
|
313
|
+
setDiskUsage(DISK_PRESSURE_CLEAR_THRESHOLD_PERCENT - 2);
|
|
314
|
+
const stepped = evaluateDiskPressureNow();
|
|
315
|
+
expect(stepped.state).toBe("warning");
|
|
316
|
+
expect(stepped.locked).toBe(false);
|
|
317
|
+
|
|
318
|
+
// Now within the warning clear deadband — warning must hold, not flap to ok.
|
|
319
|
+
setDiskUsage(DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT + 1);
|
|
320
|
+
expect(evaluateDiskPressureNow().state).toBe("warning");
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("holds warning when a critical lock drops straight into the warning deadband", () => {
|
|
324
|
+
setDiskUsage(DISK_PRESSURE_THRESHOLD_PERCENT + 1);
|
|
325
|
+
expect(evaluateDiskPressureNow().state).toBe("critical");
|
|
326
|
+
|
|
327
|
+
// A single large cleanup drops usage directly from critical to below the
|
|
328
|
+
// 80% warning threshold but still at/above the 77% clear threshold. The
|
|
329
|
+
// deadband must apply when stepping down out of critical too, so this holds
|
|
330
|
+
// as warning rather than flapping to ok (which would reopen the flap window).
|
|
331
|
+
setDiskUsage(DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT + 1);
|
|
332
|
+
const stepped = evaluateDiskPressureNow();
|
|
333
|
+
expect(stepped.state).toBe("warning");
|
|
334
|
+
expect(stepped.locked).toBe(false);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("clears straight to ok when a critical lock drops below the warning clear threshold", () => {
|
|
338
|
+
setDiskUsage(DISK_PRESSURE_THRESHOLD_PERCENT + 1);
|
|
339
|
+
expect(evaluateDiskPressureNow().state).toBe("critical");
|
|
340
|
+
|
|
341
|
+
// A drop below even the warning-clear threshold is a genuine recovery.
|
|
342
|
+
setDiskUsage(DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT - 1);
|
|
343
|
+
expect(evaluateDiskPressureNow().state).toBe("ok");
|
|
344
|
+
});
|
|
279
345
|
});
|
|
@@ -49,10 +49,17 @@ const {
|
|
|
49
49
|
evaluateDiskPressureNow,
|
|
50
50
|
} = await import("../daemon/disk-pressure-guard.js");
|
|
51
51
|
const { assistantEventHub } = await import("../runtime/assistant-event-hub.js");
|
|
52
|
-
const { getPolicy } = await import("../runtime/auth/route-policy.js");
|
|
53
52
|
const { RouteError } = await import("../runtime/routes/errors.js");
|
|
54
53
|
const { ROUTES } = await import("../runtime/routes/disk-pressure-routes.js");
|
|
55
54
|
|
|
55
|
+
/** Look up a route's policy by endpoint+method on the module's ROUTES. */
|
|
56
|
+
function routePolicy(endpoint: string, method?: string) {
|
|
57
|
+
const route = ROUTES.find(
|
|
58
|
+
(r) => r.endpoint === endpoint && (!method || r.method === method),
|
|
59
|
+
);
|
|
60
|
+
return route?.policy ?? null;
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
type DiskPressureRouteResult = {
|
|
57
64
|
status: {
|
|
58
65
|
enabled: boolean;
|
|
@@ -152,7 +159,7 @@ describe("disk pressure routes", () => {
|
|
|
152
159
|
(route) => route.endpoint === endpoint && route.method === method,
|
|
153
160
|
),
|
|
154
161
|
).toBe(true);
|
|
155
|
-
expect(
|
|
162
|
+
expect(routePolicy(endpoint)).not.toBeNull();
|
|
156
163
|
}
|
|
157
164
|
});
|
|
158
165
|
|
|
@@ -34,9 +34,14 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
34
34
|
conversationId: string,
|
|
35
35
|
role: string,
|
|
36
36
|
content: string,
|
|
37
|
-
metadata?: Record<string, unknown
|
|
37
|
+
options?: { metadata?: Record<string, unknown> },
|
|
38
38
|
) => {
|
|
39
|
-
addMessageCalls.push({
|
|
39
|
+
addMessageCalls.push({
|
|
40
|
+
conversationId,
|
|
41
|
+
role,
|
|
42
|
+
content,
|
|
43
|
+
metadata: options?.metadata,
|
|
44
|
+
});
|
|
40
45
|
return { id: `persisted-${addMessageCalls.length}` };
|
|
41
46
|
},
|
|
42
47
|
getConversation: () => null,
|
|
@@ -57,6 +57,74 @@ describe("Tool definition includes dynamic_page", () => {
|
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Tool execution guard
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
describe("ui_show dynamic_page app substitute guard", () => {
|
|
65
|
+
test("rejects dynamic_page when the model labels it as an app build", async () => {
|
|
66
|
+
let proxied = false;
|
|
67
|
+
|
|
68
|
+
const result = await uiShowTool.execute(
|
|
69
|
+
{
|
|
70
|
+
surface_type: "dynamic_page",
|
|
71
|
+
title: "JARVIS 1020 Test Counter",
|
|
72
|
+
activity: "Building the JARVIS 1020 Test Counter app",
|
|
73
|
+
data: {
|
|
74
|
+
html: "<button>Increment</button>",
|
|
75
|
+
preview: {
|
|
76
|
+
title: "JARVIS 1020 Test Counter",
|
|
77
|
+
subtitle: "Click INCREMENT to count up",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
conversationId: "conversation-123",
|
|
83
|
+
workingDir: "/tmp",
|
|
84
|
+
trustClass: "guardian",
|
|
85
|
+
proxyToolResolver: async () => {
|
|
86
|
+
proxied = true;
|
|
87
|
+
return { content: "proxied", isError: false };
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
expect(result.isError).toBe(true);
|
|
93
|
+
expect(result.content).toContain('skill: "app-builder"');
|
|
94
|
+
expect(proxied).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("allows transient non-app dynamic_page surfaces", async () => {
|
|
98
|
+
let proxied = false;
|
|
99
|
+
|
|
100
|
+
const result = await uiShowTool.execute(
|
|
101
|
+
{
|
|
102
|
+
surface_type: "dynamic_page",
|
|
103
|
+
title: "My Slides",
|
|
104
|
+
data: {
|
|
105
|
+
html: "<h1>Hello</h1>",
|
|
106
|
+
preview: {
|
|
107
|
+
title: "Slides",
|
|
108
|
+
subtitle: "3 slides about Apple",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
conversationId: "conversation-123",
|
|
114
|
+
workingDir: "/tmp",
|
|
115
|
+
trustClass: "guardian",
|
|
116
|
+
proxyToolResolver: async () => {
|
|
117
|
+
proxied = true;
|
|
118
|
+
return { content: "proxied", isError: false };
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(result).toEqual({ content: "proxied", isError: false });
|
|
124
|
+
expect(proxied).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
60
128
|
// ---------------------------------------------------------------------------
|
|
61
129
|
// UiSurfaceShowDynamicPage structure
|
|
62
130
|
// ---------------------------------------------------------------------------
|
|
@@ -76,8 +76,7 @@ async function seedSlackMessage(opts: {
|
|
|
76
76
|
inboundResult.conversationId,
|
|
77
77
|
"user",
|
|
78
78
|
initialContent,
|
|
79
|
-
{ userMessageChannel: "slack" },
|
|
80
|
-
{ skipIndexing: true },
|
|
79
|
+
{ metadata: { userMessageChannel: "slack" }, skipIndexing: true },
|
|
81
80
|
);
|
|
82
81
|
|
|
83
82
|
linkMessage(inboundResult.eventId, inserted.id);
|
|
@@ -19,10 +19,8 @@
|
|
|
19
19
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
20
20
|
|
|
21
21
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
defaultEmptyResponseTerminal,
|
|
25
|
-
} from "../plugins/defaults/empty-response.js";
|
|
22
|
+
import { defaultEmptyResponsePlugin } from "../plugins/defaults/empty-response/register.js";
|
|
23
|
+
import { defaultEmptyResponseTerminal } from "../plugins/defaults/empty-response/terminal.js";
|
|
26
24
|
import { DEFAULT_TIMEOUTS, runPipeline } from "../plugins/pipeline.js";
|
|
27
25
|
import {
|
|
28
26
|
getMiddlewaresFor,
|
|
@@ -73,10 +71,21 @@ function makeArgs(
|
|
|
73
71
|
emptyResponseRetries: 0,
|
|
74
72
|
maxEmptyResponseRetries: 1,
|
|
75
73
|
priorAssistantHadVisibleText: false,
|
|
74
|
+
// Default to `null` (no stop reason reported) so existing fixtures
|
|
75
|
+
// exercise the "organic empty turn" path. The refusal branch
|
|
76
|
+
// dedicated tests below set this to `"refusal"` explicitly.
|
|
77
|
+
stopReason: null,
|
|
76
78
|
...overrides,
|
|
77
79
|
};
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Refusal-specific nudge text — keep in sync with `register.ts`. Clients
|
|
84
|
+
* (and the model) may match on this exact text.
|
|
85
|
+
*/
|
|
86
|
+
const CANONICAL_REFUSAL_NUDGE_TEXT =
|
|
87
|
+
'<system_notice>Your previous response was empty because the upstream provider returned stop_reason="refusal". Please answer the user\'s last message directly with a plain-text response. Do not use any tools — just respond with text.</system_notice>';
|
|
88
|
+
|
|
80
89
|
async function runEmpty(
|
|
81
90
|
args: EmptyResponseArgs,
|
|
82
91
|
): Promise<EmptyResponseDecision> {
|
|
@@ -167,15 +176,128 @@ describe("emptyResponse pipeline — default decisions", () => {
|
|
|
167
176
|
|
|
168
177
|
test("no prior tool-use turn (toolUseTurns === 0) → accept", async () => {
|
|
169
178
|
// Empty first assistant response with no tools is not the pattern the
|
|
170
|
-
// nudge guards against. Default accepts
|
|
179
|
+
// organic-empty-turn nudge guards against. Default accepts (unless the
|
|
180
|
+
// stop reason is `"refusal"` — see the refusal-specific tests below).
|
|
181
|
+
const decision = await runEmpty(
|
|
182
|
+
makeArgs({
|
|
183
|
+
responseContent: [],
|
|
184
|
+
toolUseTurns: 0,
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
expect(decision.action).toBe("accept");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// ─── Refusal stop ────────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
test("stopReason='refusal' on turn 0 with no content → nudge with refusal text", async () => {
|
|
193
|
+
// The canonical failure mode this branch exists to catch: Anthropic's
|
|
194
|
+
// safety classifier zeros the response on the very first model call,
|
|
195
|
+
// returning a single thinking block and `stopReason: "refusal"`. Without
|
|
196
|
+
// this branch, the terminal would `accept` and the loop would persist
|
|
197
|
+
// an empty assistant bubble to the user.
|
|
198
|
+
const decision = await runEmpty(
|
|
199
|
+
makeArgs({
|
|
200
|
+
stopReason: "refusal",
|
|
201
|
+
responseContent: [],
|
|
202
|
+
toolUseBlocksLength: 0,
|
|
203
|
+
toolUseTurns: 0,
|
|
204
|
+
emptyResponseRetries: 0,
|
|
205
|
+
priorAssistantHadVisibleText: false,
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
expect(decision.action).toBe("nudge");
|
|
209
|
+
expect(decision.nudgeText).toBe(CANONICAL_REFUSAL_NUDGE_TEXT);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("stopReason='refusal' with a thinking-only block still nudges", async () => {
|
|
213
|
+
// Thinking blocks aren't visible text — the user sees nothing. A
|
|
214
|
+
// refusal with only thinking content matches the same shape the
|
|
215
|
+
// production log captured (`contentBlocks: 1, toolUseCount: 0`).
|
|
216
|
+
const decision = await runEmpty(
|
|
217
|
+
makeArgs({
|
|
218
|
+
stopReason: "refusal",
|
|
219
|
+
responseContent: [
|
|
220
|
+
{
|
|
221
|
+
type: "thinking",
|
|
222
|
+
thinking: "...",
|
|
223
|
+
signature: "sig",
|
|
224
|
+
} as ContentBlock,
|
|
225
|
+
],
|
|
226
|
+
toolUseBlocksLength: 0,
|
|
227
|
+
toolUseTurns: 0,
|
|
228
|
+
}),
|
|
229
|
+
);
|
|
230
|
+
expect(decision.action).toBe("nudge");
|
|
231
|
+
expect(decision.nudgeText).toBe(CANONICAL_REFUSAL_NUDGE_TEXT);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("stopReason='refusal' but visible text present → accept (model recovered)", async () => {
|
|
235
|
+
// The classifier can flag a partial response; if the model already
|
|
236
|
+
// delivered some visible text before refusing, the user has something
|
|
237
|
+
// to see. Accept.
|
|
238
|
+
const decision = await runEmpty(
|
|
239
|
+
makeArgs({
|
|
240
|
+
stopReason: "refusal",
|
|
241
|
+
responseContent: [{ type: "text", text: "partial answer" }],
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
expect(decision.action).toBe("accept");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("stopReason='refusal' but tool_use blocks present → accept", async () => {
|
|
248
|
+
// A refusal with tool_use blocks is unusual (the model wouldn't normally
|
|
249
|
+
// issue tools after a classifier hit) but we still shouldn't nudge —
|
|
250
|
+
// the loop will execute the tools and the model will get another shot.
|
|
171
251
|
const decision = await runEmpty(
|
|
172
252
|
makeArgs({
|
|
253
|
+
stopReason: "refusal",
|
|
254
|
+
responseContent: [
|
|
255
|
+
{
|
|
256
|
+
type: "tool_use",
|
|
257
|
+
id: "tu-1",
|
|
258
|
+
name: "read",
|
|
259
|
+
input: { path: "/tmp/x" },
|
|
260
|
+
} as ContentBlock,
|
|
261
|
+
],
|
|
262
|
+
toolUseBlocksLength: 1,
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
expect(decision.action).toBe("accept");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("stopReason='refusal' but retries exhausted → accept (no infinite loop)", async () => {
|
|
269
|
+
// Persistent classifier hit shouldn't burn turns indefinitely. Once
|
|
270
|
+
// we've used our retry budget, accept (the user will see an empty
|
|
271
|
+
// bubble, but the loop terminates).
|
|
272
|
+
const decision = await runEmpty(
|
|
273
|
+
makeArgs({
|
|
274
|
+
stopReason: "refusal",
|
|
173
275
|
responseContent: [],
|
|
174
276
|
toolUseTurns: 0,
|
|
277
|
+
emptyResponseRetries: 1,
|
|
278
|
+
maxEmptyResponseRetries: 1,
|
|
175
279
|
}),
|
|
176
280
|
);
|
|
177
281
|
expect(decision.action).toBe("accept");
|
|
178
282
|
});
|
|
283
|
+
|
|
284
|
+
test("stopReason='refusal' beats post-tool-empty nudge text (refusal-specific wording)", async () => {
|
|
285
|
+
// When both branches would fire, refusal wins because the refusal
|
|
286
|
+
// text is more accurate ("safety classifier zeroed the response"
|
|
287
|
+
// vs. "summary of what you found or did"). This guards against a
|
|
288
|
+
// future refactor that orders the branches differently.
|
|
289
|
+
const decision = await runEmpty(
|
|
290
|
+
makeArgs({
|
|
291
|
+
stopReason: "refusal",
|
|
292
|
+
responseContent: [],
|
|
293
|
+
toolUseBlocksLength: 0,
|
|
294
|
+
toolUseTurns: 2, // would trip the post-tool branch too
|
|
295
|
+
priorAssistantHadVisibleText: false,
|
|
296
|
+
}),
|
|
297
|
+
);
|
|
298
|
+
expect(decision.action).toBe("nudge");
|
|
299
|
+
expect(decision.nudgeText).toBe(CANONICAL_REFUSAL_NUDGE_TEXT);
|
|
300
|
+
});
|
|
179
301
|
});
|
|
180
302
|
|
|
181
303
|
describe("emptyResponse pipeline — custom middleware overrides", () => {
|
|
@@ -155,7 +155,7 @@ describe("FilingService", () => {
|
|
|
155
155
|
conversationId: args[0] as string,
|
|
156
156
|
content: args[1] as string,
|
|
157
157
|
options:
|
|
158
|
-
(args[
|
|
158
|
+
(args[2] as { speed?: string; callSite?: string } | undefined) ??
|
|
159
159
|
undefined,
|
|
160
160
|
});
|
|
161
161
|
return { messageId: "msg-1" };
|
|
@@ -319,7 +319,7 @@ describe("FilingService", () => {
|
|
|
319
319
|
let compactionCalls = 0;
|
|
320
320
|
|
|
321
321
|
setTestProcessMessage((...args: unknown[]) => {
|
|
322
|
-
const callSite = (args[
|
|
322
|
+
const callSite = (args[2] as { callSite?: string } | undefined)
|
|
323
323
|
?.callSite;
|
|
324
324
|
if (callSite === "filingAgent") {
|
|
325
325
|
filingCalls += 1;
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
isWakeUpGreeting,
|
|
9
9
|
getCannedFirstGreeting,
|
|
10
10
|
buildScanFirstMessage,
|
|
11
|
+
buildSelfIntroMessage,
|
|
11
12
|
CANNED_FIRST_GREETING,
|
|
12
13
|
} = await import("../daemon/first-greeting.js");
|
|
13
14
|
import type { OnboardingGreetingContext } from "../daemon/first-greeting.js";
|
|
@@ -96,7 +97,7 @@ describe("first-greeting", () => {
|
|
|
96
97
|
assistantName: "Pax",
|
|
97
98
|
});
|
|
98
99
|
expect(greeting).toBe(
|
|
99
|
-
"Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.
|
|
100
|
+
"Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll learn from it fast. Best head start you can give me.",
|
|
100
101
|
);
|
|
101
102
|
});
|
|
102
103
|
|
|
@@ -108,7 +109,7 @@ describe("first-greeting", () => {
|
|
|
108
109
|
assistantName: "Remy",
|
|
109
110
|
});
|
|
110
111
|
expect(greeting).toBe(
|
|
111
|
-
"Hey Alice, I'm Remy. Good to meet you.\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.
|
|
112
|
+
"Hey Alice, I'm Remy. Good to meet you.\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here. And you don't have to start me from scratch — if there's a ChatGPT or Claude that already knows you, bring it over and I'll get up to speed fast. Honestly, it's the best head start you could give me.",
|
|
112
113
|
);
|
|
113
114
|
});
|
|
114
115
|
|
|
@@ -119,7 +120,7 @@ describe("first-greeting", () => {
|
|
|
119
120
|
assistantName: "Pax",
|
|
120
121
|
});
|
|
121
122
|
expect(greeting).toBe(
|
|
122
|
-
"Hey, I'm Pax. Let's see what you've got.\n\nWe can jump straight into whatever you've got, or take a few minutes to just talk first.
|
|
123
|
+
"Hey, I'm Pax. Let's see what you've got.\n\nWe can jump straight into whatever you've got, or take a few minutes to just talk first. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll get up to speed fast. Best head start you can give me — want to start there?",
|
|
123
124
|
);
|
|
124
125
|
});
|
|
125
126
|
|
|
@@ -131,7 +132,7 @@ describe("first-greeting", () => {
|
|
|
131
132
|
assistantName: "Pax",
|
|
132
133
|
});
|
|
133
134
|
expect(greeting).toBe(
|
|
134
|
-
"Hey Alice, I'm Pax.\n\nWe can start with whatever's in front of you, or just talk for a bit first. Either way.
|
|
135
|
+
"Hey Alice, I'm Pax.\n\nWe can start with whatever's in front of you, or just talk for a bit first. Either way. And you don't have to start me from nothing — if there's a ChatGPT or Claude that already knows you, bring it over and I'll learn from it. The best head start you could give me.",
|
|
135
136
|
);
|
|
136
137
|
});
|
|
137
138
|
|
|
@@ -142,7 +143,7 @@ describe("first-greeting", () => {
|
|
|
142
143
|
userName: "Alice",
|
|
143
144
|
});
|
|
144
145
|
expect(greeting).toBe(
|
|
145
|
-
"Hey Alice,\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.
|
|
146
|
+
"Hey Alice,\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll learn from it fast. Best head start you can give me.",
|
|
146
147
|
);
|
|
147
148
|
});
|
|
148
149
|
|
|
@@ -153,7 +154,7 @@ describe("first-greeting", () => {
|
|
|
153
154
|
assistantName: "Pax",
|
|
154
155
|
});
|
|
155
156
|
expect(greeting).toBe(
|
|
156
|
-
"Hey, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.
|
|
157
|
+
"Hey, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll learn from it fast. Best head start you can give me.",
|
|
157
158
|
);
|
|
158
159
|
});
|
|
159
160
|
|
|
@@ -173,7 +174,7 @@ describe("first-greeting", () => {
|
|
|
173
174
|
tone: "warm",
|
|
174
175
|
});
|
|
175
176
|
expect(greeting).toBe(
|
|
176
|
-
"Hey,\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.
|
|
177
|
+
"Hey,\n\nWe can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here. And you don't have to start me from scratch — if there's a ChatGPT or Claude that already knows you, bring it over and I'll get up to speed fast. Honestly, it's the best head start you could give me.",
|
|
177
178
|
);
|
|
178
179
|
});
|
|
179
180
|
|
|
@@ -204,7 +205,7 @@ describe("first-greeting", () => {
|
|
|
204
205
|
assistantName: "Pax",
|
|
205
206
|
});
|
|
206
207
|
expect(greeting).toBe(
|
|
207
|
-
"Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you.
|
|
208
|
+
"Hey Alice, I'm Pax.\n\nWe can get into whatever you've got, or just talk first — that tends to go better. Up to you. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll learn from it fast. Best head start you can give me.",
|
|
208
209
|
);
|
|
209
210
|
});
|
|
210
211
|
|
|
@@ -234,7 +235,7 @@ describe("first-greeting", () => {
|
|
|
234
235
|
const [intro, invite] = greeting.split("\n\n");
|
|
235
236
|
expect(intro).toBe("Hey Bob, I'm Pax.");
|
|
236
237
|
expect(invite).toBe(
|
|
237
|
-
"We can get into whatever you've got, or just talk first — that tends to go better. Up to you.
|
|
238
|
+
"We can get into whatever you've got, or just talk first — that tends to go better. Up to you. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll learn from it fast. Best head start you can give me.",
|
|
238
239
|
);
|
|
239
240
|
});
|
|
240
241
|
|
|
@@ -243,7 +244,7 @@ describe("first-greeting", () => {
|
|
|
243
244
|
const [intro, invite] = greeting.split("\n\n");
|
|
244
245
|
expect(intro).toBe("Hey Bob, I'm Pax. Good to meet you.");
|
|
245
246
|
expect(invite).toBe(
|
|
246
|
-
"We can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here.
|
|
247
|
+
"We can start on something specific, or just talk for a bit first — honestly that tends to work out better. Either way, I'm here. And you don't have to start me from scratch — if there's a ChatGPT or Claude that already knows you, bring it over and I'll get up to speed fast. Honestly, it's the best head start you could give me.",
|
|
247
248
|
);
|
|
248
249
|
});
|
|
249
250
|
|
|
@@ -252,7 +253,7 @@ describe("first-greeting", () => {
|
|
|
252
253
|
const [intro, invite] = greeting.split("\n\n");
|
|
253
254
|
expect(intro).toBe("Hey Bob, I'm Pax. Let's see what you've got.");
|
|
254
255
|
expect(invite).toBe(
|
|
255
|
-
"We can jump straight into whatever you've got, or take a few minutes to just talk first.
|
|
256
|
+
"We can jump straight into whatever you've got, or take a few minutes to just talk first. And you don't have to start me from scratch — if you've built up a ChatGPT or Claude, bring it over and I'll get up to speed fast. Best head start you can give me — want to start there?",
|
|
256
257
|
);
|
|
257
258
|
});
|
|
258
259
|
|
|
@@ -261,7 +262,7 @@ describe("first-greeting", () => {
|
|
|
261
262
|
const [intro, invite] = greeting.split("\n\n");
|
|
262
263
|
expect(intro).toBe("Hey Bob, I'm Pax.");
|
|
263
264
|
expect(invite).toBe(
|
|
264
|
-
"We can start with whatever's in front of you, or just talk for a bit first. Either way.
|
|
265
|
+
"We can start with whatever's in front of you, or just talk for a bit first. Either way. And you don't have to start me from nothing — if there's a ChatGPT or Claude that already knows you, bring it over and I'll learn from it. The best head start you could give me.",
|
|
265
266
|
);
|
|
266
267
|
});
|
|
267
268
|
|
|
@@ -286,7 +287,7 @@ describe("first-greeting", () => {
|
|
|
286
287
|
});
|
|
287
288
|
|
|
288
289
|
describe("migration offer is present in every variant", () => {
|
|
289
|
-
const MIGRATION_MARKER = "
|
|
290
|
+
const MIGRATION_MARKER = "head start";
|
|
290
291
|
|
|
291
292
|
it("no-onboarding greeting includes the migration offer", () => {
|
|
292
293
|
expect(getCannedFirstGreeting(undefined)).toContain(MIGRATION_MARKER);
|
|
@@ -309,7 +310,7 @@ describe("first-greeting", () => {
|
|
|
309
310
|
assistantName: "Pax",
|
|
310
311
|
});
|
|
311
312
|
expect(greeting).toContain(MIGRATION_MARKER);
|
|
312
|
-
expect(greeting).toContain("
|
|
313
|
+
expect(greeting).toContain("ChatGPT or Claude");
|
|
313
314
|
}
|
|
314
315
|
});
|
|
315
316
|
|
|
@@ -375,4 +376,44 @@ describe("first-greeting", () => {
|
|
|
375
376
|
expect(greeting).not.toContain("personal");
|
|
376
377
|
});
|
|
377
378
|
});
|
|
379
|
+
|
|
380
|
+
describe("buildSelfIntroMessage", () => {
|
|
381
|
+
const ctx = (
|
|
382
|
+
over: Partial<OnboardingGreetingContext> = {},
|
|
383
|
+
): OnboardingGreetingContext => ({
|
|
384
|
+
tools: [],
|
|
385
|
+
tasks: [],
|
|
386
|
+
tone: "grounded",
|
|
387
|
+
...over,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("uses both names when present", () => {
|
|
391
|
+
expect(
|
|
392
|
+
buildSelfIntroMessage(ctx({ assistantName: "Vela", userName: "alex" })),
|
|
393
|
+
).toBe("Hi Vela, I'm alex. Nice to meet you.");
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("drops the missing user name", () => {
|
|
397
|
+
expect(buildSelfIntroMessage(ctx({ assistantName: "Vela" }))).toBe(
|
|
398
|
+
"Hi Vela. Nice to meet you.",
|
|
399
|
+
);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("drops the missing assistant name", () => {
|
|
403
|
+
expect(buildSelfIntroMessage(ctx({ userName: "alex" }))).toBe(
|
|
404
|
+
"Hi, I'm alex. Nice to meet you.",
|
|
405
|
+
);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("treats whitespace-only names as missing", () => {
|
|
409
|
+
expect(
|
|
410
|
+
buildSelfIntroMessage(ctx({ assistantName: " ", userName: " " })),
|
|
411
|
+
).toBeUndefined();
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("returns undefined when neither name is known (caller keeps canned greeting)", () => {
|
|
415
|
+
expect(buildSelfIntroMessage(ctx())).toBeUndefined();
|
|
416
|
+
expect(buildSelfIntroMessage(undefined)).toBeUndefined();
|
|
417
|
+
});
|
|
418
|
+
});
|
|
378
419
|
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
base64ByteLength,
|
|
5
|
+
estimateGeminiAudioTokens,
|
|
6
|
+
GEMINI_MAX_INLINE_AUDIO_BYTES,
|
|
7
|
+
normalizeGeminiAudioMime,
|
|
8
|
+
} from "../providers/gemini/inline-media.js";
|
|
9
|
+
|
|
10
|
+
describe("normalizeGeminiAudioMime", () => {
|
|
11
|
+
test("maps audio/mpeg onto Gemini's audio/mp3 spelling", () => {
|
|
12
|
+
expect(normalizeGeminiAudioMime("audio/mpeg")).toBe("audio/mp3");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("passes supported audio types through unchanged", () => {
|
|
16
|
+
for (const mime of [
|
|
17
|
+
"audio/wav",
|
|
18
|
+
"audio/mp3",
|
|
19
|
+
"audio/aiff",
|
|
20
|
+
"audio/aac",
|
|
21
|
+
"audio/ogg",
|
|
22
|
+
"audio/flac",
|
|
23
|
+
]) {
|
|
24
|
+
expect(normalizeGeminiAudioMime(mime)).toBe(mime);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("is case-insensitive and strips parameters", () => {
|
|
29
|
+
expect(normalizeGeminiAudioMime("AUDIO/MPEG")).toBe("audio/mp3");
|
|
30
|
+
expect(normalizeGeminiAudioMime("audio/ogg; codecs=opus")).toBe(
|
|
31
|
+
"audio/ogg",
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("returns null for types Gemini cannot take inline", () => {
|
|
36
|
+
for (const mime of [
|
|
37
|
+
"audio/x-m4a",
|
|
38
|
+
"audio/mp4",
|
|
39
|
+
"audio/opus",
|
|
40
|
+
"application/pdf",
|
|
41
|
+
"image/png",
|
|
42
|
+
"",
|
|
43
|
+
]) {
|
|
44
|
+
expect(normalizeGeminiAudioMime(mime)).toBeNull();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("base64ByteLength", () => {
|
|
50
|
+
test("approximates raw bytes from base64 length", () => {
|
|
51
|
+
// 8 base64 chars → 6 raw bytes
|
|
52
|
+
expect(base64ByteLength("QUJDREVG")).toBe(6);
|
|
53
|
+
expect(base64ByteLength("")).toBe(0);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("estimateGeminiAudioTokens", () => {
|
|
58
|
+
test("scales with payload size and stays far below the base64-as-text count", () => {
|
|
59
|
+
// ~3 MB of base64 ≈ ~2.25 MB raw ≈ ~140s at 16 KB/s ≈ ~4.5k tokens.
|
|
60
|
+
const data = "A".repeat(3 * 1024 * 1024);
|
|
61
|
+
const tokens = estimateGeminiAudioTokens(data);
|
|
62
|
+
expect(tokens).toBeGreaterThan(1_000);
|
|
63
|
+
// Must be a small fraction of the naive base64-length/4 estimate (~786k).
|
|
64
|
+
expect(tokens).toBeLessThan(data.length / 40);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("returns zero for empty data", () => {
|
|
68
|
+
expect(estimateGeminiAudioTokens("")).toBe(0);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("inline audio cap leaves headroom under Gemini's 20 MB request limit", () => {
|
|
73
|
+
expect(GEMINI_MAX_INLINE_AUDIO_BYTES).toBe(12 * 1024 * 1024);
|
|
74
|
+
// Base64 of the cap (~16 MB) must stay under the 20 MB wire limit.
|
|
75
|
+
expect(GEMINI_MAX_INLINE_AUDIO_BYTES * (4 / 3)).toBeLessThan(
|
|
76
|
+
20 * 1024 * 1024,
|
|
77
|
+
);
|
|
78
|
+
});
|