@vellumai/assistant 0.8.2 → 0.8.4
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 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Memory v2 — `assistant` CLI subcommands → embedded capability entries
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// Enumerate the top-level `assistant` CLI subcommands, render each as a prose
|
|
6
|
+
// capability statement that wraps the full `helpInformation()` output, embed
|
|
7
|
+
// dense + sparse, and upsert into `memory_v2_concept_pages` under the slug
|
|
8
|
+
// `cli-commands/<name>`. The router scores these alongside concept pages and
|
|
9
|
+
// skill entries; the injection layer surfaces hits under `### CLI Commands
|
|
10
|
+
// You Can Use` so the model can semantically discover a CLI capability it
|
|
11
|
+
// would not otherwise know to reach for.
|
|
12
|
+
//
|
|
13
|
+
// Mirrors `skill-store.ts` deliberately: same single-flight + generation
|
|
14
|
+
// coalescing, same dense + sparse + corpus-stats-aware sparse encoding, same
|
|
15
|
+
// payload-kind discriminator, same atomic cache replacement. Differences:
|
|
16
|
+
// - No remote catalog — the source of truth is the local Commander tree.
|
|
17
|
+
// - No per-entry feature-flag filter — flag gating already happens during
|
|
18
|
+
// `buildCliProgramTree` (e.g. email/plugins commands are conditionally
|
|
19
|
+
// registered).
|
|
20
|
+
// - No MCP-style augmentation — Commander's description is the canonical
|
|
21
|
+
// summary.
|
|
22
|
+
|
|
23
|
+
import { getConfig } from "../../config/loader.js";
|
|
24
|
+
import { getLogger } from "../../util/logger.js";
|
|
25
|
+
import { applyCorrectionIfCalibrated } from "../anisotropy.js";
|
|
26
|
+
import {
|
|
27
|
+
embedWithBackend,
|
|
28
|
+
generateSparseEmbedding,
|
|
29
|
+
} from "../embedding-backend.js";
|
|
30
|
+
import { buildCliCommandContent } from "./cli-command-content.js";
|
|
31
|
+
import { invalidatePageIndex } from "./page-index.js";
|
|
32
|
+
import {
|
|
33
|
+
backfillKindOnPointsWithPrefix,
|
|
34
|
+
pruneSlugsWithPrefixExcept,
|
|
35
|
+
upsertConceptPageEmbedding,
|
|
36
|
+
} from "./qdrant.js";
|
|
37
|
+
import {
|
|
38
|
+
generateBm25DocEmbedding,
|
|
39
|
+
getConceptPageCorpusStats,
|
|
40
|
+
} from "./sparse-bm25.js";
|
|
41
|
+
import type { CliCommandEntry } from "./types.js";
|
|
42
|
+
|
|
43
|
+
const log = getLogger("memory-v2-cli-command-store");
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Slug prefix under which CLI-command embeddings are indexed in
|
|
47
|
+
* `memory_v2_concept_pages`. Concept-page slugs must match
|
|
48
|
+
* `[a-z0-9][a-z0-9-]*(/...)*`, and `cli-commands` matches that pattern, so the
|
|
49
|
+
* prefix coexists with hand-authored concept pages without escape work.
|
|
50
|
+
*/
|
|
51
|
+
export const CLI_COMMAND_SLUG_PREFIX = "cli-commands/";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Payload discriminator written on every CLI-command-seeded Qdrant point.
|
|
55
|
+
* Keeps CLI rows distinguishable from user-authored concept pages and from
|
|
56
|
+
* skill rows that happen to live in adjacent namespaces, so prefix pruning
|
|
57
|
+
* never deletes a hand-authored page sitting under `cli-commands/...`.
|
|
58
|
+
*/
|
|
59
|
+
const CLI_COMMAND_PAYLOAD_KIND = "cli-command";
|
|
60
|
+
|
|
61
|
+
/** Compose the unified-collection slug for a CLI command name. */
|
|
62
|
+
export function cliCommandSlugFor(name: string): string {
|
|
63
|
+
return `${CLI_COMMAND_SLUG_PREFIX}${name}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Module-level cache of rendered CLI-command entries keyed by command name.
|
|
68
|
+
* `null` until the first successful seed run completes; replaced atomically
|
|
69
|
+
* on each successful re-seed so callers always see a consistent snapshot.
|
|
70
|
+
*/
|
|
71
|
+
let entries: Map<string, CliCommandEntry> | null = null;
|
|
72
|
+
let requestedSeedGeneration = 0;
|
|
73
|
+
let processedSeedGeneration = 0;
|
|
74
|
+
let activeSeedDrain: Promise<void> | null = null;
|
|
75
|
+
let lastSeedError: unknown = null;
|
|
76
|
+
const seedWaiters: Array<{ generation: number; resolve: () => void }> = [];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* In-process latch for the legacy `kind` backfill. New upserts always write
|
|
80
|
+
* `kind`, so once the latch is set there is no follow-up work to do this
|
|
81
|
+
* process.
|
|
82
|
+
*/
|
|
83
|
+
let legacyKindBackfillDone = false;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Seed (or re-seed) CLI-command embeddings into the unified concept-page
|
|
87
|
+
* collection. Idempotent. Best-effort for background callers (errors are
|
|
88
|
+
* logged but swallowed); pass `{ throwOnError: true }` from synchronous CLI
|
|
89
|
+
* paths that want failures surfaced.
|
|
90
|
+
*
|
|
91
|
+
* Single-flight + coalesced: at most one seed runs at a time. Requests made
|
|
92
|
+
* while a seed is in flight advance the requested generation; stale in-flight
|
|
93
|
+
* snapshots are skipped before they write embeddings or replace the cache.
|
|
94
|
+
*/
|
|
95
|
+
export async function seedV2CliCommandEntries(
|
|
96
|
+
opts: { throwOnError?: boolean } = {},
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
const generation = ++requestedSeedGeneration;
|
|
99
|
+
const waiter = new Promise<void>((resolve) => {
|
|
100
|
+
seedWaiters.push({ generation, resolve });
|
|
101
|
+
});
|
|
102
|
+
startSeedDrainIfNeeded();
|
|
103
|
+
await waiter;
|
|
104
|
+
if (opts.throwOnError && lastSeedError) {
|
|
105
|
+
throw lastSeedError;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function startSeedDrainIfNeeded(): void {
|
|
110
|
+
if (activeSeedDrain) return;
|
|
111
|
+
if (processedSeedGeneration >= requestedSeedGeneration) return;
|
|
112
|
+
|
|
113
|
+
activeSeedDrain = drainSeedQueue().finally(() => {
|
|
114
|
+
activeSeedDrain = null;
|
|
115
|
+
startSeedDrainIfNeeded();
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function drainSeedQueue(): Promise<void> {
|
|
120
|
+
while (processedSeedGeneration < requestedSeedGeneration) {
|
|
121
|
+
const generationToProcess = requestedSeedGeneration;
|
|
122
|
+
await runSeedV2CliCommandEntries(generationToProcess);
|
|
123
|
+
processedSeedGeneration = generationToProcess;
|
|
124
|
+
resolveSeedWaiters();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveSeedWaiters(): void {
|
|
129
|
+
for (let i = seedWaiters.length - 1; i >= 0; i -= 1) {
|
|
130
|
+
const waiter = seedWaiters[i]!;
|
|
131
|
+
if (waiter.generation > processedSeedGeneration) continue;
|
|
132
|
+
seedWaiters.splice(i, 1);
|
|
133
|
+
waiter.resolve();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
138
|
+
try {
|
|
139
|
+
const config = getConfig();
|
|
140
|
+
// Dynamic import so callers that only need `getCliCommandCapability` or
|
|
141
|
+
// `listCliCommandEntries` (e.g. the render path inside `injection.ts` and
|
|
142
|
+
// the `page-index.ts` dependency loader) never drag the full CLI command
|
|
143
|
+
// graph into their import tree. The CLI tree pulls in many provider and
|
|
144
|
+
// workspace modules whose presence has been a recurring source of test-
|
|
145
|
+
// mock cascades and circular-import surprises.
|
|
146
|
+
const { buildCliProgramTree } = await import("../../cli/program.js");
|
|
147
|
+
const program = buildCliProgramTree();
|
|
148
|
+
|
|
149
|
+
const seeds: CliCommandEntry[] = [];
|
|
150
|
+
for (const cmd of program.commands) {
|
|
151
|
+
const name = cmd.name();
|
|
152
|
+
// Skip the `help` builtin Commander adds automatically — it carries no
|
|
153
|
+
// capability information of its own and is uniform across commands.
|
|
154
|
+
if (name === "help") continue;
|
|
155
|
+
const description = cmd.description();
|
|
156
|
+
const content = buildCliCommandContent(
|
|
157
|
+
name,
|
|
158
|
+
description,
|
|
159
|
+
cmd.helpInformation(),
|
|
160
|
+
);
|
|
161
|
+
seeds.push({ id: name, description, content });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const nextEntries = new Map<string, CliCommandEntry>();
|
|
165
|
+
let denseVectors: number[][] = [];
|
|
166
|
+
let encodeSparse: (
|
|
167
|
+
input: string,
|
|
168
|
+
) => ReturnType<typeof generateSparseEmbedding> = generateSparseEmbedding;
|
|
169
|
+
if (seeds.length > 0) {
|
|
170
|
+
const embedded = await embedWithBackend(
|
|
171
|
+
config,
|
|
172
|
+
seeds.map((s) => s.content),
|
|
173
|
+
);
|
|
174
|
+
denseVectors = await Promise.all(
|
|
175
|
+
embedded.vectors.map((v) =>
|
|
176
|
+
applyCorrectionIfCalibrated(v, embedded.provider, embedded.model),
|
|
177
|
+
),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// CLI commands share the concept-page Qdrant collection, so the sparse
|
|
181
|
+
// vector must use the same stemmed BM25 encoding as the concept-page
|
|
182
|
+
// documents. Fall back to the legacy TF encoder only during the cold-
|
|
183
|
+
// start window before corpus stats finish building — same rationale as
|
|
184
|
+
// the skill-store path.
|
|
185
|
+
const corpusStats = getConceptPageCorpusStats();
|
|
186
|
+
encodeSparse = (input: string) =>
|
|
187
|
+
corpusStats
|
|
188
|
+
? generateBm25DocEmbedding(input, corpusStats, {
|
|
189
|
+
k1: config.memory.v2.bm25_k1,
|
|
190
|
+
b: config.memory.v2.bm25_b,
|
|
191
|
+
})
|
|
192
|
+
: generateSparseEmbedding(input);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (generation !== requestedSeedGeneration) {
|
|
196
|
+
log.info(
|
|
197
|
+
{ generation, latestGeneration: requestedSeedGeneration },
|
|
198
|
+
"Skipping stale v2 CLI-command seed result",
|
|
199
|
+
);
|
|
200
|
+
lastSeedError = null;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (seeds.length > 0) {
|
|
205
|
+
const now = Date.now();
|
|
206
|
+
await Promise.all(
|
|
207
|
+
seeds.map((seed, i) =>
|
|
208
|
+
upsertConceptPageEmbedding({
|
|
209
|
+
slug: cliCommandSlugFor(seed.id),
|
|
210
|
+
dense: denseVectors[i],
|
|
211
|
+
sparse: encodeSparse(seed.content),
|
|
212
|
+
updatedAt: now,
|
|
213
|
+
kind: CLI_COMMAND_PAYLOAD_KIND,
|
|
214
|
+
}),
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
for (const seed of seeds) {
|
|
218
|
+
nextEntries.set(seed.id, seed);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// The CLI tree is always available (no remote catalog), so pruning is
|
|
223
|
+
// unconditional. Run the legacy `kind` backfill once per process so
|
|
224
|
+
// pre-discriminator rows become prunable.
|
|
225
|
+
const knownIds = new Set(seeds.map((s) => s.id));
|
|
226
|
+
if (!legacyKindBackfillDone) {
|
|
227
|
+
try {
|
|
228
|
+
await backfillKindOnPointsWithPrefix(
|
|
229
|
+
CLI_COMMAND_SLUG_PREFIX,
|
|
230
|
+
CLI_COMMAND_PAYLOAD_KIND,
|
|
231
|
+
knownIds,
|
|
232
|
+
);
|
|
233
|
+
legacyKindBackfillDone = true;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
log.warn(
|
|
236
|
+
{ err },
|
|
237
|
+
"Failed to backfill kind on legacy CLI-command points — pruning may leave orphans this run",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
await pruneSlugsWithPrefixExcept(
|
|
242
|
+
CLI_COMMAND_SLUG_PREFIX,
|
|
243
|
+
seeds.map((s) => s.id),
|
|
244
|
+
{ kind: CLI_COMMAND_PAYLOAD_KIND },
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Atomically replace the cache only after every step above succeeds.
|
|
248
|
+
entries = nextEntries;
|
|
249
|
+
invalidatePageIndex();
|
|
250
|
+
lastSeedError = null;
|
|
251
|
+
} catch (err) {
|
|
252
|
+
lastSeedError = err;
|
|
253
|
+
log.warn({ err }, "Failed to seed v2 CLI-command entries");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Synchronous lookup of a previously-seeded `CliCommandEntry` by command
|
|
259
|
+
* name. Returns `null` when the cache has not yet been populated, when the
|
|
260
|
+
* id is unknown, or when a prior seed run dropped the id.
|
|
261
|
+
*
|
|
262
|
+
* Accepts either a bare command name (`attachment`) or its unified-collection
|
|
263
|
+
* slug (`cli-commands/attachment`) so render-side callers can pass through
|
|
264
|
+
* what they have without a manual prefix strip.
|
|
265
|
+
*
|
|
266
|
+
* Returns a frozen copy so callers cannot mutate the underlying cache entry.
|
|
267
|
+
*/
|
|
268
|
+
export function getCliCommandCapability(
|
|
269
|
+
idOrSlug: string,
|
|
270
|
+
): CliCommandEntry | null {
|
|
271
|
+
const id = idOrSlug.startsWith(CLI_COMMAND_SLUG_PREFIX)
|
|
272
|
+
? idOrSlug.slice(CLI_COMMAND_SLUG_PREFIX.length)
|
|
273
|
+
: idOrSlug;
|
|
274
|
+
const entry = entries?.get(id);
|
|
275
|
+
return entry ? Object.freeze({ ...entry }) : null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** True iff the slug refers to a CLI-command entry in the unified collection. */
|
|
279
|
+
export function isCliCommandSlug(slug: string): boolean {
|
|
280
|
+
return slug.startsWith(CLI_COMMAND_SLUG_PREFIX);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Snapshot of the in-process CLI-command cache, sorted by command name (ASCII
|
|
285
|
+
* order) for determinism. Returns a freshly allocated array of frozen entry
|
|
286
|
+
* copies on each call.
|
|
287
|
+
*/
|
|
288
|
+
export function listCliCommandEntries(): CliCommandEntry[] {
|
|
289
|
+
if (!entries) return [];
|
|
290
|
+
return [...entries.values()]
|
|
291
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
|
292
|
+
.map((entry) => Object.freeze({ ...entry }));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** @internal Test-only: clear the module-level cache. */
|
|
296
|
+
export function _resetCliCommandStoreForTests(): void {
|
|
297
|
+
entries = null;
|
|
298
|
+
requestedSeedGeneration = 0;
|
|
299
|
+
processedSeedGeneration = 0;
|
|
300
|
+
activeSeedDrain = null;
|
|
301
|
+
seedWaiters.splice(0, seedWaiters.length);
|
|
302
|
+
lastSeedError = null;
|
|
303
|
+
legacyKindBackfillDone = false;
|
|
304
|
+
}
|
|
@@ -244,6 +244,20 @@ function readBufferContent(bufferPath: string): string {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Count non-empty lines in `memory/buffer.md`. Used by the scheduler to
|
|
249
|
+
* implement the size-based consolidation trigger. Missing file → 0.
|
|
250
|
+
*
|
|
251
|
+
* Each entry is one line (`- [Mon D, h:mm AM/PM] …\n`), so non-empty-line
|
|
252
|
+
* count == entry count for a well-formed buffer; blank lines and trailing
|
|
253
|
+
* newlines don't inflate the count.
|
|
254
|
+
*/
|
|
255
|
+
export function countBufferLines(bufferPath: string): number {
|
|
256
|
+
const content = readBufferContent(bufferPath);
|
|
257
|
+
if (content.length === 0) return 0;
|
|
258
|
+
return content.split("\n").filter((line) => line.trim().length > 0).length;
|
|
259
|
+
}
|
|
260
|
+
|
|
247
261
|
/**
|
|
248
262
|
* Atomically create the lock file with `wx` (O_CREAT | O_EXCL) flags. Returns
|
|
249
263
|
* `null` on success, or the current holder string (file contents, typically
|
|
@@ -22,6 +22,7 @@ import { join } from "node:path";
|
|
|
22
22
|
|
|
23
23
|
import { parse as parseYaml } from "yaml";
|
|
24
24
|
|
|
25
|
+
import type { AssistantConfig } from "../../config/schema.js";
|
|
25
26
|
import { FRONTMATTER_REGEX } from "../../skills/frontmatter.js";
|
|
26
27
|
import { getLogger } from "../../util/logger.js";
|
|
27
28
|
import { listPages } from "./page-store.js";
|
|
@@ -32,11 +33,16 @@ const log = getLogger("memory-v2-frontmatter-sweep");
|
|
|
32
33
|
/**
|
|
33
34
|
* Validate every concept page's frontmatter against the strict schema and
|
|
34
35
|
* emit a `warn` per offender. Never throws — daemon startup must not block
|
|
35
|
-
* on this safety net.
|
|
36
|
+
* on this safety net. Self-gates on `config.memory.v2.enabled`: when v2
|
|
37
|
+
* is off, concept pages never enter a retrieval top-K so any warns here
|
|
38
|
+
* would be pure noise.
|
|
36
39
|
*/
|
|
37
40
|
export async function sweepConceptPageFrontmatter(
|
|
41
|
+
config: AssistantConfig,
|
|
38
42
|
workspaceDir: string,
|
|
39
43
|
): Promise<void> {
|
|
44
|
+
if (!config.memory.v2.enabled) return;
|
|
45
|
+
|
|
40
46
|
let slugs: string[];
|
|
41
47
|
try {
|
|
42
48
|
slugs = await listPages(workspaceDir);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { getLogger } from "../../util/logger.js";
|
|
2
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
3
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
4
|
+
|
|
5
|
+
const log = getLogger("memory-v2-injection-events");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Half-life of the injection-frequency decay, in milliseconds.
|
|
9
|
+
*
|
|
10
|
+
* Per the memory router v4 spec: a +1 from 3 days ago contributes 0.5; from
|
|
11
|
+
* 6 days ago 0.25. Decoupled from turn volume — busy and quiet days decay
|
|
12
|
+
* at the same wall-clock rate.
|
|
13
|
+
*/
|
|
14
|
+
export const INJECTION_SCORE_HALF_LIFE_MS = 3 * 24 * 60 * 60 * 1000;
|
|
15
|
+
|
|
16
|
+
export const INJECTION_SCORE_LAMBDA =
|
|
17
|
+
Math.log(2) / INJECTION_SCORE_HALF_LIFE_MS;
|
|
18
|
+
|
|
19
|
+
// Events past 6 half-lives contribute <1.6% each. Reads bound the scan to
|
|
20
|
+
// this window so per-slug score computation stays cheap as history grows.
|
|
21
|
+
const READ_WINDOW_MS = 6 * INJECTION_SCORE_HALF_LIFE_MS;
|
|
22
|
+
|
|
23
|
+
function decayContribution(elapsedMs: number): number {
|
|
24
|
+
return Math.exp(-INJECTION_SCORE_LAMBDA * elapsedMs);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Append one event per slug. Best-effort — a SQLite write must never abort
|
|
29
|
+
* the agent turn on top of a successful routing decision the rest of the
|
|
30
|
+
* caller depends on.
|
|
31
|
+
*/
|
|
32
|
+
export function recordInjectionEvents(
|
|
33
|
+
database: DrizzleDb,
|
|
34
|
+
slugs: readonly string[],
|
|
35
|
+
injectedAt: number,
|
|
36
|
+
): void {
|
|
37
|
+
if (slugs.length === 0) return;
|
|
38
|
+
try {
|
|
39
|
+
const raw = getSqliteFrom(database);
|
|
40
|
+
const insert = raw.prepare(
|
|
41
|
+
`INSERT INTO memory_v2_injection_events (slug, injected_at) VALUES (?, ?)`,
|
|
42
|
+
);
|
|
43
|
+
const append = raw.transaction((items: readonly string[]) => {
|
|
44
|
+
for (const slug of items) insert.run(slug, injectedAt);
|
|
45
|
+
});
|
|
46
|
+
append(slugs);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
log.warn(
|
|
49
|
+
{ err, slugCount: slugs.length },
|
|
50
|
+
"failed to record injection events; continuing",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** `score(now) = Σᵢ exp(-λ × (now - tᵢ))` over events within READ_WINDOW_MS. */
|
|
56
|
+
export function computeInjectionScore(
|
|
57
|
+
database: DrizzleDb,
|
|
58
|
+
slug: string,
|
|
59
|
+
now: number,
|
|
60
|
+
): number {
|
|
61
|
+
const cutoff = now - READ_WINDOW_MS;
|
|
62
|
+
const raw = getSqliteFrom(database);
|
|
63
|
+
const rows = raw
|
|
64
|
+
.query(
|
|
65
|
+
`SELECT injected_at FROM memory_v2_injection_events
|
|
66
|
+
WHERE slug = ? AND injected_at >= ?`,
|
|
67
|
+
)
|
|
68
|
+
.all(slug, cutoff) as Array<{ injected_at: number }>;
|
|
69
|
+
let score = 0;
|
|
70
|
+
for (const row of rows) score += decayContribution(now - row.injected_at);
|
|
71
|
+
return score;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Batch variant of `computeInjectionScore` — single SQL pass scoped to the
|
|
76
|
+
* requested slugs so tier assignment doesn't issue O(M) queries. Slugs
|
|
77
|
+
* with no events in the read window are omitted from the result; callers
|
|
78
|
+
* should treat a missing entry as score 0.
|
|
79
|
+
*/
|
|
80
|
+
export function computeInjectionScores(
|
|
81
|
+
database: DrizzleDb,
|
|
82
|
+
slugs: readonly string[],
|
|
83
|
+
now: number,
|
|
84
|
+
): Map<string, number> {
|
|
85
|
+
const out = new Map<string, number>();
|
|
86
|
+
if (slugs.length === 0) return out;
|
|
87
|
+
const cutoff = now - READ_WINDOW_MS;
|
|
88
|
+
const raw = getSqliteFrom(database);
|
|
89
|
+
const placeholders = slugs.map(() => "?").join(",");
|
|
90
|
+
const rows = raw
|
|
91
|
+
.query(
|
|
92
|
+
`SELECT slug, injected_at FROM memory_v2_injection_events
|
|
93
|
+
WHERE slug IN (${placeholders}) AND injected_at >= ?`,
|
|
94
|
+
)
|
|
95
|
+
.all(...slugs, cutoff) as Array<{ slug: string; injected_at: number }>;
|
|
96
|
+
for (const row of rows) {
|
|
97
|
+
const prev = out.get(row.slug) ?? 0;
|
|
98
|
+
out.set(row.slug, prev + decayContribution(now - row.injected_at));
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
@@ -38,7 +38,12 @@ import {
|
|
|
38
38
|
spreadActivation,
|
|
39
39
|
} from "./activation.js";
|
|
40
40
|
import { hydrate, save } from "./activation-store.js";
|
|
41
|
+
import {
|
|
42
|
+
getCliCommandCapability,
|
|
43
|
+
isCliCommandSlug,
|
|
44
|
+
} from "./cli-command-store.js";
|
|
41
45
|
import { getEdgeIndex } from "./edge-index.js";
|
|
46
|
+
import { recordInjectionEvents } from "./injection-events.js";
|
|
42
47
|
import { readPage, renderPageContent } from "./page-store.js";
|
|
43
48
|
import { runRouter } from "./router.js";
|
|
44
49
|
import { getSkillCapability, isSkillSlug } from "./skill-store.js";
|
|
@@ -355,20 +360,22 @@ async function finalizeInjection(args: {
|
|
|
355
360
|
// on that user message and the agent keeps seeing it across subsequent turns
|
|
356
361
|
// until compaction evicts the turn.
|
|
357
362
|
//
|
|
358
|
-
//
|
|
359
|
-
// between the
|
|
360
|
-
// at an uninstalled skill
|
|
361
|
-
// per-turn runs re-attempt attachment
|
|
362
|
-
// this, the slug would be marked
|
|
363
|
-
// silently dropped it.
|
|
364
|
-
const
|
|
363
|
+
// Synthetic slugs (skills, CLI commands) whose in-process cache entry is
|
|
364
|
+
// missing (e.g. startup race between the seed and the first turn, or stale
|
|
365
|
+
// Qdrant index pointing at an uninstalled skill / removed CLI command) are
|
|
366
|
+
// excluded from `everInjected` so future per-turn runs re-attempt attachment
|
|
367
|
+
// once the cache is populated. Without this, the slug would be marked
|
|
368
|
+
// injected even though `renderInjectionBlock` silently dropped it.
|
|
369
|
+
const missingSyntheticSlugs = new Set(
|
|
365
370
|
slugsToRender.filter(
|
|
366
|
-
(slug) =>
|
|
371
|
+
(slug) =>
|
|
372
|
+
(isSkillSlug(slug) && !getSkillCapability(slug)) ||
|
|
373
|
+
(isCliCommandSlug(slug) && !getCliCommandCapability(slug)),
|
|
367
374
|
),
|
|
368
375
|
);
|
|
369
376
|
const everInjectedSet = new Set(priorEverInjected.map((entry) => entry.slug));
|
|
370
377
|
const newlyInjected = slugsToRender.filter(
|
|
371
|
-
(slug) => !everInjectedSet.has(slug) && !
|
|
378
|
+
(slug) => !everInjectedSet.has(slug) && !missingSyntheticSlugs.has(slug),
|
|
372
379
|
);
|
|
373
380
|
const nextEverInjected: EverInjectedEntry[] = [
|
|
374
381
|
...priorEverInjected,
|
|
@@ -546,9 +553,19 @@ async function injectViaRouter(args: {
|
|
|
546
553
|
nowText,
|
|
547
554
|
priorEverInjected,
|
|
548
555
|
config,
|
|
556
|
+
database,
|
|
549
557
|
...(signal ? { signal } : {}),
|
|
550
558
|
});
|
|
551
559
|
|
|
560
|
+
// Record router selections to the EMA event log. The router decided these
|
|
561
|
+
// slugs are relevant THIS turn (regardless of whether they're newly
|
|
562
|
+
// rendered or re-picked from prior context). The helper swallows its own
|
|
563
|
+
// errors — a SQLite write must not abort the turn on top of a successful
|
|
564
|
+
// routing decision the rest of this function depends on.
|
|
565
|
+
if (routerResult.failureReason === null) {
|
|
566
|
+
recordInjectionEvents(database, routerResult.selectedSlugs, Date.now());
|
|
567
|
+
}
|
|
568
|
+
|
|
552
569
|
if (routerResult.failureReason !== null) {
|
|
553
570
|
log.warn(
|
|
554
571
|
{ failureReason: routerResult.failureReason },
|
|
@@ -597,12 +614,12 @@ async function injectViaRouter(args: {
|
|
|
597
614
|
|
|
598
615
|
// Build minimal telemetry rows for the union of router-selected slugs and
|
|
599
616
|
// prior `everInjected` slugs. Router-mode rows zero out every activation
|
|
600
|
-
// value (no spreading activation runs). Slugs the router picked
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
// overwritten by
|
|
604
|
-
|
|
605
|
-
const telemetrySlugs = new Set<string>(
|
|
617
|
+
// value (no spreading activation runs). Slugs the router picked carry
|
|
618
|
+
// their batch's tier tag from `routerResult.sourceBySlug` (e.g. `tier1`,
|
|
619
|
+
// `tier3:2`); prior-everInjected slugs the router did NOT re-pick get
|
|
620
|
+
// `source: "carry_over"`. The `status` placeholder is overwritten by
|
|
621
|
+
// `finalizeInjection`.
|
|
622
|
+
const telemetrySlugs = new Set<string>(routerResult.selectedSlugs);
|
|
606
623
|
for (const entry of priorEverInjected) telemetrySlugs.add(entry.slug);
|
|
607
624
|
const telemetryRows: MemoryV2ConceptRowRecord[] = [...telemetrySlugs].map(
|
|
608
625
|
(slug) => ({
|
|
@@ -617,7 +634,7 @@ async function injectViaRouter(args: {
|
|
|
617
634
|
simAssistantRerankBoost: 0,
|
|
618
635
|
inRerankPool: false,
|
|
619
636
|
spreadContribution: 0,
|
|
620
|
-
source:
|
|
637
|
+
source: routerResult.sourceBySlug.get(slug) ?? "carry_over",
|
|
621
638
|
status: "not_injected",
|
|
622
639
|
}),
|
|
623
640
|
);
|
|
@@ -706,7 +723,7 @@ interface RenderInjectionBlockResult {
|
|
|
706
723
|
* the agent into wasted reads.
|
|
707
724
|
*/
|
|
708
725
|
const INJECTION_HEADER =
|
|
709
|
-
"
|
|
726
|
+
'Use `file_read("memory/concepts/path/to/file.md")` to read the full pages for any of the injected memory summaries you want more information on.';
|
|
710
727
|
|
|
711
728
|
/**
|
|
712
729
|
* Render the inner content of the `<memory>` block for a list of slugs.
|
|
@@ -728,20 +745,20 @@ const INJECTION_HEADER =
|
|
|
728
745
|
* distinguish "file vanished" (stale index) from "file is malformed"
|
|
729
746
|
* (data-corruption / programmer error).
|
|
730
747
|
*
|
|
731
|
-
* Skill slugs whose entry the cache no longer
|
|
732
|
-
* mid-run
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
* bug.
|
|
748
|
+
* Skill and CLI-command slugs whose entry the in-process cache no longer
|
|
749
|
+
* knows (e.g. uninstalled mid-run, or a CLI command removed between seeds)
|
|
750
|
+
* are silently dropped, mirroring the missing-pages behavior but without
|
|
751
|
+
* entering `missingSlugs` — the synthetic catalogs are the source of truth
|
|
752
|
+
* for those entries, not on-disk concept pages.
|
|
737
753
|
*
|
|
738
754
|
* Each concept-page section is rendered as a path header followed by either
|
|
739
755
|
* the page's `summary` (when present in frontmatter) or the full page (the
|
|
740
|
-
* fallback for pages predating the summary field). Skills sit
|
|
741
|
-
* under `### Skills You Can Use`,
|
|
742
|
-
*
|
|
756
|
+
* fallback for pages predating the summary field). Skills sit after the
|
|
757
|
+
* concept sections under `### Skills You Can Use`, and CLI subcommands sit
|
|
758
|
+
* after the skills under `### CLI Commands You Can Use`. The leading
|
|
759
|
+
* instruction line tells the agent how to read the block.
|
|
743
760
|
*
|
|
744
|
-
*
|
|
761
|
+
* Use `file_read("memory/concepts/path/to/file.md")` to read the full pages for any of the injected memory summaries you want more information on.
|
|
745
762
|
*
|
|
746
763
|
* # memory/concepts/<concept-slug-1>.md
|
|
747
764
|
* <summary-1>
|
|
@@ -758,13 +775,24 @@ const INJECTION_HEADER =
|
|
|
758
775
|
* ### Skills You Can Use
|
|
759
776
|
* - <skill-1 content>
|
|
760
777
|
* - <skill-2 content>
|
|
778
|
+
*
|
|
779
|
+
* ### CLI Commands You Can Use
|
|
780
|
+
* Run `assistant <command> --help` for full usage.
|
|
781
|
+
* - `assistant <name-1>`: <description-1>
|
|
782
|
+
* - `assistant <name-2>`: <description-2>
|
|
761
783
|
*/
|
|
762
784
|
async function renderInjectionBlock(
|
|
763
785
|
workspaceDir: string,
|
|
764
786
|
slugs: string[],
|
|
765
787
|
): Promise<RenderInjectionBlockResult> {
|
|
766
|
-
const conceptSlugs =
|
|
767
|
-
const skillSlugs =
|
|
788
|
+
const conceptSlugs: string[] = [];
|
|
789
|
+
const skillSlugs: string[] = [];
|
|
790
|
+
const cliCommandSlugs: string[] = [];
|
|
791
|
+
for (const slug of slugs) {
|
|
792
|
+
if (isSkillSlug(slug)) skillSlugs.push(slug);
|
|
793
|
+
else if (isCliCommandSlug(slug)) cliCommandSlugs.push(slug);
|
|
794
|
+
else conceptSlugs.push(slug);
|
|
795
|
+
}
|
|
768
796
|
|
|
769
797
|
const settled = await Promise.allSettled(
|
|
770
798
|
conceptSlugs.map((slug) => readPage(workspaceDir, slug)),
|
|
@@ -815,6 +843,18 @@ async function renderInjectionBlock(
|
|
|
815
843
|
sections.push(`### Skills You Can Use\n${skillLines.join("\n")}`);
|
|
816
844
|
}
|
|
817
845
|
|
|
846
|
+
const cliCommandLines: string[] = [];
|
|
847
|
+
for (const slug of cliCommandSlugs) {
|
|
848
|
+
const entry = getCliCommandCapability(slug);
|
|
849
|
+
if (!entry) continue;
|
|
850
|
+
cliCommandLines.push(`- \`assistant ${entry.id}\`: ${entry.description}`);
|
|
851
|
+
}
|
|
852
|
+
if (cliCommandLines.length > 0) {
|
|
853
|
+
sections.push(
|
|
854
|
+
`### CLI Commands You Can Use\nRun \`assistant <command> --help\` for full usage.\n${cliCommandLines.join("\n")}`,
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
818
858
|
if (sections.length === 0) {
|
|
819
859
|
return { block: null, missingSlugs, corruptSlugs };
|
|
820
860
|
}
|