@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -9,6 +9,13 @@ import type {
|
|
|
9
9
|
import { APP_VERSION } from "../version.js";
|
|
10
10
|
import { getDb } from "./db-connection.js";
|
|
11
11
|
import { rawAll } from "./raw-query.js";
|
|
12
|
+
import {
|
|
13
|
+
buildScheduleAttributionSubquery,
|
|
14
|
+
buildScheduleRunWindowExists,
|
|
15
|
+
normalizeScheduleAttributionFilter,
|
|
16
|
+
type ScheduleAttributionFilter,
|
|
17
|
+
type ScheduleAttributionSqlParam,
|
|
18
|
+
} from "./schedule-attribution-sql.js";
|
|
12
19
|
import { conversations, llmUsageEvents } from "./schema.js";
|
|
13
20
|
import {
|
|
14
21
|
bucketEventsByDay,
|
|
@@ -282,6 +289,8 @@ export interface UsageTimeRange {
|
|
|
282
289
|
to: number;
|
|
283
290
|
}
|
|
284
291
|
|
|
292
|
+
export type UsageAggregationFilter = ScheduleAttributionFilter;
|
|
293
|
+
|
|
285
294
|
/** Aggregate totals across a time range. */
|
|
286
295
|
export interface UsageTotals {
|
|
287
296
|
/** Direct input tokens only; cache traffic is reported separately below. */
|
|
@@ -332,14 +341,15 @@ export interface UsageGroupBreakdown {
|
|
|
332
341
|
/**
|
|
333
342
|
* Stable identifier for the group. Populated with the conversation id when
|
|
334
343
|
* `groupBy === "conversation"` (and `null` for that mode's "Other" bucket,
|
|
335
|
-
* which aggregates events with no conversation id)
|
|
336
|
-
*
|
|
344
|
+
* which aggregates events with no conversation id) or with the schedule id
|
|
345
|
+
* when `groupBy === "schedule"` (and `null` for "Other"). For all other
|
|
346
|
+
* group-bys this is always `null`.
|
|
337
347
|
*/
|
|
338
348
|
groupId: string | null;
|
|
339
349
|
/**
|
|
340
350
|
* Raw stored grouping value for dimensions whose display label may differ
|
|
341
|
-
* from storage (`call_site`, `inference_profile`). Omitted for
|
|
342
|
-
* dimensions where `group` is already the raw value.
|
|
351
|
+
* from storage (`call_site`, `inference_profile`, `schedule`). Omitted for
|
|
352
|
+
* legacy dimensions where `group` is already the raw value.
|
|
343
353
|
*/
|
|
344
354
|
groupKey?: string | null;
|
|
345
355
|
/** Direct input tokens only; cache traffic is reported separately below. */
|
|
@@ -367,6 +377,7 @@ interface TotalsRow {
|
|
|
367
377
|
interface GroupRow {
|
|
368
378
|
group_key: string | null;
|
|
369
379
|
group_id: string | null;
|
|
380
|
+
group_label: string | null;
|
|
370
381
|
total_input_tokens: number;
|
|
371
382
|
total_output_tokens: number;
|
|
372
383
|
total_cache_creation_tokens: number;
|
|
@@ -375,6 +386,39 @@ interface GroupRow {
|
|
|
375
386
|
event_count: number;
|
|
376
387
|
}
|
|
377
388
|
|
|
389
|
+
type UsageQueryParam = ScheduleAttributionSqlParam;
|
|
390
|
+
|
|
391
|
+
function normalizeUsageAggregationFilter(
|
|
392
|
+
filter?: UsageAggregationFilter,
|
|
393
|
+
): UsageAggregationFilter {
|
|
394
|
+
return normalizeScheduleAttributionFilter(filter);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function buildUsageAggregationWhere(
|
|
398
|
+
range: UsageTimeRange,
|
|
399
|
+
filter?: UsageAggregationFilter,
|
|
400
|
+
eventAlias?: string,
|
|
401
|
+
now: number = Date.now(),
|
|
402
|
+
): { sql: string; params: UsageQueryParam[] } {
|
|
403
|
+
const normalized = normalizeUsageAggregationFilter(filter);
|
|
404
|
+
const eventTable = eventAlias ?? "llm_usage_events";
|
|
405
|
+
const createdAt = `${eventTable}.created_at`;
|
|
406
|
+
const clauses = [`${createdAt} >= ? AND ${createdAt} <= ?`];
|
|
407
|
+
const params: UsageQueryParam[] = [range.from, range.to];
|
|
408
|
+
|
|
409
|
+
if (normalized.scheduleId) {
|
|
410
|
+
const exists = buildScheduleRunWindowExists({
|
|
411
|
+
eventAlias: eventTable,
|
|
412
|
+
filter: normalized,
|
|
413
|
+
now,
|
|
414
|
+
});
|
|
415
|
+
clauses.push(exists.sql);
|
|
416
|
+
params.push(...exists.params);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return { sql: clauses.join(" AND "), params };
|
|
420
|
+
}
|
|
421
|
+
|
|
378
422
|
/**
|
|
379
423
|
* Return aggregate usage for a single conversation (e.g. a subagent).
|
|
380
424
|
*/
|
|
@@ -406,10 +450,38 @@ export function getConversationUsageTotals(conversationId: string): {
|
|
|
406
450
|
};
|
|
407
451
|
}
|
|
408
452
|
|
|
453
|
+
export function getUsageCostForConversationWindow({
|
|
454
|
+
conversationId,
|
|
455
|
+
from,
|
|
456
|
+
to,
|
|
457
|
+
}: {
|
|
458
|
+
conversationId: string;
|
|
459
|
+
from: number;
|
|
460
|
+
to: number;
|
|
461
|
+
}): number {
|
|
462
|
+
const rows = rawAll<{ total_cost: number | null }>(
|
|
463
|
+
/*sql*/ `
|
|
464
|
+
SELECT COALESCE(SUM(estimated_cost_usd), 0) AS total_cost
|
|
465
|
+
FROM llm_usage_events
|
|
466
|
+
WHERE conversation_id = ?1
|
|
467
|
+
AND created_at >= ?2
|
|
468
|
+
AND created_at <= ?3
|
|
469
|
+
`,
|
|
470
|
+
conversationId,
|
|
471
|
+
from,
|
|
472
|
+
to,
|
|
473
|
+
);
|
|
474
|
+
return rows[0]?.total_cost ?? 0;
|
|
475
|
+
}
|
|
476
|
+
|
|
409
477
|
/**
|
|
410
478
|
* Return aggregate totals for all usage events within the given time range.
|
|
411
479
|
*/
|
|
412
|
-
export function getUsageTotals(
|
|
480
|
+
export function getUsageTotals(
|
|
481
|
+
range: UsageTimeRange,
|
|
482
|
+
filter?: UsageAggregationFilter,
|
|
483
|
+
): UsageTotals {
|
|
484
|
+
const where = buildUsageAggregationWhere(range, filter);
|
|
413
485
|
const rows = rawAll<TotalsRow>(
|
|
414
486
|
/*sql*/ `
|
|
415
487
|
SELECT
|
|
@@ -422,10 +494,9 @@ export function getUsageTotals(range: UsageTimeRange): UsageTotals {
|
|
|
422
494
|
COUNT(CASE WHEN pricing_status = 'priced' THEN 1 END) AS priced_event_count,
|
|
423
495
|
COUNT(CASE WHEN pricing_status = 'unpriced' THEN 1 END) AS unpriced_event_count
|
|
424
496
|
FROM llm_usage_events
|
|
425
|
-
WHERE
|
|
497
|
+
WHERE ${where.sql}
|
|
426
498
|
`,
|
|
427
|
-
|
|
428
|
-
range.to,
|
|
499
|
+
...where.params,
|
|
429
500
|
);
|
|
430
501
|
const row = rows[0];
|
|
431
502
|
return {
|
|
@@ -441,7 +512,11 @@ export function getUsageTotals(range: UsageTimeRange): UsageTotals {
|
|
|
441
512
|
}
|
|
442
513
|
|
|
443
514
|
/** Fetch raw events in a time range for in-memory bucketing. */
|
|
444
|
-
function fetchRawBucketRows(
|
|
515
|
+
function fetchRawBucketRows(
|
|
516
|
+
range: UsageTimeRange,
|
|
517
|
+
filter?: UsageAggregationFilter,
|
|
518
|
+
): UsageEventBucketRow[] {
|
|
519
|
+
const where = buildUsageAggregationWhere(range, filter);
|
|
445
520
|
return rawAll<UsageEventBucketRow>(
|
|
446
521
|
/*sql*/ `
|
|
447
522
|
SELECT
|
|
@@ -451,11 +526,10 @@ function fetchRawBucketRows(range: UsageTimeRange): UsageEventBucketRow[] {
|
|
|
451
526
|
estimated_cost_usd,
|
|
452
527
|
llm_call_count
|
|
453
528
|
FROM llm_usage_events
|
|
454
|
-
WHERE
|
|
529
|
+
WHERE ${where.sql}
|
|
455
530
|
ORDER BY created_at ASC
|
|
456
531
|
`,
|
|
457
|
-
|
|
458
|
-
range.to,
|
|
532
|
+
...where.params,
|
|
459
533
|
);
|
|
460
534
|
}
|
|
461
535
|
|
|
@@ -482,8 +556,9 @@ export function getUsageDayBuckets(
|
|
|
482
556
|
range: UsageTimeRange,
|
|
483
557
|
tz: string = "UTC",
|
|
484
558
|
options: UsageBucketOptions = {},
|
|
559
|
+
filter?: UsageAggregationFilter,
|
|
485
560
|
): UsageDayBucket[] {
|
|
486
|
-
const rows = fetchRawBucketRows(range);
|
|
561
|
+
const rows = fetchRawBucketRows(range, filter);
|
|
487
562
|
return bucketEventsByDay(rows, range, tz, options);
|
|
488
563
|
}
|
|
489
564
|
|
|
@@ -500,8 +575,9 @@ export function getUsageHourBuckets(
|
|
|
500
575
|
range: UsageTimeRange,
|
|
501
576
|
tz: string = "UTC",
|
|
502
577
|
options: UsageBucketOptions = {},
|
|
578
|
+
filter?: UsageAggregationFilter,
|
|
503
579
|
): UsageDayBucket[] {
|
|
504
|
-
const rows = fetchRawBucketRows(range);
|
|
580
|
+
const rows = fetchRawBucketRows(range, filter);
|
|
505
581
|
return bucketEventsByHour(rows, range, tz, options);
|
|
506
582
|
}
|
|
507
583
|
|
|
@@ -512,6 +588,7 @@ export const USAGE_GROUP_BY_DIMENSIONS = [
|
|
|
512
588
|
"conversation",
|
|
513
589
|
"call_site",
|
|
514
590
|
"inference_profile",
|
|
591
|
+
"schedule",
|
|
515
592
|
] as const;
|
|
516
593
|
|
|
517
594
|
export type GroupByDimension = (typeof USAGE_GROUP_BY_DIMENSIONS)[number];
|
|
@@ -522,10 +599,11 @@ export const USAGE_SERIES_GROUP_BY_DIMENSIONS = [
|
|
|
522
599
|
"model",
|
|
523
600
|
"call_site",
|
|
524
601
|
"inference_profile",
|
|
602
|
+
"schedule",
|
|
525
603
|
] as const satisfies readonly GroupByDimension[];
|
|
526
604
|
|
|
527
605
|
const GROUP_BY_COLUMNS: Record<
|
|
528
|
-
Exclude<GroupByDimension, "conversation">,
|
|
606
|
+
Exclude<GroupByDimension, "conversation" | "schedule">,
|
|
529
607
|
string
|
|
530
608
|
> = {
|
|
531
609
|
actor: "actor",
|
|
@@ -550,9 +628,11 @@ function mapGroupRow(
|
|
|
550
628
|
groupBy: GroupByDimension,
|
|
551
629
|
): UsageGroupBreakdown {
|
|
552
630
|
const includeGroupKey =
|
|
553
|
-
groupBy === "call_site" ||
|
|
631
|
+
groupBy === "call_site" ||
|
|
632
|
+
groupBy === "inference_profile" ||
|
|
633
|
+
groupBy === "schedule";
|
|
554
634
|
return {
|
|
555
|
-
group: displayUsageGroup(groupBy, row.group_key),
|
|
635
|
+
group: row.group_label ?? displayUsageGroup(groupBy, row.group_key),
|
|
556
636
|
groupId: row.group_id,
|
|
557
637
|
...(includeGroupKey ? { groupKey: row.group_key } : {}),
|
|
558
638
|
totalInputTokens: row.total_input_tokens,
|
|
@@ -571,12 +651,16 @@ function mapGroupRow(
|
|
|
571
651
|
export function getUsageGroupBreakdown(
|
|
572
652
|
range: UsageTimeRange,
|
|
573
653
|
groupBy: GroupByDimension,
|
|
654
|
+
filter?: UsageAggregationFilter,
|
|
574
655
|
): UsageGroupBreakdown[] {
|
|
575
656
|
// Runtime allowlist — defense-in-depth against SQL injection via type assertions.
|
|
576
657
|
assertGroupByDimension(groupBy);
|
|
577
658
|
|
|
659
|
+
const normalizedFilter = normalizeUsageAggregationFilter(filter);
|
|
660
|
+
|
|
578
661
|
// Conversation grouping requires a JOIN with conversations to resolve titles.
|
|
579
662
|
if (groupBy === "conversation") {
|
|
663
|
+
const where = buildUsageAggregationWhere(range, normalizedFilter, "e");
|
|
580
664
|
const rows = rawAll<GroupRow>(
|
|
581
665
|
/*sql*/ `
|
|
582
666
|
SELECT
|
|
@@ -584,6 +668,7 @@ export function getUsageGroupBreakdown(
|
|
|
584
668
|
ELSE COALESCE(c.title, 'Untitled')
|
|
585
669
|
END AS group_key,
|
|
586
670
|
e.conversation_id AS group_id,
|
|
671
|
+
NULL AS group_label,
|
|
587
672
|
COALESCE(SUM(e.input_tokens), 0) AS total_input_tokens,
|
|
588
673
|
COALESCE(SUM(e.output_tokens), 0) AS total_output_tokens,
|
|
589
674
|
COALESCE(SUM(e.cache_creation_input_tokens), 0) AS total_cache_creation_tokens,
|
|
@@ -592,36 +677,81 @@ export function getUsageGroupBreakdown(
|
|
|
592
677
|
COALESCE(SUM(COALESCE(e.llm_call_count, 1)), 0) AS event_count
|
|
593
678
|
FROM llm_usage_events e
|
|
594
679
|
LEFT JOIN conversations c ON e.conversation_id = c.id
|
|
595
|
-
WHERE
|
|
680
|
+
WHERE ${where.sql}
|
|
596
681
|
GROUP BY e.conversation_id
|
|
597
682
|
ORDER BY total_estimated_cost_usd DESC
|
|
598
683
|
LIMIT 50
|
|
599
684
|
`,
|
|
600
|
-
|
|
601
|
-
|
|
685
|
+
...where.params,
|
|
686
|
+
);
|
|
687
|
+
return rows.map((row) => mapGroupRow(row, groupBy));
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (groupBy === "schedule") {
|
|
691
|
+
const now = Date.now();
|
|
692
|
+
const where = buildUsageAggregationWhere(range, normalizedFilter, "e", now);
|
|
693
|
+
const groupKeySubquery = buildScheduleAttributionSubquery({
|
|
694
|
+
eventAlias: "e",
|
|
695
|
+
filter: normalizedFilter,
|
|
696
|
+
now,
|
|
697
|
+
selectExpression: "schedule_attr_runs.job_id",
|
|
698
|
+
});
|
|
699
|
+
const rows = rawAll<GroupRow>(
|
|
700
|
+
/*sql*/ `
|
|
701
|
+
WITH schedule_usage AS (
|
|
702
|
+
SELECT
|
|
703
|
+
e.input_tokens,
|
|
704
|
+
e.output_tokens,
|
|
705
|
+
e.cache_creation_input_tokens,
|
|
706
|
+
e.cache_read_input_tokens,
|
|
707
|
+
e.estimated_cost_usd,
|
|
708
|
+
e.llm_call_count,
|
|
709
|
+
${groupKeySubquery.sql} AS group_key
|
|
710
|
+
FROM llm_usage_events e
|
|
711
|
+
WHERE ${where.sql}
|
|
712
|
+
)
|
|
713
|
+
SELECT
|
|
714
|
+
schedule_usage.group_key AS group_key,
|
|
715
|
+
schedule_usage.group_key AS group_id,
|
|
716
|
+
MAX(schedule_group_jobs.name) AS group_label,
|
|
717
|
+
COALESCE(SUM(schedule_usage.input_tokens), 0) AS total_input_tokens,
|
|
718
|
+
COALESCE(SUM(schedule_usage.output_tokens), 0) AS total_output_tokens,
|
|
719
|
+
COALESCE(SUM(schedule_usage.cache_creation_input_tokens), 0) AS total_cache_creation_tokens,
|
|
720
|
+
COALESCE(SUM(schedule_usage.cache_read_input_tokens), 0) AS total_cache_read_tokens,
|
|
721
|
+
COALESCE(SUM(schedule_usage.estimated_cost_usd), 0) AS total_estimated_cost_usd,
|
|
722
|
+
COALESCE(SUM(COALESCE(schedule_usage.llm_call_count, 1)), 0) AS event_count
|
|
723
|
+
FROM schedule_usage
|
|
724
|
+
LEFT JOIN cron_jobs schedule_group_jobs
|
|
725
|
+
ON schedule_group_jobs.id = schedule_usage.group_key
|
|
726
|
+
GROUP BY schedule_usage.group_key
|
|
727
|
+
ORDER BY total_estimated_cost_usd DESC
|
|
728
|
+
`,
|
|
729
|
+
...groupKeySubquery.params,
|
|
730
|
+
...where.params,
|
|
602
731
|
);
|
|
603
732
|
return rows.map((row) => mapGroupRow(row, groupBy));
|
|
604
733
|
}
|
|
605
734
|
|
|
606
735
|
const column = GROUP_BY_COLUMNS[groupBy];
|
|
736
|
+
const where = buildUsageAggregationWhere(range, normalizedFilter, "e");
|
|
607
737
|
const rows = rawAll<GroupRow>(
|
|
608
738
|
/*sql*/ `
|
|
609
739
|
SELECT
|
|
610
|
-
|
|
611
|
-
NULL
|
|
612
|
-
|
|
613
|
-
COALESCE(SUM(
|
|
614
|
-
COALESCE(SUM(
|
|
615
|
-
COALESCE(SUM(
|
|
616
|
-
COALESCE(SUM(
|
|
617
|
-
COALESCE(SUM(
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
740
|
+
e.${column} AS group_key,
|
|
741
|
+
NULL AS group_id,
|
|
742
|
+
NULL AS group_label,
|
|
743
|
+
COALESCE(SUM(e.input_tokens), 0) AS total_input_tokens,
|
|
744
|
+
COALESCE(SUM(e.output_tokens), 0) AS total_output_tokens,
|
|
745
|
+
COALESCE(SUM(e.cache_creation_input_tokens), 0) AS total_cache_creation_tokens,
|
|
746
|
+
COALESCE(SUM(e.cache_read_input_tokens), 0) AS total_cache_read_tokens,
|
|
747
|
+
COALESCE(SUM(e.estimated_cost_usd), 0) AS total_estimated_cost_usd,
|
|
748
|
+
COALESCE(SUM(COALESCE(e.llm_call_count, 1)), 0) AS event_count
|
|
749
|
+
FROM llm_usage_events e
|
|
750
|
+
WHERE ${where.sql}
|
|
751
|
+
GROUP BY e.${column}
|
|
621
752
|
ORDER BY total_estimated_cost_usd DESC
|
|
622
753
|
`,
|
|
623
|
-
|
|
624
|
-
range.to,
|
|
754
|
+
...where.params,
|
|
625
755
|
);
|
|
626
756
|
return rows.map((row) => mapGroupRow(row, groupBy));
|
|
627
757
|
}
|
|
@@ -632,29 +762,73 @@ export function getUsageGroupedSeries(
|
|
|
632
762
|
granularity: UsageGranularity,
|
|
633
763
|
tz: string = "UTC",
|
|
634
764
|
options: UsageBucketOptions = {},
|
|
765
|
+
filter?: UsageAggregationFilter,
|
|
635
766
|
): UsageGroupedSeriesBucket[] {
|
|
636
767
|
assertGroupByDimension(groupBy);
|
|
637
768
|
if (groupBy === "conversation") {
|
|
638
769
|
throw new Error("Grouped usage series does not support conversation");
|
|
639
770
|
}
|
|
640
771
|
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
772
|
+
const normalizedFilter = normalizeUsageAggregationFilter(filter);
|
|
773
|
+
let rows: UsageGroupedBucketRow[];
|
|
774
|
+
|
|
775
|
+
if (groupBy === "schedule") {
|
|
776
|
+
const now = Date.now();
|
|
777
|
+
const where = buildUsageAggregationWhere(range, normalizedFilter, "e", now);
|
|
778
|
+
const groupKeySubquery = buildScheduleAttributionSubquery({
|
|
779
|
+
eventAlias: "e",
|
|
780
|
+
filter: normalizedFilter,
|
|
781
|
+
now,
|
|
782
|
+
selectExpression: "schedule_attr_runs.job_id",
|
|
783
|
+
});
|
|
784
|
+
rows = rawAll<UsageGroupedBucketRow>(
|
|
785
|
+
/*sql*/ `
|
|
786
|
+
WITH schedule_usage AS (
|
|
787
|
+
SELECT
|
|
788
|
+
e.created_at,
|
|
789
|
+
e.input_tokens,
|
|
790
|
+
e.output_tokens,
|
|
791
|
+
e.estimated_cost_usd,
|
|
792
|
+
e.llm_call_count,
|
|
793
|
+
${groupKeySubquery.sql} AS group_key
|
|
794
|
+
FROM llm_usage_events e
|
|
795
|
+
WHERE ${where.sql}
|
|
796
|
+
)
|
|
797
|
+
SELECT
|
|
798
|
+
schedule_usage.created_at,
|
|
799
|
+
schedule_usage.input_tokens,
|
|
800
|
+
schedule_usage.output_tokens,
|
|
801
|
+
schedule_usage.estimated_cost_usd,
|
|
802
|
+
schedule_usage.llm_call_count,
|
|
803
|
+
schedule_usage.group_key,
|
|
804
|
+
schedule_group_jobs.name AS group_label
|
|
805
|
+
FROM schedule_usage
|
|
806
|
+
LEFT JOIN cron_jobs schedule_group_jobs
|
|
807
|
+
ON schedule_group_jobs.id = schedule_usage.group_key
|
|
808
|
+
ORDER BY schedule_usage.created_at ASC
|
|
809
|
+
`,
|
|
810
|
+
...groupKeySubquery.params,
|
|
811
|
+
...where.params,
|
|
812
|
+
);
|
|
813
|
+
} else {
|
|
814
|
+
const column = GROUP_BY_COLUMNS[groupBy];
|
|
815
|
+
const where = buildUsageAggregationWhere(range, normalizedFilter, "e");
|
|
816
|
+
rows = rawAll<UsageGroupedBucketRow>(
|
|
817
|
+
/*sql*/ `
|
|
818
|
+
SELECT
|
|
819
|
+
e.created_at,
|
|
820
|
+
e.input_tokens,
|
|
821
|
+
e.output_tokens,
|
|
822
|
+
e.estimated_cost_usd,
|
|
823
|
+
e.llm_call_count,
|
|
824
|
+
e.${column} AS group_key
|
|
825
|
+
FROM llm_usage_events e
|
|
826
|
+
WHERE ${where.sql}
|
|
827
|
+
ORDER BY e.created_at ASC
|
|
828
|
+
`,
|
|
829
|
+
...where.params,
|
|
830
|
+
);
|
|
831
|
+
}
|
|
658
832
|
|
|
659
833
|
return bucketGroupedUsageEvents(rows, range, tz, {
|
|
660
834
|
...options,
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { isPlaceholderSentinelText } from "../../providers/
|
|
1
|
+
import { isPlaceholderSentinelText } from "../../providers/placeholder-sentinels.js";
|
|
2
2
|
import type { DrizzleDb } from "../db-connection.js";
|
|
3
3
|
import { getSqliteFrom } from "../db-connection.js";
|
|
4
4
|
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Strip
|
|
8
|
-
*
|
|
7
|
+
* Strip provider placeholder sentinel text blocks from persisted assistant
|
|
8
|
+
* messages.
|
|
9
9
|
*
|
|
10
10
|
* PLACEHOLDER_EMPTY_TURN and PLACEHOLDER_BLOCKS_OMITTED are injected into
|
|
11
|
-
* outbound
|
|
12
|
-
*
|
|
11
|
+
* outbound provider request bodies (Anthropic role alternation, OpenAI-
|
|
12
|
+
* compatible "content or tool_calls" constraint) when an assistant turn would
|
|
13
|
+
* otherwise be empty. They are never supposed to be
|
|
13
14
|
* persisted, but a leak path caused them to be stored in the messages table
|
|
14
15
|
* where they render in chat bubbles as bold "PLACEHOLDER[...]" (markdown
|
|
15
16
|
* interprets the double-underscore surround as bold).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
export function migrateScheduleSourceConversation(database: DrizzleDb): void {
|
|
5
|
+
const raw = getSqliteFrom(database);
|
|
6
|
+
try {
|
|
7
|
+
raw.exec(
|
|
8
|
+
`ALTER TABLE cron_jobs ADD COLUMN created_from_conversation_id TEXT`,
|
|
9
|
+
);
|
|
10
|
+
} catch {
|
|
11
|
+
// Column already exists — nothing to do.
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create the auth_fallback_events table for tracking legacy-loopback
|
|
5
|
+
* auth-fallback counts forwarded by the gateway. One row per
|
|
6
|
+
* (guard, path, failure_kind) per flush window.
|
|
7
|
+
*/
|
|
8
|
+
export function createAuthFallbackEventsTable(database: DrizzleDb): void {
|
|
9
|
+
database.run(/*sql*/ `
|
|
10
|
+
CREATE TABLE IF NOT EXISTS auth_fallback_events (
|
|
11
|
+
id TEXT PRIMARY KEY,
|
|
12
|
+
created_at INTEGER NOT NULL,
|
|
13
|
+
guard TEXT NOT NULL,
|
|
14
|
+
path TEXT NOT NULL,
|
|
15
|
+
failure_kind TEXT NOT NULL,
|
|
16
|
+
count INTEGER NOT NULL,
|
|
17
|
+
window_start INTEGER NOT NULL,
|
|
18
|
+
window_end INTEGER NOT NULL
|
|
19
|
+
)
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
4
|
+
|
|
5
|
+
const CHECKPOINT_KEY = "migration_acp_session_history_cwd_v1";
|
|
6
|
+
|
|
7
|
+
const TABLE = "acp_session_history";
|
|
8
|
+
const COLUMN = "cwd";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add a nullable `cwd TEXT` column to `acp_session_history`.
|
|
12
|
+
*
|
|
13
|
+
* Records the working directory the ACP agent process was spawned with so
|
|
14
|
+
* that a persisted session can later be resumed (`session/load` /
|
|
15
|
+
* `session/resume` both require the original cwd to reconstruct params).
|
|
16
|
+
*
|
|
17
|
+
* `NULL` for rows persisted before this migration ran — legacy sessions
|
|
18
|
+
* simply are not resumable.
|
|
19
|
+
*
|
|
20
|
+
* Idempotent — the PRAGMA guard makes re-running a no-op once the column
|
|
21
|
+
* exists.
|
|
22
|
+
*/
|
|
23
|
+
export function migrateAcpSessionHistoryCwd(database: DrizzleDb): void {
|
|
24
|
+
withCrashRecovery(database, CHECKPOINT_KEY, () => {
|
|
25
|
+
const raw = getSqliteFrom(database);
|
|
26
|
+
|
|
27
|
+
const columns = raw.query(`PRAGMA table_info(${TABLE})`).all() as Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
}>;
|
|
30
|
+
const columnNames = new Set(columns.map((c) => c.name));
|
|
31
|
+
|
|
32
|
+
if (!columnNames.has(COLUMN)) {
|
|
33
|
+
raw.exec(`ALTER TABLE ${TABLE} ADD COLUMN ${COLUMN} TEXT`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -257,6 +257,9 @@ export { migrateLlmUsageEventsAddAssistantVersion } from "./267-llm-usage-events
|
|
|
257
257
|
export { migrateAddMemoryV3Selections } from "./268-add-memory-v3-selections.js";
|
|
258
258
|
export { migrateScheduleScriptTimeout } from "./269-schedule-script-timeout.js";
|
|
259
259
|
export { migrateMessagesRoleCreatedAtIndex } from "./270-messages-role-created-at-index.js";
|
|
260
|
+
export { migrateScheduleSourceConversation } from "./270-schedule-source-conversation.js";
|
|
261
|
+
export { createAuthFallbackEventsTable } from "./271-create-auth-fallback-events.js";
|
|
262
|
+
export { migrateAcpSessionHistoryCwd } from "./272-acp-session-history-cwd.js";
|
|
260
263
|
export {
|
|
261
264
|
MIGRATION_REGISTRY,
|
|
262
265
|
type MigrationRegistryEntry,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution of the PKB auto-inject file list (`pkb/_autoinject.md`).
|
|
3
|
+
*
|
|
4
|
+
* Which PKB files are always loaded into context is a PKB-domain concern
|
|
5
|
+
* owned here next to {@link getPkbRoot}, so both the runtime injector and the
|
|
6
|
+
* reminder-hint tracker resolve the list from a single canonical source
|
|
7
|
+
* rather than threading it through the orchestrator.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
|
|
13
|
+
import { stripCommentLines } from "../../util/strip-comment-lines.js";
|
|
14
|
+
|
|
15
|
+
const PKB_DEFAULT_FILES = [
|
|
16
|
+
"INDEX.md",
|
|
17
|
+
"essentials.md",
|
|
18
|
+
"threads.md",
|
|
19
|
+
"buffer.md",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const AUTOINJECT_FILENAME = "_autoinject.md";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read `_autoinject.md` from the PKB directory and return the list of
|
|
26
|
+
* filenames to inject.
|
|
27
|
+
*
|
|
28
|
+
* - Returns `null` when the file is missing or unreadable — callers
|
|
29
|
+
* should fall back to the hardcoded defaults.
|
|
30
|
+
* - Returns `[]` when the file exists but has no entries (empty or
|
|
31
|
+
* comments only) — an explicit opt-out meaning "inject nothing."
|
|
32
|
+
*/
|
|
33
|
+
export function readAutoinjectList(pkbDir: string): string[] | null {
|
|
34
|
+
const filePath = join(pkbDir, AUTOINJECT_FILENAME);
|
|
35
|
+
if (!existsSync(filePath)) return null;
|
|
36
|
+
try {
|
|
37
|
+
const raw = stripCommentLines(readFileSync(filePath, "utf-8"));
|
|
38
|
+
const files = raw
|
|
39
|
+
.split("\n")
|
|
40
|
+
.map((l) => l.trim())
|
|
41
|
+
.filter((l) => l.length > 0);
|
|
42
|
+
return files.length > 0 ? files : [];
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the effective list of auto-inject filenames for a PKB directory.
|
|
50
|
+
*
|
|
51
|
+
* This is the single source of truth used both by `readPkbContext` (which
|
|
52
|
+
* actually injects the files) and by the PKB reminder-hint injector (which
|
|
53
|
+
* needs to know what's already in context so it doesn't redundantly
|
|
54
|
+
* recommend those files).
|
|
55
|
+
*
|
|
56
|
+
* Returns `PKB_DEFAULT_FILES` when `_autoinject.md` is missing/unreadable,
|
|
57
|
+
* or the parsed list (possibly empty) when it is present.
|
|
58
|
+
*/
|
|
59
|
+
export function getPkbAutoInjectList(pkbRoot: string): string[] {
|
|
60
|
+
return readAutoinjectList(pkbRoot) ?? PKB_DEFAULT_FILES;
|
|
61
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
5
|
+
import { stripCommentLines } from "../../util/strip-comment-lines.js";
|
|
6
|
+
import { getPkbAutoInjectList } from "./autoinject.js";
|
|
7
|
+
|
|
8
|
+
/** Max buffer.md lines injected into prompts — keeps context bounded even when filing is off. */
|
|
9
|
+
const MAX_BUFFER_LINES = 50;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Read the always-loaded PKB files and append a nudge encouraging the
|
|
13
|
+
* assistant to proactively read topic files and use `remember` aggressively.
|
|
14
|
+
*
|
|
15
|
+
* Which files are loaded is determined by `pkb/_autoinject.md` (one filename
|
|
16
|
+
* per line). Falls back to the built-in defaults when that file is absent.
|
|
17
|
+
*
|
|
18
|
+
* Returns the concatenated content ready for injection, or `null` if all
|
|
19
|
+
* files are missing or empty.
|
|
20
|
+
*/
|
|
21
|
+
export function readPkbContext(): string | null {
|
|
22
|
+
const pkbDir = join(getWorkspaceDir(), "pkb");
|
|
23
|
+
if (!existsSync(pkbDir)) return null;
|
|
24
|
+
|
|
25
|
+
const filesToInject = getPkbAutoInjectList(pkbDir);
|
|
26
|
+
|
|
27
|
+
const parts: string[] = [];
|
|
28
|
+
for (const file of filesToInject) {
|
|
29
|
+
// Path traversal guard: reject entries that escape the pkb directory
|
|
30
|
+
const filePath = resolve(pkbDir, file);
|
|
31
|
+
if (!filePath.startsWith(pkbDir + "/")) continue;
|
|
32
|
+
|
|
33
|
+
if (!existsSync(filePath)) continue;
|
|
34
|
+
try {
|
|
35
|
+
let content = stripCommentLines(readFileSync(filePath, "utf-8")).trim();
|
|
36
|
+
if (file === "buffer.md" && content.length > 0) {
|
|
37
|
+
// Cap buffer entries to prevent unbounded growth when filing is disabled
|
|
38
|
+
const lines = content.split("\n");
|
|
39
|
+
if (lines.length > MAX_BUFFER_LINES) {
|
|
40
|
+
content = lines.slice(-MAX_BUFFER_LINES).join("\n");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (content.length > 0) parts.push(content);
|
|
44
|
+
} catch {
|
|
45
|
+
// Skip unreadable files
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
50
|
+
}
|
package/src/memory/pkb/types.ts
CHANGED
|
@@ -5,8 +5,22 @@
|
|
|
5
5
|
* index writer) land in later PRs.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
10
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
11
|
+
|
|
8
12
|
export const PKB_TARGET_TYPE = "pkb_file" as const;
|
|
9
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Absolute path to the workspace's PKB directory (`<workspace>/pkb`). PKB is
|
|
16
|
+
* a workspace-shared resource with a single on-disk location, so this is the
|
|
17
|
+
* canonical way to resolve its root rather than re-joining the workspace dir
|
|
18
|
+
* at each call site.
|
|
19
|
+
*/
|
|
20
|
+
export function getPkbRoot(): string {
|
|
21
|
+
return join(getWorkspaceDir(), "pkb");
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
/**
|
|
11
25
|
* Sentinel `memory_scope_id` under which ALL PKB points are indexed and
|
|
12
26
|
* searched. PKB files are a workspace-shared resource: one copy on disk is
|