@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- 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__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- 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 +25 -7
- 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-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 +264 -81
- 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__/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__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -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-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-chain.test.ts +2 -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__/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-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- 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.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- 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__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- 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-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- 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/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- 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 +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- 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/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- 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 +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- 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 +135 -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 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- 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-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- 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/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -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/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- 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/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -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/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- 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/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 +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- 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__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- 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/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 +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- 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/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -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 +151 -56
- package/src/runtime/auth/route-policy.ts +7 -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-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- 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/chatgpt-subscription-auth-routes.ts +246 -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 +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -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 +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -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/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/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/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/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- 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/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- 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/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
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
isCliCommandSlug,
|
|
44
44
|
} from "./cli-command-store.js";
|
|
45
45
|
import { getEdgeIndex } from "./edge-index.js";
|
|
46
|
+
import { recordInjectionEvents } from "./injection-events.js";
|
|
46
47
|
import { readPage, renderPageContent } from "./page-store.js";
|
|
47
48
|
import { runRouter } from "./router.js";
|
|
48
49
|
import { getSkillCapability, isSkillSlug } from "./skill-store.js";
|
|
@@ -552,9 +553,19 @@ async function injectViaRouter(args: {
|
|
|
552
553
|
nowText,
|
|
553
554
|
priorEverInjected,
|
|
554
555
|
config,
|
|
556
|
+
database,
|
|
555
557
|
...(signal ? { signal } : {}),
|
|
556
558
|
});
|
|
557
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
|
+
|
|
558
569
|
if (routerResult.failureReason !== null) {
|
|
559
570
|
log.warn(
|
|
560
571
|
{ failureReason: routerResult.failureReason },
|
|
@@ -603,12 +614,12 @@ async function injectViaRouter(args: {
|
|
|
603
614
|
|
|
604
615
|
// Build minimal telemetry rows for the union of router-selected slugs and
|
|
605
616
|
// prior `everInjected` slugs. Router-mode rows zero out every activation
|
|
606
|
-
// value (no spreading activation runs). Slugs the router picked
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
// overwritten by
|
|
610
|
-
|
|
611
|
-
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);
|
|
612
623
|
for (const entry of priorEverInjected) telemetrySlugs.add(entry.slug);
|
|
613
624
|
const telemetryRows: MemoryV2ConceptRowRecord[] = [...telemetrySlugs].map(
|
|
614
625
|
(slug) => ({
|
|
@@ -623,7 +634,7 @@ async function injectViaRouter(args: {
|
|
|
623
634
|
simAssistantRerankBoost: 0,
|
|
624
635
|
inRerankPool: false,
|
|
625
636
|
spreadContribution: 0,
|
|
626
|
-
source:
|
|
637
|
+
source: routerResult.sourceBySlug.get(slug) ?? "carry_over",
|
|
627
638
|
status: "not_injected",
|
|
628
639
|
}),
|
|
629
640
|
);
|
|
@@ -712,7 +723,7 @@ interface RenderInjectionBlockResult {
|
|
|
712
723
|
* the agent into wasted reads.
|
|
713
724
|
*/
|
|
714
725
|
const INJECTION_HEADER =
|
|
715
|
-
"
|
|
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.';
|
|
716
727
|
|
|
717
728
|
/**
|
|
718
729
|
* Render the inner content of the `<memory>` block for a list of slugs.
|
|
@@ -745,9 +756,9 @@ const INJECTION_HEADER =
|
|
|
745
756
|
* fallback for pages predating the summary field). Skills sit after the
|
|
746
757
|
* concept sections under `### Skills You Can Use`, and CLI subcommands sit
|
|
747
758
|
* after the skills under `### CLI Commands You Can Use`. The leading
|
|
748
|
-
*
|
|
759
|
+
* instruction line tells the agent how to read the block.
|
|
749
760
|
*
|
|
750
|
-
*
|
|
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.
|
|
751
762
|
*
|
|
752
763
|
* # memory/concepts/<concept-slug-1>.md
|
|
753
764
|
* <summary-1>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { getLogger } from "../../util/logger.js";
|
|
20
|
-
import { listPages, readPage } from "./page-store.js";
|
|
20
|
+
import { getPageMtimeMs, listPages, readPage } from "./page-store.js";
|
|
21
21
|
|
|
22
22
|
// Dynamic import for `./skill-store.js` happens inside `getPageIndex` so that
|
|
23
23
|
// modules that only need `invalidatePageIndex` (page-store.ts,
|
|
@@ -55,6 +55,12 @@ export interface PageIndexEntry {
|
|
|
55
55
|
summary: string;
|
|
56
56
|
/** Numeric IDs of outgoing edges, in sorted order. */
|
|
57
57
|
edges: number[];
|
|
58
|
+
/**
|
|
59
|
+
* File mtime in epoch ms; 0 for synthetic entries (skills, CLI commands)
|
|
60
|
+
* that have no on-disk source file. Used by `splitTier1` to rank pages
|
|
61
|
+
* by recency.
|
|
62
|
+
*/
|
|
63
|
+
modifiedAt: number;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
/**
|
|
@@ -94,18 +100,25 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
94
100
|
|
|
95
101
|
const slugs = await listPages(workspaceDir);
|
|
96
102
|
|
|
97
|
-
// Read pages in parallel
|
|
98
|
-
// so a single broken page never blocks the rest
|
|
103
|
+
// Read pages and stat their mtimes in parallel. Pages whose read rejects
|
|
104
|
+
// are dropped with a warn so a single broken page never blocks the rest
|
|
105
|
+
// of the index. mtime is stat'd alongside readPage so tier-1 sorting has
|
|
106
|
+
// recency without a second pass over the filesystem.
|
|
99
107
|
const settled = await Promise.allSettled(
|
|
100
|
-
slugs.map((slug) =>
|
|
108
|
+
slugs.map(async (slug) => {
|
|
109
|
+
const [page, mtimeMs] = await Promise.all([
|
|
110
|
+
readPage(workspaceDir, slug),
|
|
111
|
+
getPageMtimeMs(workspaceDir, slug),
|
|
112
|
+
]);
|
|
113
|
+
return { page, mtimeMs };
|
|
114
|
+
}),
|
|
101
115
|
);
|
|
102
116
|
|
|
103
|
-
// Intermediate shape used while we still need the raw outgoing slugs to
|
|
104
|
-
// resolve into numeric IDs after sorting.
|
|
105
117
|
interface DraftEntry {
|
|
106
118
|
slug: string;
|
|
107
119
|
summary: string;
|
|
108
120
|
outgoingSlugs: string[];
|
|
121
|
+
modifiedAt: number;
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
const [
|
|
@@ -143,7 +156,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
143
156
|
);
|
|
144
157
|
continue;
|
|
145
158
|
}
|
|
146
|
-
const page = result.value;
|
|
159
|
+
const { page, mtimeMs } = result.value;
|
|
147
160
|
if (!page) continue;
|
|
148
161
|
if (skillSlugs.has(slug)) {
|
|
149
162
|
log.warn(
|
|
@@ -164,6 +177,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
164
177
|
slug,
|
|
165
178
|
summary: normalizeSummary(summarySource),
|
|
166
179
|
outgoingSlugs: page.frontmatter.edges,
|
|
180
|
+
modifiedAt: mtimeMs,
|
|
167
181
|
});
|
|
168
182
|
}
|
|
169
183
|
|
|
@@ -172,6 +186,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
172
186
|
slug: `${SKILL_SLUG_PREFIX}${entry.id}`,
|
|
173
187
|
summary: normalizeSummary(entry.content),
|
|
174
188
|
outgoingSlugs: [],
|
|
189
|
+
modifiedAt: 0,
|
|
175
190
|
});
|
|
176
191
|
}
|
|
177
192
|
|
|
@@ -180,6 +195,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
180
195
|
slug: `${CLI_COMMAND_SLUG_PREFIX}${entry.id}`,
|
|
181
196
|
summary: normalizeSummary(entry.description),
|
|
182
197
|
outgoingSlugs: [],
|
|
198
|
+
modifiedAt: 0,
|
|
183
199
|
});
|
|
184
200
|
}
|
|
185
201
|
|
|
@@ -194,6 +210,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
194
210
|
slug: draft.slug,
|
|
195
211
|
summary: draft.summary,
|
|
196
212
|
edges: [],
|
|
213
|
+
modifiedAt: draft.modifiedAt,
|
|
197
214
|
};
|
|
198
215
|
bySlug.set(entry.slug, entry);
|
|
199
216
|
byId.set(entry.id, entry);
|
|
@@ -244,3 +261,188 @@ function renderIndex(entries: readonly PageIndexEntry[]): string {
|
|
|
244
261
|
});
|
|
245
262
|
return lines.length > 0 ? `${lines.join("\n")}\n` : "";
|
|
246
263
|
}
|
|
264
|
+
|
|
265
|
+
// FNV-1a 32-bit. Stable across runtimes — never change the constants or
|
|
266
|
+
// future releases will silently reshuffle batches and torch every batch's
|
|
267
|
+
// KV cache simultaneously.
|
|
268
|
+
function fnv1aHash(input: string): number {
|
|
269
|
+
let h = 0x811c9dc5;
|
|
270
|
+
for (let i = 0; i < input.length; i++) {
|
|
271
|
+
h ^= input.charCodeAt(i);
|
|
272
|
+
h = Math.imul(h, 0x01000193);
|
|
273
|
+
}
|
|
274
|
+
return h >>> 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Split a global `PageIndex` into batches of approximately `batchSize`
|
|
279
|
+
* entries for parallel routing. Each batch is a self-contained `PageIndex`
|
|
280
|
+
* with batch-local 1-based IDs and a re-rendered prompt block.
|
|
281
|
+
*
|
|
282
|
+
* `batchSize === null` or `entries.length <= batchSize` short-circuits to
|
|
283
|
+
* `[pageIndex]` (the same object) so single-batch callers send a request
|
|
284
|
+
* bit-identical to the pre-batching code path and reuse v3's KV cache
|
|
285
|
+
* untouched.
|
|
286
|
+
*
|
|
287
|
+
* Assignment uses FNV-1a on the slug: adding or removing one page only
|
|
288
|
+
* invalidates the KV cache of the one batch it lands in, instead of
|
|
289
|
+
* cascading through every batch the way index-modulo chunking would.
|
|
290
|
+
*
|
|
291
|
+
* Edges are re-resolved to batch-local IDs — edges pointing to pages in
|
|
292
|
+
* other batches drop silently (the model can't reference them anyway).
|
|
293
|
+
*/
|
|
294
|
+
export function partitionPageIndex(
|
|
295
|
+
pageIndex: PageIndex,
|
|
296
|
+
batchSize: number | null,
|
|
297
|
+
): PageIndex[] {
|
|
298
|
+
if (batchSize === null || pageIndex.entries.length <= batchSize) {
|
|
299
|
+
return [pageIndex];
|
|
300
|
+
}
|
|
301
|
+
const batchCount = Math.ceil(pageIndex.entries.length / batchSize);
|
|
302
|
+
const buckets: PageIndexEntry[][] = Array.from(
|
|
303
|
+
{ length: batchCount },
|
|
304
|
+
() => [],
|
|
305
|
+
);
|
|
306
|
+
for (const entry of pageIndex.entries) {
|
|
307
|
+
buckets[fnv1aHash(entry.slug) % batchCount].push(entry);
|
|
308
|
+
}
|
|
309
|
+
return buckets
|
|
310
|
+
.filter((b) => b.length > 0)
|
|
311
|
+
.map((entries) => buildLocalPageIndex(entries, pageIndex));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Build a self-contained `PageIndex` from a subset of another index's
|
|
316
|
+
* entries. Local entries get fresh 1-based IDs in input order, edges are
|
|
317
|
+
* remapped through the source index's `byId` to local IDs (cross-batch
|
|
318
|
+
* edges drop silently), and the prompt block is re-rendered.
|
|
319
|
+
*/
|
|
320
|
+
function buildLocalPageIndex(
|
|
321
|
+
entries: readonly PageIndexEntry[],
|
|
322
|
+
source: PageIndex,
|
|
323
|
+
): PageIndex {
|
|
324
|
+
const localBySlug = new Map<string, PageIndexEntry>();
|
|
325
|
+
const localById = new Map<number, PageIndexEntry>();
|
|
326
|
+
const localEntries: PageIndexEntry[] = entries.map((src, i) => {
|
|
327
|
+
const local: PageIndexEntry = {
|
|
328
|
+
id: i + 1,
|
|
329
|
+
slug: src.slug,
|
|
330
|
+
summary: src.summary,
|
|
331
|
+
edges: [],
|
|
332
|
+
modifiedAt: src.modifiedAt,
|
|
333
|
+
};
|
|
334
|
+
localBySlug.set(local.slug, local);
|
|
335
|
+
localById.set(local.id, local);
|
|
336
|
+
return local;
|
|
337
|
+
});
|
|
338
|
+
for (let i = 0; i < localEntries.length; i++) {
|
|
339
|
+
const localEdges: number[] = [];
|
|
340
|
+
for (const globalEdgeId of entries[i].edges) {
|
|
341
|
+
const target = source.byId.get(globalEdgeId);
|
|
342
|
+
if (!target) continue;
|
|
343
|
+
const localTarget = localBySlug.get(target.slug);
|
|
344
|
+
if (localTarget) localEdges.push(localTarget.id);
|
|
345
|
+
}
|
|
346
|
+
localEdges.sort((a, b) => a - b);
|
|
347
|
+
localEntries[i].edges = localEdges;
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
entries: localEntries,
|
|
351
|
+
bySlug: localBySlug,
|
|
352
|
+
byId: localById,
|
|
353
|
+
rendered: renderIndex(localEntries),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Carve the top-N most recently modified pages into their own batch (tier
|
|
359
|
+
* 1 in the v4 router architecture) and return the leftover as a second
|
|
360
|
+
* `PageIndex` for downstream partitioning.
|
|
361
|
+
*
|
|
362
|
+
* `tier1Size === null` is a no-op — `{ tier1: null, rest: pageIndex }`
|
|
363
|
+
* with the original index reference preserved so the single-batch path
|
|
364
|
+
* stays bit-identical to v3 and the KV cache survives.
|
|
365
|
+
*
|
|
366
|
+
* Tier 1 entries are sorted by `modifiedAt` descending; ties break by
|
|
367
|
+
* slug ASCII so the order is deterministic when several pages share a
|
|
368
|
+
* mtime (e.g. fresh workspaces). Synthetic entries (mtime=0) sort to the
|
|
369
|
+
* bottom and only enter tier 1 when there aren't enough real pages to
|
|
370
|
+
* fill the pool. The rest is sorted by slug ASCII so downstream
|
|
371
|
+
* hash-bucketing produces stable batches across mtime churn.
|
|
372
|
+
*/
|
|
373
|
+
export function splitTier1(
|
|
374
|
+
pageIndex: PageIndex,
|
|
375
|
+
tier1Size: number | null,
|
|
376
|
+
): { tier1: PageIndex | null; rest: PageIndex } {
|
|
377
|
+
if (tier1Size === null || pageIndex.entries.length === 0) {
|
|
378
|
+
return { tier1: null, rest: pageIndex };
|
|
379
|
+
}
|
|
380
|
+
const sortedByRecency = [...pageIndex.entries].sort((a, b) => {
|
|
381
|
+
if (a.modifiedAt !== b.modifiedAt) return b.modifiedAt - a.modifiedAt;
|
|
382
|
+
return a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0;
|
|
383
|
+
});
|
|
384
|
+
const tier1Entries = sortedByRecency.slice(0, tier1Size);
|
|
385
|
+
const tier1Slugs = new Set(tier1Entries.map((e) => e.slug));
|
|
386
|
+
const restEntries = pageIndex.entries.filter((e) => !tier1Slugs.has(e.slug));
|
|
387
|
+
|
|
388
|
+
const tier1 = buildLocalPageIndex(tier1Entries, pageIndex);
|
|
389
|
+
if (restEntries.length === 0) {
|
|
390
|
+
return { tier1, rest: emptyPageIndex() };
|
|
391
|
+
}
|
|
392
|
+
return { tier1, rest: buildLocalPageIndex(restEntries, pageIndex) };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Carve the top-M highest-EMA pages into their own batch (tier 2 in the
|
|
397
|
+
* v4 router architecture). Caller computes `scores` via
|
|
398
|
+
* `computeInjectionScores`; this function stays pure so unit tests don't
|
|
399
|
+
* need a database.
|
|
400
|
+
*
|
|
401
|
+
* `tier2Size === null` is a no-op. Pages with `score <= 0` (no events in
|
|
402
|
+
* the read window) are ineligible regardless of `tier2Size` — a stale
|
|
403
|
+
* page with zero score belongs in tier 3, not in the "useful" pool.
|
|
404
|
+
* Ordering is score desc, slug-ASCII tiebreak.
|
|
405
|
+
*
|
|
406
|
+
* Expected call shape: orchestrator passes the *post-tier-1* `PageIndex`,
|
|
407
|
+
* so we never re-promote a tier-1 page to tier 2.
|
|
408
|
+
*/
|
|
409
|
+
export function splitTier2(
|
|
410
|
+
pageIndex: PageIndex,
|
|
411
|
+
tier2Size: number | null,
|
|
412
|
+
scores: ReadonlyMap<string, number>,
|
|
413
|
+
): { tier2: PageIndex | null; rest: PageIndex } {
|
|
414
|
+
if (tier2Size === null || pageIndex.entries.length === 0) {
|
|
415
|
+
return { tier2: null, rest: pageIndex };
|
|
416
|
+
}
|
|
417
|
+
const eligible = pageIndex.entries
|
|
418
|
+
.map((entry) => ({ entry, score: scores.get(entry.slug) ?? 0 }))
|
|
419
|
+
.filter((x) => x.score > 0)
|
|
420
|
+
.sort((a, b) => {
|
|
421
|
+
if (a.score !== b.score) return b.score - a.score;
|
|
422
|
+
return a.entry.slug < b.entry.slug
|
|
423
|
+
? -1
|
|
424
|
+
: a.entry.slug > b.entry.slug
|
|
425
|
+
? 1
|
|
426
|
+
: 0;
|
|
427
|
+
});
|
|
428
|
+
const tier2Entries = eligible.slice(0, tier2Size).map((x) => x.entry);
|
|
429
|
+
if (tier2Entries.length === 0) {
|
|
430
|
+
return { tier2: null, rest: pageIndex };
|
|
431
|
+
}
|
|
432
|
+
const tier2Slugs = new Set(tier2Entries.map((e) => e.slug));
|
|
433
|
+
const restEntries = pageIndex.entries.filter((e) => !tier2Slugs.has(e.slug));
|
|
434
|
+
const tier2 = buildLocalPageIndex(tier2Entries, pageIndex);
|
|
435
|
+
if (restEntries.length === 0) {
|
|
436
|
+
return { tier2, rest: emptyPageIndex() };
|
|
437
|
+
}
|
|
438
|
+
return { tier2, rest: buildLocalPageIndex(restEntries, pageIndex) };
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function emptyPageIndex(): PageIndex {
|
|
442
|
+
return {
|
|
443
|
+
entries: [],
|
|
444
|
+
bySlug: new Map(),
|
|
445
|
+
byId: new Map(),
|
|
446
|
+
rendered: "",
|
|
447
|
+
};
|
|
448
|
+
}
|
|
@@ -260,6 +260,24 @@ export async function readPage(
|
|
|
260
260
|
return { slug, frontmatter, body };
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
/**
|
|
264
|
+
* File mtime for a concept page, in epoch ms. Returns 0 when the file is
|
|
265
|
+
* missing or unreadable — callers treat 0 as "no mtime" so tier-1 sorting
|
|
266
|
+
* can rank synthetic entries (skills, CLI commands) below real pages.
|
|
267
|
+
*/
|
|
268
|
+
export async function getPageMtimeMs(
|
|
269
|
+
workspaceDir: string,
|
|
270
|
+
slug: string,
|
|
271
|
+
): Promise<number> {
|
|
272
|
+
validateSlug(slug);
|
|
273
|
+
try {
|
|
274
|
+
const s = await stat(getPagePath(workspaceDir, slug));
|
|
275
|
+
return s.mtimeMs;
|
|
276
|
+
} catch {
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
263
281
|
/**
|
|
264
282
|
* Write a concept page atomically (temp file + rename). A crash between the
|
|
265
283
|
* temp write and the rename leaves the prior file intact; a crash after the
|