@vellumai/assistant 0.8.2 → 0.8.4
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/ARCHITECTURE.md +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -219,36 +219,7 @@ describe("token estimator", () => {
|
|
|
219
219
|
expect(largeFileTokens).toBe(smallFileTokens);
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
test("scales image token estimate with base64 payload size (non-Anthropic)", () => {
|
|
224
|
-
const smallImageTokens = estimateContentBlockTokens(
|
|
225
|
-
{
|
|
226
|
-
type: "image",
|
|
227
|
-
source: {
|
|
228
|
-
type: "base64",
|
|
229
|
-
media_type: "image/png",
|
|
230
|
-
data: "a".repeat(64),
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
{ providerName: "openai" },
|
|
234
|
-
);
|
|
235
|
-
const largeImageTokens = estimateContentBlockTokens(
|
|
236
|
-
{
|
|
237
|
-
type: "image",
|
|
238
|
-
source: {
|
|
239
|
-
type: "base64",
|
|
240
|
-
media_type: "image/png",
|
|
241
|
-
data: "a".repeat(60_000),
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
{ providerName: "openai" },
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
expect(largeImageTokens).toBeGreaterThan(smallImageTokens);
|
|
248
|
-
expect(largeImageTokens - smallImageTokens).toBeGreaterThan(1000);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
test("estimates Anthropic image tokens from dimensions, not base64 size", () => {
|
|
222
|
+
test("estimates image tokens from dimensions, not base64 size", () => {
|
|
252
223
|
// Build a minimal valid PNG header encoding 1920x1080 dimensions.
|
|
253
224
|
// PNG header: 8-byte signature + 4-byte IHDR length + 4-byte "IHDR" + 4-byte width + 4-byte height = 24 bytes minimum
|
|
254
225
|
const pngHeader = Buffer.alloc(24);
|
|
@@ -278,55 +249,139 @@ describe("token estimator", () => {
|
|
|
278
249
|
const fullPayload = Buffer.concat([pngHeader, padding]);
|
|
279
250
|
const base64Data = fullPayload.toString("base64");
|
|
280
251
|
|
|
281
|
-
const anthropicTokens = estimateContentBlockTokens(
|
|
282
|
-
{
|
|
283
|
-
type: "image",
|
|
284
|
-
source: { type: "base64", media_type: "image/png", data: base64Data },
|
|
285
|
-
},
|
|
286
|
-
{ providerName: "anthropic" },
|
|
287
|
-
);
|
|
288
|
-
|
|
289
252
|
// 1920x1080 scaled to fit 1568px bounding box: dimScale = 1568/1920 = 0.8167
|
|
290
253
|
// scaledWidth = round(1920 * 0.8167) = 1568, scaledHeight = round(1080 * 0.8167) = 882
|
|
291
254
|
// pixels = 1568 * 882 = 1,382,976 > 1,200,000 → mpScale = sqrt(1200000/1382976) = 0.9315
|
|
292
255
|
// scaledWidth = round(1568 * 0.9315) = 1461, scaledHeight = round(882 * 0.9315) = 822
|
|
293
256
|
// tokens = ceil(1461 * 822 / 750) = ceil(1601.26) = ~1,602
|
|
294
|
-
// With IMAGE_BLOCK_OVERHEAD_TOKENS and media_type overhead, still well under 5000
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
257
|
+
// With IMAGE_BLOCK_OVERHEAD_TOKENS and media_type overhead, still well under 5000.
|
|
258
|
+
// Same result for every provider — dimension-based estimate is universal.
|
|
259
|
+
for (const providerName of [
|
|
260
|
+
"anthropic",
|
|
261
|
+
"openai",
|
|
262
|
+
"openrouter",
|
|
263
|
+
"gemini",
|
|
264
|
+
]) {
|
|
265
|
+
const tokens = estimateContentBlockTokens(
|
|
266
|
+
{
|
|
267
|
+
type: "image",
|
|
268
|
+
source: { type: "base64", media_type: "image/png", data: base64Data },
|
|
269
|
+
},
|
|
270
|
+
{ providerName },
|
|
271
|
+
);
|
|
272
|
+
expect(tokens).toBeLessThan(5_000);
|
|
273
|
+
}
|
|
306
274
|
});
|
|
307
275
|
|
|
308
|
-
test("falls back to max tokens when
|
|
276
|
+
test("falls back to max tokens when image dimensions can't be parsed", () => {
|
|
309
277
|
// Corrupted base64 that won't parse as a valid image header
|
|
310
278
|
const corruptedData = Buffer.from(
|
|
311
279
|
"not-a-valid-image-header-at-all",
|
|
312
280
|
).toString("base64");
|
|
313
281
|
|
|
282
|
+
for (const providerName of ["anthropic", "openai", "openrouter"]) {
|
|
283
|
+
const tokens = estimateContentBlockTokens(
|
|
284
|
+
{
|
|
285
|
+
type: "image",
|
|
286
|
+
source: {
|
|
287
|
+
type: "base64",
|
|
288
|
+
media_type: "image/png",
|
|
289
|
+
data: corruptedData,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{ providerName },
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Falls back to the per-image cap (1,600 tokens). Total = 16 (block
|
|
296
|
+
// overhead) + ceil(9/4) (media_type) + 1600 = 1619.
|
|
297
|
+
expect(tokens).toBeGreaterThanOrEqual(1_600);
|
|
298
|
+
expect(tokens).toBeLessThan(2_000);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("Gemini falls back to its max-tile budget for unparseable / HEIC images", () => {
|
|
303
|
+
// HEIC/HEIF coming from iOS attachments aren't parsed by
|
|
304
|
+
// parseImageDimensions, so the estimator sees null dims. The generic
|
|
305
|
+
// 1,600-token cap would under-count by ~2.5x for a typical iPhone photo
|
|
306
|
+
// that ends up at Gemini's 16-tile / 4,128-token ceiling. Use the
|
|
307
|
+
// Gemini-specific cap instead to avoid skipping compaction.
|
|
308
|
+
for (const mediaType of [
|
|
309
|
+
"image/heic",
|
|
310
|
+
"image/heif",
|
|
311
|
+
"image/png", // corrupted PNG also exercises the fallback
|
|
312
|
+
]) {
|
|
313
|
+
const data = Buffer.from("not-a-valid-image-header-at-all").toString(
|
|
314
|
+
"base64",
|
|
315
|
+
);
|
|
316
|
+
const tokens = estimateContentBlockTokens(
|
|
317
|
+
{
|
|
318
|
+
type: "image",
|
|
319
|
+
source: { type: "base64", media_type: mediaType, data },
|
|
320
|
+
},
|
|
321
|
+
{ providerName: "gemini" },
|
|
322
|
+
);
|
|
323
|
+
// 4128 (16 tiles * 258) + 16 (block overhead) + ceil(mediaType len / 4)
|
|
324
|
+
expect(tokens).toBeGreaterThanOrEqual(4_128);
|
|
325
|
+
expect(tokens).toBeLessThan(4_200);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("Gemini image tokens scale with image area via 768x768 tiling", () => {
|
|
330
|
+
// Per Google's docs, Gemini tiles images larger than 384px into 768x768
|
|
331
|
+
// chunks at 258 tokens each, after resizing the longest side to ≤3072px.
|
|
332
|
+
// 3000x3000 (under the cap) → ceil(3000/768)^2 = 4*4 = 16 tiles → 4,128
|
|
333
|
+
// image tokens.
|
|
314
334
|
const tokens = estimateContentBlockTokens(
|
|
315
335
|
{
|
|
316
336
|
type: "image",
|
|
317
337
|
source: {
|
|
318
338
|
type: "base64",
|
|
319
339
|
media_type: "image/png",
|
|
320
|
-
data:
|
|
340
|
+
data: makePngBase64(3000, 3000),
|
|
321
341
|
},
|
|
322
342
|
},
|
|
323
|
-
{ providerName: "
|
|
343
|
+
{ providerName: "gemini" },
|
|
344
|
+
);
|
|
345
|
+
expect(tokens).toBeGreaterThanOrEqual(4_128);
|
|
346
|
+
expect(tokens).toBeLessThan(4_200);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("Gemini clamps image dimensions to 3072px before tiling", () => {
|
|
350
|
+
// Google's docs state images are resized to a 3072px max side before
|
|
351
|
+
// tiling. Without the clamp, a 4000x4000 image would be counted as
|
|
352
|
+
// ceil(4000/768)^2 = 36 tiles (~9,288 tokens) instead of the actual
|
|
353
|
+
// ceil(3072/768)^2 = 16 tiles (~4,128 tokens), over-counting by ~2.25x
|
|
354
|
+
// and triggering spurious compaction.
|
|
355
|
+
const tokens = estimateContentBlockTokens(
|
|
356
|
+
{
|
|
357
|
+
type: "image",
|
|
358
|
+
source: {
|
|
359
|
+
type: "base64",
|
|
360
|
+
media_type: "image/png",
|
|
361
|
+
data: makePngBase64(4000, 4000),
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
{ providerName: "gemini" },
|
|
324
365
|
);
|
|
366
|
+
expect(tokens).toBeGreaterThanOrEqual(4_128);
|
|
367
|
+
expect(tokens).toBeLessThan(4_200);
|
|
368
|
+
});
|
|
325
369
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
370
|
+
test("Gemini images ≤384px on both sides count as a single 258-token tile", () => {
|
|
371
|
+
const tokens = estimateContentBlockTokens(
|
|
372
|
+
{
|
|
373
|
+
type: "image",
|
|
374
|
+
source: {
|
|
375
|
+
type: "base64",
|
|
376
|
+
media_type: "image/png",
|
|
377
|
+
data: makePngBase64(200, 200),
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{ providerName: "gemini" },
|
|
381
|
+
);
|
|
382
|
+
// 258 (tile) + 16 (block overhead) + 3 (media type) = 277
|
|
383
|
+
expect(tokens).toBeGreaterThanOrEqual(258);
|
|
384
|
+
expect(tokens).toBeLessThan(300);
|
|
330
385
|
});
|
|
331
386
|
|
|
332
387
|
test("Anthropic image tokens are the same for same-dimension images regardless of payload size", () => {
|
|
@@ -19,7 +19,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
19
19
|
|
|
20
20
|
mock.module("../providers/registry.js", () => ({
|
|
21
21
|
getProvider: () => ({ name: "mock-provider" }),
|
|
22
|
-
initializeProviders: () => {},
|
|
22
|
+
initializeProviders: async () => {},
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
25
|
mock.module("../config/loader.js", () => ({
|
|
@@ -78,7 +78,11 @@ mock.module("../config/loader.js", () => ({
|
|
|
78
78
|
},
|
|
79
79
|
},
|
|
80
80
|
},
|
|
81
|
-
profiles: {
|
|
81
|
+
profiles: {
|
|
82
|
+
"quality-optimized": {
|
|
83
|
+
contextWindow: { maxInputTokens: 50000 },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
82
86
|
callSites: {},
|
|
83
87
|
pricingOverrides: [],
|
|
84
88
|
},
|
|
@@ -165,6 +169,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
165
169
|
getConversationOriginChannel: () => null,
|
|
166
170
|
getMessageById: () => null,
|
|
167
171
|
getLastUserTimestampBefore: () => 0,
|
|
172
|
+
setLastNotifiedInferenceProfile: () => {},
|
|
168
173
|
}));
|
|
169
174
|
|
|
170
175
|
mock.module("../memory/conversation-disk-view.js", () => ({
|
|
@@ -350,12 +355,17 @@ import {
|
|
|
350
355
|
|
|
351
356
|
// Captures every positional argument the loop passes to `agentLoop.run`.
|
|
352
357
|
// The 8th positional argument is the per-turn `overrideProfile`, which is
|
|
353
|
-
// what
|
|
358
|
+
// what most tests assert on. The 10th and 11th positional arguments re-resolve
|
|
359
|
+
// that profile and its max-token budget between provider calls.
|
|
354
360
|
interface CapturedAgentLoopRun {
|
|
355
361
|
callSite: LLMCallSite | undefined;
|
|
356
362
|
overrideProfile: string | undefined;
|
|
363
|
+
resolvedOverrideProfile: string | undefined;
|
|
364
|
+
resolvedEffectiveMaxInputTokens: number | undefined;
|
|
357
365
|
}
|
|
358
366
|
|
|
367
|
+
let mutateBeforeResolveOverrideProfile: (() => void) | undefined;
|
|
368
|
+
|
|
359
369
|
function makeCtx(
|
|
360
370
|
captured: CapturedAgentLoopRun[],
|
|
361
371
|
overrides?: Partial<AgentLoopConversationContext>,
|
|
@@ -371,8 +381,17 @@ function makeCtx(
|
|
|
371
381
|
callSite?: LLMCallSite,
|
|
372
382
|
_turnContext?: unknown,
|
|
373
383
|
overrideProfile?: string,
|
|
384
|
+
_effectiveMaxInputTokens?: number,
|
|
385
|
+
resolveOverrideProfile?: () => string | undefined,
|
|
386
|
+
resolveEffectiveMaxInputTokens?: () => number | undefined,
|
|
374
387
|
): Promise<Message[]> => {
|
|
375
|
-
|
|
388
|
+
mutateBeforeResolveOverrideProfile?.();
|
|
389
|
+
captured.push({
|
|
390
|
+
callSite,
|
|
391
|
+
overrideProfile,
|
|
392
|
+
resolvedOverrideProfile: resolveOverrideProfile?.(),
|
|
393
|
+
resolvedEffectiveMaxInputTokens: resolveEffectiveMaxInputTokens?.(),
|
|
394
|
+
});
|
|
376
395
|
return [
|
|
377
396
|
...messages,
|
|
378
397
|
{
|
|
@@ -511,6 +530,7 @@ beforeEach(() => {
|
|
|
511
530
|
totalEstimatedCost: 0,
|
|
512
531
|
title: null,
|
|
513
532
|
};
|
|
533
|
+
mutateBeforeResolveOverrideProfile = undefined;
|
|
514
534
|
resetPluginRegistryAndRegisterDefaults();
|
|
515
535
|
});
|
|
516
536
|
|
|
@@ -631,4 +651,35 @@ describe("runAgentLoopImpl — per-conversation inferenceProfile", () => {
|
|
|
631
651
|
expect(call.overrideProfile).toBe("fast");
|
|
632
652
|
}
|
|
633
653
|
});
|
|
654
|
+
|
|
655
|
+
test("re-resolves inferenceProfile when a tool changes it mid-turn", async () => {
|
|
656
|
+
mockConversationRow = {
|
|
657
|
+
id: "conv-1",
|
|
658
|
+
conversationType: "standard",
|
|
659
|
+
inferenceProfile: null,
|
|
660
|
+
contextSummary: null,
|
|
661
|
+
contextCompactedMessageCount: 0,
|
|
662
|
+
totalInputTokens: 0,
|
|
663
|
+
totalOutputTokens: 0,
|
|
664
|
+
totalEstimatedCost: 0,
|
|
665
|
+
title: null,
|
|
666
|
+
};
|
|
667
|
+
mutateBeforeResolveOverrideProfile = () => {
|
|
668
|
+
mockConversationRow = {
|
|
669
|
+
...mockConversationRow!,
|
|
670
|
+
inferenceProfile: "quality-optimized",
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
const captured: CapturedAgentLoopRun[] = [];
|
|
675
|
+
const ctx = makeCtx(captured);
|
|
676
|
+
|
|
677
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", () => {});
|
|
678
|
+
|
|
679
|
+
expect(captured.length).toBeGreaterThan(0);
|
|
680
|
+
expect(captured[0].overrideProfile).toBeUndefined();
|
|
681
|
+
expect(captured[0].resolvedOverrideProfile).toBe("quality-optimized");
|
|
682
|
+
expect(captured[0].resolvedEffectiveMaxInputTokens).toBe(50000);
|
|
683
|
+
expect(ctx.currentTurnOverrideProfile).toBeUndefined();
|
|
684
|
+
});
|
|
634
685
|
});
|
|
@@ -195,6 +195,9 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
195
195
|
updateMessageContent: () => {},
|
|
196
196
|
updateMessageMetadata: () => {},
|
|
197
197
|
clearStrippedInjectionMetadataForConversation: () => {},
|
|
198
|
+
setLastNotifiedInferenceProfile: () => {},
|
|
199
|
+
getLastUserTimestampBefore: () => 0,
|
|
200
|
+
getConversationOverrideProfileFromRow: () => undefined,
|
|
198
201
|
}));
|
|
199
202
|
|
|
200
203
|
afterAll(() => {
|
|
@@ -301,6 +304,7 @@ mock.module("../daemon/history-repair.js", () => ({
|
|
|
301
304
|
}));
|
|
302
305
|
|
|
303
306
|
const recordUsageMock = mock((..._args: unknown[]) => {});
|
|
307
|
+
const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
|
|
304
308
|
mock.module("../daemon/conversation-usage.js", () => ({
|
|
305
309
|
recordUsage: recordUsageMock,
|
|
306
310
|
}));
|
|
@@ -349,12 +353,23 @@ mock.module("../workspace/git-service.js", () => ({
|
|
|
349
353
|
}));
|
|
350
354
|
|
|
351
355
|
mock.module("../daemon/conversation-error.js", () => ({
|
|
352
|
-
classifyConversationError: (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
356
|
+
classifyConversationError: (err: unknown, _ctx: unknown) => {
|
|
357
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
358
|
+
if (/context.?length.?exceeded/i.test(message)) {
|
|
359
|
+
return {
|
|
360
|
+
code: "CONTEXT_TOO_LARGE",
|
|
361
|
+
userMessage: "Context too large.",
|
|
362
|
+
retryable: false,
|
|
363
|
+
errorCategory: "context_too_large",
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
code: "CONVERSATION_PROCESSING_FAILED",
|
|
368
|
+
userMessage: "Something went wrong processing your message.",
|
|
369
|
+
retryable: false,
|
|
370
|
+
errorCategory: "processing_failed",
|
|
371
|
+
};
|
|
372
|
+
},
|
|
358
373
|
isUserCancellation: (err: unknown, ctx: { aborted?: boolean }) => {
|
|
359
374
|
if (!ctx.aborted) return false;
|
|
360
375
|
if (err instanceof DOMException && err.name === "AbortError") return true;
|
|
@@ -394,6 +409,7 @@ mock.module("../agent/message-types.js", () => ({
|
|
|
394
409
|
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
395
410
|
recordRequestLog: () => {},
|
|
396
411
|
backfillMessageIdOnLogs: () => {},
|
|
412
|
+
setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
|
|
397
413
|
}));
|
|
398
414
|
|
|
399
415
|
mock.module("../memory/archive-store.js", () => ({
|
|
@@ -616,6 +632,7 @@ beforeEach(() => {
|
|
|
616
632
|
mockOverflowAction = "fail_gracefully";
|
|
617
633
|
mockApplyRuntimeInjections = (msgs) => msgs;
|
|
618
634
|
recordUsageMock.mockClear();
|
|
635
|
+
setAgentLoopExitReasonOnLatestLogMock.mockClear();
|
|
619
636
|
// Reset the plugin registry and re-register every default so the
|
|
620
637
|
// orchestrator's pipelines (`overflowReduce`, `persistence`, …) dispatch to
|
|
621
638
|
// the default middleware, which in turn hits the mocked collaborators
|
|
@@ -1907,6 +1924,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
1907
1924
|
// After exhausting mid-loop attempts, the convergence loop should
|
|
1908
1925
|
// have been triggered (contextTooLargeDetected set to true)
|
|
1909
1926
|
expect(convergenceReducerCalled).toBe(true);
|
|
1927
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
1928
|
+
"test-conv",
|
|
1929
|
+
"context_too_large",
|
|
1930
|
+
);
|
|
1910
1931
|
});
|
|
1911
1932
|
|
|
1912
1933
|
// ── Test 9 ────────────────────────────────────────────────────────
|
|
@@ -2068,6 +2089,10 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
2068
2089
|
|
|
2069
2090
|
// Agent loop: 1 initial + 3 mid-loop re-entries + 2 convergence re-runs = 6 calls
|
|
2070
2091
|
expect(agentLoopCallCount).toBe(6);
|
|
2092
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
2093
|
+
"test-conv",
|
|
2094
|
+
"context_too_large",
|
|
2095
|
+
);
|
|
2071
2096
|
});
|
|
2072
2097
|
|
|
2073
2098
|
// ── Test 8 ────────────────────────────────────────────────────────
|
|
@@ -283,6 +283,7 @@ let mockSlackChronologicalContext: {
|
|
|
283
283
|
renderedMessages: Array<{
|
|
284
284
|
message: Message;
|
|
285
285
|
sourceChannelTs: string | null;
|
|
286
|
+
tagLineProvenance: "none" | "slack-reaction" | "slack-timezone-message";
|
|
286
287
|
}>;
|
|
287
288
|
messages: Message[];
|
|
288
289
|
compactableStartIndex: number;
|
|
@@ -386,6 +387,8 @@ mock.module("../daemon/history-repair.js", () => ({
|
|
|
386
387
|
|
|
387
388
|
const recordUsageMock = mock(() => {});
|
|
388
389
|
const recordRequestLogMock = mock(() => {});
|
|
390
|
+
const backfillMessageIdOnLogsMock = mock(() => {});
|
|
391
|
+
const setAgentLoopExitReasonOnLatestLogMock = mock(() => {});
|
|
389
392
|
mock.module("../daemon/conversation-usage.js", () => ({
|
|
390
393
|
recordUsage: recordUsageMock,
|
|
391
394
|
}));
|
|
@@ -482,7 +485,8 @@ mock.module("../memory/archive-store.js", () => ({
|
|
|
482
485
|
|
|
483
486
|
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
484
487
|
recordRequestLog: recordRequestLogMock,
|
|
485
|
-
backfillMessageIdOnLogs:
|
|
488
|
+
backfillMessageIdOnLogs: backfillMessageIdOnLogsMock,
|
|
489
|
+
setAgentLoopExitReasonOnLatestLog: setAgentLoopExitReasonOnLatestLogMock,
|
|
486
490
|
}));
|
|
487
491
|
|
|
488
492
|
let mockHasProactiveArtifactCompleted = true;
|
|
@@ -658,6 +662,8 @@ beforeEach(() => {
|
|
|
658
662
|
mockInjectionBlocks = {};
|
|
659
663
|
recordUsageMock.mockClear();
|
|
660
664
|
recordRequestLogMock.mockClear();
|
|
665
|
+
backfillMessageIdOnLogsMock.mockClear();
|
|
666
|
+
setAgentLoopExitReasonOnLatestLogMock.mockClear();
|
|
661
667
|
syncMessageToDiskMock.mockClear();
|
|
662
668
|
rebuildConversationDiskViewFromDbStateMock.mockClear();
|
|
663
669
|
updateMessageMetadataMock.mockClear();
|
|
@@ -2298,6 +2304,10 @@ describe("session-agent-loop", () => {
|
|
|
2298
2304
|
|
|
2299
2305
|
const handoff = events.find((e) => e.type === "generation_handoff");
|
|
2300
2306
|
expect(handoff).toBeDefined();
|
|
2307
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).toHaveBeenCalledWith(
|
|
2308
|
+
"test-conv",
|
|
2309
|
+
"checkpoint_handoff",
|
|
2310
|
+
);
|
|
2301
2311
|
});
|
|
2302
2312
|
|
|
2303
2313
|
test("continues when canHandoffAtCheckpoint returns false", async () => {
|
|
@@ -2357,6 +2367,10 @@ describe("session-agent-loop", () => {
|
|
|
2357
2367
|
|
|
2358
2368
|
const handoff = events.find((e) => e.type === "generation_handoff");
|
|
2359
2369
|
expect(handoff).toBeUndefined();
|
|
2370
|
+
expect(setAgentLoopExitReasonOnLatestLogMock).not.toHaveBeenCalledWith(
|
|
2371
|
+
"test-conv",
|
|
2372
|
+
"checkpoint_handoff",
|
|
2373
|
+
);
|
|
2360
2374
|
const complete = events.find((e) => e.type === "message_complete");
|
|
2361
2375
|
expect(complete).toBeDefined();
|
|
2362
2376
|
});
|
|
@@ -2855,6 +2869,57 @@ describe("session-agent-loop", () => {
|
|
|
2855
2869
|
);
|
|
2856
2870
|
expect(conversationErrors.length).toBeGreaterThanOrEqual(1);
|
|
2857
2871
|
});
|
|
2872
|
+
|
|
2873
|
+
test("pipes synthetic assistant message id into provider-error log rows via backfill", async () => {
|
|
2874
|
+
// Codex P1 regression test: the provider-failure turn must not leave
|
|
2875
|
+
// its `llm_request_logs` row orphaned. Without the backfill call in
|
|
2876
|
+
// the synthetic-message branch, a later turn's `handleMessageComplete`
|
|
2877
|
+
// sweep would wrong-attach this row to the wrong assistant message.
|
|
2878
|
+
const events: ServerMessage[] = [];
|
|
2879
|
+
|
|
2880
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
2881
|
+
// 1) handleProviderError -> writes an `llm_request_logs` row with
|
|
2882
|
+
// messageId=null (the orphan we are trying to link).
|
|
2883
|
+
onEvent({
|
|
2884
|
+
type: "provider_error",
|
|
2885
|
+
error: new Error("upstream 500"),
|
|
2886
|
+
rawRequest: { model: "gpt-4.1", messages: [] },
|
|
2887
|
+
actualProvider: "openai",
|
|
2888
|
+
});
|
|
2889
|
+
// 2) handleError -> sets `state.providerErrorUserMessage`, which
|
|
2890
|
+
// activates the synthetic-message branch below the loop.
|
|
2891
|
+
onEvent({
|
|
2892
|
+
type: "error",
|
|
2893
|
+
error: new Error("upstream 500"),
|
|
2894
|
+
});
|
|
2895
|
+
// Provider returned no assistant content — same messages back.
|
|
2896
|
+
return messages;
|
|
2897
|
+
};
|
|
2898
|
+
|
|
2899
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
2900
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
2901
|
+
|
|
2902
|
+
// The orphan was written with messageId=undefined.
|
|
2903
|
+
expect(recordRequestLogMock).toHaveBeenCalledTimes(1);
|
|
2904
|
+
const recordCall = recordRequestLogMock.mock.calls[0] as unknown as [
|
|
2905
|
+
string,
|
|
2906
|
+
string,
|
|
2907
|
+
string,
|
|
2908
|
+
string | undefined,
|
|
2909
|
+
string | undefined,
|
|
2910
|
+
];
|
|
2911
|
+
expect(recordCall[0]).toBe("test-conv");
|
|
2912
|
+
expect(recordCall[3]).toBeUndefined();
|
|
2913
|
+
|
|
2914
|
+
// The synthetic-message branch then piped the assigned message id
|
|
2915
|
+
// (from the mocked `addMessage` -> `{ id: "mock-msg-id" }`) into the
|
|
2916
|
+
// backfill primitive, scoped to this conversation.
|
|
2917
|
+
expect(backfillMessageIdOnLogsMock).toHaveBeenCalledTimes(1);
|
|
2918
|
+
const backfillCall = backfillMessageIdOnLogsMock.mock
|
|
2919
|
+
.calls[0] as unknown as [string, string];
|
|
2920
|
+
expect(backfillCall[0]).toBe("test-conv");
|
|
2921
|
+
expect(backfillCall[1]).toBe("mock-msg-id");
|
|
2922
|
+
});
|
|
2858
2923
|
});
|
|
2859
2924
|
|
|
2860
2925
|
describe("pkbSystemReminderBlock metadata persistence", () => {
|
|
@@ -2967,6 +3032,7 @@ describe("session-agent-loop", () => {
|
|
|
2967
3032
|
"1700000020.000000",
|
|
2968
3033
|
"1700000030.000000",
|
|
2969
3034
|
][index]!,
|
|
3035
|
+
tagLineProvenance: "none",
|
|
2970
3036
|
})),
|
|
2971
3037
|
compactableStartIndex: 0,
|
|
2972
3038
|
};
|
|
@@ -3066,6 +3132,7 @@ describe("session-agent-loop", () => {
|
|
|
3066
3132
|
"1700000020.000000",
|
|
3067
3133
|
"1700000030.000000",
|
|
3068
3134
|
][index]!,
|
|
3135
|
+
tagLineProvenance: "none",
|
|
3069
3136
|
})),
|
|
3070
3137
|
compactableStartIndex: 0,
|
|
3071
3138
|
};
|
|
@@ -3182,6 +3249,7 @@ describe("session-agent-loop", () => {
|
|
|
3182
3249
|
"1700000030.000000",
|
|
3183
3250
|
"1700000040.000000",
|
|
3184
3251
|
][index]!,
|
|
3252
|
+
tagLineProvenance: "none",
|
|
3185
3253
|
})),
|
|
3186
3254
|
compactableStartIndex: 0,
|
|
3187
3255
|
};
|
|
@@ -3287,9 +3355,10 @@ describe("session-agent-loop", () => {
|
|
|
3287
3355
|
{
|
|
3288
3356
|
message: firstSummaryMessage,
|
|
3289
3357
|
sourceChannelTs: null,
|
|
3358
|
+
tagLineProvenance: "none",
|
|
3290
3359
|
},
|
|
3291
|
-
mockSlackChronologicalContext
|
|
3292
|
-
mockSlackChronologicalContext
|
|
3360
|
+
mockSlackChronologicalContext!.renderedMessages[2],
|
|
3361
|
+
mockSlackChronologicalContext!.renderedMessages[3],
|
|
3293
3362
|
],
|
|
3294
3363
|
messages: firstCompactedMessages,
|
|
3295
3364
|
compactableStartIndex: 1,
|
|
@@ -3328,6 +3397,7 @@ describe("session-agent-loop", () => {
|
|
|
3328
3397
|
"1700000020.000000",
|
|
3329
3398
|
"1700000030.000000",
|
|
3330
3399
|
][index]!,
|
|
3400
|
+
tagLineProvenance: "none",
|
|
3331
3401
|
})),
|
|
3332
3402
|
compactableStartIndex: 0,
|
|
3333
3403
|
};
|
|
@@ -3485,6 +3555,7 @@ describe("session-agent-loop", () => {
|
|
|
3485
3555
|
],
|
|
3486
3556
|
},
|
|
3487
3557
|
sourceChannelTs: null,
|
|
3558
|
+
tagLineProvenance: "none",
|
|
3488
3559
|
},
|
|
3489
3560
|
{
|
|
3490
3561
|
message: {
|
|
@@ -3492,6 +3563,7 @@ describe("session-agent-loop", () => {
|
|
|
3492
3563
|
content: [{ type: "text", text: "after watermark reply" }],
|
|
3493
3564
|
},
|
|
3494
3565
|
sourceChannelTs: "1700000020.000000",
|
|
3566
|
+
tagLineProvenance: "none",
|
|
3495
3567
|
},
|
|
3496
3568
|
],
|
|
3497
3569
|
compactableStartIndex: 1,
|
|
@@ -3586,6 +3658,7 @@ describe("session-agent-loop", () => {
|
|
|
3586
3658
|
],
|
|
3587
3659
|
},
|
|
3588
3660
|
sourceChannelTs: null,
|
|
3661
|
+
tagLineProvenance: "none",
|
|
3589
3662
|
},
|
|
3590
3663
|
{
|
|
3591
3664
|
message: {
|
|
@@ -3598,6 +3671,7 @@ describe("session-agent-loop", () => {
|
|
|
3598
3671
|
],
|
|
3599
3672
|
},
|
|
3600
3673
|
sourceChannelTs: "1700000121.000000",
|
|
3674
|
+
tagLineProvenance: "none",
|
|
3601
3675
|
},
|
|
3602
3676
|
],
|
|
3603
3677
|
compactableStartIndex: 1,
|
|
@@ -22,7 +22,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
|
|
23
23
|
mock.module("../providers/registry.js", () => ({
|
|
24
24
|
getProvider: () => ({ name: "mock-provider" }),
|
|
25
|
-
initializeProviders: () => {},
|
|
25
|
+
initializeProviders: async () => {},
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
28
|
mock.module("../config/loader.js", () => ({
|