@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
|
@@ -1,23 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for the memory-v3
|
|
3
|
-
*
|
|
2
|
+
* Tests for the memory-v3 step-0 branch in `applyRuntimeInjections`:
|
|
3
|
+
* spotlight strip + v2 tail suppression.
|
|
4
4
|
*
|
|
5
5
|
* When the `memory-v3-live` flag is on AND the v3 injector (id `memory-v3`,
|
|
6
|
-
* placement `after-memory-prefix`)
|
|
7
|
-
* assembly strips the v2 `<memory>` prefix from
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* placement `after-memory-prefix`) produces a block — possibly EMPTY-TEXT on
|
|
7
|
+
* an all-repeat turn — runtime assembly strips the v2 `<memory>` prefix from
|
|
8
|
+
* the TAIL user message only before splicing the v3 block. Historical user
|
|
9
|
+
* messages keep their memory blocks byte-identical (frozen v3 cards and
|
|
10
|
+
* pre-cutover v2 blocks both ride the cached prefix); the old whole-layer
|
|
11
|
+
* strip is gone.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* The ephemeral `<memory_spotlight>` block is strip-and-replaced every turn:
|
|
14
|
+
* stale spotlights are removed from every user message unconditionally (a
|
|
15
|
+
* scoped, single-id strip), and the spotlight injector's `append-user-tail`
|
|
16
|
+
* block lands at the tail.
|
|
13
17
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
18
|
+
* v2 suppression stays keyed off whether v3 produced a block, NOT off the
|
|
19
|
+
* flag alone: a v3 failure (`produce()` → null) leaves v2's block intact
|
|
20
|
+
* (fallback-to-v2). The flag-off path must be byte-for-byte identical to
|
|
21
|
+
* today — that is the load-bearing regression guard. `applyRuntimeInjections`
|
|
22
|
+
* reads the flag itself, so these tests drive it through the override cache.
|
|
23
|
+
*
|
|
24
|
+
* The strip discriminates v2's dynamic block by IDENTITY, not by prefix: v2's
|
|
25
|
+
* `INJECTION_HEADER` and v3's `V3_CARDS_INJECTION_HEADER` are deliberately
|
|
26
|
+
* byte-identical, and v2's router block leads with that header whenever any
|
|
27
|
+
* summary section is present, so no prefix can tell the layers apart. The
|
|
28
|
+
* identity — the exact text v2 prepended this turn — is read off the live
|
|
29
|
+
* graph-memory handle, which these tests register and seed per test.
|
|
17
30
|
*/
|
|
18
31
|
|
|
19
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
32
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
20
33
|
|
|
34
|
+
import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
35
|
+
import { wrapMemorySpotlightBlock } from "../memory/memory-marker.js";
|
|
36
|
+
import { INJECTION_HEADER } from "../memory/v2/injection.js";
|
|
37
|
+
import { V3_CARDS_INJECTION_HEADER } from "../plugins/defaults/memory-v3-shadow/render-injection.js";
|
|
38
|
+
import { MEMORY_V3_COMMIT_META_KEY } from "../plugins/defaults/memory-v3-shadow/types.js";
|
|
21
39
|
import type {
|
|
22
40
|
InjectionBlock,
|
|
23
41
|
Injector,
|
|
@@ -28,7 +46,7 @@ import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
|
28
46
|
|
|
29
47
|
// Drive the suppression branch by controlling the static injector chain that
|
|
30
48
|
// `applyRuntimeInjections` walks. The slot is mutated per-test to stand in for
|
|
31
|
-
// the memory-v3
|
|
49
|
+
// the memory-v3 injectors producing (or not producing) blocks.
|
|
32
50
|
const injectorChainSlot: Injector[] = [];
|
|
33
51
|
mock.module("../plugins/defaults/memory-retrieval/injector-chain.js", () => ({
|
|
34
52
|
getInjectorChain: () => injectorChainSlot,
|
|
@@ -46,13 +64,31 @@ function makeTurnContext(): TurnContext {
|
|
|
46
64
|
};
|
|
47
65
|
}
|
|
48
66
|
|
|
67
|
+
/** Live graph handles registered by the current test (disposed afterwards). */
|
|
68
|
+
const seededGraphs: ConversationGraphMemory[] = [];
|
|
69
|
+
|
|
49
70
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
71
|
+
* Register a live graph-memory handle for `conv-test-1` whose last retrieval
|
|
72
|
+
* injected `text` — the identity the suppression strip resolves via
|
|
73
|
+
* `getLiveGraphMemory(conversationId)?.lastInjectedBlockText`. The production
|
|
74
|
+
* setter is `prepareMemory` (which needs a live DB), so the cached block is
|
|
75
|
+
* seeded directly through the private field.
|
|
54
76
|
*/
|
|
55
|
-
function
|
|
77
|
+
function seedV2Identity(text: string | null): void {
|
|
78
|
+
const graph = new ConversationGraphMemory("conv-test-1");
|
|
79
|
+
(graph as unknown as { lastInjectedBlock: string | null }).lastInjectedBlock =
|
|
80
|
+
text;
|
|
81
|
+
seededGraphs.push(graph);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A fake v3 cards injector that mirrors the real one's id + placement. The
|
|
86
|
+
* real injector wraps its content in `<memory>\n…\n</memory>`, so the fake
|
|
87
|
+
* does too. `inner === null` means the injector produced nothing this turn
|
|
88
|
+
* (error/empty selection); `inner === ""` mirrors an all-repeat turn (block
|
|
89
|
+
* produced, empty text — nothing attached, but v2 is still suppressed).
|
|
90
|
+
*/
|
|
91
|
+
function v3Injector(inner: string | null, commit?: () => void): Injector {
|
|
56
92
|
return {
|
|
57
93
|
name: "memory-v3-shadow",
|
|
58
94
|
order: 1000,
|
|
@@ -60,8 +96,24 @@ function v3Injector(inner: string | null): Injector {
|
|
|
60
96
|
if (inner === null) return null;
|
|
61
97
|
return {
|
|
62
98
|
id: "memory-v3",
|
|
63
|
-
text: `<memory>\n${inner}\n</memory>`,
|
|
99
|
+
text: inner === "" ? "" : `<memory>\n${inner}\n</memory>`,
|
|
64
100
|
placement: "after-memory-prefix",
|
|
101
|
+
...(commit ? { meta: { [MEMORY_V3_COMMIT_META_KEY]: commit } } : {}),
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A fake v3 spotlight injector mirroring the real id + tail placement. */
|
|
108
|
+
function spotlightInjector(inner: string): Injector {
|
|
109
|
+
return {
|
|
110
|
+
name: "memory-v3-spotlight",
|
|
111
|
+
order: 1001,
|
|
112
|
+
async produce(): Promise<InjectionBlock | null> {
|
|
113
|
+
return {
|
|
114
|
+
id: "memory-v3-spotlight",
|
|
115
|
+
text: wrapMemorySpotlightBlock(inner),
|
|
116
|
+
placement: "append-user-tail",
|
|
65
117
|
};
|
|
66
118
|
},
|
|
67
119
|
};
|
|
@@ -94,14 +146,25 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
94
146
|
setOverridesForTesting({});
|
|
95
147
|
});
|
|
96
148
|
|
|
97
|
-
|
|
149
|
+
afterEach(() => {
|
|
150
|
+
for (const graph of seededGraphs) graph.dispose();
|
|
151
|
+
seededGraphs.length = 0;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("flag ON + v3 produced a block → TAIL v2 stripped, historical memory blocks frozen in place", async () => {
|
|
98
155
|
setOverridesForTesting({ "memory-v3-live": true });
|
|
99
|
-
injectorChainSlot.push(v3Injector("
|
|
156
|
+
injectorChainSlot.push(v3Injector("net-new cards"));
|
|
157
|
+
seedV2Identity("fresh recalled fact");
|
|
100
158
|
|
|
101
|
-
// History: a prior user turn
|
|
102
|
-
//
|
|
159
|
+
// History: a prior user turn carrying a frozen memory block (a v3 card
|
|
160
|
+
// block or a pre-cutover v2 block — same wrapper), plus the current tail
|
|
161
|
+
// user turn with v2's freshly-prepended block.
|
|
162
|
+
const historicalUser = userMsgWithV2Memory(
|
|
163
|
+
"frozen card from turn 1",
|
|
164
|
+
"earlier question",
|
|
165
|
+
);
|
|
103
166
|
const runMessages: Message[] = [
|
|
104
|
-
|
|
167
|
+
historicalUser,
|
|
105
168
|
{
|
|
106
169
|
role: "assistant",
|
|
107
170
|
content: [{ type: "text", text: "earlier answer" }],
|
|
@@ -113,31 +176,292 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
113
176
|
...makeTurnContext(),
|
|
114
177
|
});
|
|
115
178
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
120
|
-
.map((b) => b.text),
|
|
121
|
-
);
|
|
122
|
-
const memoryBlocks = allTexts.filter((t) => t.startsWith("<memory>"));
|
|
123
|
-
expect(memoryBlocks).toHaveLength(1);
|
|
124
|
-
// And it is the v3 block, not a v2 one.
|
|
125
|
-
expect(memoryBlocks[0]).toBe("<memory>\nv3 working set\n</memory>");
|
|
179
|
+
// The HISTORICAL user turn keeps its memory block byte-identical — the
|
|
180
|
+
// frozen-card cache contract (no whole-layer strip).
|
|
181
|
+
expect(result.messages[0].content).toEqual(historicalUser.content);
|
|
126
182
|
|
|
127
|
-
// The
|
|
128
|
-
const firstUser = result.messages[0];
|
|
129
|
-
expect(
|
|
130
|
-
firstUser.content.every(
|
|
131
|
-
(b) => !(b.type === "text" && b.text.startsWith("<memory>")),
|
|
132
|
-
),
|
|
133
|
-
).toBe(true);
|
|
134
|
-
|
|
135
|
-
// The v3 block lands at the head of the tail user message; original user
|
|
183
|
+
// The tail's fresh v2 block is replaced by the v3 block; original user
|
|
136
184
|
// text is preserved right after it.
|
|
137
185
|
const texts = tailTexts(result.messages);
|
|
138
|
-
expect(texts[0]).toBe("<memory>\
|
|
186
|
+
expect(texts[0]).toBe("<memory>\nnet-new cards\n</memory>");
|
|
139
187
|
expect(texts[1]).toBe("current question");
|
|
140
188
|
expect(texts).toHaveLength(2);
|
|
189
|
+
|
|
190
|
+
// The v3 block is captured for metadata persistence (UNWRAPPED) and the
|
|
191
|
+
// turn is marked v3-active so the hook skips v2's metadata write.
|
|
192
|
+
expect(result.blocks.memoryV3InjectedBlock).toBe("net-new cards");
|
|
193
|
+
expect(result.blocks.memoryV3Active).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("flag ON + EMPTY-TEXT v3 block (all-repeat turn) → tail v2 stripped, nothing attached", async () => {
|
|
197
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
198
|
+
injectorChainSlot.push(v3Injector(""));
|
|
199
|
+
seedV2Identity("fresh recalled fact");
|
|
200
|
+
|
|
201
|
+
const runMessages: Message[] = [
|
|
202
|
+
userMsgWithV2Memory("fresh recalled fact", "current question"),
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
206
|
+
...makeTurnContext(),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// v2's fresh block is gone and NO v3 content replaced it — the frozen
|
|
210
|
+
// cards from prior turns already carry the memory.
|
|
211
|
+
const texts = tailTexts(result.messages);
|
|
212
|
+
expect(texts).toEqual(["current question"]);
|
|
213
|
+
// No bytes to persist, but the turn is still v3-active (gates v2's
|
|
214
|
+
// metadata write so the stripped block is not rehydrated on reload).
|
|
215
|
+
expect(result.blocks.memoryV3InjectedBlock).toBeUndefined();
|
|
216
|
+
expect(result.blocks.memoryV3Active).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("stale spotlight blocks are stripped from EVERY user message; the new spotlight lands at the tail", async () => {
|
|
220
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
221
|
+
injectorChainSlot.push(v3Injector(""), spotlightInjector("fresh sections"));
|
|
222
|
+
|
|
223
|
+
const staleSpotlight = {
|
|
224
|
+
type: "text" as const,
|
|
225
|
+
text: wrapMemorySpotlightBlock("stale sections"),
|
|
226
|
+
};
|
|
227
|
+
const runMessages: Message[] = [
|
|
228
|
+
{
|
|
229
|
+
role: "user",
|
|
230
|
+
content: [{ type: "text", text: "earlier question" }, staleSpotlight],
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
role: "assistant",
|
|
234
|
+
content: [{ type: "text", text: "earlier answer" }],
|
|
235
|
+
},
|
|
236
|
+
{ role: "user", content: [{ type: "text", text: "current question" }] },
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
240
|
+
...makeTurnContext(),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// The stale spotlight is gone from the historical turn…
|
|
244
|
+
expect(result.messages[0].content).toEqual([
|
|
245
|
+
{ type: "text", text: "earlier question" },
|
|
246
|
+
]);
|
|
247
|
+
// …and exactly one fresh spotlight sits at the tail.
|
|
248
|
+
const texts = tailTexts(result.messages);
|
|
249
|
+
expect(texts).toEqual([
|
|
250
|
+
"current question",
|
|
251
|
+
wrapMemorySpotlightBlock("fresh sections"),
|
|
252
|
+
]);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("convergence re-entry: a tail leading with this turn's frozen v3 cards (and <info>) is NOT stripped", async () => {
|
|
256
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
257
|
+
// Re-entry shape: the memo returns the same selections, every slug is now
|
|
258
|
+
// in the everInjected store, so the injector produces an EMPTY block —
|
|
259
|
+
// while the tail still carries the v3 card block frozen on first entry
|
|
260
|
+
// (leading the content because no workspace / <turn_context> prepend
|
|
261
|
+
// fired this turn) plus the <info> static block. The graph handle still
|
|
262
|
+
// holds this turn's v2 identity (stripped on first entry, so absent from
|
|
263
|
+
// the tail) — it must not match the frozen cards.
|
|
264
|
+
injectorChainSlot.push(v3Injector(""));
|
|
265
|
+
seedV2Identity("fresh recalled fact");
|
|
266
|
+
|
|
267
|
+
const frozenV3Block = `<memory>\n${V3_CARDS_INJECTION_HEADER}\n\n# memory/concepts/page-a.md\nhead\n</memory>`;
|
|
268
|
+
const infoBlock = "<info>\nstatic memory\n</info>";
|
|
269
|
+
const runMessages: Message[] = [
|
|
270
|
+
{
|
|
271
|
+
role: "user",
|
|
272
|
+
content: [
|
|
273
|
+
{ type: "text", text: infoBlock },
|
|
274
|
+
{ type: "text", text: frozenV3Block },
|
|
275
|
+
{ type: "text", text: "current question" },
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
281
|
+
...makeTurnContext(),
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// The just-frozen cards and the static block survive re-entry — only
|
|
285
|
+
// v2's fresh dynamic prefix (absent here; stripped on first entry) is
|
|
286
|
+
// ever removed.
|
|
287
|
+
expect(tailTexts(result.messages)).toEqual([
|
|
288
|
+
infoBlock,
|
|
289
|
+
frozenV3Block,
|
|
290
|
+
"current question",
|
|
291
|
+
]);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("first entry with a v2 prefix AND a frozen v3 block strips ONLY the v2 block", async () => {
|
|
295
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
296
|
+
injectorChainSlot.push(v3Injector(""));
|
|
297
|
+
seedV2Identity("fresh recalled fact");
|
|
298
|
+
|
|
299
|
+
const frozenV3Block = `<memory>\n${V3_CARDS_INJECTION_HEADER}\n\n# memory/concepts/page-a.md\nhead\n</memory>`;
|
|
300
|
+
const runMessages: Message[] = [
|
|
301
|
+
{
|
|
302
|
+
role: "user",
|
|
303
|
+
content: [
|
|
304
|
+
{ type: "text", text: "<memory>\nfresh recalled fact\n</memory>" },
|
|
305
|
+
{ type: "text", text: frozenV3Block },
|
|
306
|
+
{ type: "text", text: "current question" },
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
312
|
+
...makeTurnContext(),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
expect(tailTexts(result.messages)).toEqual([
|
|
316
|
+
frozenV3Block,
|
|
317
|
+
"current question",
|
|
318
|
+
]);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("REGRESSION: a v2 block leading with the REAL summary header (byte-identical to v3's) is stripped; v3 cards and <info> survive (first entry)", async () => {
|
|
322
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
323
|
+
injectorChainSlot.push(v3Injector(""));
|
|
324
|
+
|
|
325
|
+
// The collision this guards: v2's router block leads with
|
|
326
|
+
// INJECTION_HEADER whenever any summary section is present — the dominant
|
|
327
|
+
// production case — and v3's card header is deliberately the same bytes.
|
|
328
|
+
expect(INJECTION_HEADER).toBe(V3_CARDS_INJECTION_HEADER);
|
|
329
|
+
|
|
330
|
+
const v2Inner = `${INJECTION_HEADER}\n\n## memory/concepts/page-b.md\nsummary of page b`;
|
|
331
|
+
seedV2Identity(v2Inner);
|
|
332
|
+
|
|
333
|
+
const frozenV3Block = `<memory>\n${V3_CARDS_INJECTION_HEADER}\n\n# memory/concepts/page-a.md\nhead\n</memory>`;
|
|
334
|
+
const infoBlock = "<info>\nstatic memory\n</info>";
|
|
335
|
+
const runMessages: Message[] = [
|
|
336
|
+
{
|
|
337
|
+
role: "user",
|
|
338
|
+
content: [
|
|
339
|
+
{ type: "text", text: `<memory>\n${v2Inner}\n</memory>` },
|
|
340
|
+
{ type: "text", text: frozenV3Block },
|
|
341
|
+
{ type: "text", text: infoBlock },
|
|
342
|
+
{ type: "text", text: "current question" },
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
];
|
|
346
|
+
|
|
347
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
348
|
+
...makeTurnContext(),
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Despite sharing v3's header prefix, v2's block is gone (identity
|
|
352
|
+
// match); the v3 cards and <info> blocks are byte-identical in place.
|
|
353
|
+
expect(tailTexts(result.messages)).toEqual([
|
|
354
|
+
frozenV3Block,
|
|
355
|
+
infoBlock,
|
|
356
|
+
"current question",
|
|
357
|
+
]);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("REGRESSION: re-entry with a header-bearing v2 identity keeps the first entry's frozen v3 cards", async () => {
|
|
361
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
362
|
+
// Re-entry: produce() returns the EMPTY block (cards already claimed by
|
|
363
|
+
// the store) while the tail carries the FIRST entry's v3 block — and the
|
|
364
|
+
// graph handle still holds the summary-bearing v2 identity from this
|
|
365
|
+
// turn's retrieval. Identity must be matched against the v2 block, never
|
|
366
|
+
// against "whatever leads with the shared header".
|
|
367
|
+
injectorChainSlot.push(v3Injector(""));
|
|
368
|
+
|
|
369
|
+
const v2Inner = `${INJECTION_HEADER}\n\n## memory/concepts/page-b.md\nsummary of page b`;
|
|
370
|
+
seedV2Identity(v2Inner);
|
|
371
|
+
|
|
372
|
+
const frozenV3Block = `<memory>\n${V3_CARDS_INJECTION_HEADER}\n\n# memory/concepts/page-a.md\nhead\n</memory>`;
|
|
373
|
+
const runMessages: Message[] = [
|
|
374
|
+
{
|
|
375
|
+
role: "user",
|
|
376
|
+
content: [
|
|
377
|
+
{ type: "text", text: frozenV3Block },
|
|
378
|
+
{ type: "text", text: "current question" },
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
];
|
|
382
|
+
|
|
383
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
384
|
+
...makeTurnContext(),
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
expect(tailTexts(result.messages)).toEqual([
|
|
388
|
+
frozenV3Block,
|
|
389
|
+
"current question",
|
|
390
|
+
]);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("v2 memory-image groups and legacy <memory __injected> blocks are stripped even without an identity", async () => {
|
|
394
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
395
|
+
injectorChainSlot.push(v3Injector("net-new cards"));
|
|
396
|
+
// No live graph handle: identity unknown (null). Image groups and legacy
|
|
397
|
+
// blocks are unambiguously v2's and are stripped regardless; the shared
|
|
398
|
+
// `<memory>` wrapper is left alone without an identity to match.
|
|
399
|
+
const runMessages: Message[] = [
|
|
400
|
+
{
|
|
401
|
+
role: "user",
|
|
402
|
+
content: [
|
|
403
|
+
{ type: "text", text: "<memory_image __injected>\na chart" },
|
|
404
|
+
{
|
|
405
|
+
type: "image",
|
|
406
|
+
source: { type: "base64", media_type: "image/png", data: "aGk=" },
|
|
407
|
+
},
|
|
408
|
+
{ type: "text", text: "</memory_image>" },
|
|
409
|
+
{ type: "text", text: "<memory __injected>\nlegacy recalled fact" },
|
|
410
|
+
{ type: "text", text: "current question" },
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
];
|
|
414
|
+
|
|
415
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
416
|
+
...makeTurnContext(),
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const texts = tailTexts(result.messages);
|
|
420
|
+
expect(texts).toEqual([
|
|
421
|
+
"<memory>\nnet-new cards\n</memory>",
|
|
422
|
+
"current question",
|
|
423
|
+
]);
|
|
424
|
+
// The injected image is gone too (the 3-block group strips as a unit).
|
|
425
|
+
expect(
|
|
426
|
+
result.messages[result.messages.length - 1].content.some(
|
|
427
|
+
(b) => b.type === "image",
|
|
428
|
+
),
|
|
429
|
+
).toBe(false);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test("the v3 block's attachment-commit callback fires on a user tail", async () => {
|
|
433
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
434
|
+
const commit = mock(() => {});
|
|
435
|
+
injectorChainSlot.push(v3Injector("net-new cards", commit));
|
|
436
|
+
|
|
437
|
+
const runMessages: Message[] = [
|
|
438
|
+
userMsgWithV2Memory("fresh recalled fact", "current question"),
|
|
439
|
+
];
|
|
440
|
+
await applyRuntimeInjections(runMessages, { ...makeTurnContext() });
|
|
441
|
+
|
|
442
|
+
expect(commit).toHaveBeenCalledTimes(1);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test("the commit callback is SKIPPED when the tail is not a user message (block never attaches)", async () => {
|
|
446
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
447
|
+
const commit = mock(() => {});
|
|
448
|
+
injectorChainSlot.push(v3Injector("net-new cards", commit));
|
|
449
|
+
|
|
450
|
+
const runMessages: Message[] = [
|
|
451
|
+
{ role: "user", content: [{ type: "text", text: "question" }] },
|
|
452
|
+
{ role: "assistant", content: [{ type: "text", text: "answer" }] },
|
|
453
|
+
];
|
|
454
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
455
|
+
...makeTurnContext(),
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Neither attached nor captured nor committed: the store must not claim
|
|
459
|
+
// cards that never reached history.
|
|
460
|
+
expect(commit).not.toHaveBeenCalled();
|
|
461
|
+
expect(result.blocks.memoryV3InjectedBlock).toBeUndefined();
|
|
462
|
+
expect(result.messages[1].content).toEqual([
|
|
463
|
+
{ type: "text", text: "answer" },
|
|
464
|
+
]);
|
|
141
465
|
});
|
|
142
466
|
|
|
143
467
|
test("flag ON but v3 produced NOTHING → v2 block left intact (fallback-to-v2)", async () => {
|
|
@@ -158,10 +482,11 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
158
482
|
expect(texts[1]).toBe("current question");
|
|
159
483
|
// No v3 block was added.
|
|
160
484
|
expect(texts).toHaveLength(2);
|
|
485
|
+
expect(result.blocks.memoryV3Active).toBe(false);
|
|
161
486
|
});
|
|
162
487
|
|
|
163
488
|
test("flag OFF → byte-for-byte identical to today even when v3 would have produced a block", async () => {
|
|
164
|
-
injectorChainSlot.push(v3Injector("v3
|
|
489
|
+
injectorChainSlot.push(v3Injector("v3 cards"));
|
|
165
490
|
|
|
166
491
|
const runMessages: Message[] = [
|
|
167
492
|
userMsgWithV2Memory("old recalled fact", "earlier question"),
|
|
@@ -185,14 +510,15 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
185
510
|
// behavior change and it is fully gated off here.
|
|
186
511
|
const texts = tailTexts(offResult.messages);
|
|
187
512
|
expect(texts[0]).toBe("<memory>\nfresh recalled fact\n</memory>");
|
|
188
|
-
expect(texts[1]).toBe("<memory>\nv3
|
|
513
|
+
expect(texts[1]).toBe("<memory>\nv3 cards\n</memory>");
|
|
189
514
|
expect(texts[2]).toBe("current question");
|
|
190
515
|
|
|
191
|
-
// Historical user turn keeps its v2 block
|
|
516
|
+
// Historical user turn keeps its v2 block.
|
|
192
517
|
const firstUserTexts = offResult.messages[0].content
|
|
193
518
|
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
194
519
|
.map((b) => b.text);
|
|
195
520
|
expect(firstUserTexts[0]).toBe("<memory>\nold recalled fact\n</memory>");
|
|
521
|
+
expect(offResult.blocks.memoryV3Active).toBe(false);
|
|
196
522
|
});
|
|
197
523
|
|
|
198
524
|
test("no v3 injector registered + flag ON → no stripping, messages untouched", async () => {
|
|
@@ -83,6 +83,13 @@ describe("schedule_create tool description", () => {
|
|
|
83
83
|
expect(scheduleCreateDef.description).toContain("cron");
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
test("requires an authored description", () => {
|
|
87
|
+
expect(scheduleCreateDef.input_schema.required).toContain("description");
|
|
88
|
+
expect(
|
|
89
|
+
scheduleCreateDef.input_schema.properties.description.description,
|
|
90
|
+
).toContain("what it does and why");
|
|
91
|
+
});
|
|
92
|
+
|
|
86
93
|
test('warns against using for "add to my tasks" requests', () => {
|
|
87
94
|
expect(scheduleCreateDef.description).toContain(
|
|
88
95
|
'Do NOT use this for "add to my tasks"',
|