@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
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guard: the DEFAULT consolidation prompt must not leak flag-gated feature
|
|
3
|
+
* content to installs that don't have the feature enabled.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: the consolidation prompt ships to every assistant with no
|
|
6
|
+
* `memory.v2.consolidation_prompt_path` override, and it is processed without
|
|
7
|
+
* any flag check at run time — so flag-gated instructions added to the shared
|
|
8
|
+
* template reach the entire default population at their next consolidation.
|
|
9
|
+
* That happened once: the memory-v3 `core-pages.md` curation step landed
|
|
10
|
+
* inline and instructed v2-only installs to curate a file nothing on their
|
|
11
|
+
* install reads, under premises ("kept in reach every turn", "the hot set")
|
|
12
|
+
* that are false without the v3 lanes. This is the same leak class
|
|
13
|
+
* `workspace-release-notes-feature-flag-guard.test.ts` blocks for UPDATES.md,
|
|
14
|
+
* via a path that guard doesn't cover.
|
|
15
|
+
*
|
|
16
|
+
* The rule this enforces: flag-gated content enters the template only via a
|
|
17
|
+
* `{{...}}` placeholder substituted by `renderConsolidationPrompt` under a
|
|
18
|
+
* `ConsolidationPromptOptions` gate. When you add a gated section, register
|
|
19
|
+
* it in GATED_SECTIONS below so the guard covers it.
|
|
20
|
+
*/
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { describe, expect, test } from "bun:test";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
CONSOLIDATION_PROMPT_V3,
|
|
27
|
+
CORE_PAGES_CONSOLIDATION_SECTION,
|
|
28
|
+
renderConsolidationPrompt,
|
|
29
|
+
} from "../prompts/consolidation.js";
|
|
30
|
+
|
|
31
|
+
type Registry = {
|
|
32
|
+
flags: Array<{
|
|
33
|
+
key: string;
|
|
34
|
+
scope: string;
|
|
35
|
+
defaultEnabled: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Every flag-gated section of the consolidation prompt, with a short
|
|
41
|
+
* distinctive marker that would survive light rewording of the section body.
|
|
42
|
+
* The guard asserts neither appears in the all-gates-off rendering.
|
|
43
|
+
*/
|
|
44
|
+
const GATED_SECTIONS: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
section: string;
|
|
47
|
+
marker: string;
|
|
48
|
+
}> = [
|
|
49
|
+
{
|
|
50
|
+
name: "memory-v3 core-pages curation (gate: memory-v3-shadow|live)",
|
|
51
|
+
section: CORE_PAGES_CONSOLIDATION_SECTION,
|
|
52
|
+
marker: "core-pages",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/** All `ConsolidationPromptOptions` gates forced off — the v2-only rendering. */
|
|
57
|
+
const ALL_GATES_OFF = {
|
|
58
|
+
includeCorePagesSection: false,
|
|
59
|
+
articleShape: "v2" as const,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Phrases distinctive to the v3 article-shape template. The all-gates-off
|
|
64
|
+
* rendering must contain none of them — the v3 shape drops the `summary:`
|
|
65
|
+
* field that v2 injection depends on, so leaking it to a v2 install corrupts
|
|
66
|
+
* the corpus the install actually serves prompts from.
|
|
67
|
+
*/
|
|
68
|
+
const V3_SHAPE_MARKERS = [
|
|
69
|
+
"The lead IS the card",
|
|
70
|
+
"status: cc-draft",
|
|
71
|
+
"kind: index",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
// Registry helpers mirror workspace-release-notes-feature-flag-guard.test.ts.
|
|
75
|
+
function loadDefaultDisabledAssistantFlagKeys(): string[] {
|
|
76
|
+
const registryPath = join(
|
|
77
|
+
process.cwd(),
|
|
78
|
+
"..",
|
|
79
|
+
"meta",
|
|
80
|
+
"feature-flags",
|
|
81
|
+
"feature-flag-registry.json",
|
|
82
|
+
);
|
|
83
|
+
const registry = JSON.parse(readFileSync(registryPath, "utf-8")) as Registry;
|
|
84
|
+
return registry.flags
|
|
85
|
+
.filter((flag) => flag.scope === "assistant" && !flag.defaultEnabled)
|
|
86
|
+
.map((flag) => flag.key)
|
|
87
|
+
.sort();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function escapeRegExp(value: string): string {
|
|
91
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function featureFlagKeyPattern(key: string): RegExp {
|
|
95
|
+
const escaped = escapeRegExp(key);
|
|
96
|
+
if (key.includes("-")) {
|
|
97
|
+
return new RegExp(`(?:^|[^a-z0-9-])${escaped}(?:$|[^a-z0-9-])`, "i");
|
|
98
|
+
}
|
|
99
|
+
return new RegExp("[`'\"]" + escaped + "[`'\"]", "i");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe("default consolidation prompt flag-gating guard", () => {
|
|
103
|
+
const rendered = renderConsolidationPrompt("Jan 1, 12:00 AM", ALL_GATES_OFF);
|
|
104
|
+
|
|
105
|
+
test("leaves no unsubstituted {{...}} placeholders", () => {
|
|
106
|
+
// A placeholder added to the template but not wired into
|
|
107
|
+
// renderConsolidationPrompt's substitutions would ship raw braces to
|
|
108
|
+
// every default-prompt install.
|
|
109
|
+
const residue = rendered.match(/\{\{[A-Z0-9_]+\}\}/g) ?? [];
|
|
110
|
+
expect(
|
|
111
|
+
residue,
|
|
112
|
+
"Unsubstituted placeholder(s) in the default consolidation prompt — " +
|
|
113
|
+
"wire them through renderConsolidationPrompt and gate them via " +
|
|
114
|
+
"ConsolidationPromptOptions.",
|
|
115
|
+
).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("contains no flag-gated section content when all gates are off", () => {
|
|
119
|
+
const violations: string[] = [];
|
|
120
|
+
for (const { name, section, marker } of GATED_SECTIONS) {
|
|
121
|
+
if (rendered.includes(section)) {
|
|
122
|
+
violations.push(`${name}: full section text present`);
|
|
123
|
+
}
|
|
124
|
+
if (rendered.includes(marker)) {
|
|
125
|
+
violations.push(`${name}: marker "${marker}" present`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const message = [
|
|
130
|
+
"The default consolidation prompt reaches every install with no prompt",
|
|
131
|
+
"override, regardless of feature flags. Flag-gated feature instructions",
|
|
132
|
+
"must enter via a {{...}} placeholder substituted under a",
|
|
133
|
+
"ConsolidationPromptOptions gate (see CORE_PAGES_PLACEHOLDER for the",
|
|
134
|
+
"pattern) — never inline in CONSOLIDATION_PROMPT.",
|
|
135
|
+
"",
|
|
136
|
+
"Violations:",
|
|
137
|
+
...violations.map((violation) => ` - ${violation}`),
|
|
138
|
+
].join("\n");
|
|
139
|
+
expect(violations, message).toEqual([]);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("references no default-disabled assistant feature flags", () => {
|
|
143
|
+
const violations: string[] = [];
|
|
144
|
+
for (const key of loadDefaultDisabledAssistantFlagKeys()) {
|
|
145
|
+
if (featureFlagKeyPattern(key).test(rendered)) {
|
|
146
|
+
violations.push(`references default-disabled flag "${key}"`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
expect(
|
|
150
|
+
violations,
|
|
151
|
+
"The default consolidation prompt must not mention default-disabled " +
|
|
152
|
+
"assistant feature flags by name.\n" +
|
|
153
|
+
violations.map((violation) => ` - ${violation}`).join("\n"),
|
|
154
|
+
).toEqual([]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("contains no v3 article-shape content when the shape gate is v2", () => {
|
|
158
|
+
const violations = V3_SHAPE_MARKERS.filter((marker) =>
|
|
159
|
+
rendered.includes(marker),
|
|
160
|
+
);
|
|
161
|
+
expect(
|
|
162
|
+
violations,
|
|
163
|
+
"The v2 (all-gates-off) rendering must not teach the v3 article shape — " +
|
|
164
|
+
"v2 installs depend on `summary:` fragment pages. v3-shape content " +
|
|
165
|
+
"belongs only in CONSOLIDATION_PROMPT_V3, selected via " +
|
|
166
|
+
"options.articleShape.\nLeaked markers: " +
|
|
167
|
+
violations.join(", "),
|
|
168
|
+
).toEqual([]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("the v3 rendering teaches the v3 shape and drops the v2 summary requirement", () => {
|
|
172
|
+
const v3 = renderConsolidationPrompt("Jan 1, 12:00 AM", {
|
|
173
|
+
includeCorePagesSection: true,
|
|
174
|
+
articleShape: "v3",
|
|
175
|
+
});
|
|
176
|
+
for (const marker of V3_SHAPE_MARKERS) {
|
|
177
|
+
expect(v3).toContain(marker);
|
|
178
|
+
}
|
|
179
|
+
// The v3 shape must not instruct a required `summary:` field — its absence
|
|
180
|
+
// is the wiki↔v2 coupling the deploy sequence is built around.
|
|
181
|
+
expect(v3).not.toContain("The `summary` field is required");
|
|
182
|
+
// §10 rides along under the live flag.
|
|
183
|
+
expect(v3).toContain(CORE_PAGES_CONSOLIDATION_SECTION);
|
|
184
|
+
// The raw template keeps both placeholders wired.
|
|
185
|
+
expect(CONSOLIDATION_PROMPT_V3).toContain("{{CUTOFF}}");
|
|
186
|
+
expect(CONSOLIDATION_PROMPT_V3).toContain("{{CORE_PAGES_SECTION}}");
|
|
187
|
+
expect(v3.match(/\{\{[A-Z0-9_]+\}\}/g) ?? []).toEqual([]);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
extractInjectedConceptSlugs,
|
|
5
|
+
injectedConceptHeader,
|
|
6
|
+
readInjectedBlock,
|
|
7
|
+
} from "../injected-block-slugs.js";
|
|
8
|
+
|
|
9
|
+
describe("extractInjectedConceptSlugs", () => {
|
|
10
|
+
test("extracts nested concept slugs from page headers", () => {
|
|
11
|
+
const block = [
|
|
12
|
+
'Use `file_read("memory/concepts/path/to/file.md")` to read the full pages for any of the injected memory summaries you want more information on.',
|
|
13
|
+
"",
|
|
14
|
+
"# memory/concepts/topics/page-a.md",
|
|
15
|
+
"Summary of page a.",
|
|
16
|
+
"",
|
|
17
|
+
"# memory/concepts/arcs/deep/nested/page-b.md",
|
|
18
|
+
"Summary of page b.",
|
|
19
|
+
].join("\n");
|
|
20
|
+
|
|
21
|
+
expect(extractInjectedConceptSlugs(block)).toEqual([
|
|
22
|
+
"topics/page-a",
|
|
23
|
+
"arcs/deep/nested/page-b",
|
|
24
|
+
]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("handles a <memory>-wrapped block the same as an unwrapped one", () => {
|
|
28
|
+
const wrapped =
|
|
29
|
+
"<memory>\n# memory/concepts/topics/page-a.md\nSummary.\n</memory>";
|
|
30
|
+
expect(extractInjectedConceptSlugs(wrapped)).toEqual(["topics/page-a"]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("ignores skill and CLI sections and non-header lines", () => {
|
|
34
|
+
const block = [
|
|
35
|
+
"# memory/concepts/topics/page-a.md",
|
|
36
|
+
"Summary mentioning memory/concepts/topics/page-x.md inline.",
|
|
37
|
+
"",
|
|
38
|
+
"### Skills You Can Use",
|
|
39
|
+
"- Meeting joiner skill → use skill_load to activate",
|
|
40
|
+
"",
|
|
41
|
+
"### CLI Commands You Can Use",
|
|
42
|
+
"Run `assistant <command> --help` for full usage.",
|
|
43
|
+
"- `assistant export`: export a conversation",
|
|
44
|
+
].join("\n");
|
|
45
|
+
|
|
46
|
+
expect(extractInjectedConceptSlugs(block)).toEqual(["topics/page-a"]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("dedupes repeated headers and returns [] when none match", () => {
|
|
50
|
+
const block =
|
|
51
|
+
"# memory/concepts/topics/page-a.md\nA.\n\n# memory/concepts/topics/page-a.md\nA again.";
|
|
52
|
+
expect(extractInjectedConceptSlugs(block)).toEqual(["topics/page-a"]);
|
|
53
|
+
expect(extractInjectedConceptSlugs("no headers here")).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("injectedConceptHeader", () => {
|
|
58
|
+
test("builds the header the extractor recovers (builder/parser round-trip)", () => {
|
|
59
|
+
const header = injectedConceptHeader("topics/page-a");
|
|
60
|
+
expect(header).toBe("# memory/concepts/topics/page-a.md");
|
|
61
|
+
expect(extractInjectedConceptSlugs(`${header}\nBody.`)).toEqual([
|
|
62
|
+
"topics/page-a",
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("readInjectedBlock", () => {
|
|
68
|
+
test("reads the requested key off valid metadata JSON", () => {
|
|
69
|
+
const metadata = JSON.stringify({
|
|
70
|
+
memoryInjectedBlock: "v2 block",
|
|
71
|
+
memoryV3InjectedBlock: "v3 block",
|
|
72
|
+
});
|
|
73
|
+
expect(readInjectedBlock(metadata, "memoryInjectedBlock")).toBe("v2 block");
|
|
74
|
+
expect(readInjectedBlock(metadata, "memoryV3InjectedBlock")).toBe(
|
|
75
|
+
"v3 block",
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("returns null for absent, non-string, malformed, or non-object metadata", () => {
|
|
80
|
+
expect(readInjectedBlock(null, "memoryInjectedBlock")).toBeNull();
|
|
81
|
+
expect(readInjectedBlock(undefined, "memoryInjectedBlock")).toBeNull();
|
|
82
|
+
expect(readInjectedBlock("", "memoryInjectedBlock")).toBeNull();
|
|
83
|
+
expect(readInjectedBlock("not json", "memoryInjectedBlock")).toBeNull();
|
|
84
|
+
expect(readInjectedBlock('["array"]', "memoryInjectedBlock")).toBeNull();
|
|
85
|
+
expect(
|
|
86
|
+
readInjectedBlock('{"memoryInjectedBlock": 42}', "memoryInjectedBlock"),
|
|
87
|
+
).toBeNull();
|
|
88
|
+
expect(readInjectedBlock("{}", "memoryInjectedBlock")).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -208,6 +208,39 @@ describe("writePage + readPage round-trip", () => {
|
|
|
208
208
|
]);
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
test("accepts and round-trips the v3 wiki-article frontmatter fields", async () => {
|
|
212
|
+
// The full shape CONSOLIDATION_PROMPT_V3 teaches and migrated corpora
|
|
213
|
+
// arrive in. readPage() throws on schema failure and one invalid page in
|
|
214
|
+
// a turn's top-K no-ops the whole v2 injection block, so rejecting these
|
|
215
|
+
// fields would break every article of a migrated corpus at once.
|
|
216
|
+
const page = makePage({
|
|
217
|
+
slug: "wiki-shaped",
|
|
218
|
+
frontmatter: {
|
|
219
|
+
edges: [],
|
|
220
|
+
ref_files: [],
|
|
221
|
+
ref_urls: [],
|
|
222
|
+
title: "Wiki Shaped — A Display Title",
|
|
223
|
+
slug: "wiki-shaped",
|
|
224
|
+
tags: ["topic-area", "index"],
|
|
225
|
+
main: "parent-hub",
|
|
226
|
+
kind: "index",
|
|
227
|
+
status: "cc-draft",
|
|
228
|
+
links: ["child-slug — why this link exists"],
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
await writePage(workspaceDir, page);
|
|
232
|
+
|
|
233
|
+
const read = await readPage(workspaceDir, "wiki-shaped");
|
|
234
|
+
expect(read).not.toBeNull();
|
|
235
|
+
expect(read!.frontmatter.title).toBe("Wiki Shaped — A Display Title");
|
|
236
|
+
expect(read!.frontmatter.tags).toEqual(["topic-area", "index"]);
|
|
237
|
+
expect(read!.frontmatter.main).toBe("parent-hub");
|
|
238
|
+
expect(read!.frontmatter.kind).toBe("index");
|
|
239
|
+
// The draft marker must survive a programmatic write → read round-trip:
|
|
240
|
+
// stripping it would silently un-draft a page mid-voice-pass.
|
|
241
|
+
expect(read!.frontmatter.status).toBe("cc-draft");
|
|
242
|
+
});
|
|
243
|
+
|
|
211
244
|
test("renders frontmatter at the top with --- delimiters", async () => {
|
|
212
245
|
const page = makePage();
|
|
213
246
|
await writePage(workspaceDir, page);
|
|
@@ -60,13 +60,32 @@ afterAll(() => {
|
|
|
60
60
|
rmSync(tmpWorkspace, { recursive: true, force: true });
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
const {
|
|
64
|
-
|
|
63
|
+
const {
|
|
64
|
+
CONSOLIDATION_PROMPT,
|
|
65
|
+
CORE_PAGES_CONSOLIDATION_SECTION,
|
|
66
|
+
CORE_PAGES_PLACEHOLDER,
|
|
67
|
+
CUTOFF_PLACEHOLDER,
|
|
68
|
+
resolveConsolidationPrompt,
|
|
69
|
+
} = await import("../prompts/consolidation.js");
|
|
65
70
|
|
|
66
71
|
const CUTOFF = "2026-05-01T12:00:00.000Z";
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
/** Options for tests not exercising the v3 core-pages gate. */
|
|
74
|
+
const NO_CORE = { includeCorePagesSection: false, articleShape: "v2" as const };
|
|
75
|
+
const WITH_CORE = {
|
|
76
|
+
includeCorePagesSection: true,
|
|
77
|
+
articleShape: "v2" as const,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const bundledPrompt = (includeCorePagesSection = false): string =>
|
|
81
|
+
(CONSOLIDATION_PROMPT as string)
|
|
82
|
+
.replaceAll(CUTOFF_PLACEHOLDER, CUTOFF)
|
|
83
|
+
.replaceAll(
|
|
84
|
+
CORE_PAGES_PLACEHOLDER,
|
|
85
|
+
includeCorePagesSection
|
|
86
|
+
? (CORE_PAGES_CONSOLIDATION_SECTION as string)
|
|
87
|
+
: "",
|
|
88
|
+
);
|
|
70
89
|
|
|
71
90
|
beforeEach(() => {
|
|
72
91
|
warnCalls.length = 0;
|
|
@@ -88,7 +107,7 @@ afterEach(() => {
|
|
|
88
107
|
|
|
89
108
|
describe("resolveConsolidationPrompt — no override", () => {
|
|
90
109
|
test("returns the bundled prompt with {{CUTOFF}} substituted when overridePath is null", () => {
|
|
91
|
-
const result = resolveConsolidationPrompt(null, CUTOFF);
|
|
110
|
+
const result = resolveConsolidationPrompt(null, CUTOFF, NO_CORE);
|
|
92
111
|
expect(result).toContain("You are running memory consolidation");
|
|
93
112
|
expect(result).toContain(CUTOFF);
|
|
94
113
|
expect(result).not.toContain(CUTOFF_PLACEHOLDER);
|
|
@@ -96,12 +115,49 @@ describe("resolveConsolidationPrompt — no override", () => {
|
|
|
96
115
|
});
|
|
97
116
|
});
|
|
98
117
|
|
|
118
|
+
describe("resolveConsolidationPrompt — core-pages gate", () => {
|
|
119
|
+
test("omits the core-pages section (and any placeholder residue) when the v3 gate is off", () => {
|
|
120
|
+
// v2-only installs must not be told to curate a file nothing reads.
|
|
121
|
+
const result = resolveConsolidationPrompt(null, CUTOFF, NO_CORE);
|
|
122
|
+
expect(result).not.toContain("core-pages");
|
|
123
|
+
expect(result).not.toContain(CORE_PAGES_PLACEHOLDER);
|
|
124
|
+
// The section's slot collapses cleanly: §9 flows straight into the
|
|
125
|
+
// separator with no stray blank lines.
|
|
126
|
+
expect(result).toContain(
|
|
127
|
+
"never wholesale-clear.\n\n---\n\n# What NOT to do",
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("includes the core-pages section exactly once, in place, when the v3 gate is on", () => {
|
|
132
|
+
const result = resolveConsolidationPrompt(null, CUTOFF, WITH_CORE);
|
|
133
|
+
expect(result).toContain("## 10. Review `memory/core-pages.md`");
|
|
134
|
+
expect(result.split("## 10. Review").length - 1).toBe(1);
|
|
135
|
+
expect(result).not.toContain(CORE_PAGES_PLACEHOLDER);
|
|
136
|
+
// Positioned between §9 and the don'ts, as authored.
|
|
137
|
+
const sectionAt = result.indexOf("## 10. Review");
|
|
138
|
+
expect(sectionAt).toBeGreaterThan(result.indexOf("## 9. Trim"));
|
|
139
|
+
expect(sectionAt).toBeLessThan(result.indexOf("# What NOT to do"));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("substitutes the placeholder in override files per the same gate", () => {
|
|
143
|
+
const path = join(tmpWorkspace, "custom-prompt.md");
|
|
144
|
+
writeFileSync(path, "Before\n{{CORE_PAGES_SECTION}}After {{CUTOFF}}\n");
|
|
145
|
+
|
|
146
|
+
const withCore = resolveConsolidationPrompt(path, CUTOFF, WITH_CORE);
|
|
147
|
+
expect(withCore).toContain("## 10. Review `memory/core-pages.md`");
|
|
148
|
+
expect(withCore).not.toContain(CORE_PAGES_PLACEHOLDER);
|
|
149
|
+
|
|
150
|
+
const withoutCore = resolveConsolidationPrompt(path, CUTOFF, NO_CORE);
|
|
151
|
+
expect(withoutCore).toBe(`Before\nAfter ${CUTOFF}\n`);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
99
155
|
describe("resolveConsolidationPrompt — with override", () => {
|
|
100
156
|
test("loads an absolute path verbatim and substitutes {{CUTOFF}}", () => {
|
|
101
157
|
const path = join(tmpWorkspace, "custom-prompt.md");
|
|
102
158
|
writeFileSync(path, "Custom prompt at {{CUTOFF}}\n");
|
|
103
159
|
|
|
104
|
-
const result = resolveConsolidationPrompt(path, CUTOFF);
|
|
160
|
+
const result = resolveConsolidationPrompt(path, CUTOFF, NO_CORE);
|
|
105
161
|
|
|
106
162
|
expect(result).toBe(`Custom prompt at ${CUTOFF}\n`);
|
|
107
163
|
expect(warnCalls).toHaveLength(0);
|
|
@@ -113,7 +169,11 @@ describe("resolveConsolidationPrompt — with override", () => {
|
|
|
113
169
|
"Workspace-relative {{CUTOFF}}\n",
|
|
114
170
|
);
|
|
115
171
|
|
|
116
|
-
const result = resolveConsolidationPrompt(
|
|
172
|
+
const result = resolveConsolidationPrompt(
|
|
173
|
+
"custom-prompt.md",
|
|
174
|
+
CUTOFF,
|
|
175
|
+
NO_CORE,
|
|
176
|
+
);
|
|
117
177
|
|
|
118
178
|
expect(result).toBe(`Workspace-relative ${CUTOFF}\n`);
|
|
119
179
|
expect(warnCalls).toHaveLength(0);
|
|
@@ -124,7 +184,11 @@ describe("resolveConsolidationPrompt — with override", () => {
|
|
|
124
184
|
const path = join(homedir(), filename);
|
|
125
185
|
writeFileSync(path, "Home dir {{CUTOFF}}\n");
|
|
126
186
|
try {
|
|
127
|
-
const result = resolveConsolidationPrompt(
|
|
187
|
+
const result = resolveConsolidationPrompt(
|
|
188
|
+
`~/${filename}`,
|
|
189
|
+
CUTOFF,
|
|
190
|
+
NO_CORE,
|
|
191
|
+
);
|
|
128
192
|
expect(result).toBe(`Home dir ${CUTOFF}\n`);
|
|
129
193
|
expect(warnCalls).toHaveLength(0);
|
|
130
194
|
} finally {
|
|
@@ -136,7 +200,11 @@ describe("resolveConsolidationPrompt — with override", () => {
|
|
|
136
200
|
const body = "No placeholder here. Just a plain prompt.\n";
|
|
137
201
|
writeFileSync(join(tmpWorkspace, "no-placeholder.md"), body);
|
|
138
202
|
|
|
139
|
-
const result = resolveConsolidationPrompt(
|
|
203
|
+
const result = resolveConsolidationPrompt(
|
|
204
|
+
"no-placeholder.md",
|
|
205
|
+
CUTOFF,
|
|
206
|
+
NO_CORE,
|
|
207
|
+
);
|
|
140
208
|
|
|
141
209
|
expect(result).toBe(body);
|
|
142
210
|
expect(warnCalls).toHaveLength(0);
|
|
@@ -148,7 +216,11 @@ describe("resolveConsolidationPrompt — with override", () => {
|
|
|
148
216
|
"{{CUTOFF}} ... {{CUTOFF}} ... {{CUTOFF}}",
|
|
149
217
|
);
|
|
150
218
|
|
|
151
|
-
const result = resolveConsolidationPrompt(
|
|
219
|
+
const result = resolveConsolidationPrompt(
|
|
220
|
+
"custom-prompt.md",
|
|
221
|
+
CUTOFF,
|
|
222
|
+
NO_CORE,
|
|
223
|
+
);
|
|
152
224
|
|
|
153
225
|
expect(result).toBe(`${CUTOFF} ... ${CUTOFF} ... ${CUTOFF}`);
|
|
154
226
|
expect(result).not.toContain(CUTOFF_PLACEHOLDER);
|
|
@@ -160,6 +232,7 @@ describe("resolveConsolidationPrompt — failure modes", () => {
|
|
|
160
232
|
const result = resolveConsolidationPrompt(
|
|
161
233
|
"/this/path/does/not/exist.md",
|
|
162
234
|
CUTOFF,
|
|
235
|
+
NO_CORE,
|
|
163
236
|
);
|
|
164
237
|
|
|
165
238
|
expect(result).toBe(bundledPrompt());
|
|
@@ -173,7 +246,7 @@ describe("resolveConsolidationPrompt — failure modes", () => {
|
|
|
173
246
|
const path = join(tmpWorkspace, "empty.md");
|
|
174
247
|
writeFileSync(path, "");
|
|
175
248
|
|
|
176
|
-
const result = resolveConsolidationPrompt(path, CUTOFF);
|
|
249
|
+
const result = resolveConsolidationPrompt(path, CUTOFF, NO_CORE);
|
|
177
250
|
|
|
178
251
|
expect(result).toBe(bundledPrompt());
|
|
179
252
|
expect(warnCalls).toHaveLength(1);
|
|
@@ -185,7 +258,7 @@ describe("resolveConsolidationPrompt — failure modes", () => {
|
|
|
185
258
|
const path = join(tmpWorkspace, "empty.md");
|
|
186
259
|
writeFileSync(path, " \n\n\t\n");
|
|
187
260
|
|
|
188
|
-
const result = resolveConsolidationPrompt(path, CUTOFF);
|
|
261
|
+
const result = resolveConsolidationPrompt(path, CUTOFF, NO_CORE);
|
|
189
262
|
|
|
190
263
|
expect(result).toBe(bundledPrompt());
|
|
191
264
|
expect(warnCalls).toHaveLength(1);
|
|
@@ -198,7 +271,7 @@ describe("resolveConsolidationPrompt — failure modes", () => {
|
|
|
198
271
|
// 1 MiB + 1 byte — just over the cap so we don't waste test memory.
|
|
199
272
|
writeFileSync(path, Buffer.alloc(1 * 1024 * 1024 + 1, 0x61));
|
|
200
273
|
|
|
201
|
-
const result = resolveConsolidationPrompt(path, CUTOFF);
|
|
274
|
+
const result = resolveConsolidationPrompt(path, CUTOFF, NO_CORE);
|
|
202
275
|
|
|
203
276
|
expect(result).toBe(bundledPrompt());
|
|
204
277
|
expect(warnCalls).toHaveLength(1);
|
|
@@ -213,7 +286,7 @@ describe("resolveConsolidationPrompt — failure modes", () => {
|
|
|
213
286
|
const link = join(tmpWorkspace, "link.md");
|
|
214
287
|
symlinkSync(target, link);
|
|
215
288
|
|
|
216
|
-
const result = resolveConsolidationPrompt(link, CUTOFF);
|
|
289
|
+
const result = resolveConsolidationPrompt(link, CUTOFF, NO_CORE);
|
|
217
290
|
|
|
218
291
|
expect(result).toBe(bundledPrompt());
|
|
219
292
|
expect(warnCalls).toHaveLength(1);
|
|
@@ -230,7 +303,7 @@ describe("resolveConsolidationPrompt — failure modes", () => {
|
|
|
230
303
|
return;
|
|
231
304
|
}
|
|
232
305
|
|
|
233
|
-
const result = resolveConsolidationPrompt(fifoPath, CUTOFF);
|
|
306
|
+
const result = resolveConsolidationPrompt(fifoPath, CUTOFF, NO_CORE);
|
|
234
307
|
|
|
235
308
|
expect(result).toBe(bundledPrompt());
|
|
236
309
|
expect(warnCalls).toHaveLength(1);
|
|
@@ -11,7 +11,11 @@ import { eq } from "drizzle-orm";
|
|
|
11
11
|
|
|
12
12
|
import type { DrizzleDb } from "../db-connection.js";
|
|
13
13
|
import { activationState } from "../schema.js";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type ActivationState,
|
|
16
|
+
ActivationStateSchema,
|
|
17
|
+
type EverInjectedEntry,
|
|
18
|
+
} from "./types.js";
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
21
|
* Load the activation state for a conversation, or `null` if no row exists.
|
|
@@ -118,6 +122,51 @@ export function forkActivationState(
|
|
|
118
122
|
.run();
|
|
119
123
|
}
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Seed a truncated fork's activation row from the concept slugs whose
|
|
127
|
+
* `<memory>` attachments the child actually inherited.
|
|
128
|
+
*
|
|
129
|
+
* Truncated forks cannot take the wholesale `forkActivationState` copy: the
|
|
130
|
+
* parent row marks slugs injected on turns the child does not contain, so
|
|
131
|
+
* copying it would suppress those pages in the child forever (silent recall
|
|
132
|
+
* holes). Seeding nothing has the opposite failure — every attachment already
|
|
133
|
+
* present in the copied history gets re-selected and re-attached on the
|
|
134
|
+
* child's next turns as a duplicate. Deriving `everInjected` from the
|
|
135
|
+
* inherited attachments themselves is exact by construction.
|
|
136
|
+
*
|
|
137
|
+
* Activation scores and the turn counter intentionally start fresh: the turn
|
|
138
|
+
* counter is supplied by the graph tracker, which is also not copied for
|
|
139
|
+
* truncated forks, so both counters restart together. Inherited entries are
|
|
140
|
+
* stamped `turn: 0` — dedup is slug-set membership (`turn` is bookkeeping
|
|
141
|
+
* only) and compaction clears the list wholesale.
|
|
142
|
+
*
|
|
143
|
+
* No-op when the child inherited no memory attachments. Synchronous so it can
|
|
144
|
+
* run inside the transaction that wraps `forkConversation()`.
|
|
145
|
+
*/
|
|
146
|
+
export function seedForkActivationState(
|
|
147
|
+
database: DrizzleDb,
|
|
148
|
+
newConversationId: string,
|
|
149
|
+
inheritedSlugs: string[],
|
|
150
|
+
): void {
|
|
151
|
+
if (inheritedSlugs.length === 0) return;
|
|
152
|
+
|
|
153
|
+
const everInjected: EverInjectedEntry[] = inheritedSlugs.map((slug) => ({
|
|
154
|
+
slug,
|
|
155
|
+
turn: 0,
|
|
156
|
+
}));
|
|
157
|
+
database
|
|
158
|
+
.insert(activationState)
|
|
159
|
+
.values({
|
|
160
|
+
conversationId: newConversationId,
|
|
161
|
+
messageId: `${newConversationId}:turn:0`,
|
|
162
|
+
stateJson: "{}",
|
|
163
|
+
everInjectedJson: JSON.stringify(everInjected),
|
|
164
|
+
currentTurn: 0,
|
|
165
|
+
updatedAt: Date.now(),
|
|
166
|
+
})
|
|
167
|
+
.run();
|
|
168
|
+
}
|
|
169
|
+
|
|
121
170
|
/**
|
|
122
171
|
* Clear all `everInjected` entries. Used after compaction: the cached
|
|
123
172
|
* `<memory>` attachments those slugs lived on are gone, so future turns
|