@vellumai/assistant 0.8.1 → 0.8.2
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 +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -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 +325 -3
- 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-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 +41 -0
- 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 +1 -0
- 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 +2 -0
- 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-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 +76 -9
- 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__/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 +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- 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 +266 -10
- 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-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -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-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -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__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- 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} +1 -1
- 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 +142 -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} +8 -8
- 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__/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 +737 -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-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -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 +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -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__/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 +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- 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/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/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- 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 +34 -0
- 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.ts +169 -9
- 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 +16 -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 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- 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 +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -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/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- 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 +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- 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 +110 -10
- package/src/memory/db-init.ts +6 -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 +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- 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 +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- 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/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- 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/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- 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/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/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- 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 +36 -3
- 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 +105 -32
- 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 +31 -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 +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- 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 +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- 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__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- 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 +121 -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 +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -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 +6 -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 +65 -21
- 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/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 +57 -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 +15 -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/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 +72 -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/registry.ts +8 -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/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -5,6 +5,7 @@ import type { RecallSearchContext } from "../memory/context-search/types.js";
|
|
|
5
5
|
import { getDb } from "../memory/db-connection.js";
|
|
6
6
|
import { initializeDb } from "../memory/db-init.js";
|
|
7
7
|
import { rawRun } from "../memory/raw-query.js";
|
|
8
|
+
import { writeSlackMetadata } from "../messaging/providers/slack/message-metadata.js";
|
|
8
9
|
initializeDb();
|
|
9
10
|
|
|
10
11
|
let seedId = 0;
|
|
@@ -225,6 +226,99 @@ describe("searchConversationSource", () => {
|
|
|
225
226
|
);
|
|
226
227
|
});
|
|
227
228
|
|
|
229
|
+
test("preserves external_content boundaries in recall evidence", async () => {
|
|
230
|
+
const { conversation, message } = await seedConversation({
|
|
231
|
+
title: "Slack recall",
|
|
232
|
+
content:
|
|
233
|
+
'<external_content source="slack" origin="@alice">\nThe recalltoken decision came from Slack.\n</external_content>',
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const result = await searchConversationSource(
|
|
237
|
+
"recalltoken",
|
|
238
|
+
makeContext(),
|
|
239
|
+
1,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
expect(result.evidence).toHaveLength(1);
|
|
243
|
+
expect(result.evidence[0]).toMatchObject({
|
|
244
|
+
locator: `${conversation.id}#${message.id}`,
|
|
245
|
+
excerpt:
|
|
246
|
+
'<external_content source="slack" origin="@alice">\nThe recalltoken decision came from Slack.\n</external_content>',
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("wraps raw non-guardian Slack recall evidence from metadata", async () => {
|
|
251
|
+
const { conversation, message } = await seedConversation({
|
|
252
|
+
title: "Raw Slack recall",
|
|
253
|
+
role: "user",
|
|
254
|
+
content: "The rawrecalltoken decision came from Slack.",
|
|
255
|
+
metadata: slackMetadata("1700000100.000000", {
|
|
256
|
+
provenanceTrustClass: "unknown",
|
|
257
|
+
}),
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const result = await searchConversationSource(
|
|
261
|
+
"rawrecalltoken",
|
|
262
|
+
makeContext(),
|
|
263
|
+
1,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
expect(result.evidence).toHaveLength(1);
|
|
267
|
+
expect(result.evidence[0]).toMatchObject({
|
|
268
|
+
locator: `${conversation.id}#${message.id}`,
|
|
269
|
+
excerpt:
|
|
270
|
+
'<external_content source="slack" origin="@alice">\nThe rawrecalltoken decision came from Slack.\n</external_content>',
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("wraps raw non-guardian Slack recall evidence that mentions external_content", async () => {
|
|
275
|
+
const { conversation, message } = await seedConversation({
|
|
276
|
+
title: "Raw Slack tag mention recall",
|
|
277
|
+
role: "user",
|
|
278
|
+
content:
|
|
279
|
+
"The tagmentionrecalltoken text mentions <external_content but is raw Slack content.",
|
|
280
|
+
metadata: slackMetadata("1700000102.000000", {
|
|
281
|
+
provenanceTrustClass: "unknown",
|
|
282
|
+
}),
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const result = await searchConversationSource(
|
|
286
|
+
"tagmentionrecalltoken",
|
|
287
|
+
makeContext(),
|
|
288
|
+
1,
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
expect(result.evidence).toHaveLength(1);
|
|
292
|
+
expect(result.evidence[0]).toMatchObject({
|
|
293
|
+
locator: `${conversation.id}#${message.id}`,
|
|
294
|
+
excerpt:
|
|
295
|
+
'<external_content source="slack" origin="@alice">\nThe tagmentionrecalltoken text mentions <external_content but is raw Slack content.\n</external_content>',
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("does not wrap guardian Slack recall evidence", async () => {
|
|
300
|
+
const { conversation, message } = await seedConversation({
|
|
301
|
+
title: "Guardian Slack recall",
|
|
302
|
+
role: "user",
|
|
303
|
+
content: "The guardianrecalltoken decision came from Slack.",
|
|
304
|
+
metadata: slackMetadata("1700000101.000000", {
|
|
305
|
+
provenanceTrustClass: "guardian",
|
|
306
|
+
}),
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const result = await searchConversationSource(
|
|
310
|
+
"guardianrecalltoken",
|
|
311
|
+
makeContext(),
|
|
312
|
+
1,
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(result.evidence).toHaveLength(1);
|
|
316
|
+
expect(result.evidence[0]).toMatchObject({
|
|
317
|
+
locator: `${conversation.id}#${message.id}`,
|
|
318
|
+
excerpt: "The guardianrecalltoken decision came from Slack.",
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
228
322
|
test("broadens overconstrained recall queries to salient terms", async () => {
|
|
229
323
|
const specific = await seedConversation({
|
|
230
324
|
title: "Birthday cake plan",
|
|
@@ -258,6 +352,7 @@ function seedConversation(opts: {
|
|
|
258
352
|
memoryScopeId?: string;
|
|
259
353
|
role?: string;
|
|
260
354
|
content: string;
|
|
355
|
+
metadata?: string;
|
|
261
356
|
}) {
|
|
262
357
|
const id = ++seedId;
|
|
263
358
|
const now = Date.now() + id;
|
|
@@ -297,19 +392,39 @@ function seedConversation(opts: {
|
|
|
297
392
|
conversation_id,
|
|
298
393
|
role,
|
|
299
394
|
content,
|
|
300
|
-
created_at
|
|
301
|
-
|
|
395
|
+
created_at,
|
|
396
|
+
metadata
|
|
397
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
302
398
|
`,
|
|
303
399
|
message.id,
|
|
304
400
|
conversation.id,
|
|
305
401
|
opts.role ?? "assistant",
|
|
306
402
|
opts.content,
|
|
307
403
|
now,
|
|
404
|
+
opts.metadata ?? null,
|
|
308
405
|
);
|
|
309
406
|
|
|
310
407
|
return { conversation, message };
|
|
311
408
|
}
|
|
312
409
|
|
|
410
|
+
function slackMetadata(
|
|
411
|
+
channelTs: string,
|
|
412
|
+
extra: Record<string, unknown>,
|
|
413
|
+
): string {
|
|
414
|
+
return JSON.stringify({
|
|
415
|
+
userMessageChannel: "slack",
|
|
416
|
+
assistantMessageChannel: "slack",
|
|
417
|
+
slackMeta: writeSlackMetadata({
|
|
418
|
+
source: "slack",
|
|
419
|
+
channelId: "C0123",
|
|
420
|
+
channelTs,
|
|
421
|
+
eventKind: "message",
|
|
422
|
+
displayName: "@alice",
|
|
423
|
+
}),
|
|
424
|
+
...extra,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
313
428
|
function makeContext(
|
|
314
429
|
overrides: Partial<RecallSearchContext> = {},
|
|
315
430
|
): RecallSearchContext {
|
|
@@ -126,10 +126,15 @@ describe("searchWorkspaceSource", () => {
|
|
|
126
126
|
writeWorkspaceFile(root, "api-key.md", "needle key");
|
|
127
127
|
writeWorkspaceFile(root, "secret-plan.md", "needle secret");
|
|
128
128
|
writeWorkspaceFile(root, "token-cache.md", "needle token");
|
|
129
|
+
writeWorkspaceFile(root, "apiKey.json", "needle camel key");
|
|
130
|
+
writeWorkspaceFile(root, "userToken.txt", "needle camel token");
|
|
131
|
+
writeWorkspaceFile(root, "MySecret.md", "needle camel secret");
|
|
129
132
|
writeWorkspaceFile(root, "credentials.json", "needle credentials");
|
|
130
133
|
writeWorkspaceFile(root, "protected/readme.md", "needle protected");
|
|
131
134
|
writeWorkspaceFile(root, "gateway-security/readme.md", "needle gateway");
|
|
132
135
|
writeWorkspaceFile(root, "ces-security/readme.md", "needle ces");
|
|
136
|
+
writeWorkspaceFile(root, "keyboard.ts", "needle keyboard");
|
|
137
|
+
writeWorkspaceFile(root, "tokenizer.py", "needle tokenizer");
|
|
133
138
|
writeWorkspaceFile(root, "src/readme.md", "needle safe");
|
|
134
139
|
|
|
135
140
|
const result = await searchWorkspaceSource("needle", makeContext(root), 10);
|
|
@@ -138,7 +143,9 @@ describe("searchWorkspaceSource", () => {
|
|
|
138
143
|
".hidden.md:1",
|
|
139
144
|
".notes/.format-rec.md:1",
|
|
140
145
|
".notes/cake.md:1",
|
|
146
|
+
"keyboard.ts:1",
|
|
141
147
|
"src/readme.md:1",
|
|
148
|
+
"tokenizer.py:1",
|
|
142
149
|
]);
|
|
143
150
|
});
|
|
144
151
|
|
|
@@ -33,6 +33,7 @@ function makePngBase64(width: number, height: number): string {
|
|
|
33
33
|
describe("token estimator", () => {
|
|
34
34
|
test("estimates text tokens from character length", () => {
|
|
35
35
|
expect(estimateTextTokens("")).toBe(0);
|
|
36
|
+
expect(estimateTextTokens(undefined)).toBe(0);
|
|
36
37
|
expect(estimateTextTokens("abcd")).toBe(1);
|
|
37
38
|
expect(estimateTextTokens("abcde")).toBe(2);
|
|
38
39
|
});
|
|
@@ -55,7 +55,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
55
55
|
pricingOverrides: [],
|
|
56
56
|
},
|
|
57
57
|
rateLimit: { maxRequestsPerMinute: 0 },
|
|
58
|
-
memory: {
|
|
58
|
+
memory: {
|
|
59
|
+
v2: { enabled: false },
|
|
60
|
+
retrieval: { scratchpadInjection: { enabled: true } },
|
|
61
|
+
},
|
|
59
62
|
daemon: {
|
|
60
63
|
startupSocketWaitMs: 5000,
|
|
61
64
|
stopTimeoutMs: 5000,
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* 2. Token estimation significantly underestimates actual token count
|
|
8
8
|
* 3. No mid-loop budget check to prevent hitting the provider limit
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Most tests are test.todo — they document expected behavior for bugs
|
|
11
11
|
* to be fixed in subsequent PRs (PR 2 for tests 1–5, PR 3 for tests 6–7).
|
|
12
|
+
* Tests 2, 8, 9, and 10 are now active and passing against current code.
|
|
12
13
|
*/
|
|
13
14
|
import { createRequire } from "node:module";
|
|
14
15
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -87,7 +88,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
87
88
|
llm: mockLlmConfig,
|
|
88
89
|
rateLimit: { maxRequestsPerMinute: 0 },
|
|
89
90
|
workspaceGit: { turnCommitMaxWaitMs: 10 },
|
|
91
|
+
memory: { retrieval: { scratchpadInjection: { enabled: true } } },
|
|
90
92
|
ui: {},
|
|
93
|
+
compaction: { enabled: true, autoThreshold: 0.7 },
|
|
91
94
|
}),
|
|
92
95
|
loadRawConfig: () => ({}),
|
|
93
96
|
saveRawConfig: () => {},
|
|
@@ -851,111 +854,108 @@ describe("session-agent-loop overflow recovery (JARVIS-110)", () => {
|
|
|
851
854
|
// When estimation says we're within budget but the provider rejects,
|
|
852
855
|
// the post-run convergence loop should kick in and recover.
|
|
853
856
|
// This test should PASS against current code (when no progress is made).
|
|
854
|
-
test
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
let callCount = 0;
|
|
859
|
-
let reducerCalled = false;
|
|
857
|
+
test("overflow recovery compacts below limit even when estimation underestimates", async () => {
|
|
858
|
+
const events: ServerMessage[] = [];
|
|
859
|
+
let callCount = 0;
|
|
860
|
+
let reducerCalled = false;
|
|
860
861
|
|
|
861
|
-
|
|
862
|
-
|
|
862
|
+
// Estimator says 185k (below 190k budget = 200k * 0.95)
|
|
863
|
+
mockEstimateTokens = 185_000;
|
|
863
864
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
865
|
+
// Reducer successfully compacts
|
|
866
|
+
mockReducerStepFn = (msgs: Message[]) => {
|
|
867
|
+
reducerCalled = true;
|
|
868
|
+
return {
|
|
869
|
+
messages: msgs,
|
|
870
|
+
tier: "forced_compaction",
|
|
871
|
+
state: {
|
|
872
|
+
appliedTiers: ["forced_compaction"],
|
|
873
|
+
injectionMode: "full",
|
|
874
|
+
exhausted: false,
|
|
875
|
+
},
|
|
876
|
+
estimatedTokens: 100_000,
|
|
877
|
+
compactionResult: {
|
|
878
|
+
compacted: true,
|
|
868
879
|
messages: msgs,
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
previousEstimatedInputTokens: 185_000,
|
|
882
|
-
estimatedInputTokens: 100_000,
|
|
883
|
-
maxInputTokens: 200_000,
|
|
884
|
-
thresholdTokens: 160_000,
|
|
885
|
-
compactedMessages: 20,
|
|
886
|
-
summaryCalls: 1,
|
|
887
|
-
summaryInputTokens: 800,
|
|
888
|
-
summaryOutputTokens: 300,
|
|
889
|
-
summaryModel: "mock-model",
|
|
890
|
-
},
|
|
891
|
-
};
|
|
880
|
+
compactedPersistedMessages: 10,
|
|
881
|
+
summaryText: "Summary",
|
|
882
|
+
previousEstimatedInputTokens: 185_000,
|
|
883
|
+
estimatedInputTokens: 100_000,
|
|
884
|
+
maxInputTokens: 200_000,
|
|
885
|
+
thresholdTokens: 160_000,
|
|
886
|
+
compactedMessages: 20,
|
|
887
|
+
summaryCalls: 1,
|
|
888
|
+
summaryInputTokens: 800,
|
|
889
|
+
summaryOutputTokens: 300,
|
|
890
|
+
summaryModel: "mock-model",
|
|
891
|
+
},
|
|
892
892
|
};
|
|
893
|
+
};
|
|
893
894
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
onEvent({
|
|
900
|
-
type: "error",
|
|
901
|
-
error: new Error(
|
|
902
|
-
"prompt is too long: 242201 tokens > 200000 maximum",
|
|
903
|
-
),
|
|
904
|
-
});
|
|
905
|
-
onEvent({
|
|
906
|
-
type: "usage",
|
|
907
|
-
inputTokens: 0,
|
|
908
|
-
outputTokens: 0,
|
|
909
|
-
model: "test-model",
|
|
910
|
-
providerDurationMs: 10,
|
|
911
|
-
});
|
|
912
|
-
// No progress — return same messages
|
|
913
|
-
return messages;
|
|
914
|
-
}
|
|
915
|
-
// Second call succeeds
|
|
895
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
896
|
+
callCount++;
|
|
897
|
+
if (callCount === 1) {
|
|
898
|
+
// Provider rejects with "prompt is too long: 242201 tokens > 200000"
|
|
899
|
+
// even though estimator said 185k
|
|
916
900
|
onEvent({
|
|
917
|
-
type: "
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
},
|
|
901
|
+
type: "error",
|
|
902
|
+
error: new Error(
|
|
903
|
+
"prompt is too long: 242201 tokens > 200000 maximum",
|
|
904
|
+
),
|
|
922
905
|
});
|
|
923
906
|
onEvent({
|
|
924
907
|
type: "usage",
|
|
925
|
-
inputTokens:
|
|
926
|
-
outputTokens:
|
|
908
|
+
inputTokens: 0,
|
|
909
|
+
outputTokens: 0,
|
|
927
910
|
model: "test-model",
|
|
928
|
-
providerDurationMs:
|
|
911
|
+
providerDurationMs: 10,
|
|
929
912
|
});
|
|
930
|
-
return
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
agentLoopRun,
|
|
941
|
-
contextWindowManager: {
|
|
942
|
-
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
943
|
-
maybeCompact: async () => ({ compacted: false }),
|
|
944
|
-
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
913
|
+
// No progress — return same messages
|
|
914
|
+
return messages;
|
|
915
|
+
}
|
|
916
|
+
// Second call succeeds
|
|
917
|
+
onEvent({
|
|
918
|
+
type: "message_complete",
|
|
919
|
+
message: {
|
|
920
|
+
role: "assistant",
|
|
921
|
+
content: [{ type: "text", text: "recovered" }],
|
|
922
|
+
},
|
|
945
923
|
});
|
|
924
|
+
onEvent({
|
|
925
|
+
type: "usage",
|
|
926
|
+
inputTokens: 80_000,
|
|
927
|
+
outputTokens: 200,
|
|
928
|
+
model: "test-model",
|
|
929
|
+
providerDurationMs: 500,
|
|
930
|
+
});
|
|
931
|
+
return [
|
|
932
|
+
...messages,
|
|
933
|
+
{
|
|
934
|
+
role: "assistant" as const,
|
|
935
|
+
content: [{ type: "text", text: "recovered" }] as ContentBlock[],
|
|
936
|
+
},
|
|
937
|
+
];
|
|
938
|
+
};
|
|
946
939
|
|
|
947
|
-
|
|
940
|
+
const ctx = makeCtx({
|
|
941
|
+
agentLoopRun,
|
|
942
|
+
contextWindowManager: {
|
|
943
|
+
shouldCompact: () => ({ needed: false, estimatedTokens: 0 }),
|
|
944
|
+
maybeCompact: async () => ({ compacted: false }),
|
|
945
|
+
} as unknown as AgentLoopConversationContext["contextWindowManager"],
|
|
946
|
+
});
|
|
948
947
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
948
|
+
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
949
|
+
|
|
950
|
+
// The reducer should be called in the convergence loop
|
|
951
|
+
expect(reducerCalled).toBe(true);
|
|
952
|
+
// Should recover without conversation_error
|
|
953
|
+
const conversationError = events.find(
|
|
954
|
+
(e) => e.type === "conversation_error",
|
|
955
|
+
);
|
|
956
|
+
expect(conversationError).toBeUndefined();
|
|
957
|
+
expect(callCount).toBe(2);
|
|
958
|
+
});
|
|
959
959
|
|
|
960
960
|
// ── Test 3 ────────────────────────────────────────────────────────
|
|
961
961
|
// BUG: When the provider rejection reveals actual token count (e.g.,
|
|
@@ -61,7 +61,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
61
61
|
},
|
|
62
62
|
rateLimit: { maxRequestsPerMinute: 0 },
|
|
63
63
|
workspaceGit: { turnCommitMaxWaitMs: 10 },
|
|
64
|
+
memory: { retrieval: { scratchpadInjection: { enabled: true } } },
|
|
64
65
|
ui: mockUiConfig,
|
|
66
|
+
compaction: { enabled: true, autoThreshold: 0.7 },
|
|
65
67
|
}),
|
|
66
68
|
loadRawConfig: () => ({}),
|
|
67
69
|
saveRawConfig: () => {},
|
|
@@ -596,12 +596,51 @@ describe("classifyConversationError", () => {
|
|
|
596
596
|
expect(result.retryable).toBe(true);
|
|
597
597
|
});
|
|
598
598
|
|
|
599
|
-
it("classifies ProviderError with 401 as
|
|
599
|
+
it("classifies ProviderError with 401 as PROVIDER_INVALID_KEY (non-retryable)", () => {
|
|
600
|
+
// 401 means the upstream provider rejected the configured key
|
|
601
|
+
// (vs. PROVIDER_NOT_CONFIGURED which is for a never-set key).
|
|
602
|
+
// The macOS chat renders these on different banners.
|
|
600
603
|
const err = new ProviderError("Unauthorized", "anthropic", 401);
|
|
601
604
|
const result = classifyConversationError(err, baseCtx);
|
|
602
|
-
expect(result.code).toBe("
|
|
605
|
+
expect(result.code).toBe("PROVIDER_INVALID_KEY");
|
|
603
606
|
expect(result.retryable).toBe(false);
|
|
604
|
-
expect(result.errorCategory).toBe("
|
|
607
|
+
expect(result.errorCategory).toBe("provider_invalid_key");
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it("classifies ProviderError 401 with 'invalid x-api-key' message as PROVIDER_INVALID_KEY", () => {
|
|
611
|
+
// Regex-match branch — Anthropic's standard 401 wording.
|
|
612
|
+
const err = new ProviderError(
|
|
613
|
+
"Anthropic API error: invalid x-api-key",
|
|
614
|
+
"anthropic",
|
|
615
|
+
401,
|
|
616
|
+
);
|
|
617
|
+
const result = classifyConversationError(err, baseCtx);
|
|
618
|
+
expect(result.code).toBe("PROVIDER_INVALID_KEY");
|
|
619
|
+
expect(result.errorCategory).toBe("provider_invalid_key");
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("classifies ProviderError 403 with 'invalid api key' message as PROVIDER_INVALID_KEY", () => {
|
|
623
|
+
const err = new ProviderError(
|
|
624
|
+
"OpenAI: Invalid API key",
|
|
625
|
+
"openai",
|
|
626
|
+
403,
|
|
627
|
+
);
|
|
628
|
+
const result = classifyConversationError(err, baseCtx);
|
|
629
|
+
expect(result.code).toBe("PROVIDER_INVALID_KEY");
|
|
630
|
+
expect(result.errorCategory).toBe("provider_invalid_key");
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it("includes connection/profile attribution in PROVIDER_INVALID_KEY when provided", () => {
|
|
634
|
+
const err = new ProviderError("Unauthorized", "anthropic", 401);
|
|
635
|
+
const result = classifyConversationError(err, {
|
|
636
|
+
...baseCtx,
|
|
637
|
+
connectionName: "my-anthropic",
|
|
638
|
+
profileName: "personal",
|
|
639
|
+
});
|
|
640
|
+
expect(result.code).toBe("PROVIDER_INVALID_KEY");
|
|
641
|
+
expect(result.connectionName).toBe("my-anthropic");
|
|
642
|
+
expect(result.profileName).toBe("personal");
|
|
643
|
+
expect(result.userMessage).toContain("personal");
|
|
605
644
|
});
|
|
606
645
|
|
|
607
646
|
it("classifies direct ProviderError with 402 as provider_billing (non-retryable)", () => {
|
|
@@ -609,6 +609,88 @@ describe("forkConversation", () => {
|
|
|
609
609
|
expect(await hydrateActivationState(db, fork.id)).toBeNull();
|
|
610
610
|
expect(loadGraphMemoryState(fork.id)).toBeNull();
|
|
611
611
|
});
|
|
612
|
+
|
|
613
|
+
test("does not copy memory state when the fork is truncated mid-history", async () => {
|
|
614
|
+
const source = createConversation("Truncated thread");
|
|
615
|
+
const firstMessage = await addMessage(
|
|
616
|
+
source.id,
|
|
617
|
+
"user",
|
|
618
|
+
"first turn",
|
|
619
|
+
undefined,
|
|
620
|
+
{ skipIndexing: true },
|
|
621
|
+
);
|
|
622
|
+
await addMessage(source.id, "assistant", "first reply", undefined, {
|
|
623
|
+
skipIndexing: true,
|
|
624
|
+
});
|
|
625
|
+
const lastMessage = await addMessage(
|
|
626
|
+
source.id,
|
|
627
|
+
"user",
|
|
628
|
+
"second turn",
|
|
629
|
+
undefined,
|
|
630
|
+
{ skipIndexing: true },
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
const db = getDb();
|
|
634
|
+
db.insert(activationState)
|
|
635
|
+
.values({
|
|
636
|
+
conversationId: source.id,
|
|
637
|
+
messageId: lastMessage.id,
|
|
638
|
+
stateJson: JSON.stringify({ "concepts/foo": 0.5 }),
|
|
639
|
+
everInjectedJson: JSON.stringify([{ slug: "concepts/foo", turn: 2 }]),
|
|
640
|
+
currentTurn: 2,
|
|
641
|
+
updatedAt: 1_700_000_000_000,
|
|
642
|
+
})
|
|
643
|
+
.run();
|
|
644
|
+
saveGraphMemoryState(
|
|
645
|
+
source.id,
|
|
646
|
+
JSON.stringify({
|
|
647
|
+
initialized: true,
|
|
648
|
+
needsReload: false,
|
|
649
|
+
inContext: ["node-foo"],
|
|
650
|
+
log: [{ nodeId: "node-foo", turn: 2 }],
|
|
651
|
+
currentTurn: 2,
|
|
652
|
+
}),
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
const fork = forkConversation({
|
|
656
|
+
conversationId: source.id,
|
|
657
|
+
throughMessageId: firstMessage.id,
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
expect(await hydrateActivationState(db, fork.id)).toBeNull();
|
|
661
|
+
expect(loadGraphMemoryState(fork.id)).toBeNull();
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
test("copies memory state when throughMessageId points at the last message", async () => {
|
|
665
|
+
const source = createConversation("Through-last thread");
|
|
666
|
+
const lastMessage = await addMessage(
|
|
667
|
+
source.id,
|
|
668
|
+
"user",
|
|
669
|
+
"only turn",
|
|
670
|
+
undefined,
|
|
671
|
+
{ skipIndexing: true },
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
const db = getDb();
|
|
675
|
+
db.insert(activationState)
|
|
676
|
+
.values({
|
|
677
|
+
conversationId: source.id,
|
|
678
|
+
messageId: lastMessage.id,
|
|
679
|
+
stateJson: JSON.stringify({ "concepts/foo": 0.9 }),
|
|
680
|
+
everInjectedJson: JSON.stringify([{ slug: "concepts/foo", turn: 1 }]),
|
|
681
|
+
currentTurn: 1,
|
|
682
|
+
updatedAt: 1_700_000_000_000,
|
|
683
|
+
})
|
|
684
|
+
.run();
|
|
685
|
+
|
|
686
|
+
const fork = forkConversation({
|
|
687
|
+
conversationId: source.id,
|
|
688
|
+
throughMessageId: lastMessage.id,
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
const childState = await hydrateActivationState(db, fork.id);
|
|
692
|
+
expect(childState?.currentTurn).toBe(1);
|
|
693
|
+
});
|
|
612
694
|
});
|
|
613
695
|
|
|
614
696
|
describe("forkConversation + memory_retrospective_state", () => {
|