@vellumai/assistant 0.9.1-staging.1 → 0.10.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/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -135,7 +135,16 @@ export function resolveConversationId(idOrKey: string): string | null {
|
|
|
135
135
|
*/
|
|
136
136
|
export function getOrCreateConversation(
|
|
137
137
|
conversationKey: string,
|
|
138
|
-
opts?: {
|
|
138
|
+
opts?: {
|
|
139
|
+
conversationType?: "standard";
|
|
140
|
+
/**
|
|
141
|
+
* Caller-supplied title for the conversation, used only when this call
|
|
142
|
+
* actually creates the row. Treated as a user-set title (`isAutoTitle = 0`)
|
|
143
|
+
* so the async LLM title generator's safe-overwrite check leaves it
|
|
144
|
+
* untouched. Ignored when the conversation already exists.
|
|
145
|
+
*/
|
|
146
|
+
title?: string;
|
|
147
|
+
},
|
|
139
148
|
): {
|
|
140
149
|
conversationId: string;
|
|
141
150
|
conversationType: string;
|
|
@@ -205,13 +214,19 @@ export function getOrCreateConversation(
|
|
|
205
214
|
|
|
206
215
|
const now = Date.now();
|
|
207
216
|
const conversationId = uuid();
|
|
208
|
-
const
|
|
217
|
+
const customTitle = opts?.title?.trim();
|
|
218
|
+
const title = customTitle || GENERATING_TITLE;
|
|
209
219
|
const memoryScopeId = "default";
|
|
210
220
|
|
|
211
221
|
tx.insert(conversations)
|
|
212
222
|
.values({
|
|
213
223
|
id: conversationId,
|
|
214
224
|
title,
|
|
225
|
+
// A caller-supplied title is user-set: mark it non-auto (0) so the
|
|
226
|
+
// async LLM title generator's `canReplaceTitle` check won't overwrite
|
|
227
|
+
// it. Without one, omit the column so it takes its default
|
|
228
|
+
// (AUTO_TITLE_LLM) and follows the auto-generated placeholder flow.
|
|
229
|
+
...(customTitle ? { isAutoTitle: 0 } : {}),
|
|
215
230
|
createdAt: now,
|
|
216
231
|
updatedAt: now,
|
|
217
232
|
totalInputTokens: 0,
|
|
@@ -166,6 +166,7 @@ export async function generateAndPersistConversationTitle(
|
|
|
166
166
|
const fallback = deriveFallbackTitle(context) ?? UNTITLED_FALLBACK;
|
|
167
167
|
updateConversationTitle(conversationId, fallback, AUTO_TITLE_DETERMINISTIC);
|
|
168
168
|
publishConversationTitleChanged(conversationId, fallback);
|
|
169
|
+
logRetryableFallback(params, "no_provider");
|
|
169
170
|
return { title: fallback, updated: true };
|
|
170
171
|
}
|
|
171
172
|
|
|
@@ -206,6 +207,7 @@ export async function generateAndPersistConversationTitle(
|
|
|
206
207
|
const fallback = deriveFallbackTitle(context) ?? UNTITLED_FALLBACK;
|
|
207
208
|
updateConversationTitle(conversationId, fallback, AUTO_TITLE_DETERMINISTIC);
|
|
208
209
|
publishConversationTitleChanged(conversationId, fallback);
|
|
210
|
+
logRetryableFallback(params, "empty_output");
|
|
209
211
|
return { title: fallback, updated: true };
|
|
210
212
|
}
|
|
211
213
|
|
|
@@ -230,7 +232,7 @@ export const titleMutex = new Mutex();
|
|
|
230
232
|
/**
|
|
231
233
|
* Fire-and-forget wrapper for title generation. Failures are logged
|
|
232
234
|
* but do not propagate. On failure, replaces loading placeholder with
|
|
233
|
-
* a
|
|
235
|
+
* a retryable fallback title so loading state is never permanent.
|
|
234
236
|
*
|
|
235
237
|
* Calls are serialized via {@link titleMutex} so burst conversation
|
|
236
238
|
* creation does not overwhelm the LLM provider.
|
|
@@ -244,16 +246,20 @@ export function queueGenerateConversationTitle(
|
|
|
244
246
|
})
|
|
245
247
|
.catch((err) => {
|
|
246
248
|
log.warn(
|
|
247
|
-
|
|
248
|
-
"
|
|
249
|
+
retryableFallbackLogFields(params, "generation_error", err),
|
|
250
|
+
"Conversation title generation used retryable fallback",
|
|
249
251
|
);
|
|
250
|
-
// Replace loading placeholder with
|
|
252
|
+
// Replace loading placeholder with a retryable fallback.
|
|
251
253
|
try {
|
|
252
254
|
const conversation = getConversation(params.conversationId);
|
|
253
255
|
if (conversation && conversation.title === GENERATING_TITLE) {
|
|
254
256
|
const fallback =
|
|
255
257
|
deriveFallbackTitle(params.context) ?? UNTITLED_FALLBACK;
|
|
256
|
-
updateConversationTitle(
|
|
258
|
+
updateConversationTitle(
|
|
259
|
+
params.conversationId,
|
|
260
|
+
fallback,
|
|
261
|
+
AUTO_TITLE_DETERMINISTIC,
|
|
262
|
+
);
|
|
257
263
|
publishConversationTitleChanged(params.conversationId, fallback);
|
|
258
264
|
}
|
|
259
265
|
} catch {
|
|
@@ -268,6 +274,11 @@ export interface RegenerateTitleParams {
|
|
|
268
274
|
conversationId: string;
|
|
269
275
|
provider?: Provider;
|
|
270
276
|
signal?: AbortSignal;
|
|
277
|
+
/**
|
|
278
|
+
* Limit regeneration to placeholder or deterministic titles. Used for retrying
|
|
279
|
+
* failed initial generation without racing against a successful initial title.
|
|
280
|
+
*/
|
|
281
|
+
onlyIfReplaceable?: boolean;
|
|
271
282
|
}
|
|
272
283
|
|
|
273
284
|
/**
|
|
@@ -278,12 +289,15 @@ export interface RegenerateTitleParams {
|
|
|
278
289
|
export async function regenerateConversationTitle(
|
|
279
290
|
params: RegenerateTitleParams,
|
|
280
291
|
): Promise<{ title: string; updated: boolean }> {
|
|
281
|
-
const { conversationId, signal } = params;
|
|
292
|
+
const { conversationId, onlyIfReplaceable, signal } = params;
|
|
282
293
|
|
|
283
294
|
const conversation = getConversation(conversationId);
|
|
284
295
|
if (!conversation || !conversation.isAutoTitle) {
|
|
285
296
|
return { title: conversation?.title ?? UNTITLED_FALLBACK, updated: false };
|
|
286
297
|
}
|
|
298
|
+
if (onlyIfReplaceable && !canReplaceTitle(conversation)) {
|
|
299
|
+
return { title: conversation.title ?? UNTITLED_FALLBACK, updated: false };
|
|
300
|
+
}
|
|
287
301
|
|
|
288
302
|
const provider =
|
|
289
303
|
params.provider ?? (await getConfiguredProvider("conversationTitle"));
|
|
@@ -317,7 +331,11 @@ export async function regenerateConversationTitle(
|
|
|
317
331
|
if (title) {
|
|
318
332
|
// Re-check isAutoTitle before persisting (race guard against manual rename)
|
|
319
333
|
const current = getConversation(conversationId);
|
|
320
|
-
if (
|
|
334
|
+
if (
|
|
335
|
+
!current ||
|
|
336
|
+
!current.isAutoTitle ||
|
|
337
|
+
(onlyIfReplaceable && !canReplaceTitle(current))
|
|
338
|
+
) {
|
|
321
339
|
return { title: current?.title ?? UNTITLED_FALLBACK, updated: false };
|
|
322
340
|
}
|
|
323
341
|
|
|
@@ -408,6 +426,45 @@ function buildTitlePrompt(
|
|
|
408
426
|
return parts.join("\n");
|
|
409
427
|
}
|
|
410
428
|
|
|
429
|
+
function titleGenerationLogFields(params: GenerateTitleParams) {
|
|
430
|
+
return {
|
|
431
|
+
conversationId: params.conversationId,
|
|
432
|
+
contextOrigin: params.context?.origin,
|
|
433
|
+
hasContext: Boolean(params.context),
|
|
434
|
+
userMessageLength: params.userMessage?.length ?? 0,
|
|
435
|
+
assistantResponseLength: params.assistantResponse?.length ?? 0,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
type TitleGenerationFallbackReason =
|
|
440
|
+
| "no_provider"
|
|
441
|
+
| "empty_output"
|
|
442
|
+
| "generation_error";
|
|
443
|
+
|
|
444
|
+
function retryableFallbackLogFields(
|
|
445
|
+
params: GenerateTitleParams,
|
|
446
|
+
reason: TitleGenerationFallbackReason,
|
|
447
|
+
err?: unknown,
|
|
448
|
+
): Record<string, unknown> {
|
|
449
|
+
const fields: Record<string, unknown> = {
|
|
450
|
+
...titleGenerationLogFields(params),
|
|
451
|
+
reason,
|
|
452
|
+
fallbackSource: params.context ? "context" : "untitled",
|
|
453
|
+
};
|
|
454
|
+
if (err) fields.err = err;
|
|
455
|
+
return fields;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function logRetryableFallback(
|
|
459
|
+
params: GenerateTitleParams,
|
|
460
|
+
reason: TitleGenerationFallbackReason,
|
|
461
|
+
): void {
|
|
462
|
+
log.warn(
|
|
463
|
+
retryableFallbackLogFields(params, reason),
|
|
464
|
+
"Conversation title generation used retryable fallback",
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
411
468
|
const META_FAILURE_TITLES = new Set([
|
|
412
469
|
"missing context",
|
|
413
470
|
"no context",
|
package/src/memory/db-init.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
createSkillLoadedEventsTable,
|
|
41
41
|
createTasksAndWorkItemsTables,
|
|
42
42
|
createWatchersAndLogsTables,
|
|
43
|
+
dropApprovalPromptTsTrackerTable,
|
|
43
44
|
migrate230AcpSessionHistory,
|
|
44
45
|
migrate231RepairMemoryGraphEventDates,
|
|
45
46
|
migrateA2ATasks,
|
|
@@ -102,6 +103,7 @@ import {
|
|
|
102
103
|
migrateDropConflicts,
|
|
103
104
|
migrateDropContactInteractionColumns,
|
|
104
105
|
migrateDropEntityTables,
|
|
106
|
+
migrateDropExternalUserId,
|
|
105
107
|
migrateDropLegacyMemberGuardianTables,
|
|
106
108
|
migrateDropLoopbackPortColumn,
|
|
107
109
|
migrateDropMemoryItemsTables,
|
|
@@ -195,7 +197,9 @@ import {
|
|
|
195
197
|
migrateRenameVerificationSessionIdColumn,
|
|
196
198
|
migrateRenameVerificationTable,
|
|
197
199
|
migrateRenameVoiceToPhone,
|
|
200
|
+
migrateRewriteBalancedEconomyProfilePins,
|
|
198
201
|
migrateScheduleCapabilities,
|
|
202
|
+
migrateScheduleDefaultNoReuseConversation,
|
|
199
203
|
migrateScheduleDescription,
|
|
200
204
|
migrateScheduleInferenceProfile,
|
|
201
205
|
migrateScheduleOneShotRouting,
|
|
@@ -223,6 +227,7 @@ import {
|
|
|
223
227
|
migrateUsageLlmCallCount,
|
|
224
228
|
migrateVoiceInviteColumns,
|
|
225
229
|
migrateVoiceInviteDisplayMetadata,
|
|
230
|
+
migrateWorkflowJournalLeafTokens,
|
|
226
231
|
migrateWorkflowRuns,
|
|
227
232
|
migrateWorkflowRunTrust,
|
|
228
233
|
recoverCrashedMigrations,
|
|
@@ -521,6 +526,11 @@ export function initializeDb(): void {
|
|
|
521
526
|
migrateContactChannelsUniqueExtUser,
|
|
522
527
|
migrateScheduleCapabilities,
|
|
523
528
|
migrateContactChannelsRenormalizeAddresses,
|
|
529
|
+
migrateScheduleDefaultNoReuseConversation,
|
|
530
|
+
migrateWorkflowJournalLeafTokens,
|
|
531
|
+
migrateDropExternalUserId,
|
|
532
|
+
dropApprovalPromptTsTrackerTable,
|
|
533
|
+
migrateRewriteBalancedEconomyProfilePins,
|
|
524
534
|
];
|
|
525
535
|
|
|
526
536
|
// Run each migration step, catching and logging individual failures so one
|
|
@@ -497,6 +497,20 @@ export async function getMemoryBackendStatus(config: AssistantConfig): Promise<{
|
|
|
497
497
|
};
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Thrown by {@link embedWithBackend} when no embedding backend is configured or
|
|
502
|
+
* available. This is a PROCESS-WIDE condition (backend selection is effectively
|
|
503
|
+
* cached for the run), so a caller embedding many items should treat the first
|
|
504
|
+
* occurrence as fatal to the whole batch rather than a per-item failure — see
|
|
505
|
+
* `backfillAllSections`, which aborts on it instead of churning through deletes.
|
|
506
|
+
*/
|
|
507
|
+
export class EmbeddingBackendUnavailableError extends Error {
|
|
508
|
+
constructor(message = "No memory embedding backend configured") {
|
|
509
|
+
super(message);
|
|
510
|
+
this.name = "EmbeddingBackendUnavailableError";
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
500
514
|
export async function embedWithBackend(
|
|
501
515
|
config: AssistantConfig,
|
|
502
516
|
inputs: EmbeddingInput[],
|
|
@@ -514,7 +528,7 @@ export async function embedWithBackend(
|
|
|
514
528
|
|
|
515
529
|
const selection = await selectEmbeddingBackend(config);
|
|
516
530
|
if (!selection.backend) {
|
|
517
|
-
throw new
|
|
531
|
+
throw new EmbeddingBackendUnavailableError(
|
|
518
532
|
selection.reason ?? "No memory embedding backend configured",
|
|
519
533
|
);
|
|
520
534
|
}
|
|
@@ -2,6 +2,7 @@ import { join } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
4
4
|
import { getConfig } from "../config/loader.js";
|
|
5
|
+
import { isMemoryV3Live } from "../config/memory-v3-gate.js";
|
|
5
6
|
import type { AssistantConfig } from "../config/types.js";
|
|
6
7
|
import {
|
|
7
8
|
checkDiskPressureBackgroundGate,
|
|
@@ -852,7 +853,7 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
852
853
|
// this guard is belt-and-suspenders that also avoids a wasted enqueue.
|
|
853
854
|
if (
|
|
854
855
|
isAssistantFeatureFlagEnabled("memory-v3-shadow", config) ||
|
|
855
|
-
|
|
856
|
+
isMemoryV3Live(config)
|
|
856
857
|
) {
|
|
857
858
|
schedule.push({
|
|
858
859
|
key: GRAPH_MAINTENANCE_CHECKPOINTS.memoryV3Maintain,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { and, asc, eq, gt, or } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getCachedShareAnalytics } from "../platform/consent-cache.js";
|
|
5
5
|
import { getDb } from "./db-connection.js";
|
|
6
6
|
import { lifecycleEvents } from "./schema.js";
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ export interface LifecycleEvent {
|
|
|
13
13
|
|
|
14
14
|
/** Record a lifecycle event (e.g. app_open, hatch). Returns null when usage data collection is disabled. */
|
|
15
15
|
export function recordLifecycleEvent(eventName: string): LifecycleEvent | null {
|
|
16
|
-
if (!
|
|
16
|
+
if (!getCachedShareAnalytics()) return null;
|
|
17
17
|
const db = getDb();
|
|
18
18
|
const event: LifecycleEvent = {
|
|
19
19
|
id: uuid(),
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
// - Source conversation isn't a memory-retrospective conversation itself
|
|
7
7
|
// (recursion guard — we never run a retrospective over reflective
|
|
8
8
|
// musings from the retrospective agent's own writes).
|
|
9
|
+
// - Source isn't a `scheduled` thread or a memory-consolidation background
|
|
10
|
+
// (low yield — see `isLowYieldRetrospectiveSource`).
|
|
9
11
|
//
|
|
10
12
|
// All four trigger types funnel through `upsertMemoryRetrospectiveJob` which
|
|
11
13
|
// coalesces rapid enqueues into a single pending row per conversation.
|
|
@@ -13,14 +15,13 @@
|
|
|
13
15
|
// after the corresponding signal settles; `interval` and `message_count`
|
|
14
16
|
// fire immediately.
|
|
15
17
|
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
type TrustClass,
|
|
19
|
-
} from "../runtime/actor-trust-resolver.js";
|
|
18
|
+
import { type TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
19
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
20
20
|
import { getLogger } from "../util/logger.js";
|
|
21
|
-
import { getConversationSource } from "./conversation-crud.js";
|
|
21
|
+
import { getConversation, getConversationSource } from "./conversation-crud.js";
|
|
22
22
|
import { isMemoryEnabled, upsertMemoryRetrospectiveJob } from "./jobs-store.js";
|
|
23
23
|
import { isMemoryRetrospectiveSource } from "./memory-retrospective-constants.js";
|
|
24
|
+
import { MEMORY_V2_CONSOLIDATION_SOURCE } from "./v2/constants.js";
|
|
24
25
|
|
|
25
26
|
const log = getLogger("memory-retrospective-enqueue");
|
|
26
27
|
|
|
@@ -50,6 +51,14 @@ export function enqueueMemoryRetrospectiveIfEnabled(args: {
|
|
|
50
51
|
return;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
if (isLowYieldRetrospectiveSource(conversationId)) {
|
|
55
|
+
log.debug(
|
|
56
|
+
{ conversationId, trigger },
|
|
57
|
+
"Skipping memory-retrospective enqueue: scheduled or consolidation source",
|
|
58
|
+
);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
53
62
|
const runAfter =
|
|
54
63
|
trigger === "compaction" ? Date.now() + COMPACTION_DEBOUNCE_MS : Date.now();
|
|
55
64
|
|
|
@@ -75,6 +84,22 @@ export function isMemoryRetrospectiveConversation(
|
|
|
75
84
|
return source !== null && isMemoryRetrospectiveSource(source);
|
|
76
85
|
}
|
|
77
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Scheduled task threads (location/health pulses) rarely carry anything worth
|
|
89
|
+
* remembering, and memory-consolidation conversations already persist their
|
|
90
|
+
* output to the corpus — a retrospective over either burns an inference pass
|
|
91
|
+
* for no unique gain (and, for consolidation, re-stores already-captured
|
|
92
|
+
* content). Heartbeat (`background`) and standard conversations are unaffected.
|
|
93
|
+
*/
|
|
94
|
+
function isLowYieldRetrospectiveSource(conversationId: string): boolean {
|
|
95
|
+
const conversation = getConversation(conversationId);
|
|
96
|
+
if (!conversation) return false;
|
|
97
|
+
return (
|
|
98
|
+
conversation.conversationType === "scheduled" ||
|
|
99
|
+
conversation.source === MEMORY_V2_CONSOLIDATION_SOURCE
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
78
103
|
/**
|
|
79
104
|
* Fire a memory-retrospective enqueue from the compaction site. Mirrors
|
|
80
105
|
* `enqueueAutoAnalysisOnCompaction` — same trust-class gate (don't run a
|
|
@@ -86,7 +111,7 @@ export function enqueueMemoryRetrospectiveOnCompaction(
|
|
|
86
111
|
conversationId: string,
|
|
87
112
|
trustClass: TrustClass | undefined,
|
|
88
113
|
): void {
|
|
89
|
-
if (
|
|
114
|
+
if (!resolveCapabilities(trustClass).canAccessMemory) {
|
|
90
115
|
return;
|
|
91
116
|
}
|
|
92
117
|
try {
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
getMessagesAfter,
|
|
66
66
|
resolveOverrideProfile,
|
|
67
67
|
} from "./conversation-crud.js";
|
|
68
|
+
import { isBackgroundConversationType } from "./conversation-types.js";
|
|
68
69
|
import {
|
|
69
70
|
enqueueMemoryJob,
|
|
70
71
|
type MemoryJob,
|
|
@@ -313,6 +314,14 @@ async function runForkBasedRetrospective(
|
|
|
313
314
|
// {@link WakeToolContextPin}.
|
|
314
315
|
toolGateMode: "execution" as const,
|
|
315
316
|
toolContextPin,
|
|
317
|
+
// Reproduce the source's turn block for message-tier cache-prefix
|
|
318
|
+
// parity: background/scheduled sources ran non-interactive live turns
|
|
319
|
+
// (which carry `<background_turn>`), so the fork must run non-interactive
|
|
320
|
+
// too. Standard/user sources ran interactive (no `<background_turn>`), so
|
|
321
|
+
// the fork stays interactive — preserving their existing byte parity.
|
|
322
|
+
isNonInteractive: isBackgroundConversationType(
|
|
323
|
+
sourceConversation.conversationType,
|
|
324
|
+
),
|
|
316
325
|
// Profile forcing (model/thinking/effort parity) is a separate concern
|
|
317
326
|
// and stays keyed on `matchConversationProfile` via `matchedProfile`.
|
|
318
327
|
...(matchedProfile !== undefined
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Add verification and access-control columns to contact_channels for
|
|
@@ -9,13 +9,20 @@ import type { DrizzleDb } from "../db-connection.js";
|
|
|
9
9
|
* Uses ALTER TABLE ADD COLUMN with try/catch for idempotency.
|
|
10
10
|
*/
|
|
11
11
|
export function migrateContactChannelsAccessFields(database: DrizzleDb): void {
|
|
12
|
-
//
|
|
13
|
-
|
|
12
|
+
// external_user_id: only add on first run. migration 294 drops this column;
|
|
13
|
+
// if it's absent but other 129-era columns already exist, 294 has run and
|
|
14
|
+
// we must not re-add it (avoids a table-rewrite cycle on every startup).
|
|
15
|
+
const raw = getSqliteFrom(database);
|
|
16
|
+
const cols = raw.prepare("PRAGMA table_info(contact_channels)").all() as {
|
|
17
|
+
name: string;
|
|
18
|
+
}[];
|
|
19
|
+
const colNames = new Set(cols.map((c) => c.name));
|
|
20
|
+
const needsExternalUserId =
|
|
21
|
+
!colNames.has("external_user_id") && !colNames.has("status");
|
|
22
|
+
if (needsExternalUserId) {
|
|
14
23
|
database.run(
|
|
15
24
|
/*sql*/ `ALTER TABLE contact_channels ADD COLUMN external_user_id TEXT`,
|
|
16
25
|
);
|
|
17
|
-
} catch {
|
|
18
|
-
/* already exists */
|
|
19
26
|
}
|
|
20
27
|
// Delivery/notification routing address (e.g., Telegram chat ID for DMs)
|
|
21
28
|
try {
|
|
@@ -98,8 +105,10 @@ export function migrateContactChannelsAccessFields(database: DrizzleDb): void {
|
|
|
98
105
|
/* already exists */
|
|
99
106
|
}
|
|
100
107
|
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
// Only create the index when the column exists (migration 294 drops both).
|
|
109
|
+
if (needsExternalUserId || colNames.has("external_user_id")) {
|
|
110
|
+
database.run(
|
|
111
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_contact_channels_type_ext_user ON contact_channels(type, external_user_id)`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
105
114
|
}
|
|
@@ -16,6 +16,18 @@ export function migrateDropLegacyMemberGuardianTables(
|
|
|
16
16
|
): void {
|
|
17
17
|
const raw = getSqliteFrom(database);
|
|
18
18
|
|
|
19
|
+
// The safety-sync below reads and writes contact_channels.external_user_id.
|
|
20
|
+
// A later migration drops that column; this step re-runs on every startup,
|
|
21
|
+
// so once the column is gone the sync cannot run (and is moot —
|
|
22
|
+
// identities live in `address`). Skip the sync in that case but still
|
|
23
|
+
// drop the legacy tables, so this step completes instead of failing every
|
|
24
|
+
// boot. Reaching here with the tables still present means the sync never
|
|
25
|
+
// succeeded, so nothing synced is lost.
|
|
26
|
+
const cols = raw.prepare(`PRAGMA table_info(contact_channels)`).all() as {
|
|
27
|
+
name: string;
|
|
28
|
+
}[];
|
|
29
|
+
const hasExternalUserId = cols.some((c) => c.name === "external_user_id");
|
|
30
|
+
|
|
19
31
|
// ── Safety sync: guardian bindings → contacts ─────────────────────
|
|
20
32
|
const guardianTableExists = raw
|
|
21
33
|
.prepare(
|
|
@@ -23,7 +35,7 @@ export function migrateDropLegacyMemberGuardianTables(
|
|
|
23
35
|
)
|
|
24
36
|
.get();
|
|
25
37
|
|
|
26
|
-
if (guardianTableExists) {
|
|
38
|
+
if (guardianTableExists && hasExternalUserId) {
|
|
27
39
|
// Insert any active guardian bindings not already present in contacts.
|
|
28
40
|
// We match on (type, external_user_id) to avoid duplicating existing rows.
|
|
29
41
|
raw.exec(/*sql*/ `
|
|
@@ -77,7 +89,7 @@ export function migrateDropLegacyMemberGuardianTables(
|
|
|
77
89
|
)
|
|
78
90
|
.get();
|
|
79
91
|
|
|
80
|
-
if (membersTableExists) {
|
|
92
|
+
if (membersTableExists && hasExternalUserId) {
|
|
81
93
|
// Insert non-pending members not already present in contacts.
|
|
82
94
|
raw.exec(/*sql*/ `
|
|
83
95
|
INSERT INTO contacts (id, display_name, created_at, updated_at)
|
|
@@ -18,6 +18,16 @@ const log = getLogger("migration-289");
|
|
|
18
18
|
export function migrateContactChannelsUniqueExtUser(database: DrizzleDb): void {
|
|
19
19
|
const raw = getSqliteFrom(database);
|
|
20
20
|
|
|
21
|
+
// A later migration drops external_user_id; once it has run this historical
|
|
22
|
+
// dedup is obsolete (Steps 2–3 below reference the column). This step re-runs
|
|
23
|
+
// on every startup, so skip when the column is absent rather than failing.
|
|
24
|
+
const cols = raw.prepare("PRAGMA table_info(contact_channels)").all() as {
|
|
25
|
+
name: string;
|
|
26
|
+
}[];
|
|
27
|
+
if (!cols.some((c) => c.name === "external_user_id")) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
// Step 1: Deduplicate historical case collisions. After this, the existing
|
|
22
32
|
// case-sensitive UNIQUE(type, address) constraint remains valid because
|
|
23
33
|
// only one row per case-insensitive group survives.
|
|
@@ -16,6 +16,16 @@ export function migrateContactChannelsRenormalizeAddresses(
|
|
|
16
16
|
): void {
|
|
17
17
|
const raw = getSqliteFrom(database);
|
|
18
18
|
|
|
19
|
+
// A later migration drops external_user_id; once it has run there is nothing
|
|
20
|
+
// to renormalize from. This step re-runs on every startup, so skip when the
|
|
21
|
+
// column is absent rather than referencing it and failing.
|
|
22
|
+
const cols = raw.prepare("PRAGMA table_info(contact_channels)").all() as {
|
|
23
|
+
name: string;
|
|
24
|
+
}[];
|
|
25
|
+
if (!cols.some((c) => c.name === "external_user_id")) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
// Remove rows that would block normalization due to cross-column collisions.
|
|
20
30
|
raw.run(/*sql*/ `
|
|
21
31
|
DELETE FROM contact_channels
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateScheduleDefaultNoReuseConversation } from "./292-schedule-default-no-reuse-conversation.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
// Post-210 shape: reuse_conversation column present (trimmed to the columns
|
|
12
|
+
// the migration and assertions touch).
|
|
13
|
+
sqlite.exec(/*sql*/ `
|
|
14
|
+
CREATE TABLE cron_jobs (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
name TEXT NOT NULL,
|
|
17
|
+
message TEXT NOT NULL,
|
|
18
|
+
next_run_at INTEGER NOT NULL,
|
|
19
|
+
created_by TEXT NOT NULL,
|
|
20
|
+
created_at INTEGER NOT NULL,
|
|
21
|
+
updated_at INTEGER NOT NULL,
|
|
22
|
+
reuse_conversation INTEGER NOT NULL DEFAULT 0
|
|
23
|
+
)
|
|
24
|
+
`);
|
|
25
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function insertJob(sqlite: Database, id: string, reuse: number) {
|
|
29
|
+
sqlite
|
|
30
|
+
.query(
|
|
31
|
+
/*sql*/ `INSERT INTO cron_jobs
|
|
32
|
+
(id, name, message, next_run_at, created_by, created_at, updated_at, reuse_conversation)
|
|
33
|
+
VALUES (?, ?, 'msg', 1000, 'agent', 1000, 1000, ?)`,
|
|
34
|
+
)
|
|
35
|
+
.run(id, id, reuse);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function reuseValue(sqlite: Database, id: string): number {
|
|
39
|
+
return (
|
|
40
|
+
sqlite
|
|
41
|
+
.query("SELECT reuse_conversation FROM cron_jobs WHERE id = ?")
|
|
42
|
+
.get(id) as { reuse_conversation: number }
|
|
43
|
+
).reuse_conversation;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("migration 292: schedules default to no conversation reuse", () => {
|
|
47
|
+
test("flips existing reuse_conversation=1 rows to 0", () => {
|
|
48
|
+
const { sqlite, db } = createTestDb();
|
|
49
|
+
insertJob(sqlite, "reusing", 1);
|
|
50
|
+
insertJob(sqlite, "already-fresh", 0);
|
|
51
|
+
|
|
52
|
+
migrateScheduleDefaultNoReuseConversation(db);
|
|
53
|
+
|
|
54
|
+
expect(reuseValue(sqlite, "reusing")).toBe(0);
|
|
55
|
+
expect(reuseValue(sqlite, "already-fresh")).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("is idempotent — re-run is a no-op", () => {
|
|
59
|
+
const { sqlite, db } = createTestDb();
|
|
60
|
+
insertJob(sqlite, "reusing", 1);
|
|
61
|
+
|
|
62
|
+
migrateScheduleDefaultNoReuseConversation(db);
|
|
63
|
+
expect(() => migrateScheduleDefaultNoReuseConversation(db)).not.toThrow();
|
|
64
|
+
|
|
65
|
+
expect(reuseValue(sqlite, "reusing")).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Disable conversation reuse on all existing schedules.
|
|
5
|
+
*
|
|
6
|
+
* Recurring schedules previously defaulted to `reuse_conversation = 1`, so
|
|
7
|
+
* every fire appended to one long-lived conversation. That unbounded,
|
|
8
|
+
* self-similar transcript is a drift hazard for weaker models (it primes them
|
|
9
|
+
* to repeat or extend the prior run) and grows per-fire token cost without
|
|
10
|
+
* adding correctness — durable cross-run state already lives in workspace files
|
|
11
|
+
* and memory. Schedules now default to a fresh conversation per fire; this
|
|
12
|
+
* aligns existing rows with that contract.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent: the guarded UPDATE matches nothing once every row is already 0.
|
|
15
|
+
*/
|
|
16
|
+
export function migrateScheduleDefaultNoReuseConversation(
|
|
17
|
+
database: DrizzleDb,
|
|
18
|
+
): void {
|
|
19
|
+
const raw = getSqliteFrom(database);
|
|
20
|
+
raw
|
|
21
|
+
.query(
|
|
22
|
+
/*sql*/ `UPDATE cron_jobs SET reuse_conversation = 0 WHERE reuse_conversation != 0`,
|
|
23
|
+
)
|
|
24
|
+
.run();
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Add nullable `input_tokens` / `output_tokens` columns to `workflow_journal`.
|
|
6
|
+
*
|
|
7
|
+
* Persists per-leaf token usage so the journal route can attribute usage to each
|
|
8
|
+
* leaf. The client then computes run-level token metrics from the per-leaf sum
|
|
9
|
+
* (a single source of truth), counting each leaf exactly once regardless of
|
|
10
|
+
* whether its usage arrives via a live `leaf_finished` event or a journal
|
|
11
|
+
* backfill — which avoids the undercount that arose when a mid-run journal
|
|
12
|
+
* aggregate counted a leaf the journal could not itself attribute.
|
|
13
|
+
*
|
|
14
|
+
* Nullable — legacy rows and non-completed leaves (failures, nested
|
|
15
|
+
* `workflow`-kind entries) stay NULL and contribute zero to the sum.
|
|
16
|
+
*
|
|
17
|
+
* Idempotent — each ALTER is wrapped so a re-run (column already present) is a
|
|
18
|
+
* no-op.
|
|
19
|
+
*/
|
|
20
|
+
export function migrateWorkflowJournalLeafTokens(database: DrizzleDb): void {
|
|
21
|
+
const raw = getSqliteFrom(database);
|
|
22
|
+
try {
|
|
23
|
+
raw.exec(`ALTER TABLE workflow_journal ADD COLUMN input_tokens INTEGER`);
|
|
24
|
+
} catch {
|
|
25
|
+
/* Column already exists */
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
raw.exec(`ALTER TABLE workflow_journal ADD COLUMN output_tokens INTEGER`);
|
|
29
|
+
} catch {
|
|
30
|
+
/* Column already exists */
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getLogger } from "../../util/logger.js";
|
|
2
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
const log = getLogger("migration-294");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Drops the `external_user_id` column and its index from `contact_channels`.
|
|
8
|
+
*
|
|
9
|
+
* `address` is the single canonical identity column; `external_user_id` is
|
|
10
|
+
* redundant. The index `idx_contact_channels_type_ext_user` must be dropped
|
|
11
|
+
* first — SQLite refuses to drop a column referenced by an index.
|
|
12
|
+
*
|
|
13
|
+
* Idempotent: skips if the column has already been dropped.
|
|
14
|
+
*/
|
|
15
|
+
export function migrateDropExternalUserId(database: DrizzleDb): void {
|
|
16
|
+
const raw = getSqliteFrom(database);
|
|
17
|
+
|
|
18
|
+
const cols = raw.prepare("PRAGMA table_info(contact_channels)").all() as {
|
|
19
|
+
name: string;
|
|
20
|
+
}[];
|
|
21
|
+
const hasColumn = cols.some((c) => c.name === "external_user_id");
|
|
22
|
+
|
|
23
|
+
if (!hasColumn) {
|
|
24
|
+
log.info("external_user_id column already absent — skipping");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
raw.run("DROP INDEX IF EXISTS idx_contact_channels_type_ext_user");
|
|
29
|
+
raw.run("ALTER TABLE contact_channels DROP COLUMN external_user_id");
|
|
30
|
+
log.info("Dropped external_user_id column from contact_channels");
|
|
31
|
+
}
|