@vellumai/assistant 0.8.12 → 0.9.0-staging.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/AGENTS.md +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +219 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/mock-gateway-ipc.ts +23 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/resolve-app-id.test.ts +56 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +0 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +167 -3
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +28 -11
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +152 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +91 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -30,15 +30,22 @@
|
|
|
30
30
|
* stable-prefix card and as a finder line (its current matched section);
|
|
31
31
|
* selections are deduped by slug.
|
|
32
32
|
*
|
|
33
|
-
* Failure handling
|
|
33
|
+
* Failure handling distinguishes a DELIBERATE empty selection from an
|
|
34
|
+
* INFRASTRUCTURE failure — the two are different outcomes, not the same one:
|
|
34
35
|
* - explicit `ids` → select exactly those candidates,
|
|
35
|
-
* - explicit empty `ids: []` → select none (deliberate abstention)
|
|
36
|
+
* - explicit empty `ids: []` → select none (deliberate abstention) — a
|
|
37
|
+
* normal, non-error result; the turn proceeds,
|
|
36
38
|
* - omitted `ids` → keep ALL candidates (the recall-safe "all of these are
|
|
37
39
|
* relevant" signal),
|
|
38
|
-
* -
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* - empty candidate pool → return none (nothing to select),
|
|
41
|
+
* - infrastructure failure (selector provider unavailable — e.g. a transient
|
|
42
|
+
* CES credential blip drops the API key — or no usable `tool_use` / schema
|
|
43
|
+
* mismatch surviving the short re-prompt retry) → throw
|
|
44
|
+
* {@link MemoryV3RetrievalUnavailableError}. There is NO deterministic-lane
|
|
45
|
+
* fallback: the LIVE injector propagates this to hard-fail the turn
|
|
46
|
+
* (retryable) rather than silently shipping it with no `<memory>` block,
|
|
47
|
+
* while the shadow/observation path swallows it and lets v2 retrieval serve
|
|
48
|
+
* the turn.
|
|
42
49
|
*/
|
|
43
50
|
|
|
44
51
|
import { z } from "zod";
|
|
@@ -60,6 +67,24 @@ import type { MemoryRoutingTurn, SelectedPage, Slug } from "./types.js";
|
|
|
60
67
|
|
|
61
68
|
const log = getLogger("memory-v3-pool-select");
|
|
62
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Thrown when the pool selector cannot produce a selection because of an
|
|
72
|
+
* INFRASTRUCTURE failure — its LLM provider is unavailable (e.g. a transient
|
|
73
|
+
* CES credential blip drops the API key), or no usable `tool_use` survives the
|
|
74
|
+
* re-prompt retry. Deliberately DISTINCT from a deliberate empty selection
|
|
75
|
+
* (`ids: []`) and an empty candidate pool, both of which return normally.
|
|
76
|
+
*
|
|
77
|
+
* The LIVE memory-v3 injector propagates this to hard-fail the turn (a clean,
|
|
78
|
+
* retryable failure) rather than silently shipping with no memory; the
|
|
79
|
+
* shadow/observation path catches and swallows it.
|
|
80
|
+
*/
|
|
81
|
+
export class MemoryV3RetrievalUnavailableError extends Error {
|
|
82
|
+
constructor(message: string) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.name = "MemoryV3RetrievalUnavailableError";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
/** A dynamic-tail (finder) candidate: the slug plus the descriptor that
|
|
64
89
|
* justifies it — a matched section for a needle/dense hit, or a curated link
|
|
65
90
|
* description for an edge page. Rendered as a one-line snippet, prefixed
|
|
@@ -213,9 +238,11 @@ export async function selectPool(
|
|
|
213
238
|
if (!provider) {
|
|
214
239
|
log.warn(
|
|
215
240
|
{ candidateCount: ordered.length },
|
|
216
|
-
"pool selector provider unavailable
|
|
241
|
+
"pool selector provider unavailable — failing the turn rather than dropping memory",
|
|
242
|
+
);
|
|
243
|
+
throw new MemoryV3RetrievalUnavailableError(
|
|
244
|
+
"memory-v3 pool selector provider unavailable",
|
|
217
245
|
);
|
|
218
|
-
return [];
|
|
219
246
|
}
|
|
220
247
|
|
|
221
248
|
// Two content blocks: the stable prefix (cards) carries the cache
|
|
@@ -265,9 +292,11 @@ export async function selectPool(
|
|
|
265
292
|
if (parsed === null) {
|
|
266
293
|
log.warn(
|
|
267
294
|
{ candidateCount: ordered.length },
|
|
268
|
-
"pool selector could not obtain a selection after retries
|
|
295
|
+
"pool selector could not obtain a selection after retries — failing the turn rather than dropping memory",
|
|
296
|
+
);
|
|
297
|
+
throw new MemoryV3RetrievalUnavailableError(
|
|
298
|
+
"memory-v3 pool selector returned no usable selection after retries",
|
|
269
299
|
);
|
|
270
|
-
return [];
|
|
271
300
|
}
|
|
272
301
|
|
|
273
302
|
// Omitted `ids` is the recall-safe "keep all candidates" signal.
|
|
@@ -86,7 +86,40 @@ export async function ensureSectionCollection(
|
|
|
86
86
|
const onDisk = config.memory.qdrant.onDisk;
|
|
87
87
|
|
|
88
88
|
const exists = await client.collectionExists(SECTION_COLLECTION);
|
|
89
|
-
|
|
89
|
+
let needsCreate = !exists.exists;
|
|
90
|
+
|
|
91
|
+
// An existing collection sized to a different embedding dimension (e.g. a
|
|
92
|
+
// 384-dim collection now serving a 3072-dim embedder) carries the right
|
|
93
|
+
// vector at the wrong size — every upsert then fails with HTTP 400 and the
|
|
94
|
+
// collection is never repaired. Probe the schema and recreate on dimension
|
|
95
|
+
// drift; on a probe failure, assume compatible rather than risk a destructive
|
|
96
|
+
// recreate (mirrors the v2 concept-page collection posture). The next
|
|
97
|
+
// `backfillAllSections` regenerates the embeddings.
|
|
98
|
+
if (exists.exists) {
|
|
99
|
+
try {
|
|
100
|
+
const info = await client.getCollection(SECTION_COLLECTION);
|
|
101
|
+
const vectors = info.config?.params?.vectors;
|
|
102
|
+
const size =
|
|
103
|
+
vectors && typeof vectors === "object" && "size" in vectors
|
|
104
|
+
? (vectors as { size?: unknown }).size
|
|
105
|
+
: undefined;
|
|
106
|
+
if (typeof size === "number" && size !== vectorSize) {
|
|
107
|
+
log.warn(
|
|
108
|
+
{ collection: SECTION_COLLECTION, expected: vectorSize, found: size },
|
|
109
|
+
"Memory v3 section collection dimension drift — deleting and recreating; embeddings will be regenerated by the next backfill",
|
|
110
|
+
);
|
|
111
|
+
await client.deleteCollection(SECTION_COLLECTION);
|
|
112
|
+
needsCreate = true;
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
log.warn(
|
|
116
|
+
{ err, collection: SECTION_COLLECTION },
|
|
117
|
+
"Failed to probe v3 section collection schema; assuming compatible",
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (needsCreate) {
|
|
90
123
|
log.info(
|
|
91
124
|
{ collection: SECTION_COLLECTION, vectorSize },
|
|
92
125
|
"Creating Qdrant collection for memory v3 sections",
|
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Read-side store for the inspector's Memory V3
|
|
3
|
-
* `memory_v3_selections` rows for a turn
|
|
4
|
-
* `<memory>` block for what v3 selected, so the inspector can
|
|
5
|
-
* selection without re-running orchestration — which would be
|
|
6
|
-
* since the hot lane is frecency-stateful and can't be reproduced
|
|
7
|
-
* fact.
|
|
2
|
+
* Read-side store for the inspector's Memory V3 panel. Reads the persisted
|
|
3
|
+
* `memory_v3_selections` rows for a turn (by the turn's message ids) and
|
|
4
|
+
* re-renders the `<memory>` block for what v3 selected, so the inspector can
|
|
5
|
+
* show the turn's selection without re-running orchestration — which would be
|
|
6
|
+
* wrong anyway, since the hot lane is frecency-stateful and can't be reproduced
|
|
7
|
+
* after the fact.
|
|
8
8
|
*
|
|
9
|
-
* The rendered text is inspector-only and NOT byte-identical to live
|
|
10
|
-
*
|
|
11
|
-
* (`renderV3CardContent`) plus an ephemeral spotlight
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* The rendered text is inspector-only and NOT byte-identical to live injection:
|
|
10
|
+
* the live injector freezes net-new compact CARDS into history
|
|
11
|
+
* (`renderV3CardContent`) plus an ephemeral spotlight. Here we re-render each
|
|
12
|
+
* selection's MATCHED SECTION — resolved from the persisted `(slug, ordinal)`
|
|
13
|
+
* against the current page — when one was recorded, falling back to the
|
|
14
|
+
* full/lead page otherwise. Section text is re-derived from the current page,
|
|
15
|
+
* so it reflects bounded page-drift if the page changed since the turn (the
|
|
16
|
+
* same approximation the v2 inspector accepts).
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
import type { MemoryV3SelectionLog } from "../../../api/responses/memory-v3-selection-log.js";
|
|
18
20
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
19
21
|
import { getConfig } from "../../../config/loader.js";
|
|
20
22
|
import { getDb, getSqliteFrom } from "../../../memory/db-connection.js";
|
|
23
|
+
import { readPage } from "../../../memory/v2/page-store.js";
|
|
24
|
+
import { getWorkspaceDir } from "../../../util/platform.js";
|
|
25
|
+
import { capabilityOrDiskBody } from "./capabilities.js";
|
|
26
|
+
import { sectionByOrdinal } from "./orchestrate.js";
|
|
21
27
|
import { renderV3SectionContent } from "./page-content.js";
|
|
22
28
|
import { renderMemoryBlock } from "./render-injection.js";
|
|
29
|
+
import { buildSectionIndex } from "./sections.js";
|
|
23
30
|
import {
|
|
24
31
|
type Section,
|
|
25
32
|
SELECTION_SOURCES,
|
|
@@ -31,16 +38,21 @@ const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
|
31
38
|
const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
32
39
|
|
|
33
40
|
interface SelectionRow {
|
|
41
|
+
turn: number;
|
|
34
42
|
slug: string;
|
|
35
43
|
source: string;
|
|
36
44
|
pinned: number;
|
|
45
|
+
section_ordinal: number | null;
|
|
46
|
+
section_title: string | null;
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
const SELECTION_COLUMNS = `turn, slug, source, pinned, section_ordinal, section_title`;
|
|
50
|
+
|
|
39
51
|
function rowsForTurn(conversationId: string, turn: number): SelectionRow[] {
|
|
40
52
|
return getSqliteFrom(getDb())
|
|
41
53
|
.query(
|
|
42
54
|
/*sql*/ `
|
|
43
|
-
SELECT
|
|
55
|
+
SELECT ${SELECTION_COLUMNS} FROM memory_v3_selections
|
|
44
56
|
WHERE conversation_id = ? AND turn = ?
|
|
45
57
|
ORDER BY rowid
|
|
46
58
|
`,
|
|
@@ -48,32 +60,59 @@ function rowsForTurn(conversationId: string, turn: number): SelectionRow[] {
|
|
|
48
60
|
.all(conversationId, turn) as SelectionRow[];
|
|
49
61
|
}
|
|
50
62
|
|
|
63
|
+
function rowsForMessageIds(messageIds: string[]): SelectionRow[] {
|
|
64
|
+
if (messageIds.length === 0) return [];
|
|
65
|
+
const placeholders = messageIds.map(() => "?").join(", ");
|
|
66
|
+
return getSqliteFrom(getDb())
|
|
67
|
+
.query(
|
|
68
|
+
/*sql*/ `
|
|
69
|
+
SELECT ${SELECTION_COLUMNS} FROM memory_v3_selections
|
|
70
|
+
WHERE message_id IN (${placeholders})
|
|
71
|
+
ORDER BY rowid
|
|
72
|
+
`,
|
|
73
|
+
)
|
|
74
|
+
.all(...messageIds) as SelectionRow[];
|
|
75
|
+
}
|
|
76
|
+
|
|
51
77
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* would attribute another turn's selection (e.g. a later turn's pages and
|
|
59
|
-
* rendered block) to the inspected message, which corrupts shadow validation.
|
|
60
|
-
*
|
|
61
|
-
* Caveat: v3 rows are keyed by the orchestrator turn counter (`ctx.turnCount`)
|
|
62
|
-
* while `turn` here is the v2 memory-tracker turn. They coincide for normal
|
|
63
|
-
* turns, but if they diverge this simply yields `null` (no section) rather than
|
|
64
|
-
* wrong data. Tying v3 rows to a message id for exact per-message attribution
|
|
65
|
-
* regardless of counter drift is a documented follow-up.
|
|
66
|
-
*
|
|
67
|
-
* Selection rows are stored in selection order, so rendering them in row order
|
|
68
|
-
* reproduces the block v3 would inject.
|
|
78
|
+
* Resolve each selection's persisted matched section `(slug, ordinal)` to the
|
|
79
|
+
* concrete `Section` in the CURRENT page, so the injected block renders the
|
|
80
|
+
* matched section rather than the full page. Only slugs with a recorded ordinal
|
|
81
|
+
* are resolved (core/hot/fresh/edge selections have none and render full-page).
|
|
82
|
+
* A page edited since the turn re-derives the current section at that ordinal,
|
|
83
|
+
* or falls back to full-page when the ordinal no longer exists.
|
|
69
84
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
async function reconstructMatchedSections(
|
|
86
|
+
rows: SelectionRow[],
|
|
87
|
+
): Promise<Map<Slug, Section>> {
|
|
88
|
+
const sectionSlugs = rows
|
|
89
|
+
.filter((r) => r.section_ordinal != null)
|
|
90
|
+
.map((r) => r.slug);
|
|
91
|
+
if (sectionSlugs.length === 0) return new Map();
|
|
92
|
+
|
|
93
|
+
const workspaceDir = getWorkspaceDir();
|
|
94
|
+
const pageBody = (slug: Slug): Promise<string> =>
|
|
95
|
+
capabilityOrDiskBody(slug, async (s) => {
|
|
96
|
+
try {
|
|
97
|
+
return (await readPage(workspaceDir, s))?.body ?? "";
|
|
98
|
+
} catch {
|
|
99
|
+
return "";
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const index = await buildSectionIndex(sectionSlugs, pageBody);
|
|
103
|
+
|
|
104
|
+
const sectionBySlug = new Map<Slug, Section>();
|
|
105
|
+
for (const row of rows) {
|
|
106
|
+
if (row.section_ordinal == null) continue;
|
|
107
|
+
const section = sectionByOrdinal(index, row.slug, row.section_ordinal);
|
|
108
|
+
if (section) sectionBySlug.set(row.slug, section);
|
|
109
|
+
}
|
|
110
|
+
return sectionBySlug;
|
|
111
|
+
}
|
|
75
112
|
|
|
76
|
-
|
|
113
|
+
async function buildSelectionLog(
|
|
114
|
+
rows: SelectionRow[],
|
|
115
|
+
): Promise<MemoryV3SelectionLog | null> {
|
|
77
116
|
if (rows.length === 0) return null;
|
|
78
117
|
|
|
79
118
|
const config = getConfig();
|
|
@@ -81,25 +120,19 @@ export async function getMemoryV3SelectionForInspector(
|
|
|
81
120
|
slug: r.slug,
|
|
82
121
|
source: r.source,
|
|
83
122
|
pinned: r.pinned === 1,
|
|
123
|
+
sectionOrdinal: r.section_ordinal,
|
|
124
|
+
sectionHeading: r.section_title,
|
|
84
125
|
}));
|
|
85
126
|
const slugs: Slug[] = selections.map((s) => s.slug);
|
|
86
|
-
|
|
87
|
-
// every slug, not the exact matched section live injection used. The inspector
|
|
88
|
-
// re-renders from persisted rows without re-running orchestration, and the
|
|
89
|
-
// section identity is not persisted (`memory_v3_selections` stores
|
|
90
|
-
// slug/source/pinned, not the section ordinal), so the section map is empty
|
|
91
|
-
// and `renderV3SectionContent` falls back to the full page. Persisting the
|
|
92
|
-
// injected section (a schema migration) is deliberately deferred: the A/B's
|
|
93
|
-
// primary signal is slug-level recall via `summarizeSelections`, which is
|
|
94
|
-
// exact — only this debug-oriented injected text is an approximation.
|
|
127
|
+
const sectionBySlug = await reconstructMatchedSections(rows);
|
|
95
128
|
const injectedText = await renderMemoryBlock(
|
|
96
129
|
slugs,
|
|
97
|
-
|
|
130
|
+
sectionBySlug,
|
|
98
131
|
renderV3SectionContent,
|
|
99
132
|
);
|
|
100
133
|
|
|
101
134
|
return {
|
|
102
|
-
turn,
|
|
135
|
+
turn: rows[0]!.turn,
|
|
103
136
|
live: isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config),
|
|
104
137
|
shadow: isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config),
|
|
105
138
|
selections,
|
|
@@ -107,6 +140,38 @@ export async function getMemoryV3SelectionForInspector(
|
|
|
107
140
|
};
|
|
108
141
|
}
|
|
109
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Build the inspector's v3 selection log for the inspected message's turn,
|
|
145
|
+
* keyed by the turn's message ids. This is the durable join: `writeSelections`
|
|
146
|
+
* logs rows with `message_id = NULL` and the turn-end backfill stamps them with
|
|
147
|
+
* the assistant message id, so a per-message lookup is robust against the drift
|
|
148
|
+
* between v2's tracker turn and v3's orchestrator `turnCount`. Returns `null`
|
|
149
|
+
* when no v3 rows match (e.g. a turn predating the message-id backfill, or a
|
|
150
|
+
* conversation with no v3 data). Message ids are globally unique, so no
|
|
151
|
+
* conversation scope is needed.
|
|
152
|
+
*
|
|
153
|
+
* Selection rows are stored in selection order, so rendering them in row order
|
|
154
|
+
* reproduces the block v3 would inject.
|
|
155
|
+
*/
|
|
156
|
+
export async function getMemoryV3SelectionForInspectorByMessageIds(
|
|
157
|
+
messageIds: string[],
|
|
158
|
+
): Promise<MemoryV3SelectionLog | null> {
|
|
159
|
+
return buildSelectionLog(rowsForMessageIds(messageIds));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Turn-keyed variant, retained for callers/tests that look up by an exact
|
|
164
|
+
* `(conversation, turn)`. Returns `null` when `turn` is null or there are no
|
|
165
|
+
* rows for it.
|
|
166
|
+
*/
|
|
167
|
+
export async function getMemoryV3SelectionForInspector(
|
|
168
|
+
conversationId: string,
|
|
169
|
+
turn: number | null | undefined,
|
|
170
|
+
): Promise<MemoryV3SelectionLog | null> {
|
|
171
|
+
if (turn == null) return null;
|
|
172
|
+
return buildSelectionLog(rowsForTurn(conversationId, turn));
|
|
173
|
+
}
|
|
174
|
+
|
|
110
175
|
/**
|
|
111
176
|
* Offline A/B aggregate over a conversation's logged v3 selections. Reads every
|
|
112
177
|
* `memory_v3_selections` row for the conversation (all turns) and rolls them up
|
|
@@ -55,6 +55,7 @@ import { computeHotSet } from "./hot-set.js";
|
|
|
55
55
|
import { computeLearnedEdgeGraph } from "./learned-edges.js";
|
|
56
56
|
import type { OrchestrateResult } from "./orchestrate.js";
|
|
57
57
|
import { orchestrate } from "./orchestrate.js";
|
|
58
|
+
import { MemoryV3RetrievalUnavailableError } from "./pool-select.js";
|
|
58
59
|
import { ensureSectionCollection } from "./section-dense-store.js";
|
|
59
60
|
import type { SectionNeedle } from "./section-needle.js";
|
|
60
61
|
import { buildSectionNeedle } from "./section-needle.js";
|
|
@@ -418,6 +419,11 @@ interface SelectionRow {
|
|
|
418
419
|
slug: Slug;
|
|
419
420
|
source: SelectionSource;
|
|
420
421
|
pinned: number;
|
|
422
|
+
/** Ordinal of the matched section a finder lane surfaced; null for
|
|
423
|
+
* core/hot/fresh/edge selections with no matched section. */
|
|
424
|
+
sectionOrdinal: number | null;
|
|
425
|
+
/** Heading of the matched section; null when there is no matched section. */
|
|
426
|
+
sectionTitle: string | null;
|
|
421
427
|
}
|
|
422
428
|
|
|
423
429
|
/**
|
|
@@ -438,17 +444,24 @@ export function attributeSelections(result: OrchestrateResult): SelectionRow[] {
|
|
|
438
444
|
const finderLane = new Map(
|
|
439
445
|
result.lanes.finder.map((c) => [c.slug, c.lane] as const),
|
|
440
446
|
);
|
|
441
|
-
return result.selections.map((sel) =>
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
447
|
+
return result.selections.map((sel) => {
|
|
448
|
+
// The matched section is populated only for finder-lane hits (including
|
|
449
|
+
// hits on core/hot pages); core/hot/fresh/edge-only selections have none.
|
|
450
|
+
const section = result.matchedSections.get(sel.slug);
|
|
451
|
+
return {
|
|
452
|
+
slug: sel.slug,
|
|
453
|
+
source: core.has(sel.slug)
|
|
454
|
+
? ("core" as const)
|
|
455
|
+
: hot.has(sel.slug)
|
|
456
|
+
? ("hot" as const)
|
|
457
|
+
: fresh.has(sel.slug)
|
|
458
|
+
? ("fresh" as const)
|
|
459
|
+
: (finderLane.get(sel.slug) ?? "needle"),
|
|
460
|
+
pinned: sel.pinned ? 1 : 0,
|
|
461
|
+
sectionOrdinal: section?.ordinal ?? null,
|
|
462
|
+
sectionTitle: section?.title ?? null,
|
|
463
|
+
};
|
|
464
|
+
});
|
|
452
465
|
}
|
|
453
466
|
|
|
454
467
|
/** Write the attributed selection rows to `memory_v3_selections`. */
|
|
@@ -461,17 +474,51 @@ export function writeSelections(
|
|
|
461
474
|
const raw = getSqliteFrom(getDb());
|
|
462
475
|
// PK is (conversation_id, turn, slug); OR REPLACE keeps the write
|
|
463
476
|
// idempotent if the same turn is observed twice (e.g. a retried turn).
|
|
477
|
+
// `message_id` is written NULL here (the assistant message does not exist at
|
|
478
|
+
// injection time) and stamped at turn end by
|
|
479
|
+
// `backfillMemoryV3SelectionMessageId`.
|
|
464
480
|
const stmt = raw.query(/*sql*/ `
|
|
465
481
|
INSERT OR REPLACE INTO memory_v3_selections (
|
|
466
|
-
conversation_id, turn, slug, source, pinned, created_at
|
|
467
|
-
|
|
482
|
+
conversation_id, turn, slug, source, pinned, created_at,
|
|
483
|
+
message_id, section_ordinal, section_title
|
|
484
|
+
) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)
|
|
468
485
|
`);
|
|
469
486
|
const now = Date.now();
|
|
470
487
|
for (const row of rows) {
|
|
471
|
-
stmt.run(
|
|
488
|
+
stmt.run(
|
|
489
|
+
conversationId,
|
|
490
|
+
turn,
|
|
491
|
+
row.slug,
|
|
492
|
+
row.source,
|
|
493
|
+
row.pinned,
|
|
494
|
+
now,
|
|
495
|
+
row.sectionOrdinal,
|
|
496
|
+
row.sectionTitle,
|
|
497
|
+
);
|
|
472
498
|
}
|
|
473
499
|
}
|
|
474
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Stamp the turn's assistant message id onto the selection rows just written
|
|
503
|
+
* for it. Mirrors the v2 activation-log backfill: `writeSelections` writes
|
|
504
|
+
* `message_id = NULL` at injection time, and this runs at turn end once the
|
|
505
|
+
* assistant message exists. Relies on the single-threaded-per-conversation turn
|
|
506
|
+
* invariant — every NULL-`message_id` row for the conversation belongs to the
|
|
507
|
+
* turn that just finished. Lets the inspector look v3 selections up by the
|
|
508
|
+
* turn's message ids (robust against v2/v3 turn-counter drift).
|
|
509
|
+
*/
|
|
510
|
+
export function backfillMemoryV3SelectionMessageId(
|
|
511
|
+
conversationId: string,
|
|
512
|
+
assistantMessageId: string,
|
|
513
|
+
): void {
|
|
514
|
+
getSqliteFrom(getDb())
|
|
515
|
+
.query(
|
|
516
|
+
/*sql*/ `UPDATE memory_v3_selections SET message_id = ?
|
|
517
|
+
WHERE conversation_id = ? AND message_id IS NULL`,
|
|
518
|
+
)
|
|
519
|
+
.run(assistantMessageId, conversationId);
|
|
520
|
+
}
|
|
521
|
+
|
|
475
522
|
/**
|
|
476
523
|
* Run v3 orchestration for one turn and log the selection set, returning the
|
|
477
524
|
* orchestrate result so a live caller can render it. Never throws — all
|
|
@@ -530,6 +577,16 @@ export async function observeTurn(
|
|
|
530
577
|
writeSelections(conversationId, turnIndex, rows);
|
|
531
578
|
return result;
|
|
532
579
|
} catch (err) {
|
|
580
|
+
// An INFRASTRUCTURE failure (the selector lost its provider — e.g. a
|
|
581
|
+
// transient CES credential blip) must NOT be silently swallowed: re-throw
|
|
582
|
+
// so the LIVE injector hard-fails the turn (a clean, retryable failure)
|
|
583
|
+
// rather than shipping it with no `<memory>` block. The shadow/observation
|
|
584
|
+
// callers (the injector in shadow mode, runShadowObservation) catch this
|
|
585
|
+
// and swallow it, so observation never fails a turn. Other (non-infra)
|
|
586
|
+
// errors stay non-fatal and degrade to no v3 block, as before.
|
|
587
|
+
if (err instanceof MemoryV3RetrievalUnavailableError) {
|
|
588
|
+
throw err;
|
|
589
|
+
}
|
|
533
590
|
log.warn(
|
|
534
591
|
{ err: err instanceof Error ? err.message : String(err), conversationId },
|
|
535
592
|
"memory-v3 orchestration failed (non-fatal)",
|
|
@@ -553,5 +610,11 @@ export async function runShadowObservation(
|
|
|
553
610
|
const config = getConfig();
|
|
554
611
|
if (config.memory.enabled === false) return;
|
|
555
612
|
if (!isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config)) return;
|
|
556
|
-
|
|
613
|
+
try {
|
|
614
|
+
await observeTurn(conversationId, turnIndex);
|
|
615
|
+
} catch {
|
|
616
|
+
// Shadow observation is fire-and-forget and must NEVER fail a turn.
|
|
617
|
+
// `observeTurn` now re-throws infra failures so the LIVE injector can
|
|
618
|
+
// hard-fail on them; here (observation only) we swallow them.
|
|
619
|
+
}
|
|
557
620
|
}
|