@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
|
@@ -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
|
/**
|
|
@@ -80,11 +86,12 @@ let cache: CachedIndex | null = null;
|
|
|
80
86
|
/**
|
|
81
87
|
* Return a `PageIndex` for `workspaceDir`. Cached module-locally; the cache
|
|
82
88
|
* is invalidated by `invalidatePageIndex` (called by daemon-side hooks when
|
|
83
|
-
* concept pages or
|
|
89
|
+
* concept pages, skill entries, or CLI-command entries change).
|
|
84
90
|
*
|
|
85
91
|
* Cold builds list every concept page in parallel, drop pages whose read
|
|
86
|
-
* rejects, append seeded skill entries from `listSkillEntries()
|
|
87
|
-
*
|
|
92
|
+
* rejects, append seeded skill entries from `listSkillEntries()` and CLI
|
|
93
|
+
* command entries from `listCliCommandEntries()`, sort by slug for
|
|
94
|
+
* deterministic IDs, then resolve outgoing edges to numeric IDs.
|
|
88
95
|
*/
|
|
89
96
|
export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
90
97
|
if (cache && cache.workspaceDir === workspaceDir) {
|
|
@@ -93,34 +100,50 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
93
100
|
|
|
94
101
|
const slugs = await listPages(workspaceDir);
|
|
95
102
|
|
|
96
|
-
// Read pages in parallel
|
|
97
|
-
// 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.
|
|
98
107
|
const settled = await Promise.allSettled(
|
|
99
|
-
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
|
+
}),
|
|
100
115
|
);
|
|
101
116
|
|
|
102
|
-
// Intermediate shape used while we still need the raw outgoing slugs to
|
|
103
|
-
// resolve into numeric IDs after sorting.
|
|
104
117
|
interface DraftEntry {
|
|
105
118
|
slug: string;
|
|
106
119
|
summary: string;
|
|
107
120
|
outgoingSlugs: string[];
|
|
121
|
+
modifiedAt: number;
|
|
108
122
|
}
|
|
109
123
|
|
|
110
|
-
const
|
|
111
|
-
|
|
124
|
+
const [
|
|
125
|
+
{ listSkillEntries, SKILL_SLUG_PREFIX },
|
|
126
|
+
{ listCliCommandEntries, CLI_COMMAND_SLUG_PREFIX },
|
|
127
|
+
] = await Promise.all([
|
|
128
|
+
import("./skill-store.js"),
|
|
129
|
+
import("./cli-command-store.js"),
|
|
130
|
+
]);
|
|
112
131
|
|
|
113
|
-
// Build the
|
|
114
|
-
// Collision policy: **
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
132
|
+
// Build the synthetic-slug sets first so we can drop colliding concept
|
|
133
|
+
// pages. Collision policy: **synthetic entries win**. Skill and CLI rows
|
|
134
|
+
// are seeded from authoritative in-process catalogs; a hand-authored page
|
|
135
|
+
// sitting under `skills/<id>` or `cli-commands/<name>` is either a stale
|
|
136
|
+
// leftover from a prior write or a user mistake. `bySlug` is last-writer-
|
|
137
|
+
// wins, so without explicit dedupe one side would silently shadow the
|
|
138
|
+
// other depending on iteration order.
|
|
120
139
|
const skillEntries = listSkillEntries();
|
|
121
140
|
const skillSlugs = new Set(
|
|
122
141
|
skillEntries.map((entry) => `${SKILL_SLUG_PREFIX}${entry.id}`),
|
|
123
142
|
);
|
|
143
|
+
const cliCommandEntries = listCliCommandEntries();
|
|
144
|
+
const cliCommandSlugs = new Set(
|
|
145
|
+
cliCommandEntries.map((entry) => `${CLI_COMMAND_SLUG_PREFIX}${entry.id}`),
|
|
146
|
+
);
|
|
124
147
|
|
|
125
148
|
const drafts: DraftEntry[] = [];
|
|
126
149
|
for (let i = 0; i < settled.length; i++) {
|
|
@@ -133,7 +156,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
133
156
|
);
|
|
134
157
|
continue;
|
|
135
158
|
}
|
|
136
|
-
const page = result.value;
|
|
159
|
+
const { page, mtimeMs } = result.value;
|
|
137
160
|
if (!page) continue;
|
|
138
161
|
if (skillSlugs.has(slug)) {
|
|
139
162
|
log.warn(
|
|
@@ -142,11 +165,19 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
142
165
|
);
|
|
143
166
|
continue;
|
|
144
167
|
}
|
|
168
|
+
if (cliCommandSlugs.has(slug)) {
|
|
169
|
+
log.warn(
|
|
170
|
+
{ slug },
|
|
171
|
+
"Dropping concept page from index — slug collides with a seeded CLI-command entry; CLI command wins",
|
|
172
|
+
);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
145
175
|
const summarySource = page.frontmatter.summary?.trim() || page.body.trim();
|
|
146
176
|
drafts.push({
|
|
147
177
|
slug,
|
|
148
178
|
summary: normalizeSummary(summarySource),
|
|
149
179
|
outgoingSlugs: page.frontmatter.edges,
|
|
180
|
+
modifiedAt: mtimeMs,
|
|
150
181
|
});
|
|
151
182
|
}
|
|
152
183
|
|
|
@@ -155,6 +186,16 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
155
186
|
slug: `${SKILL_SLUG_PREFIX}${entry.id}`,
|
|
156
187
|
summary: normalizeSummary(entry.content),
|
|
157
188
|
outgoingSlugs: [],
|
|
189
|
+
modifiedAt: 0,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const entry of cliCommandEntries) {
|
|
194
|
+
drafts.push({
|
|
195
|
+
slug: `${CLI_COMMAND_SLUG_PREFIX}${entry.id}`,
|
|
196
|
+
summary: normalizeSummary(entry.description),
|
|
197
|
+
outgoingSlugs: [],
|
|
198
|
+
modifiedAt: 0,
|
|
158
199
|
});
|
|
159
200
|
}
|
|
160
201
|
|
|
@@ -169,6 +210,7 @@ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
|
|
|
169
210
|
slug: draft.slug,
|
|
170
211
|
summary: draft.summary,
|
|
171
212
|
edges: [],
|
|
213
|
+
modifiedAt: draft.modifiedAt,
|
|
172
214
|
};
|
|
173
215
|
bySlug.set(entry.slug, entry);
|
|
174
216
|
byId.set(entry.id, entry);
|
|
@@ -219,3 +261,188 @@ function renderIndex(entries: readonly PageIndexEntry[]): string {
|
|
|
219
261
|
});
|
|
220
262
|
return lines.length > 0 ? `${lines.join("\n")}\n` : "";
|
|
221
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
|