@vellumai/assistant 0.8.10 → 0.8.11-staging.1
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/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -12,6 +12,7 @@ import { z } from "zod";
|
|
|
12
12
|
import type { AssistantConfig } from "../../config/types.js";
|
|
13
13
|
import { estimateTextTokens } from "../../context/token-estimator.js";
|
|
14
14
|
import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
15
|
+
import { clearConversation as clearV3EverInjected } from "../../plugins/defaults/memory-v3-shadow/ever-injected-store.js";
|
|
15
16
|
import type {
|
|
16
17
|
ContentBlock,
|
|
17
18
|
ImageContent,
|
|
@@ -284,6 +285,19 @@ export class ConversationGraphMemory {
|
|
|
284
285
|
);
|
|
285
286
|
}
|
|
286
287
|
|
|
288
|
+
// Memory-v3's frozen-card dedup record resets at the same trigger: the
|
|
289
|
+
// cached card blocks those slugs rode were just stripped by compaction, so
|
|
290
|
+
// every slug must become re-injectable. Cleared unconditionally for the
|
|
291
|
+
// same crash-drift reason as v2's `everInjected` above.
|
|
292
|
+
try {
|
|
293
|
+
clearV3EverInjected(this.conversationId);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
log.warn(
|
|
296
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
297
|
+
"Failed to clear memory-v3 everInjected on compaction (non-fatal)",
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
287
301
|
this.needsReload = true;
|
|
288
302
|
log.info(
|
|
289
303
|
{ compactedMessageCount },
|
|
@@ -370,6 +384,22 @@ export class ConversationGraphMemory {
|
|
|
370
384
|
return this.lastPkbSparseVector;
|
|
371
385
|
}
|
|
372
386
|
|
|
387
|
+
/**
|
|
388
|
+
* The unwrapped text of the dynamic memory block the last retrieval
|
|
389
|
+
* injected (the v2 router block, or the v1 context block on legacy
|
|
390
|
+
* configs), or `null` when the last retrieval injected nothing.
|
|
391
|
+
*
|
|
392
|
+
* Runtime assembly reads this as the IDENTITY of the v2 dynamic `<memory>`
|
|
393
|
+
* block when memory-v3 supersedes the v2 layer: v2's block and v3's frozen
|
|
394
|
+
* card blocks deliberately share the same wrapper AND leading instruction
|
|
395
|
+
* header bytes, so the tail strip must match the exact block this handle
|
|
396
|
+
* prepended (`prepareMemory` → `injectTextBlock`, or a convergence
|
|
397
|
+
* re-injection of the same cached text) rather than any shared prefix.
|
|
398
|
+
*/
|
|
399
|
+
get lastInjectedBlockText(): string | null {
|
|
400
|
+
return this.lastInjectedBlock;
|
|
401
|
+
}
|
|
402
|
+
|
|
373
403
|
/**
|
|
374
404
|
* Main entry point — called on every turn before the LLM sees the messages.
|
|
375
405
|
*
|
|
@@ -432,7 +462,7 @@ export class ConversationGraphMemory {
|
|
|
432
462
|
metrics: null as RetrievalMetrics | null,
|
|
433
463
|
};
|
|
434
464
|
|
|
435
|
-
if (
|
|
465
|
+
if (config.memory.enabled === false) {
|
|
436
466
|
// Clear any cached injection so a later overflow-reduction
|
|
437
467
|
// re-injection via `reinjectCachedMemory()` cannot reintroduce a
|
|
438
468
|
// stale <memory> block after the user disables memory.
|
|
@@ -970,9 +1000,11 @@ export function stripExistingMemoryInjections(messages: Message[]): Message[] {
|
|
|
970
1000
|
/**
|
|
971
1001
|
* Strip the memory-injected prefix from a single user message. Returns the
|
|
972
1002
|
* same message reference unchanged when it is not a user message or carries no
|
|
973
|
-
* injected prefix, so callers can cheaply detect no-ops.
|
|
974
|
-
* `stripExistingMemoryInjections` (last message only)
|
|
975
|
-
*
|
|
1003
|
+
* injected prefix, so callers can cheaply detect no-ops. Used by
|
|
1004
|
+
* `stripExistingMemoryInjections` (last message only) — memory-v3's frozen
|
|
1005
|
+
* card carry means historical `<memory>` blocks are never bulk-stripped
|
|
1006
|
+
* anymore (the old whole-layer `stripAllMemoryInjections` is gone): runtime
|
|
1007
|
+
* assembly strips only the TAIL's fresh v2 prefix when v3 supersedes it.
|
|
976
1008
|
*/
|
|
977
1009
|
function stripMemoryPrefixFromUserMessage(message: Message): Message {
|
|
978
1010
|
if (message.role !== "user") return message;
|
|
@@ -981,27 +1013,6 @@ function stripMemoryPrefixFromUserMessage(message: Message): Message {
|
|
|
981
1013
|
return { ...message, content: message.content.slice(firstNonMemory) };
|
|
982
1014
|
}
|
|
983
1015
|
|
|
984
|
-
/**
|
|
985
|
-
* Remove memory-injected blocks from EVERY user message, not just the last.
|
|
986
|
-
*
|
|
987
|
-
* memory-v3 live mode strips the injected `<memory>` block from all historical
|
|
988
|
-
* user messages each turn: it keeps the prompt lean and makes history
|
|
989
|
-
* byte-stable for prompt caching (v2 strips only the last user message — see
|
|
990
|
-
* `stripExistingMemoryInjections`). Reuses the same per-message block
|
|
991
|
-
* recognition as the last-message strip. Wired into the runtime-assembly
|
|
992
|
-
* suppression step (`conversation-runtime-assembly.ts`), which calls it when
|
|
993
|
-
* `memory-v3-live` is on and the v3 injector produced a block this turn.
|
|
994
|
-
*/
|
|
995
|
-
export function stripAllMemoryInjections(messages: Message[]): Message[] {
|
|
996
|
-
let changed = false;
|
|
997
|
-
const result = messages.map((message) => {
|
|
998
|
-
const stripped = stripMemoryPrefixFromUserMessage(message);
|
|
999
|
-
if (stripped !== message) changed = true;
|
|
1000
|
-
return stripped;
|
|
1001
|
-
});
|
|
1002
|
-
return changed ? result : messages;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
1016
|
/**
|
|
1006
1017
|
* Return the memory-injected prefix blocks from the last user message, or
|
|
1007
1018
|
* an empty array when there is none. Used by runtime assembly to carry the
|
|
@@ -40,6 +40,9 @@ export function handleRemember(
|
|
|
40
40
|
if (!input.content || input.content.trim().length === 0) {
|
|
41
41
|
return { success: false, message: "content is required" };
|
|
42
42
|
}
|
|
43
|
+
if (config.memory.enabled === false) {
|
|
44
|
+
return { success: false, message: "Memory is disabled." };
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
const workspaceDir = getWorkspaceDir();
|
|
45
48
|
const now = new Date();
|
|
@@ -164,7 +164,8 @@ function parseDeletedCount(stdout: string | undefined): number {
|
|
|
164
164
|
* conversation_keys, channel_inbound_events, message_runs, call_sessions,
|
|
165
165
|
* external_conversation_bindings, assistant_inbox_conversation_state) are handled
|
|
166
166
|
* automatically. Tables without cascade (messages, tool_invocations,
|
|
167
|
-
* llm_request_logs) are deleted explicitly before
|
|
167
|
+
* llm_request_logs, skill_loaded_events) are deleted explicitly before
|
|
168
|
+
* removing the conversation row.
|
|
168
169
|
*/
|
|
169
170
|
export function pruneOldConversationsJob(
|
|
170
171
|
job: MemoryJob,
|
|
@@ -206,6 +207,7 @@ export function pruneOldConversationsJob(
|
|
|
206
207
|
rawRun(`DELETE FROM llm_request_logs WHERE conversation_id = ?`, id);
|
|
207
208
|
rawRun(`DELETE FROM tool_invocations WHERE conversation_id = ?`, id);
|
|
208
209
|
rawRun(`DELETE FROM messages WHERE conversation_id = ?`, id);
|
|
210
|
+
rawRun(`DELETE FROM skill_loaded_events WHERE conversation_id = ?`, id);
|
|
209
211
|
// Conversation row deletion cascades to remaining dependent tables
|
|
210
212
|
rawRun(`DELETE FROM conversations WHERE id = ?`, id);
|
|
211
213
|
pruned++;
|
package/src/memory/jobs-store.ts
CHANGED
|
@@ -342,34 +342,6 @@ export function hasActiveJobOfType(type: MemoryJobType): boolean {
|
|
|
342
342
|
);
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
export function isAutomaticConsolidationJob(job: MemoryJob): boolean {
|
|
346
|
-
return job.payload.trigger !== MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS.manual;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export function cancelPendingAutomaticConsolidationJobs(): number {
|
|
350
|
-
const db = getDb();
|
|
351
|
-
db.update(memoryJobs)
|
|
352
|
-
.set({
|
|
353
|
-
status: "failed",
|
|
354
|
-
lastError: "automatic_consolidation_disabled",
|
|
355
|
-
updatedAt: Date.now(),
|
|
356
|
-
})
|
|
357
|
-
.where(
|
|
358
|
-
and(
|
|
359
|
-
eq(memoryJobs.type, "memory_v2_consolidate"),
|
|
360
|
-
eq(memoryJobs.status, "pending"),
|
|
361
|
-
or(
|
|
362
|
-
sql`json_extract(${memoryJobs.payload}, '$.trigger') = ${MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS.automatic}`,
|
|
363
|
-
// Legacy rows predate trigger markers and are indistinguishable from
|
|
364
|
-
// automatic enqueues, so disabling the schedule treats them as auto.
|
|
365
|
-
sql`json_extract(${memoryJobs.payload}, '$.trigger') IS NULL`,
|
|
366
|
-
),
|
|
367
|
-
),
|
|
368
|
-
)
|
|
369
|
-
.run();
|
|
370
|
-
return rawChanges();
|
|
371
|
-
}
|
|
372
|
-
|
|
373
345
|
export function enqueuePruneOldLlmRequestLogsJob(retentionMs?: number): string {
|
|
374
346
|
const db = getDb();
|
|
375
347
|
const existing = db
|
|
@@ -68,7 +68,6 @@ import {
|
|
|
68
68
|
failMemoryJob,
|
|
69
69
|
failStalledJobs,
|
|
70
70
|
hasActiveJobOfType,
|
|
71
|
-
isAutomaticConsolidationJob,
|
|
72
71
|
MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS,
|
|
73
72
|
type MemoryJob,
|
|
74
73
|
type MemoryJobType,
|
|
@@ -242,7 +241,7 @@ export async function runMemoryJobsOnce(
|
|
|
242
241
|
options: { enableScheduledCleanup?: boolean } = {},
|
|
243
242
|
): Promise<number> {
|
|
244
243
|
const config = getConfig();
|
|
245
|
-
if (
|
|
244
|
+
if (config.memory.enabled === false) return 0;
|
|
246
245
|
const enableScheduledCleanup = options.enableScheduledCleanup === true;
|
|
247
246
|
|
|
248
247
|
const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
|
|
@@ -625,16 +624,6 @@ async function processJob(
|
|
|
625
624
|
await memoryV2SweepJob(job, config);
|
|
626
625
|
return;
|
|
627
626
|
case "memory_v2_consolidate":
|
|
628
|
-
if (
|
|
629
|
-
isAutomaticConsolidationJob(job) &&
|
|
630
|
-
!config.memory.v2.consolidation_enabled
|
|
631
|
-
) {
|
|
632
|
-
log.info(
|
|
633
|
-
{ jobId: job.id },
|
|
634
|
-
"Skipping automatic memory v2 consolidation because scheduled consolidation is disabled",
|
|
635
|
-
);
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
627
|
await memoryV2ConsolidateJob(job, config);
|
|
639
628
|
return;
|
|
640
629
|
case "memory_v2_migrate":
|
|
@@ -713,8 +702,8 @@ const GRAPH_PATTERN_SCAN_INTERVAL_MS = 24 * 60 * 60 * 1000; // 1 day
|
|
|
713
702
|
const GRAPH_NARRATIVE_INTERVAL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week
|
|
714
703
|
// Backstop cadence for v3 self-maintenance. The primary trigger is the
|
|
715
704
|
// post-consolidation follow-up (see `consolidation-job.ts`); this interval only
|
|
716
|
-
// covers the case where that follow-up is missed (enqueue failure
|
|
717
|
-
//
|
|
705
|
+
// covers the case where that follow-up is missed (enqueue failure). A
|
|
706
|
+
// conservative cadence is fine since
|
|
718
707
|
// the maintenance pass is idempotent and cheap when there's nothing to do.
|
|
719
708
|
const GRAPH_V3_MAINTAIN_INTERVAL_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
720
709
|
|
|
@@ -759,9 +748,10 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
759
748
|
config: AssistantConfig,
|
|
760
749
|
nowMs = Date.now(),
|
|
761
750
|
): void {
|
|
751
|
+
const memoryEnabled = config.memory.enabled !== false;
|
|
752
|
+
if (!memoryEnabled) return;
|
|
753
|
+
|
|
762
754
|
const v2Active = config.memory.v2.enabled;
|
|
763
|
-
const v2ConsolidationAutomaticEnabled =
|
|
764
|
-
v2Active && config.memory.v2.consolidation_enabled;
|
|
765
755
|
|
|
766
756
|
// The single buffer-drainer entry for the v2-active branch. Referenced again
|
|
767
757
|
// below by the size-based trigger.
|
|
@@ -776,9 +766,7 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
776
766
|
intervalMs: number;
|
|
777
767
|
jobType: MemoryJobType;
|
|
778
768
|
}> = v2Active
|
|
779
|
-
?
|
|
780
|
-
? [consolidateEntry]
|
|
781
|
-
: []
|
|
769
|
+
? [consolidateEntry]
|
|
782
770
|
: [
|
|
783
771
|
{
|
|
784
772
|
key: GRAPH_MAINTENANCE_CHECKPOINTS.decay,
|
|
@@ -807,8 +795,8 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
807
795
|
// runs under either branch. Gated on the same flags that gate the v3 plugin
|
|
808
796
|
// so it stays inert when v3 is off. The post-consolidation follow-up in
|
|
809
797
|
// `consolidation-job.ts` remains the primary trigger; this interval only
|
|
810
|
-
// self-heals when that follow-up is missed (failed enqueue
|
|
811
|
-
//
|
|
798
|
+
// self-heals when that follow-up is missed (failed enqueue). The job handler
|
|
799
|
+
// itself no-ops when v3 is off, so
|
|
812
800
|
// this guard is belt-and-suspenders that also avoids a wasted enqueue.
|
|
813
801
|
if (
|
|
814
802
|
isAssistantFeatureFlagEnabled("memory-v3-shadow", config) ||
|
|
@@ -859,7 +847,7 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
859
847
|
// buffer stays over threshold, flooding the queue with redundant LLM work.
|
|
860
848
|
const maxLines = config.memory.v2.consolidation_max_buffer_lines;
|
|
861
849
|
if (
|
|
862
|
-
|
|
850
|
+
v2Active &&
|
|
863
851
|
!enqueuedConsolidate &&
|
|
864
852
|
maxLines !== null &&
|
|
865
853
|
!hasActiveJobOfType(consolidateEntry.jobType)
|
|
@@ -15,3 +15,32 @@
|
|
|
15
15
|
export function wrapMemoryBlock(text: string): string {
|
|
16
16
|
return `<memory>\n${text}\n</memory>`;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Inverse of {@link wrapMemoryBlock}: recover the inner text from a wrapped
|
|
21
|
+
* block. Only unwraps when the full `<memory>\n…\n</memory>` pair is present,
|
|
22
|
+
* so payloads that merely start with the opening tag pass through unchanged —
|
|
23
|
+
* the same defensive guard the metadata rehydration in
|
|
24
|
+
* `daemon/conversation.ts` applies.
|
|
25
|
+
*/
|
|
26
|
+
export function unwrapMemoryBlock(block: string): string {
|
|
27
|
+
return block.startsWith("<memory>\n") && block.endsWith("\n</memory>")
|
|
28
|
+
? block.slice("<memory>\n".length, -"\n</memory>".length)
|
|
29
|
+
: block;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The memory-v3 ephemeral spotlight wrapper. Unlike `<memory>` card blocks
|
|
34
|
+
* (frozen into history), the spotlight block is re-rendered onto the current
|
|
35
|
+
* user tail every turn: the per-turn scoped strip in
|
|
36
|
+
* `context/strip-injections.ts` (`stripSpotlightInjections`) and the
|
|
37
|
+
* compaction matcher in `RUNTIME_INJECTION_PREFIXES` both key off this exact
|
|
38
|
+
* prefix/suffix pair, so the producer wrapper lives here beside the `<memory>`
|
|
39
|
+
* marker it parallels.
|
|
40
|
+
*/
|
|
41
|
+
export const MEMORY_SPOTLIGHT_PREFIX = "<memory_spotlight>\n";
|
|
42
|
+
export const MEMORY_SPOTLIGHT_SUFFIX = "\n</memory_spotlight>";
|
|
43
|
+
|
|
44
|
+
export function wrapMemorySpotlightBlock(text: string): string {
|
|
45
|
+
return `${MEMORY_SPOTLIGHT_PREFIX}${text}${MEMORY_SPOTLIGHT_SUFFIX}`;
|
|
46
|
+
}
|
|
@@ -93,7 +93,7 @@ export function sweepOrphanMemoryRetrospectiveConversations(
|
|
|
93
93
|
// Compute the most-recent retro per source so we can preserve it.
|
|
94
94
|
// `findMostRecentRetrospectiveFor` (called by the next retrospective run)
|
|
95
95
|
// pulls dedup context from this row; sweeping it would re-introduce the
|
|
96
|
-
// unbounded
|
|
96
|
+
// unbounded retrospective growth this cleanup exists to prevent.
|
|
97
97
|
const allRetros = db
|
|
98
98
|
.select({
|
|
99
99
|
id: conversations.id,
|
|
@@ -6,6 +6,12 @@ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
|
6
6
|
* tagged with the lane (`source`) that produced it. `pinned` marks slugs
|
|
7
7
|
* carried forward across turns rather than re-selected cold.
|
|
8
8
|
*
|
|
9
|
+
* HISTORICAL NOTE: the working-set carry this table originally backed was
|
|
10
|
+
* removed (selections are current-turn only now; cross-turn persistence lives
|
|
11
|
+
* in the injector's frozen net-new blocks). The table itself is still live —
|
|
12
|
+
* the shadow plugin logs per-turn selections to it and the inspector /
|
|
13
|
+
* `summarizeSelections` read from it.
|
|
14
|
+
*
|
|
9
15
|
* Idempotent — re-running is a no-op once the table and index exist.
|
|
10
16
|
*/
|
|
11
17
|
export function migrateAddMemoryV3Selections(database: DrizzleDb): void {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
import { withCrashRecovery } from "./validate-migration-state.js";
|
|
3
|
+
|
|
4
|
+
const CHECKPOINT_KEY = "migration_schedule_description_backfill_v1";
|
|
5
|
+
|
|
6
|
+
export function migrateScheduleDescription(database: DrizzleDb): void {
|
|
7
|
+
const raw = getSqliteFrom(database);
|
|
8
|
+
raw.exec(/*sql*/ `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS memory_checkpoints (
|
|
10
|
+
key TEXT PRIMARY KEY,
|
|
11
|
+
value TEXT NOT NULL,
|
|
12
|
+
updated_at INTEGER NOT NULL
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
try {
|
|
16
|
+
raw.exec(
|
|
17
|
+
`ALTER TABLE cron_jobs ADD COLUMN description TEXT NOT NULL DEFAULT ''`,
|
|
18
|
+
);
|
|
19
|
+
} catch {
|
|
20
|
+
// Column already exists.
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
withCrashRecovery(database, CHECKPOINT_KEY, () => {
|
|
24
|
+
raw
|
|
25
|
+
.query(
|
|
26
|
+
`UPDATE cron_jobs
|
|
27
|
+
SET description = name
|
|
28
|
+
WHERE created_by <> 'defer' AND description = ''`,
|
|
29
|
+
)
|
|
30
|
+
.run();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function downScheduleDescription(): void {
|
|
35
|
+
// Append-only schema migration; the column and backfill are intentionally kept.
|
|
36
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateToolInvocationsSkillId } from "./275-tool-invocations-add-skill-id.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
// Pre-275 shape: no skill_id column.
|
|
12
|
+
sqlite.exec(/*sql*/ `
|
|
13
|
+
CREATE TABLE tool_invocations (
|
|
14
|
+
id TEXT PRIMARY KEY,
|
|
15
|
+
conversation_id TEXT NOT NULL,
|
|
16
|
+
tool_name TEXT NOT NULL,
|
|
17
|
+
input TEXT NOT NULL,
|
|
18
|
+
result TEXT NOT NULL,
|
|
19
|
+
decision TEXT NOT NULL,
|
|
20
|
+
risk_level TEXT NOT NULL,
|
|
21
|
+
matched_trust_rule_id TEXT,
|
|
22
|
+
duration_ms INTEGER NOT NULL,
|
|
23
|
+
created_at INTEGER NOT NULL
|
|
24
|
+
)
|
|
25
|
+
`);
|
|
26
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function columnNames(sqlite: Database): string[] {
|
|
30
|
+
return (
|
|
31
|
+
sqlite.query("PRAGMA table_info(tool_invocations)").all() as Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
}>
|
|
34
|
+
).map((c) => c.name);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("migration 275: tool_invocations skill_id", () => {
|
|
38
|
+
test("adds a nullable skill_id column", () => {
|
|
39
|
+
const { sqlite, db } = createTestDb();
|
|
40
|
+
expect(columnNames(sqlite)).not.toContain("skill_id");
|
|
41
|
+
|
|
42
|
+
migrateToolInvocationsSkillId(db);
|
|
43
|
+
|
|
44
|
+
const column = (
|
|
45
|
+
sqlite.query("PRAGMA table_info(tool_invocations)").all() as Array<{
|
|
46
|
+
name: string;
|
|
47
|
+
notnull: number;
|
|
48
|
+
}>
|
|
49
|
+
).find((c) => c.name === "skill_id");
|
|
50
|
+
expect(column).toBeDefined();
|
|
51
|
+
expect(column?.notnull).toBe(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("existing rows read back with skill_id null", () => {
|
|
55
|
+
const { sqlite, db } = createTestDb();
|
|
56
|
+
sqlite.exec(/*sql*/ `
|
|
57
|
+
INSERT INTO tool_invocations
|
|
58
|
+
(id, conversation_id, tool_name, input, result, decision, risk_level, duration_ms, created_at)
|
|
59
|
+
VALUES
|
|
60
|
+
('ti-1', 'conv-1', 'bash', '{}', 'ok', 'allow', 'low', 5, 1000)
|
|
61
|
+
`);
|
|
62
|
+
|
|
63
|
+
migrateToolInvocationsSkillId(db);
|
|
64
|
+
|
|
65
|
+
const row = sqlite
|
|
66
|
+
.query("SELECT skill_id FROM tool_invocations WHERE id = 'ti-1'")
|
|
67
|
+
.get() as { skill_id: string | null };
|
|
68
|
+
expect(row.skill_id).toBeNull();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("is idempotent — re-run is a no-op", () => {
|
|
72
|
+
const { sqlite, db } = createTestDb();
|
|
73
|
+
|
|
74
|
+
migrateToolInvocationsSkillId(db);
|
|
75
|
+
expect(() => migrateToolInvocationsSkillId(db)).not.toThrow();
|
|
76
|
+
|
|
77
|
+
expect(
|
|
78
|
+
columnNames(sqlite).filter((name) => name === "skill_id"),
|
|
79
|
+
).toHaveLength(1);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { tableHasColumn } from "./schema-introspection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Add a nullable `skill_id` column to `tool_invocations`.
|
|
6
|
+
*
|
|
7
|
+
* Records which skill's `skill_execute` dispatch triggered the tool call so
|
|
8
|
+
* the `tool_execution` telemetry event can attribute skill-routed
|
|
9
|
+
* invocations. `NULL` for direct (non-skill) tool calls and for rows
|
|
10
|
+
* persisted before this migration ran.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent — re-running is a no-op once the column exists. Pure DDL with a
|
|
13
|
+
* PRAGMA guard, no registry entry needed (matches the 273 pattern).
|
|
14
|
+
*/
|
|
15
|
+
export function migrateToolInvocationsSkillId(database: DrizzleDb): void {
|
|
16
|
+
if (tableHasColumn(database, "tool_invocations", "skill_id")) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
database.run(`ALTER TABLE tool_invocations ADD COLUMN skill_id TEXT`);
|
|
20
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateToolInvocationsCreatedAtIdIndex } from "./276-tool-invocations-created-at-id-index.js";
|
|
8
|
+
|
|
9
|
+
function createTestDb() {
|
|
10
|
+
const sqlite = new Database(":memory:");
|
|
11
|
+
// Pre-276 shape: no (created_at, id) index.
|
|
12
|
+
sqlite.exec(/*sql*/ `
|
|
13
|
+
CREATE TABLE tool_invocations (
|
|
14
|
+
id TEXT PRIMARY KEY,
|
|
15
|
+
conversation_id TEXT NOT NULL,
|
|
16
|
+
tool_name TEXT NOT NULL,
|
|
17
|
+
input TEXT NOT NULL,
|
|
18
|
+
result TEXT NOT NULL,
|
|
19
|
+
decision TEXT NOT NULL,
|
|
20
|
+
risk_level TEXT NOT NULL,
|
|
21
|
+
matched_trust_rule_id TEXT,
|
|
22
|
+
skill_id TEXT,
|
|
23
|
+
duration_ms INTEGER NOT NULL,
|
|
24
|
+
created_at INTEGER NOT NULL
|
|
25
|
+
)
|
|
26
|
+
`);
|
|
27
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function indexNames(sqlite: Database): string[] {
|
|
31
|
+
return (
|
|
32
|
+
sqlite.query("PRAGMA index_list(tool_invocations)").all() as Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
}>
|
|
35
|
+
).map((i) => i.name);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("migration 276: tool_invocations (created_at, id) index", () => {
|
|
39
|
+
test("creates the index on (created_at, id)", () => {
|
|
40
|
+
const { sqlite, db } = createTestDb();
|
|
41
|
+
expect(indexNames(sqlite)).not.toContain(
|
|
42
|
+
"idx_tool_invocations_created_at_id",
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
migrateToolInvocationsCreatedAtIdIndex(db);
|
|
46
|
+
|
|
47
|
+
expect(indexNames(sqlite)).toContain("idx_tool_invocations_created_at_id");
|
|
48
|
+
const columns = (
|
|
49
|
+
sqlite
|
|
50
|
+
.query("PRAGMA index_info(idx_tool_invocations_created_at_id)")
|
|
51
|
+
.all() as Array<{ name: string }>
|
|
52
|
+
).map((c) => c.name);
|
|
53
|
+
expect(columns).toEqual(["created_at", "id"]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("is idempotent — re-run is a no-op", () => {
|
|
57
|
+
const { sqlite, db } = createTestDb();
|
|
58
|
+
|
|
59
|
+
migrateToolInvocationsCreatedAtIdIndex(db);
|
|
60
|
+
expect(() => migrateToolInvocationsCreatedAtIdIndex(db)).not.toThrow();
|
|
61
|
+
|
|
62
|
+
expect(
|
|
63
|
+
indexNames(sqlite).filter(
|
|
64
|
+
(name) => name === "idx_tool_invocations_created_at_id",
|
|
65
|
+
),
|
|
66
|
+
).toHaveLength(1);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Indexes `tool_invocations(created_at, id)` so the tool_execution telemetry
|
|
5
|
+
* cursor can seek to the next unreported batch instead of scanning the table.
|
|
6
|
+
*
|
|
7
|
+
* `queryUnreportedToolExecutionEvents` filters and orders by `(created_at, id)`
|
|
8
|
+
* every telemetry flush (~5 min); without this index each flush scans and
|
|
9
|
+
* sorts the whole audit table, which grows unboundedly by default and carries
|
|
10
|
+
* large `input`/`result` text blobs.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent — `CREATE INDEX IF NOT EXISTS` is a no-op once the index exists.
|
|
13
|
+
*/
|
|
14
|
+
export function migrateToolInvocationsCreatedAtIdIndex(
|
|
15
|
+
database: DrizzleDb,
|
|
16
|
+
): void {
|
|
17
|
+
database.run(
|
|
18
|
+
/*sql*/ `CREATE INDEX IF NOT EXISTS idx_tool_invocations_created_at_id ON tool_invocations (created_at, id)`,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds `memory_v3_ever_injected` for memory-v3's frozen-card carry: one row
|
|
5
|
+
* per (conversation, page-slug) the v3 injector ever attached as a card.
|
|
6
|
+
* `bytes` is the rendered card size used for resident-footprint accounting;
|
|
7
|
+
* `pruned_at` marks rows the prune valve removed from the live context (rows
|
|
8
|
+
* are never deleted — the record stays auditable, and a pruned page that is
|
|
9
|
+
* re-selected re-injects by clearing `pruned_at`).
|
|
10
|
+
*
|
|
11
|
+
* Idempotent — re-running is a no-op once the table and index exist.
|
|
12
|
+
*/
|
|
13
|
+
export function migrateAddMemoryV3EverInjected(database: DrizzleDb): void {
|
|
14
|
+
const raw = getSqliteFrom(database);
|
|
15
|
+
raw.exec(/*sql*/ `
|
|
16
|
+
CREATE TABLE IF NOT EXISTS memory_v3_ever_injected (
|
|
17
|
+
conversation_id TEXT NOT NULL,
|
|
18
|
+
slug TEXT NOT NULL,
|
|
19
|
+
injected_at INTEGER NOT NULL,
|
|
20
|
+
bytes INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
pruned_at INTEGER,
|
|
22
|
+
PRIMARY KEY (conversation_id, slug)
|
|
23
|
+
)
|
|
24
|
+
`);
|
|
25
|
+
raw.exec(/*sql*/ `
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_memory_v3_ever_injected_conv
|
|
27
|
+
ON memory_v3_ever_injected (conversation_id)
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import * as schema from "../schema.js";
|
|
7
|
+
import { migrateToolInvocationsTelemetryColumns } from "./278-tool-invocations-telemetry-columns.js";
|
|
8
|
+
|
|
9
|
+
const NEW_COLUMNS = [
|
|
10
|
+
"arg_bytes",
|
|
11
|
+
"result_bytes",
|
|
12
|
+
"provider",
|
|
13
|
+
"model",
|
|
14
|
+
"inference_profile",
|
|
15
|
+
"inference_profile_source",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function createTestDb() {
|
|
19
|
+
const sqlite = new Database(":memory:");
|
|
20
|
+
// Pre-278 shape: skill_id exists (275), telemetry columns do not.
|
|
21
|
+
sqlite.exec(/*sql*/ `
|
|
22
|
+
CREATE TABLE tool_invocations (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
conversation_id TEXT NOT NULL,
|
|
25
|
+
tool_name TEXT NOT NULL,
|
|
26
|
+
input TEXT NOT NULL,
|
|
27
|
+
result TEXT NOT NULL,
|
|
28
|
+
decision TEXT NOT NULL,
|
|
29
|
+
risk_level TEXT NOT NULL,
|
|
30
|
+
matched_trust_rule_id TEXT,
|
|
31
|
+
duration_ms INTEGER NOT NULL,
|
|
32
|
+
created_at INTEGER NOT NULL,
|
|
33
|
+
skill_id TEXT
|
|
34
|
+
)
|
|
35
|
+
`);
|
|
36
|
+
return { sqlite, db: drizzle(sqlite, { schema }) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function tableInfo(sqlite: Database) {
|
|
40
|
+
return sqlite.query("PRAGMA table_info(tool_invocations)").all() as Array<{
|
|
41
|
+
name: string;
|
|
42
|
+
notnull: number;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("migration 278: tool_invocations telemetry columns", () => {
|
|
47
|
+
test("adds the nullable telemetry columns", () => {
|
|
48
|
+
const { sqlite, db } = createTestDb();
|
|
49
|
+
const before = tableInfo(sqlite).map((c) => c.name);
|
|
50
|
+
for (const name of NEW_COLUMNS) {
|
|
51
|
+
expect(before).not.toContain(name);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
migrateToolInvocationsTelemetryColumns(db);
|
|
55
|
+
|
|
56
|
+
const after = tableInfo(sqlite);
|
|
57
|
+
for (const name of NEW_COLUMNS) {
|
|
58
|
+
const column = after.find((c) => c.name === name);
|
|
59
|
+
expect(column).toBeDefined();
|
|
60
|
+
expect(column?.notnull).toBe(0);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("existing rows read back with null telemetry columns", () => {
|
|
65
|
+
const { sqlite, db } = createTestDb();
|
|
66
|
+
sqlite.exec(/*sql*/ `
|
|
67
|
+
INSERT INTO tool_invocations
|
|
68
|
+
(id, conversation_id, tool_name, input, result, decision, risk_level, duration_ms, created_at)
|
|
69
|
+
VALUES
|
|
70
|
+
('ti-1', 'conv-1', 'bash', '{}', 'ok', 'allow', 'low', 5, 1000)
|
|
71
|
+
`);
|
|
72
|
+
|
|
73
|
+
migrateToolInvocationsTelemetryColumns(db);
|
|
74
|
+
|
|
75
|
+
const row = sqlite
|
|
76
|
+
.query(
|
|
77
|
+
`SELECT ${NEW_COLUMNS.join(", ")} FROM tool_invocations WHERE id = 'ti-1'`,
|
|
78
|
+
)
|
|
79
|
+
.get() as Record<string, unknown>;
|
|
80
|
+
for (const name of NEW_COLUMNS) {
|
|
81
|
+
expect(row[name]).toBeNull();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("is idempotent — re-run is a no-op", () => {
|
|
86
|
+
const { sqlite, db } = createTestDb();
|
|
87
|
+
|
|
88
|
+
migrateToolInvocationsTelemetryColumns(db);
|
|
89
|
+
expect(() => migrateToolInvocationsTelemetryColumns(db)).not.toThrow();
|
|
90
|
+
|
|
91
|
+
const names = tableInfo(sqlite).map((c) => c.name);
|
|
92
|
+
for (const name of NEW_COLUMNS) {
|
|
93
|
+
expect(names.filter((n) => n === name)).toHaveLength(1);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|