@vellumai/assistant 0.11.3 → 0.11.4-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/ARCHITECTURE.md +11 -6
- package/docs/architecture/memory.md +11 -0
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/flux-turn-detection-spike.md +243 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/openapi.yaml +139 -37
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +28 -20
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +177 -4
- package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/edit-propagation.test.ts +0 -7
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/user-plugin-loader.test.ts +1 -1
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +68 -2
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +7 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +71 -16
- package/src/calls/voice-triage-escalate.ts +104 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +43 -1
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +4 -5
- package/src/config/default-profile-catalog.ts +83 -12
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +17 -6
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +9 -1
- package/src/daemon/conversation-process.ts +36 -6
- package/src/daemon/conversation-runtime-assembly.ts +3 -4
- package/src/daemon/conversation-surfaces.ts +27 -5
- package/src/daemon/conversation-tool-setup.ts +1 -2
- package/src/daemon/conversation.ts +48 -0
- package/src/daemon/interactive-turn-sender.ts +59 -0
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +24 -24
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/documents/document-store.ts +138 -5
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +50 -6
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +535 -314
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +108 -41
- package/src/persistence/delivery-crud.ts +38 -29
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/conversation-turn.ts +168 -5
- package/src/plugin-api/index.ts +21 -5
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/memory/AGENTS.md +4 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +23 -49
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +5 -2
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/chat-completions-provider.ts +5 -6
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/worker.ts +17 -5
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +3 -3
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +112 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/inbound-message-handler.ts +17 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/plugins-routes.ts +21 -3
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +39 -14
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/manager.ts +1 -3
- package/src/subagent/types.ts +7 -6
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +32 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/registry.ts +2 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +11 -0
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
|
@@ -19,29 +19,180 @@
|
|
|
19
19
|
// Refresh cadence is owned by the caller: the agent loop only passes the
|
|
20
20
|
// content through when `mode === "full"` (first turn / post-compaction),
|
|
21
21
|
// matching the existing PKB auto-inject pattern.
|
|
22
|
+
//
|
|
23
|
+
// Three of the four files are curated aggregate views that consolidation
|
|
24
|
+
// keeps lean. `memory/buffer.md` is not: it is the raw append-only staging
|
|
25
|
+
// log that `remember()` writes and consolidation drains, so its length is
|
|
26
|
+
// bounded only by how far behind consolidation has fallen. The injected
|
|
27
|
+
// Buffer section is therefore capped: see `capBufferSection`.
|
|
22
28
|
|
|
23
29
|
import type { ChannelId } from "../../../../channels/types.js";
|
|
24
30
|
import { usesConceptPageMemory } from "../../../../config/memory-v3-gate.js";
|
|
25
31
|
import { readPromptFile } from "../../../../prompts/system-prompt.js";
|
|
32
|
+
import { type BufferEntryLines, splitBufferEntries } from "../buffer-format.js";
|
|
26
33
|
import { getMemoryConfig } from "../config.js";
|
|
27
34
|
import { getWorkspacePromptPath } from "../paths.js";
|
|
35
|
+
import { resolveSubstrateTuning } from "./tuning.js";
|
|
28
36
|
|
|
29
37
|
interface MemoryV2StaticBlock {
|
|
30
38
|
heading: string;
|
|
31
39
|
file: string;
|
|
32
40
|
}
|
|
33
41
|
|
|
42
|
+
const BUFFER_FILE = "memory/buffer.md";
|
|
43
|
+
|
|
34
44
|
const MEMORY_V2_STATIC_BLOCKS: readonly MemoryV2StaticBlock[] = [
|
|
35
45
|
{ heading: "## Essentials", file: "memory/essentials.md" },
|
|
36
46
|
{ heading: "## Threads", file: "memory/threads.md" },
|
|
37
47
|
{ heading: "## Recent", file: "memory/recent.md" },
|
|
38
|
-
{ heading: "## Buffer", file:
|
|
48
|
+
{ heading: "## Buffer", file: BUFFER_FILE },
|
|
39
49
|
];
|
|
40
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Leader line on a Buffer section that was capped, so the model knows the
|
|
53
|
+
* section is a tail rather than the whole backlog and where the rest lives.
|
|
54
|
+
*/
|
|
55
|
+
const BUFFER_INJECTION_NOTICE =
|
|
56
|
+
"(Older entries trimmed. Read memory/buffer.md for the full backlog.)";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Cap the Buffer section at its most recent `maxLines` non-empty lines,
|
|
60
|
+
* returning the content unchanged when it already fits or when `maxLines` is
|
|
61
|
+
* `null`.
|
|
62
|
+
*
|
|
63
|
+
* The bound is `consolidation_max_buffer_lines`, the same threshold at which
|
|
64
|
+
* the scheduler already considers the buffer overdue for consolidation (see
|
|
65
|
+
* `jobs-worker.ts`). Reusing it means the injected buffer never exceeds one
|
|
66
|
+
* consolidation's worth of backlog, and that a workspace whose consolidation
|
|
67
|
+
* keeps up sees byte-identical output. `null` disables the size trigger, so
|
|
68
|
+
* the operator has opted out of size-based buffer management and the
|
|
69
|
+
* injection is left unbounded to match.
|
|
70
|
+
*
|
|
71
|
+
* The unit is the entry, not the line. `remember()` stores a multiline fact
|
|
72
|
+
* as one timestamped line plus its body, so trimming by raw line count can
|
|
73
|
+
* land inside a fact and inject continuation lines stripped of the timestamp
|
|
74
|
+
* and opening clause that give them meaning. Whole entries are taken
|
|
75
|
+
* newest-first while they fit, which makes a mid-entry cut unrepresentable.
|
|
76
|
+
* Retained lines keep their original spacing; the notice replaces everything
|
|
77
|
+
* before them.
|
|
78
|
+
*
|
|
79
|
+
* `maxLines` is still measured in non-empty lines, because that is what the
|
|
80
|
+
* scheduler counts (`countBufferLines`), so the two readings of "how big is
|
|
81
|
+
* the buffer" agree. See {@link capOversizedNewestEntry} for the one case
|
|
82
|
+
* that can exceed it.
|
|
83
|
+
*/
|
|
84
|
+
function capBufferSection(content: string, maxLines: number | null): string {
|
|
85
|
+
if (maxLines === null) {
|
|
86
|
+
return content;
|
|
87
|
+
}
|
|
88
|
+
const lines = content.split("\n");
|
|
89
|
+
if (countNonEmpty(lines) <= maxLines) {
|
|
90
|
+
return content;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const entries = splitBufferEntries(lines);
|
|
94
|
+
const kept: BufferEntryLines[] = [];
|
|
95
|
+
let budget = maxLines;
|
|
96
|
+
// Newest-first: the buffer is append-only, so the tail is the most recent.
|
|
97
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
98
|
+
const entry = entries[i]!;
|
|
99
|
+
// A headless leading group is prose before the first entry, not a fact.
|
|
100
|
+
if (entry.start === null) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
const cost = countNonEmpty(entry.lines);
|
|
104
|
+
if (cost > budget) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
budget -= cost;
|
|
108
|
+
kept.unshift(entry);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const body =
|
|
112
|
+
kept.length > 0
|
|
113
|
+
? kept.flatMap((entry) => entry.lines).join("\n")
|
|
114
|
+
: capOversizedNewestEntry(lines, entries, maxLines);
|
|
115
|
+
return `${BUFFER_INJECTION_NOTICE}\n${body}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Marker replacing the elided head of an entry too large to inject whole, so
|
|
120
|
+
* the retained tail reads as a fragment of the entry above it rather than as
|
|
121
|
+
* the whole fact.
|
|
122
|
+
*/
|
|
123
|
+
const ENTRY_BODY_TRIMMED_NOTICE =
|
|
124
|
+
"(This entry's body was trimmed. Read memory/buffer.md for the rest of it.)";
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Render the tail when not even the newest entry fits inside the cap.
|
|
128
|
+
*
|
|
129
|
+
* There is no whole entry to fall back to here, and all three options cost
|
|
130
|
+
* something. Dropping the entry would leave the section with a "trimmed"
|
|
131
|
+
* notice and no facts at all, which is the worst outcome because the newest
|
|
132
|
+
* fact is usually the one that mattered. Admitting it whole would reopen the
|
|
133
|
+
* unbounded injection the cap exists to prevent, since a fact has no size
|
|
134
|
+
* limit. So the entry keeps its opening line, which carries the timestamp and
|
|
135
|
+
* first clause the tail needs to be readable, then the marker, then as much
|
|
136
|
+
* of the tail as the cap allows. Every injected line stays attributable to a
|
|
137
|
+
* timestamped entry, at a fixed cost of two lines over `maxLines`.
|
|
138
|
+
*
|
|
139
|
+
* Two cases return early. When the cut falls on the opening line's immediate
|
|
140
|
+
* successor nothing was actually elided, so the entry is returned whole
|
|
141
|
+
* without a marker that would claim a trim that did not happen. When the
|
|
142
|
+
* buffer has no entry structure at all, it is hand-written and never went
|
|
143
|
+
* through `remember()`, so there is nothing to preserve and the plain line
|
|
144
|
+
* cut stands.
|
|
145
|
+
*/
|
|
146
|
+
function capOversizedNewestEntry(
|
|
147
|
+
lines: string[],
|
|
148
|
+
entries: readonly BufferEntryLines[],
|
|
149
|
+
maxLines: number,
|
|
150
|
+
): string {
|
|
151
|
+
const cut = lineCutStart(lines, maxLines);
|
|
152
|
+
const newest = entries.at(-1);
|
|
153
|
+
if (newest === undefined || newest.start === null) {
|
|
154
|
+
return lines.slice(cut).join("\n");
|
|
155
|
+
}
|
|
156
|
+
const elidedHead = countNonEmpty(lines.slice(newest.firstLine + 1, cut)) > 0;
|
|
157
|
+
if (!elidedHead) {
|
|
158
|
+
return lines.slice(newest.firstLine).join("\n");
|
|
159
|
+
}
|
|
160
|
+
return [
|
|
161
|
+
lines[newest.firstLine]!,
|
|
162
|
+
ENTRY_BODY_TRIMMED_NOTICE,
|
|
163
|
+
...lines.slice(cut),
|
|
164
|
+
].join("\n");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Index of the first line in the trailing window of `maxLines` non-empty
|
|
169
|
+
* lines, advanced past any blank lines that would otherwise open the section.
|
|
170
|
+
*/
|
|
171
|
+
function lineCutStart(lines: readonly string[], maxLines: number): number {
|
|
172
|
+
let kept = 0;
|
|
173
|
+
let cut = lines.length;
|
|
174
|
+
while (cut > 0 && kept < maxLines) {
|
|
175
|
+
cut--;
|
|
176
|
+
if (lines[cut]!.trim().length > 0) {
|
|
177
|
+
kept++;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
while (cut < lines.length && lines[cut]!.trim().length === 0) {
|
|
181
|
+
cut++;
|
|
182
|
+
}
|
|
183
|
+
return cut;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Non-empty line count, the unit the scheduler's `countBufferLines` uses. */
|
|
187
|
+
function countNonEmpty(lines: readonly string[]): number {
|
|
188
|
+
return lines.filter((line) => line.trim().length > 0).length;
|
|
189
|
+
}
|
|
190
|
+
|
|
41
191
|
/**
|
|
42
192
|
* Build the static memory block, gated on concept-page memory being active
|
|
43
193
|
* ({@link usesConceptPageMemory}). Empty/missing files are skipped; returns
|
|
44
|
-
* `null` when the gate is off or every file is empty.
|
|
194
|
+
* `null` when the gate is off or every file is empty. The Buffer section is
|
|
195
|
+
* capped by {@link capBufferSection}.
|
|
45
196
|
*
|
|
46
197
|
* `excludeBuffer` drops the `## Buffer` section. The consolidation run sets
|
|
47
198
|
* it: the agent's contract there is the `memory/buffer.md` FILE — it reads,
|
|
@@ -57,17 +208,22 @@ export function readMemoryV2StaticContent(
|
|
|
57
208
|
if (!usesConceptPageMemory(memoryConfig)) {
|
|
58
209
|
return null;
|
|
59
210
|
}
|
|
211
|
+
const maxBufferLines =
|
|
212
|
+
resolveSubstrateTuning(memoryConfig).consolidation_max_buffer_lines;
|
|
60
213
|
|
|
61
214
|
const sections: string[] = [];
|
|
62
215
|
for (const { heading, file } of MEMORY_V2_STATIC_BLOCKS) {
|
|
63
|
-
|
|
216
|
+
const isBuffer = file === BUFFER_FILE;
|
|
217
|
+
if (options.excludeBuffer === true && isBuffer) {
|
|
64
218
|
continue;
|
|
65
219
|
}
|
|
66
220
|
const content = readPromptFile(getWorkspacePromptPath(file));
|
|
67
221
|
if (!content) {
|
|
68
222
|
continue;
|
|
69
223
|
}
|
|
70
|
-
sections.push(
|
|
224
|
+
sections.push(
|
|
225
|
+
`${heading}\n\n${isBuffer ? capBufferSection(content, maxBufferLines) : content}`,
|
|
226
|
+
);
|
|
71
227
|
}
|
|
72
228
|
return sections.length > 0 ? sections.join("\n\n") : null;
|
|
73
229
|
}
|
|
@@ -44,10 +44,8 @@ import {
|
|
|
44
44
|
conversations,
|
|
45
45
|
messages,
|
|
46
46
|
} from "../../../../persistence/schema/index.js";
|
|
47
|
-
import {
|
|
48
|
-
|
|
49
|
-
formatRememberEntry,
|
|
50
|
-
} from "../graph/tool-handlers.js";
|
|
47
|
+
import { formatRememberEntry } from "../buffer-format.js";
|
|
48
|
+
import { appendBufferAndArchive } from "../graph/tool-handlers.js";
|
|
51
49
|
import {
|
|
52
50
|
extractToolUse,
|
|
53
51
|
type ToolDefinition,
|
|
@@ -1365,7 +1365,9 @@ function resolveConversationTimestamp(conversationId: string): number | null {
|
|
|
1365
1365
|
const db = getDb();
|
|
1366
1366
|
// Use the last message timestamp, not the conversation creation time.
|
|
1367
1367
|
// A conversation can span hours/days — memories should be timestamped
|
|
1368
|
-
// to when the relevant content was actually discussed.
|
|
1368
|
+
// to when the relevant content was actually discussed. Any-state read,
|
|
1369
|
+
// deliberately: only `createdAt` is consumed, and a streaming last row
|
|
1370
|
+
// still marks when discussion happened.
|
|
1369
1371
|
const lastMsg = db
|
|
1370
1372
|
.select({ createdAt: messages.createdAt })
|
|
1371
1373
|
.from(messages)
|
|
@@ -293,6 +293,8 @@ export function collectPersistedV3Cards(conversationId: string): Set<string> {
|
|
|
293
293
|
const rows = getSqliteFrom(getDb())
|
|
294
294
|
.query(
|
|
295
295
|
/*sql*/ `
|
|
296
|
+
-- Any-state scan: only metadata markers are read, and the marker is
|
|
297
|
+
-- written by the injection path on rows it owns regardless of state.
|
|
296
298
|
SELECT metadata FROM messages
|
|
297
299
|
WHERE conversation_id = ? AND metadata LIKE '%' || ? || '%'
|
|
298
300
|
`,
|
|
@@ -97,6 +97,8 @@ function forkSourceIdsOf(messageIds: string[]): string[] {
|
|
|
97
97
|
.query(
|
|
98
98
|
/*sql*/ `
|
|
99
99
|
SELECT json_extract(metadata, '$.forkSourceMessageId') AS src
|
|
100
|
+
-- No completeness predicate: ids come from this store's own selection
|
|
101
|
+
-- log, written for rows the injection pipeline already processed.
|
|
100
102
|
FROM messages
|
|
101
103
|
WHERE id IN (${placeholders})
|
|
102
104
|
`,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
cleanupWorkerPidFile,
|
|
28
28
|
startWorkerPidFileGuard,
|
|
29
29
|
} from "../../../util/worker-process.js";
|
|
30
|
+
import { registerWorkerPluginSurface } from "../../worker-plugin-surface.js";
|
|
30
31
|
import { registerMemoryPluginJobHandlers } from "./job-handler-registration.js";
|
|
31
32
|
import { startMemoryJobsWorkerLoop } from "./jobs-worker.js";
|
|
32
33
|
import { getLogger } from "./logging.js";
|
|
@@ -59,9 +60,11 @@ async function main(): Promise<void> {
|
|
|
59
60
|
// and are rejected.
|
|
60
61
|
await rehydratePlatformCredentials();
|
|
61
62
|
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
63
|
+
// Jobs in this process wake real agent conversations, so it registers the
|
|
64
|
+
// default plugin hook surface (hooks and injectors, no init hooks) the same
|
|
65
|
+
// way the daemon does, and the job handlers the worker dispatches from: the
|
|
66
|
+
// memory plugin's own plus the host's non-plugin domain handlers.
|
|
67
|
+
registerWorkerPluginSurface();
|
|
65
68
|
registerMemoryPluginJobHandlers();
|
|
66
69
|
|
|
67
70
|
// Populate the tool registry (core built-ins + workspace tools), exactly as
|
|
@@ -92,6 +92,12 @@ const PluginPackageJsonSchema = z
|
|
|
92
92
|
version: z.string().optional(),
|
|
93
93
|
peerDependencies: z.record(z.string(), z.string()).optional(),
|
|
94
94
|
credentialKeyPatterns: z.unknown().optional(),
|
|
95
|
+
/** Human title, when the package name is not one ("@vellumai/imessage"). */
|
|
96
|
+
displayName: z.string().min(1).optional(),
|
|
97
|
+
/** Standard npm field, reused as the one-line description clients show. */
|
|
98
|
+
description: z.string().min(1).optional(),
|
|
99
|
+
/** Lucide icon name without the `lucide-` prefix, matching `ChannelInfo`. */
|
|
100
|
+
icon: z.string().min(1).optional(),
|
|
95
101
|
})
|
|
96
102
|
.passthrough();
|
|
97
103
|
|
|
@@ -466,6 +472,47 @@ export async function loadExternalPlugin(
|
|
|
466
472
|
* surface the failure through their own channel (e.g. the schedule
|
|
467
473
|
* reconciler's per-day deduped notification).
|
|
468
474
|
*/
|
|
475
|
+
/**
|
|
476
|
+
* How a plugin presents itself: its title, its one line, its glyph.
|
|
477
|
+
*
|
|
478
|
+
* Every field is optional and none of them gate anything. A surface that
|
|
479
|
+
* shows a plugin falls back rather than hiding it, so a missing `icon`
|
|
480
|
+
* costs an icon and not the row.
|
|
481
|
+
*/
|
|
482
|
+
export interface PluginPresentation {
|
|
483
|
+
displayName?: string;
|
|
484
|
+
description?: string;
|
|
485
|
+
icon?: string;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Read a plugin's presentation fields from its manifest.
|
|
490
|
+
*
|
|
491
|
+
* Separate from {@link parsePluginManifest} because the two answer different
|
|
492
|
+
* questions: that one decides whether a plugin is loadable, this one only
|
|
493
|
+
* dresses it for display. An unreadable or invalid manifest yields `undefined`
|
|
494
|
+
* here rather than an error, since a caller showing a plugin has already
|
|
495
|
+
* established it exists.
|
|
496
|
+
*/
|
|
497
|
+
export async function parsePluginPresentation(
|
|
498
|
+
pluginDir: string,
|
|
499
|
+
): Promise<PluginPresentation | undefined> {
|
|
500
|
+
let rawPkg: unknown;
|
|
501
|
+
try {
|
|
502
|
+
rawPkg = JSON.parse(
|
|
503
|
+
await readFile(join(pluginDir, "package.json"), "utf8"),
|
|
504
|
+
);
|
|
505
|
+
} catch {
|
|
506
|
+
return undefined;
|
|
507
|
+
}
|
|
508
|
+
const parsed = PluginPackageJsonSchema.safeParse(rawPkg);
|
|
509
|
+
if (!parsed.success) {
|
|
510
|
+
return undefined;
|
|
511
|
+
}
|
|
512
|
+
const { displayName, description, icon } = parsed.data;
|
|
513
|
+
return { displayName, description, icon };
|
|
514
|
+
}
|
|
515
|
+
|
|
469
516
|
export async function parsePluginManifest(
|
|
470
517
|
pluginDir: string,
|
|
471
518
|
opts: { quiet?: boolean } = {},
|