@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- 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-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- 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 +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- 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 +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -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__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- 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/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- 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 +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -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/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- 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.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- 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/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- 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 +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- 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 +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -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 +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- 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 +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- 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__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for the agent loop's `provider_error` recording path.
|
|
3
|
+
*
|
|
4
|
+
* When the `llmCall` pipeline throws (provider rejected the request before
|
|
5
|
+
* returning a usable response), the loop must emit a `provider_error` event
|
|
6
|
+
* carrying the loop-level raw request and the thrown error so downstream
|
|
7
|
+
* consumers can persist an `llm_request_logs` row. Without this, rejected
|
|
8
|
+
* calls leave nothing in the LLM inspector — only a pino log line.
|
|
9
|
+
*
|
|
10
|
+
* Coverage:
|
|
11
|
+
* - Emits `provider_error` with `rawRequest`, `error`, and `actualProvider`
|
|
12
|
+
* when the provider throws a `ProviderError`.
|
|
13
|
+
* - `rawRequest` carries the message history, tools, and system prompt the
|
|
14
|
+
* loop attempted to send — so the row replays/debugs cleanly.
|
|
15
|
+
* - `actualProvider` echoes `ProviderError.provider` when available, falling
|
|
16
|
+
* back to `provider.name` for non-ProviderError throws.
|
|
17
|
+
* - The error is still re-thrown internally (the existing `error` event
|
|
18
|
+
* still fires after the new `provider_error` event), preserving the
|
|
19
|
+
* outer-catch behavior (abort/Sentry/break).
|
|
20
|
+
* - Skips emission on user-aborted runs — there is no provider rejection
|
|
21
|
+
* worth recording when the user cancelled.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { describe, expect, test } from "bun:test";
|
|
25
|
+
|
|
26
|
+
import type { AgentEvent } from "../agent/loop.js";
|
|
27
|
+
import { AgentLoop } from "../agent/loop.js";
|
|
28
|
+
import type {
|
|
29
|
+
Message,
|
|
30
|
+
Provider,
|
|
31
|
+
ProviderResponse,
|
|
32
|
+
SendMessageOptions,
|
|
33
|
+
ToolDefinition,
|
|
34
|
+
} from "../providers/types.js";
|
|
35
|
+
import { ProviderError } from "../util/errors.js";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build a provider that throws on every `sendMessage` call. Records what
|
|
39
|
+
* the loop attempted to send so the test can assert `rawRequest` carries
|
|
40
|
+
* the right payload.
|
|
41
|
+
*/
|
|
42
|
+
function makeThrowingProvider(
|
|
43
|
+
name: string,
|
|
44
|
+
throwFn: () => Error,
|
|
45
|
+
): {
|
|
46
|
+
provider: Provider;
|
|
47
|
+
calls: Array<{
|
|
48
|
+
messages: Message[];
|
|
49
|
+
tools?: ToolDefinition[];
|
|
50
|
+
systemPrompt?: string;
|
|
51
|
+
}>;
|
|
52
|
+
} {
|
|
53
|
+
const calls: Array<{
|
|
54
|
+
messages: Message[];
|
|
55
|
+
tools?: ToolDefinition[];
|
|
56
|
+
systemPrompt?: string;
|
|
57
|
+
}> = [];
|
|
58
|
+
const provider: Provider = {
|
|
59
|
+
name,
|
|
60
|
+
async sendMessage(
|
|
61
|
+
messages: Message[],
|
|
62
|
+
tools?: ToolDefinition[],
|
|
63
|
+
systemPrompt?: string,
|
|
64
|
+
_options?: SendMessageOptions,
|
|
65
|
+
): Promise<ProviderResponse> {
|
|
66
|
+
calls.push({ messages: [...messages], tools, systemPrompt });
|
|
67
|
+
throw throwFn();
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
return { provider, calls };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("AgentLoop provider_error event emission", () => {
|
|
74
|
+
test("emits provider_error with loop-level rawRequest when provider throws ProviderError", async () => {
|
|
75
|
+
const thrown = new ProviderError(
|
|
76
|
+
"Anthropic API error (429): rate limited",
|
|
77
|
+
"anthropic",
|
|
78
|
+
429,
|
|
79
|
+
{ retryAfterMs: 1500 },
|
|
80
|
+
);
|
|
81
|
+
const { provider, calls } = makeThrowingProvider("anthropic", () => thrown);
|
|
82
|
+
|
|
83
|
+
const events: AgentEvent[] = [];
|
|
84
|
+
const loop = new AgentLoop(provider, "you are a helpful assistant");
|
|
85
|
+
|
|
86
|
+
await loop.run(
|
|
87
|
+
[{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
88
|
+
(e) => {
|
|
89
|
+
events.push(e);
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(calls).toHaveLength(1);
|
|
94
|
+
|
|
95
|
+
const providerErrorEvent = events.find((e) => e.type === "provider_error");
|
|
96
|
+
expect(providerErrorEvent).toBeDefined();
|
|
97
|
+
if (providerErrorEvent?.type !== "provider_error") {
|
|
98
|
+
throw new Error("type narrowing");
|
|
99
|
+
}
|
|
100
|
+
expect(providerErrorEvent.error).toBe(thrown);
|
|
101
|
+
expect(providerErrorEvent.actualProvider).toBe("anthropic");
|
|
102
|
+
|
|
103
|
+
// rawRequest should carry the loop-level abstract shape: messages,
|
|
104
|
+
// tools, systemPrompt, and the provider name we tried to dispatch
|
|
105
|
+
// through. The provider-specific shape (e.g. Gemini's `contents`) is
|
|
106
|
+
// never built because the provider threw before returning it.
|
|
107
|
+
const raw = providerErrorEvent.rawRequest as Record<string, unknown>;
|
|
108
|
+
expect(raw.provider).toBe("anthropic");
|
|
109
|
+
expect(raw.systemPrompt).toBe("you are a helpful assistant");
|
|
110
|
+
expect(Array.isArray(raw.messages)).toBe(true);
|
|
111
|
+
expect((raw.messages as Message[])[0].role).toBe("user");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("error event still fires after provider_error (outer catch behavior unchanged)", async () => {
|
|
115
|
+
const thrown = new ProviderError(
|
|
116
|
+
"Gemini API error (500): internal",
|
|
117
|
+
"gemini",
|
|
118
|
+
500,
|
|
119
|
+
);
|
|
120
|
+
const { provider } = makeThrowingProvider("gemini", () => thrown);
|
|
121
|
+
|
|
122
|
+
const events: AgentEvent[] = [];
|
|
123
|
+
const loop = new AgentLoop(provider, "system");
|
|
124
|
+
|
|
125
|
+
await loop.run(
|
|
126
|
+
[{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
127
|
+
(e) => {
|
|
128
|
+
events.push(e);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const providerErrorIdx = events.findIndex(
|
|
133
|
+
(e) => e.type === "provider_error",
|
|
134
|
+
);
|
|
135
|
+
const errorIdx = events.findIndex((e) => e.type === "error");
|
|
136
|
+
expect(providerErrorIdx).toBeGreaterThanOrEqual(0);
|
|
137
|
+
expect(errorIdx).toBeGreaterThanOrEqual(0);
|
|
138
|
+
// Recording-first ordering is load-bearing: a consumer that sees the
|
|
139
|
+
// generic `error` event and shuts the stream down must have already
|
|
140
|
+
// received the `provider_error` row for the rejected call.
|
|
141
|
+
expect(providerErrorIdx).toBeLessThan(errorIdx);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("falls back to provider.name when a non-ProviderError is thrown", async () => {
|
|
145
|
+
const thrown = new Error("unexpected SDK boom");
|
|
146
|
+
const { provider } = makeThrowingProvider("openai", () => thrown);
|
|
147
|
+
|
|
148
|
+
const events: AgentEvent[] = [];
|
|
149
|
+
const loop = new AgentLoop(provider, "system");
|
|
150
|
+
|
|
151
|
+
await loop.run(
|
|
152
|
+
[{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
153
|
+
(e) => {
|
|
154
|
+
events.push(e);
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const providerErrorEvent = events.find((e) => e.type === "provider_error");
|
|
159
|
+
expect(providerErrorEvent).toBeDefined();
|
|
160
|
+
if (providerErrorEvent?.type !== "provider_error") {
|
|
161
|
+
throw new Error("type narrowing");
|
|
162
|
+
}
|
|
163
|
+
// The thrown Error has no `.provider` field, so the event falls back to
|
|
164
|
+
// the dispatching provider's `name` — keeps the persisted log row's
|
|
165
|
+
// `provider` column populated even for surprise errors.
|
|
166
|
+
expect(providerErrorEvent.actualProvider).toBe("openai");
|
|
167
|
+
expect(providerErrorEvent.error).toBe(thrown);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("does NOT emit provider_error on user-aborted runs", async () => {
|
|
171
|
+
const controller = new AbortController();
|
|
172
|
+
const thrown = new Error("aborted");
|
|
173
|
+
const { provider } = makeThrowingProvider("anthropic", () => {
|
|
174
|
+
// Pre-abort then throw so the loop's catch sees `signal.aborted === true`.
|
|
175
|
+
controller.abort();
|
|
176
|
+
return thrown;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const events: AgentEvent[] = [];
|
|
180
|
+
const loop = new AgentLoop(provider, "system");
|
|
181
|
+
|
|
182
|
+
await loop.run(
|
|
183
|
+
[{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
184
|
+
(e) => {
|
|
185
|
+
events.push(e);
|
|
186
|
+
},
|
|
187
|
+
controller.signal,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const providerErrorEvent = events.find((e) => e.type === "provider_error");
|
|
191
|
+
// Cancellation should never produce a recording row — there's no
|
|
192
|
+
// provider rejection worth logging when the user pulled the plug.
|
|
193
|
+
expect(providerErrorEvent).toBeUndefined();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test: `classifyDiskPressureTurnPolicy` must receive the
|
|
3
|
+
* caller-supplied `callSite` from `wakeAgentForOpportunity`, not a
|
|
4
|
+
* hardcoded `"mainAgent"`.
|
|
5
|
+
*
|
|
6
|
+
* Today the disk-pressure classifier's `isBackgroundTurn` branches on
|
|
7
|
+
* `isDirectWake` before it consults `callSite`, so the hardcoded value
|
|
8
|
+
* did not produce a runtime regression. But the metadata recorded the
|
|
9
|
+
* wrong call site for any wake initiated by a background job (e.g.
|
|
10
|
+
* memory-v2 consolidation), and the inconsistency would bite the moment
|
|
11
|
+
* policy ever branches on `callSite` for wake turns. This test pins the
|
|
12
|
+
* forwarded value so the contract stays honest.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
DiskPressureTurnMetadata,
|
|
19
|
+
DiskPressureTurnPolicyDecision,
|
|
20
|
+
} from "../daemon/disk-pressure-policy.js";
|
|
21
|
+
import type { Message } from "../providers/types.js";
|
|
22
|
+
|
|
23
|
+
mock.module("../memory/conversation-crud.js", () => ({
|
|
24
|
+
getConversationOverrideProfile: () => undefined,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
mock.module("../config/loader.js", () => ({
|
|
28
|
+
getConfig: () => ({ llm: {} }),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
mock.module("../config/llm-context-resolution.js", () => ({
|
|
32
|
+
resolveEffectiveContextWindow: () => ({ maxInputTokens: 200_000 }),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
const classifyCalls: DiskPressureTurnMetadata[] = [];
|
|
36
|
+
mock.module("../daemon/disk-pressure-policy.js", () => ({
|
|
37
|
+
classifyDiskPressureTurnPolicy: (
|
|
38
|
+
_status: unknown,
|
|
39
|
+
metadata: DiskPressureTurnMetadata,
|
|
40
|
+
): DiskPressureTurnPolicyDecision => {
|
|
41
|
+
classifyCalls.push(metadata);
|
|
42
|
+
return { action: "allow-normal" };
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
mock.module("../daemon/disk-pressure-guard.js", () => ({
|
|
47
|
+
getDiskPressureStatus: () => ({
|
|
48
|
+
enabled: false,
|
|
49
|
+
state: "disabled",
|
|
50
|
+
locked: false,
|
|
51
|
+
acknowledged: false,
|
|
52
|
+
overrideActive: false,
|
|
53
|
+
effectivelyLocked: false,
|
|
54
|
+
lockId: null,
|
|
55
|
+
usagePercent: null,
|
|
56
|
+
thresholdPercent: 95,
|
|
57
|
+
path: null,
|
|
58
|
+
lastCheckedAt: null,
|
|
59
|
+
blockedCapabilities: [],
|
|
60
|
+
error: null,
|
|
61
|
+
}),
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
import {
|
|
65
|
+
__resetWakeChainForTests,
|
|
66
|
+
wakeAgentForOpportunity,
|
|
67
|
+
type WakeTarget,
|
|
68
|
+
} from "../runtime/agent-wake.js";
|
|
69
|
+
|
|
70
|
+
function makeTarget(): WakeTarget {
|
|
71
|
+
const history: Message[] = [];
|
|
72
|
+
let processing = false;
|
|
73
|
+
return {
|
|
74
|
+
conversationId: "conv-wake-callsite",
|
|
75
|
+
agentLoop: {
|
|
76
|
+
run: (async (messages: Message[]) =>
|
|
77
|
+
messages) as WakeTarget["agentLoop"]["run"],
|
|
78
|
+
},
|
|
79
|
+
getMessages: () => history,
|
|
80
|
+
pushMessage: (msg) => {
|
|
81
|
+
history.push(msg);
|
|
82
|
+
},
|
|
83
|
+
emitAgentEvent: () => {},
|
|
84
|
+
isProcessing: () => processing,
|
|
85
|
+
markProcessing: (on) => {
|
|
86
|
+
processing = on;
|
|
87
|
+
},
|
|
88
|
+
persistTailMessage: async () => {},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
__resetWakeChainForTests();
|
|
94
|
+
classifyCalls.length = 0;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("wakeAgentForOpportunity — disk-pressure callSite forwarding", () => {
|
|
98
|
+
test("forwards opts.callSite to classifyDiskPressureTurnPolicy", async () => {
|
|
99
|
+
const target = makeTarget();
|
|
100
|
+
|
|
101
|
+
await wakeAgentForOpportunity(
|
|
102
|
+
{
|
|
103
|
+
conversationId: target.conversationId,
|
|
104
|
+
hint: "consolidate buffer",
|
|
105
|
+
source: "memory_v2_consolidation",
|
|
106
|
+
callSite: "memoryV2Consolidation",
|
|
107
|
+
},
|
|
108
|
+
{ resolveTarget: async () => target },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(classifyCalls).toHaveLength(1);
|
|
112
|
+
expect(classifyCalls[0]!.callSite).toBe("memoryV2Consolidation");
|
|
113
|
+
expect(classifyCalls[0]!.isDirectWake).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("defaults to mainAgent when opts.callSite is omitted", async () => {
|
|
117
|
+
const target = makeTarget();
|
|
118
|
+
|
|
119
|
+
await wakeAgentForOpportunity(
|
|
120
|
+
{
|
|
121
|
+
conversationId: target.conversationId,
|
|
122
|
+
hint: "resume",
|
|
123
|
+
source: "scheduler",
|
|
124
|
+
},
|
|
125
|
+
{ resolveTarget: async () => target },
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(classifyCalls).toHaveLength(1);
|
|
129
|
+
expect(classifyCalls[0]!.callSite).toBe("mainAgent");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -223,6 +223,22 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
223
223
|
expect(system[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
224
224
|
});
|
|
225
225
|
|
|
226
|
+
test("omits empty dynamic system block after cache boundary", async () => {
|
|
227
|
+
const staticBlock = "You are a helpful assistant.";
|
|
228
|
+
const prompt = staticBlock + SYSTEM_PROMPT_CACHE_BOUNDARY;
|
|
229
|
+
|
|
230
|
+
await provider.sendMessage([userMsg("Hi")], undefined, prompt);
|
|
231
|
+
|
|
232
|
+
const system = lastStreamParams!.system as Array<{
|
|
233
|
+
type: string;
|
|
234
|
+
text: string;
|
|
235
|
+
cache_control?: { type: string; ttl?: string };
|
|
236
|
+
}>;
|
|
237
|
+
expect(system).toHaveLength(1);
|
|
238
|
+
expect(system[0].text).toBe(staticBlock);
|
|
239
|
+
expect(system[0].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
240
|
+
});
|
|
241
|
+
|
|
226
242
|
test("drops static system block cache_control when total would exceed 4", async () => {
|
|
227
243
|
const staticBlock = "You are a helpful assistant.";
|
|
228
244
|
const dynamicBlock = "User workspace files here.";
|
|
@@ -2346,6 +2362,35 @@ describe("OpenRouterProvider — Anthropic dispatch", () => {
|
|
|
2346
2362
|
expect(lastStreamParams!.reasoning).toBeUndefined();
|
|
2347
2363
|
});
|
|
2348
2364
|
|
|
2365
|
+
test("sends OpenRouter app-attribution headers on Anthropic-compatible requests", async () => {
|
|
2366
|
+
const { OpenRouterProvider } =
|
|
2367
|
+
await import("../providers/openrouter/client.js");
|
|
2368
|
+
const provider = new OpenRouterProvider(
|
|
2369
|
+
"or-key",
|
|
2370
|
+
"anthropic/claude-sonnet-4.6",
|
|
2371
|
+
);
|
|
2372
|
+
await provider.sendMessage([userMsg("hi")], undefined, undefined, {
|
|
2373
|
+
config: {
|
|
2374
|
+
usageAttributionHeaders: {
|
|
2375
|
+
"Vellum-Organization-Id": "org-123",
|
|
2376
|
+
},
|
|
2377
|
+
},
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
expect(_lastStreamOptions?.headers).toEqual(
|
|
2381
|
+
expect.objectContaining({
|
|
2382
|
+
"HTTP-Referer": "https://www.vellum.ai",
|
|
2383
|
+
"X-OpenRouter-Title": "Vellum Assistant",
|
|
2384
|
+
"X-OpenRouter-Categories": "personal-agent,cli-agent",
|
|
2385
|
+
"Vellum-Organization-Id": "org-123",
|
|
2386
|
+
}),
|
|
2387
|
+
);
|
|
2388
|
+
expect(lastStreamParams).not.toHaveProperty("HTTP-Referer");
|
|
2389
|
+
expect(lastStreamParams).not.toHaveProperty("X-OpenRouter-Title");
|
|
2390
|
+
expect(lastStreamParams).not.toHaveProperty("X-OpenRouter-Categories");
|
|
2391
|
+
expect(lastStreamParams).not.toHaveProperty("usageAttributionHeaders");
|
|
2392
|
+
});
|
|
2393
|
+
|
|
2349
2394
|
test("per-request model override routes based on the overridden model", async () => {
|
|
2350
2395
|
const { OpenRouterProvider } =
|
|
2351
2396
|
await import("../providers/openrouter/client.js");
|
|
@@ -22,15 +22,18 @@ function makeApp(overrides: Partial<AppDefinition> = {}): AppDefinition {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function makeMockStore(overrides: Partial<AppStore> = {}): AppStore {
|
|
25
|
+
const files = new Set<string>();
|
|
25
26
|
return {
|
|
26
27
|
getApp: () => makeApp(),
|
|
27
28
|
listApps: () => [makeApp()],
|
|
28
|
-
appFileExists: () =>
|
|
29
|
+
appFileExists: (_appId: string, path: string) => files.has(path),
|
|
29
30
|
createApp: (params) =>
|
|
30
31
|
makeApp({ name: params.name, description: params.description }),
|
|
31
32
|
updateApp: (id, updates) => makeApp({ id, ...updates }),
|
|
32
33
|
deleteApp: () => {},
|
|
33
|
-
writeAppFile: () => {
|
|
34
|
+
writeAppFile: (_appId: string, path: string) => {
|
|
35
|
+
files.add(path);
|
|
36
|
+
},
|
|
34
37
|
...overrides,
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -107,7 +110,10 @@ describe("app-builder skill tool scripts", () => {
|
|
|
107
110
|
isError: false,
|
|
108
111
|
});
|
|
109
112
|
const result = await appCreateScript.run(
|
|
110
|
-
{
|
|
113
|
+
{
|
|
114
|
+
name: "Auto App",
|
|
115
|
+
source_files: { "src/main.tsx": "// real code" },
|
|
116
|
+
},
|
|
111
117
|
makeContext({ proxyToolResolver: proxy }),
|
|
112
118
|
);
|
|
113
119
|
expect(result.isError).toBe(false);
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
type CompileResult = {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
errors: string[];
|
|
6
|
+
warnings: string[];
|
|
7
|
+
durationMs: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let compileResultOverride: CompileResult = {
|
|
11
|
+
ok: true,
|
|
12
|
+
errors: [],
|
|
13
|
+
warnings: [],
|
|
14
|
+
durationMs: 0,
|
|
15
|
+
};
|
|
2
16
|
|
|
3
17
|
mock.module("../bundler/app-compiler.js", () => ({
|
|
4
|
-
compileApp: async () =>
|
|
18
|
+
compileApp: async () => compileResultOverride,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
compileResultOverride = {
|
|
5
23
|
ok: true,
|
|
6
24
|
errors: [],
|
|
7
25
|
warnings: [],
|
|
8
26
|
durationMs: 0,
|
|
9
|
-
}
|
|
10
|
-
})
|
|
27
|
+
};
|
|
28
|
+
});
|
|
11
29
|
|
|
12
30
|
import type { AppDefinition } from "../memory/app-store.js";
|
|
13
31
|
import type { AppStore } from "../tools/apps/executors.js";
|
|
@@ -102,6 +120,204 @@ describe("executeAppCreate", () => {
|
|
|
102
120
|
expect(files["src/main.tsx"]).toBeDefined();
|
|
103
121
|
expect(files["src/main.tsx"]).toContain("import { render } from 'preact'");
|
|
104
122
|
expect(files["src/main.tsx"]).toContain('{"Hello, New App!"}');
|
|
123
|
+
// next_steps directive must be present so the model keeps writing
|
|
124
|
+
// real source files instead of treating the scaffold as done.
|
|
125
|
+
const parsed = JSON.parse(result.content);
|
|
126
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
127
|
+
expect(parsed.next_steps).toContain("app_refresh");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("skips auto_open on scaffold even when proxy resolver is available", async () => {
|
|
131
|
+
const files: Record<string, string> = {};
|
|
132
|
+
const app = makeMultifileApp({ name: "New App" });
|
|
133
|
+
const store: AppStore = {
|
|
134
|
+
...mockStore(app, files),
|
|
135
|
+
createApp: () => app,
|
|
136
|
+
};
|
|
137
|
+
let proxyCalled = false;
|
|
138
|
+
const proxyResolver = async () => {
|
|
139
|
+
proxyCalled = true;
|
|
140
|
+
return { content: JSON.stringify({ opened: true }), isError: false };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const result = await executeAppCreate(
|
|
144
|
+
{ name: "New App" },
|
|
145
|
+
store,
|
|
146
|
+
proxyResolver,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(result.isError).toBe(false);
|
|
150
|
+
expect(proxyCalled).toBe(false);
|
|
151
|
+
const parsed = JSON.parse(result.content);
|
|
152
|
+
expect(parsed.auto_opened).toBeUndefined();
|
|
153
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
154
|
+
expect(parsed.next_steps).toContain("app_refresh");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("omits next_steps when main.tsx was pre-written before app_create", async () => {
|
|
158
|
+
// The agent's supported workflow is to write the real source files first
|
|
159
|
+
// and then call app_create. In that case the placeholder directive would
|
|
160
|
+
// be false and risk triggering a destructive rewrite of correct code.
|
|
161
|
+
const files: Record<string, string> = {
|
|
162
|
+
"src/main.tsx": "// real code from the agent",
|
|
163
|
+
};
|
|
164
|
+
const app = makeMultifileApp({ name: "Pre-written App" });
|
|
165
|
+
const store: AppStore = {
|
|
166
|
+
...mockStore(app, files),
|
|
167
|
+
createApp: () => app,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const result = await executeAppCreate({ name: "Pre-written App" }, store);
|
|
171
|
+
|
|
172
|
+
expect(result.isError).toBe(false);
|
|
173
|
+
// The pre-written file must be preserved
|
|
174
|
+
expect(files["src/main.tsx"]).toBe("// real code from the agent");
|
|
175
|
+
const parsed = JSON.parse(result.content);
|
|
176
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("includes next_steps when compile fails on the scaffold", async () => {
|
|
180
|
+
compileResultOverride = {
|
|
181
|
+
ok: false,
|
|
182
|
+
errors: ["unexpected token"],
|
|
183
|
+
warnings: [],
|
|
184
|
+
durationMs: 3,
|
|
185
|
+
};
|
|
186
|
+
const files: Record<string, string> = {};
|
|
187
|
+
const app = makeMultifileApp({ name: "Broken App" });
|
|
188
|
+
const store: AppStore = {
|
|
189
|
+
...mockStore(app, files),
|
|
190
|
+
createApp: () => app,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = await executeAppCreate({ name: "Broken App" }, store);
|
|
194
|
+
|
|
195
|
+
expect(result.isError).toBe(false);
|
|
196
|
+
const parsed = JSON.parse(result.content);
|
|
197
|
+
expect(parsed.compile_errors).toEqual(["unexpected token"]);
|
|
198
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
199
|
+
expect(parsed.next_steps).toContain("app_refresh");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("omits next_steps when compile fails on pre-written files", async () => {
|
|
203
|
+
compileResultOverride = {
|
|
204
|
+
ok: false,
|
|
205
|
+
errors: ["unexpected token"],
|
|
206
|
+
warnings: [],
|
|
207
|
+
durationMs: 3,
|
|
208
|
+
};
|
|
209
|
+
const files: Record<string, string> = {
|
|
210
|
+
"src/main.tsx": "// agent's real (but broken) code",
|
|
211
|
+
};
|
|
212
|
+
const app = makeMultifileApp({ name: "Broken Pre-written App" });
|
|
213
|
+
const store: AppStore = {
|
|
214
|
+
...mockStore(app, files),
|
|
215
|
+
createApp: () => app,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const result = await executeAppCreate(
|
|
219
|
+
{ name: "Broken Pre-written App" },
|
|
220
|
+
store,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(result.isError).toBe(false);
|
|
224
|
+
const parsed = JSON.parse(result.content);
|
|
225
|
+
expect(parsed.compile_errors).toEqual(["unexpected token"]);
|
|
226
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("suppresses auto_open when only scaffold exists", async () => {
|
|
230
|
+
const files: Record<string, string> = {};
|
|
231
|
+
const app = makeMultifileApp({ name: "New App" });
|
|
232
|
+
const store: AppStore = {
|
|
233
|
+
...mockStore(app, files),
|
|
234
|
+
createApp: () => app,
|
|
235
|
+
};
|
|
236
|
+
let proxyCalledWith: Record<string, unknown> | undefined;
|
|
237
|
+
const proxyResolver = async (
|
|
238
|
+
toolName: string,
|
|
239
|
+
input: Record<string, unknown>,
|
|
240
|
+
) => {
|
|
241
|
+
proxyCalledWith = { toolName, input };
|
|
242
|
+
return { content: JSON.stringify({ opened: true }), isError: false };
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const result = await executeAppCreate(
|
|
246
|
+
{ name: "New App" },
|
|
247
|
+
store,
|
|
248
|
+
proxyResolver,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
expect(result.isError).toBe(false);
|
|
252
|
+
const parsed = JSON.parse(result.content);
|
|
253
|
+
expect(proxyCalledWith).toBeUndefined();
|
|
254
|
+
expect(parsed.auto_opened).toBeUndefined();
|
|
255
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("fires auto_open when source_files includes main.tsx", async () => {
|
|
259
|
+
const files: Record<string, string> = {};
|
|
260
|
+
const app = makeMultifileApp({ name: "Real App" });
|
|
261
|
+
const store: AppStore = {
|
|
262
|
+
...mockStore(app, files),
|
|
263
|
+
createApp: () => app,
|
|
264
|
+
};
|
|
265
|
+
let proxyCalledWith: Record<string, unknown> | undefined;
|
|
266
|
+
const proxyResolver = async (
|
|
267
|
+
toolName: string,
|
|
268
|
+
input: Record<string, unknown>,
|
|
269
|
+
) => {
|
|
270
|
+
proxyCalledWith = { toolName, input };
|
|
271
|
+
return { content: JSON.stringify({ opened: true }), isError: false };
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const result = await executeAppCreate(
|
|
275
|
+
{
|
|
276
|
+
name: "Real App",
|
|
277
|
+
source_files: {
|
|
278
|
+
"src/main.tsx":
|
|
279
|
+
"import { render } from 'preact';\nrender(<div>Real</div>, document.getElementById('app')!);",
|
|
280
|
+
"src/styles.css": "body { margin: 0; }",
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
store,
|
|
284
|
+
proxyResolver,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(result.isError).toBe(false);
|
|
288
|
+
const parsed = JSON.parse(result.content);
|
|
289
|
+
expect(proxyCalledWith).toBeDefined();
|
|
290
|
+
expect(parsed.auto_opened).toBe(true);
|
|
291
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
292
|
+
expect(files["src/main.tsx"]).toContain("Real");
|
|
293
|
+
expect(files["src/styles.css"]).toContain("margin");
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("source_files are written and prevent scaffold", async () => {
|
|
297
|
+
const files: Record<string, string> = {};
|
|
298
|
+
const app = makeMultifileApp({ name: "Inline App" });
|
|
299
|
+
const store: AppStore = {
|
|
300
|
+
...mockStore(app, files),
|
|
301
|
+
createApp: () => app,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const result = await executeAppCreate(
|
|
305
|
+
{
|
|
306
|
+
name: "Inline App",
|
|
307
|
+
source_files: {
|
|
308
|
+
"src/main.tsx": "// custom app code",
|
|
309
|
+
"src/components/App.tsx": "// App component",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
store,
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(result.isError).toBe(false);
|
|
316
|
+
expect(files["src/main.tsx"]).toBe("// custom app code");
|
|
317
|
+
expect(files["src/components/App.tsx"]).toBe("// App component");
|
|
318
|
+
expect(files["src/index.html"]).toBeDefined();
|
|
319
|
+
const parsed = JSON.parse(result.content);
|
|
320
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
105
321
|
});
|
|
106
322
|
|
|
107
323
|
test("rejects retired html shortcut", async () => {
|
|
@@ -585,3 +585,38 @@ describe("indexer v1/v2 mutual exclusion for graph_extract", () => {
|
|
|
585
585
|
);
|
|
586
586
|
});
|
|
587
587
|
});
|
|
588
|
+
|
|
589
|
+
// ─────────────────────────────────────────────────────────────────
|
|
590
|
+
// Indexer v1/v2 mutual exclusion: build_conversation_summary feeds
|
|
591
|
+
// v1-only readers (fetchRecentSummaries, semantic search). When
|
|
592
|
+
// memory.v2.enabled is on, the summary writes are unread, so the
|
|
593
|
+
// indexer must not enqueue them.
|
|
594
|
+
// ─────────────────────────────────────────────────────────────────
|
|
595
|
+
|
|
596
|
+
describe("indexer v1/v2 mutual exclusion for build_conversation_summary", () => {
|
|
597
|
+
const originalV2Enabled = TEST_CONFIG.memory.v2.enabled;
|
|
598
|
+
|
|
599
|
+
afterEach(() => {
|
|
600
|
+
TEST_CONFIG.memory.v2.enabled = originalV2Enabled;
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("v2 active (config on) → build_conversation_summary not enqueued", async () => {
|
|
604
|
+
TEST_CONFIG.memory.v2.enabled = true;
|
|
605
|
+
|
|
606
|
+
const source = createConversation("summary-v2-active");
|
|
607
|
+
await indexMessages(source.id, 2);
|
|
608
|
+
|
|
609
|
+
expect(countJobsOfType("build_conversation_summary", source.id)).toBe(0);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("config gate off → build_conversation_summary enqueued", async () => {
|
|
613
|
+
TEST_CONFIG.memory.v2.enabled = false;
|
|
614
|
+
|
|
615
|
+
const source = createConversation("summary-v2-config-off");
|
|
616
|
+
await indexMessages(source.id, 2);
|
|
617
|
+
|
|
618
|
+
expect(
|
|
619
|
+
countJobsOfType("build_conversation_summary", source.id),
|
|
620
|
+
).toBeGreaterThanOrEqual(1);
|
|
621
|
+
});
|
|
622
|
+
});
|