@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/docs/activation-funnel-telemetry.md +310 -0
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +263 -121
- package/package.json +2 -2
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +23 -8
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +6 -5
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/history-repair-hook.test.ts +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +5 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/web-fetch.test.ts +45 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +403 -251
- package/src/api/responses/conversation-message.ts +22 -2
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -3
- package/src/config/feature-flag-registry.json +10 -16
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- 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 +9 -2
- 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/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/external-api.ts +12 -2
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +22 -0
- package/src/providers/types.ts +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +30 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- package/src/util/map-limit.ts +0 -27
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
|
@@ -105,43 +105,15 @@ mock.module("../../jobs-store.js", () => ({
|
|
|
105
105
|
isMemoryEnabled: () => true,
|
|
106
106
|
}));
|
|
107
107
|
|
|
108
|
-
// ── v3
|
|
108
|
+
// ── v3 follow-up flag mock ──────────────────────────────────────────
|
|
109
109
|
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// mocks let each test (a) toggle the flag, (b) decide what health block the
|
|
113
|
-
// renderer produces, and (c) force the health computation to throw — without
|
|
114
|
-
// materializing a real v3 data dir. The tree/core/page-index loaders are
|
|
115
|
-
// stubbed to inert values; `computeV3Health` is a pass-through whose result is
|
|
116
|
-
// fed to the toggleable `renderV3Health`.
|
|
110
|
+
// A v3 flag being on appends `memory_v3_maintain` to the post-consolidation
|
|
111
|
+
// follow-up fan-out. This mock lets each test toggle the flag.
|
|
117
112
|
let v3FlagOn = false;
|
|
118
113
|
mock.module("../../../config/assistant-feature-flags.js", () => ({
|
|
119
114
|
isAssistantFeatureFlagEnabled: () => v3FlagOn,
|
|
120
115
|
}));
|
|
121
116
|
|
|
122
|
-
let renderedHealth = "";
|
|
123
|
-
let computeThrows = false;
|
|
124
|
-
mock.module("../../v3/health.js", () => ({
|
|
125
|
-
computeV3Health: () => {
|
|
126
|
-
if (computeThrows) throw new Error("simulated health compute failure");
|
|
127
|
-
return {};
|
|
128
|
-
},
|
|
129
|
-
renderV3Health: () => renderedHealth,
|
|
130
|
-
}));
|
|
131
|
-
|
|
132
|
-
mock.module("../../v3/tree.js", () => ({
|
|
133
|
-
loadLeafTree: async () => ({ leaves: new Map(), byPage: new Map() }),
|
|
134
|
-
resolveDataDir: () => "/tmp/v3-data-stub",
|
|
135
|
-
}));
|
|
136
|
-
|
|
137
|
-
mock.module("../../v3/core.js", () => ({
|
|
138
|
-
loadCore: async () => new Set<string>(),
|
|
139
|
-
}));
|
|
140
|
-
|
|
141
|
-
mock.module("../../v2/page-index.js", () => ({
|
|
142
|
-
getPageIndex: async () => ({ entries: [] }),
|
|
143
|
-
}));
|
|
144
|
-
|
|
145
117
|
// ── Workspace pin ───────────────────────────────────────────────────
|
|
146
118
|
let tmpWorkspace: string;
|
|
147
119
|
let previousWorkspaceEnv: string | undefined;
|
|
@@ -211,10 +183,8 @@ beforeEach(() => {
|
|
|
211
183
|
enqueuedJobs.length = 0;
|
|
212
184
|
nextJobIdCounter = 0;
|
|
213
185
|
|
|
214
|
-
// v3
|
|
186
|
+
// v3 follow-up flag default: off.
|
|
215
187
|
v3FlagOn = false;
|
|
216
|
-
renderedHealth = "";
|
|
217
|
-
computeThrows = false;
|
|
218
188
|
});
|
|
219
189
|
|
|
220
190
|
// ---------------------------------------------------------------------------
|
|
@@ -409,69 +379,6 @@ describe("memoryV2ConsolidateJob — non-empty buffer", () => {
|
|
|
409
379
|
});
|
|
410
380
|
});
|
|
411
381
|
|
|
412
|
-
describe("memoryV2ConsolidateJob — v3 health injection", () => {
|
|
413
|
-
beforeEach(() => {
|
|
414
|
-
writeFileSync(
|
|
415
|
-
bufferPath(),
|
|
416
|
-
"- [Apr 27, 9:00 AM] Alice prefers VS Code over Vim.\n",
|
|
417
|
-
);
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
test("prepends the health block when a v3 flag is on and the report is actionable", async () => {
|
|
421
|
-
v3FlagOn = true;
|
|
422
|
-
renderedHealth = "memory-v3 health:\n- 2 unassigned slug(s): a, b";
|
|
423
|
-
|
|
424
|
-
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
425
|
-
|
|
426
|
-
expect(result.kind).toBe("invoked");
|
|
427
|
-
const prompt = runnerLastArgs?.prompt as string;
|
|
428
|
-
// The health block is PREPENDED, separated by a blank line, with the
|
|
429
|
-
// base consolidation prompt still present underneath it.
|
|
430
|
-
expect(prompt.startsWith(`${renderedHealth}\n\n`)).toBe(true);
|
|
431
|
-
expect(prompt).toContain("memory consolidation");
|
|
432
|
-
expect(prompt).not.toContain(CUTOFF_PLACEHOLDER);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
test("leaves the prompt unchanged when the report is all-green (empty render)", async () => {
|
|
436
|
-
v3FlagOn = true;
|
|
437
|
-
renderedHealth = "";
|
|
438
|
-
|
|
439
|
-
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
440
|
-
|
|
441
|
-
expect(result.kind).toBe("invoked");
|
|
442
|
-
const prompt = runnerLastArgs?.prompt as string;
|
|
443
|
-
expect(prompt).not.toContain("memory-v3 health:");
|
|
444
|
-
// Prompt is exactly the resolved consolidation body.
|
|
445
|
-
expect(prompt).toContain("memory consolidation");
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
test("leaves the prompt unchanged when v3 flags are off even if a block would render", async () => {
|
|
449
|
-
v3FlagOn = false;
|
|
450
|
-
renderedHealth = "memory-v3 health:\n- 1 unassigned slug(s): a";
|
|
451
|
-
|
|
452
|
-
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
453
|
-
|
|
454
|
-
expect(result.kind).toBe("invoked");
|
|
455
|
-
const prompt = runnerLastArgs?.prompt as string;
|
|
456
|
-
expect(prompt).not.toContain("memory-v3 health:");
|
|
457
|
-
expect(prompt).toContain("memory consolidation");
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
test("falls back to the base prompt when health computation throws", async () => {
|
|
461
|
-
v3FlagOn = true;
|
|
462
|
-
computeThrows = true;
|
|
463
|
-
renderedHealth = "memory-v3 health:\n- should not appear";
|
|
464
|
-
|
|
465
|
-
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
466
|
-
|
|
467
|
-
// A health-compute failure must NEVER break consolidation.
|
|
468
|
-
expect(result.kind).toBe("invoked");
|
|
469
|
-
const prompt = runnerLastArgs?.prompt as string;
|
|
470
|
-
expect(prompt).not.toContain("memory-v3 health:");
|
|
471
|
-
expect(prompt).toContain("memory consolidation");
|
|
472
|
-
});
|
|
473
|
-
});
|
|
474
|
-
|
|
475
382
|
describe("memoryV2ConsolidateJob — concurrent invocations", () => {
|
|
476
383
|
beforeEach(() => {
|
|
477
384
|
writeFileSync(bufferPath(), "- [Apr 27, 9:00 AM] Alice prefers VS Code.\n");
|
|
@@ -186,6 +186,28 @@ describe("writePage + readPage round-trip", () => {
|
|
|
186
186
|
expect(read!.body).toBe(page.body);
|
|
187
187
|
});
|
|
188
188
|
|
|
189
|
+
test("round-trips authored links: frontmatter (memory-v3 edge lane)", async () => {
|
|
190
|
+
const page = makePage({
|
|
191
|
+
frontmatter: {
|
|
192
|
+
edges: [],
|
|
193
|
+
ref_files: [],
|
|
194
|
+
ref_urls: [],
|
|
195
|
+
links: ["bob-handoff — the partner this hands off to"],
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
await writePage(workspaceDir, page);
|
|
199
|
+
|
|
200
|
+
// A page using the optional `links:` frontmatter must NOT be rejected by the
|
|
201
|
+
// strict schema (it would otherwise be dropped from memory-v3 entirely), and
|
|
202
|
+
// the curated links must survive the render → read round-trip so the edge
|
|
203
|
+
// graph can parse them back from the rendered frontmatter.
|
|
204
|
+
const read = await readPage(workspaceDir, page.slug);
|
|
205
|
+
expect(read).not.toBeNull();
|
|
206
|
+
expect(read!.frontmatter.links).toEqual([
|
|
207
|
+
"bob-handoff — the partner this hands off to",
|
|
208
|
+
]);
|
|
209
|
+
});
|
|
210
|
+
|
|
189
211
|
test("renders frontmatter at the top with --- delimiters", async () => {
|
|
190
212
|
const page = makePage();
|
|
191
213
|
await writePage(workspaceDir, page);
|
|
@@ -73,11 +73,6 @@ import {
|
|
|
73
73
|
type MemoryJob,
|
|
74
74
|
type MemoryJobType,
|
|
75
75
|
} from "../jobs-store.js";
|
|
76
|
-
import { getPageIndex } from "../v2/page-index.js";
|
|
77
|
-
import { loadCore } from "../v3/core.js";
|
|
78
|
-
import { computeV3Health, renderV3Health } from "../v3/health.js";
|
|
79
|
-
import { loadLeafTree, resolveDataDir } from "../v3/tree.js";
|
|
80
|
-
import type { LeafPath, Slug } from "../v3/types.js";
|
|
81
76
|
import { MEMORY_V2_CONSOLIDATION_SOURCE } from "./constants.js";
|
|
82
77
|
import { resolveConsolidationPrompt } from "./prompts/consolidation.js";
|
|
83
78
|
|
|
@@ -87,8 +82,7 @@ const log = getLogger("memory-v2-consolidate");
|
|
|
87
82
|
const JOB_NAME = "memory.consolidate";
|
|
88
83
|
|
|
89
84
|
/**
|
|
90
|
-
* v3 plugin flags. Either being on
|
|
91
|
-
* consolidation prompt and (b) enqueues `memory_v3_maintain` as a
|
|
85
|
+
* v3 plugin flags. Either being on enqueues `memory_v3_maintain` as a
|
|
92
86
|
* post-consolidation follow-up. These gate the v3 plugin itself.
|
|
93
87
|
*/
|
|
94
88
|
const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
@@ -182,15 +176,10 @@ export async function memoryV2ConsolidateJob(
|
|
|
182
176
|
// it to a regular file under 1 MiB before substitution so a stray path
|
|
183
177
|
// (or a `/dev/zero`-style pseudo-file) cannot exfiltrate megabytes of
|
|
184
178
|
// bytes through the wake hint.
|
|
185
|
-
|
|
186
|
-
// That resolved prompt is the base; `maybePrependV3Health` prepends a
|
|
187
|
-
// freshly computed v3 health block when a v3 flag is on and the tree has
|
|
188
|
-
// actionable drift, leaving it untouched otherwise.
|
|
189
|
-
const basePrompt = resolveConsolidationPrompt(
|
|
179
|
+
const prompt = resolveConsolidationPrompt(
|
|
190
180
|
config.memory.v2.consolidation_prompt_path,
|
|
191
181
|
cutoff,
|
|
192
182
|
);
|
|
193
|
-
const prompt = await maybePrependV3Health(basePrompt, config);
|
|
194
183
|
|
|
195
184
|
const runResult = await runBackgroundJob({
|
|
196
185
|
jobName: JOB_NAME,
|
|
@@ -261,56 +250,6 @@ export async function memoryV2ConsolidateJob(
|
|
|
261
250
|
}
|
|
262
251
|
}
|
|
263
252
|
|
|
264
|
-
/**
|
|
265
|
-
* When a v3 flag is enabled and the v3 tree has actionable structural drift,
|
|
266
|
-
* prepend a rendered health block to `basePrompt` so the consolidation run can
|
|
267
|
-
* fold tree maintenance into its pass. Returns `basePrompt` UNCHANGED when:
|
|
268
|
-
* - neither `memory-v3-shadow` nor `memory-v3-live` is enabled,
|
|
269
|
-
* - the health report is all-green (`renderV3Health` returns ""), or
|
|
270
|
-
* - computing the report throws for any reason.
|
|
271
|
-
*
|
|
272
|
-
* The block is computed here (not baked into the prompt template) so an
|
|
273
|
-
* operator's custom consolidation prompt stays untouched and the block reflects
|
|
274
|
-
* the tree state at run time. Health computation is wrapped so a load/compute
|
|
275
|
-
* failure can never break consolidation — the run proceeds on the base prompt.
|
|
276
|
-
*/
|
|
277
|
-
async function maybePrependV3Health(
|
|
278
|
-
basePrompt: string,
|
|
279
|
-
config: AssistantConfig,
|
|
280
|
-
): Promise<string> {
|
|
281
|
-
const v3Enabled =
|
|
282
|
-
isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config) ||
|
|
283
|
-
isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config);
|
|
284
|
-
if (!v3Enabled) return basePrompt;
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
const pageIndex = await getPageIndex(getWorkspaceDir());
|
|
288
|
-
const pageLeaves = new Map<Slug, LeafPath[]>();
|
|
289
|
-
const allSlugs: Slug[] = [];
|
|
290
|
-
for (const entry of pageIndex.entries) {
|
|
291
|
-
pageLeaves.set(entry.slug, entry.leaves);
|
|
292
|
-
allSlugs.push(entry.slug);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const dataDir = resolveDataDir();
|
|
296
|
-
const [tree, core] = await Promise.all([
|
|
297
|
-
loadLeafTree(dataDir, pageLeaves),
|
|
298
|
-
loadCore(dataDir),
|
|
299
|
-
]);
|
|
300
|
-
|
|
301
|
-
const report = computeV3Health({ tree, allSlugs, core });
|
|
302
|
-
const healthBlock = renderV3Health(report);
|
|
303
|
-
if (healthBlock.length === 0) return basePrompt;
|
|
304
|
-
return `${healthBlock}\n\n${basePrompt}`;
|
|
305
|
-
} catch (err) {
|
|
306
|
-
log.warn(
|
|
307
|
-
{ err: err instanceof Error ? err.message : String(err) },
|
|
308
|
-
"consolidation: v3 health computation failed; using base prompt",
|
|
309
|
-
);
|
|
310
|
-
return basePrompt;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
253
|
/**
|
|
315
254
|
* Read `memory/buffer.md`. Missing file → empty string so the skip-on-empty
|
|
316
255
|
* branch doesn't have to distinguish "no file" from "blank file".
|
package/src/memory/v2/types.ts
CHANGED
|
@@ -41,6 +41,11 @@ export const ConceptPageFrontmatterSchema = z
|
|
|
41
41
|
ref_urls: z.array(z.string().url()).default([]),
|
|
42
42
|
summary: z.string().optional(),
|
|
43
43
|
leaves: z.array(z.string()).optional(),
|
|
44
|
+
// Optional authored `"<target-slug> — <why>"` cross-links. Curated, first-class
|
|
45
|
+
// edges for the memory-v3 edge lane. Declared here so `.strict()` does not
|
|
46
|
+
// reject a page that uses `links:`, and so `renderPageContent` round-trips the
|
|
47
|
+
// field (the edge graph reads it back from the rendered frontmatter).
|
|
48
|
+
links: z.array(z.string()).optional(),
|
|
44
49
|
})
|
|
45
50
|
.strict();
|
|
46
51
|
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { credentialKey } from "../../../security/credential-key.js";
|
|
11
|
-
import {
|
|
11
|
+
import { getSecureKeyResultAsync } from "../../../security/secure-keys.js";
|
|
12
|
+
import { BackendUnavailableError, ConfigError } from "../../../util/errors.js";
|
|
12
13
|
import { getLogger } from "../../../util/logger.js";
|
|
13
14
|
|
|
14
15
|
const log = getLogger("telegram-api");
|
|
@@ -197,13 +198,21 @@ async function retryableFetch<T>(
|
|
|
197
198
|
// ---------------------------------------------------------------------------
|
|
198
199
|
|
|
199
200
|
async function resolveBotToken(): Promise<string> {
|
|
200
|
-
const
|
|
201
|
+
const result = await getSecureKeyResultAsync(
|
|
201
202
|
credentialKey("telegram", "bot_token"),
|
|
202
203
|
);
|
|
203
|
-
if (
|
|
204
|
-
|
|
204
|
+
if (result.value) {
|
|
205
|
+
return result.value;
|
|
205
206
|
}
|
|
206
|
-
|
|
207
|
+
// Distinguish a transient credential-store outage from a token that was
|
|
208
|
+
// genuinely never set. Both surface as an absent value, but only the
|
|
209
|
+
// latter is an operator misconfiguration; the former is retryable
|
|
210
|
+
// infrastructure unavailability that callers should not treat as
|
|
211
|
+
// "not configured".
|
|
212
|
+
if (result.unreachable) {
|
|
213
|
+
throw new BackendUnavailableError("Telegram credential store unreachable");
|
|
214
|
+
}
|
|
215
|
+
throw new ConfigError("Telegram bot token not configured");
|
|
207
216
|
}
|
|
208
217
|
|
|
209
218
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the notification copy-composer — specifically the fallback
|
|
3
|
+
* path that composeFallbackCopy uses when the LLM is unavailable, and
|
|
4
|
+
* the shared deriveTitle utility.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
|
+
|
|
9
|
+
import { composeFallbackCopy, deriveTitle } from "../copy-composer.js";
|
|
10
|
+
import type { NotificationSignal } from "../signal.js";
|
|
11
|
+
import type { NotificationChannel } from "../types.js";
|
|
12
|
+
|
|
13
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
function makeSignal(
|
|
16
|
+
overrides?: Partial<NotificationSignal>,
|
|
17
|
+
): NotificationSignal {
|
|
18
|
+
return {
|
|
19
|
+
signalId: "sig-copy-test-1",
|
|
20
|
+
createdAt: Date.now(),
|
|
21
|
+
sourceChannel: "scheduler",
|
|
22
|
+
sourceContextId: "ctx-1",
|
|
23
|
+
sourceEventName: "user.send_notification",
|
|
24
|
+
contextPayload: {},
|
|
25
|
+
attentionHints: {
|
|
26
|
+
requiresAction: false,
|
|
27
|
+
urgency: "low",
|
|
28
|
+
isAsyncBackground: false,
|
|
29
|
+
visibleInSourceNow: false,
|
|
30
|
+
},
|
|
31
|
+
...overrides,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const CHANNELS: NotificationChannel[] = ["vellum" as NotificationChannel];
|
|
36
|
+
|
|
37
|
+
// ── composeFallbackCopy with requestedMessage ─────────────────────────
|
|
38
|
+
|
|
39
|
+
describe("composeFallbackCopy honors requestedMessage / requestedTitle", () => {
|
|
40
|
+
test("uses requestedMessage as body and requestedTitle as title", () => {
|
|
41
|
+
const signal = makeSignal({
|
|
42
|
+
contextPayload: {
|
|
43
|
+
requestedMessage: "Take out the trash",
|
|
44
|
+
requestedTitle: "Household Reminder",
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
48
|
+
|
|
49
|
+
expect(copy.vellum?.body).toBe("Take out the trash");
|
|
50
|
+
expect(copy.vellum?.title).toBe("Household Reminder");
|
|
51
|
+
expect(copy.vellum?.conversationSeedMessage).toBe("Take out the trash");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("derives title from body when requestedTitle is absent", () => {
|
|
55
|
+
const signal = makeSignal({
|
|
56
|
+
contextPayload: {
|
|
57
|
+
requestedMessage: "First sentence. Second sentence follows.",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
61
|
+
|
|
62
|
+
expect(copy.vellum?.body).toBe("First sentence. Second sentence follows.");
|
|
63
|
+
expect(copy.vellum?.title).toBe("First sentence.");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("derives title from body when requestedTitle is empty string", () => {
|
|
67
|
+
const signal = makeSignal({
|
|
68
|
+
contextPayload: {
|
|
69
|
+
requestedMessage: "Some message body here",
|
|
70
|
+
requestedTitle: "",
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
74
|
+
|
|
75
|
+
expect(copy.vellum?.body).toBe("Some message body here");
|
|
76
|
+
expect(copy.vellum?.title).toBe("Some message body here");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("derives title from body when requestedTitle is whitespace", () => {
|
|
80
|
+
const signal = makeSignal({
|
|
81
|
+
contextPayload: {
|
|
82
|
+
requestedMessage: "Whitespace title test",
|
|
83
|
+
requestedTitle: " ",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
87
|
+
|
|
88
|
+
expect(copy.vellum?.body).toBe("Whitespace title test");
|
|
89
|
+
expect(copy.vellum?.title).toBe("Whitespace title test");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("trims whitespace from requestedMessage", () => {
|
|
93
|
+
const signal = makeSignal({
|
|
94
|
+
contextPayload: {
|
|
95
|
+
requestedMessage: " padded message ",
|
|
96
|
+
requestedTitle: " padded title ",
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
100
|
+
|
|
101
|
+
expect(copy.vellum?.body).toBe("padded message");
|
|
102
|
+
expect(copy.vellum?.title).toBe("padded title");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("populates copy for all requested channels", () => {
|
|
106
|
+
const channels = [
|
|
107
|
+
"vellum" as NotificationChannel,
|
|
108
|
+
"telegram" as NotificationChannel,
|
|
109
|
+
];
|
|
110
|
+
const signal = makeSignal({
|
|
111
|
+
contextPayload: {
|
|
112
|
+
requestedMessage: "Multi-channel test",
|
|
113
|
+
requestedTitle: "Multi Title",
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const copy = composeFallbackCopy(signal, channels);
|
|
117
|
+
|
|
118
|
+
expect(copy.vellum?.body).toBe("Multi-channel test");
|
|
119
|
+
expect(copy.vellum?.title).toBe("Multi Title");
|
|
120
|
+
expect(copy.telegram?.body).toBe("Multi-channel test");
|
|
121
|
+
expect(copy.telegram?.title).toBe("Multi Title");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("falls through to template when requestedMessage is absent", () => {
|
|
125
|
+
const signal = makeSignal({
|
|
126
|
+
sourceEventName: "schedule.notify",
|
|
127
|
+
contextPayload: {
|
|
128
|
+
message: "Standup time",
|
|
129
|
+
label: "Daily Standup",
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
133
|
+
|
|
134
|
+
expect(copy.vellum?.title).toBe("Daily Standup");
|
|
135
|
+
expect(copy.vellum?.body).toBe("Standup time");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("falls through to template when requestedMessage is empty string", () => {
|
|
139
|
+
const signal = makeSignal({
|
|
140
|
+
sourceEventName: "schedule.notify",
|
|
141
|
+
contextPayload: {
|
|
142
|
+
requestedMessage: "",
|
|
143
|
+
message: "Standup time",
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
147
|
+
|
|
148
|
+
expect(copy.vellum?.title).toBe("Reminder");
|
|
149
|
+
expect(copy.vellum?.body).toBe("Standup time");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("falls through to template when requestedMessage is whitespace", () => {
|
|
153
|
+
const signal = makeSignal({
|
|
154
|
+
sourceEventName: "schedule.notify",
|
|
155
|
+
contextPayload: {
|
|
156
|
+
requestedMessage: " ",
|
|
157
|
+
message: "Standup time",
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
161
|
+
|
|
162
|
+
expect(copy.vellum?.title).toBe("Reminder");
|
|
163
|
+
expect(copy.vellum?.body).toBe("Standup time");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("falls through to generic copy when no requestedMessage and no template match", () => {
|
|
167
|
+
const signal = makeSignal({
|
|
168
|
+
sourceEventName: "unknown.event",
|
|
169
|
+
contextPayload: {},
|
|
170
|
+
});
|
|
171
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
172
|
+
|
|
173
|
+
expect(copy.vellum?.title).toBe("Notification");
|
|
174
|
+
expect(copy.vellum?.body).toBe("");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("requestedMessage takes priority over event-name template", () => {
|
|
178
|
+
const signal = makeSignal({
|
|
179
|
+
sourceEventName: "schedule.notify",
|
|
180
|
+
contextPayload: {
|
|
181
|
+
requestedMessage: "User-supplied content",
|
|
182
|
+
requestedTitle: "User Title",
|
|
183
|
+
message: "Template message field",
|
|
184
|
+
label: "Template label field",
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
188
|
+
|
|
189
|
+
expect(copy.vellum?.body).toBe("User-supplied content");
|
|
190
|
+
expect(copy.vellum?.title).toBe("User Title");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("works with non-assistant_tool source channels", () => {
|
|
194
|
+
for (const sourceChannel of ["scheduler", "watcher", "slack"] as const) {
|
|
195
|
+
const signal = makeSignal({
|
|
196
|
+
sourceChannel,
|
|
197
|
+
contextPayload: {
|
|
198
|
+
requestedMessage: `Message from ${sourceChannel}`,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const copy = composeFallbackCopy(signal, CHANNELS);
|
|
202
|
+
|
|
203
|
+
expect(copy.vellum?.body).toBe(`Message from ${sourceChannel}`);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ── deriveTitle ───────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
describe("deriveTitle", () => {
|
|
211
|
+
test("extracts first sentence when period is present", () => {
|
|
212
|
+
expect(deriveTitle("First sentence. Second sentence.")).toBe(
|
|
213
|
+
"First sentence.",
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("extracts first sentence on exclamation mark", () => {
|
|
218
|
+
expect(deriveTitle("Alert! More details follow.")).toBe("Alert!");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("extracts first sentence on question mark", () => {
|
|
222
|
+
expect(deriveTitle("Ready? Let me know.")).toBe("Ready?");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("uses full body when no sentence terminator", () => {
|
|
226
|
+
expect(deriveTitle("No terminator here")).toBe("No terminator here");
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("truncates to 60 characters with ellipsis", () => {
|
|
230
|
+
const long = "A".repeat(80);
|
|
231
|
+
const result = deriveTitle(long);
|
|
232
|
+
expect(result.length).toBeLessThanOrEqual(61); // 60 + ellipsis char
|
|
233
|
+
expect(result.endsWith("\u2026")).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("does not truncate at exactly 60 characters", () => {
|
|
237
|
+
const exact = "A".repeat(60);
|
|
238
|
+
expect(deriveTitle(exact)).toBe(exact);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("trims whitespace", () => {
|
|
242
|
+
expect(deriveTitle(" trimmed ")).toBe("trimmed");
|
|
243
|
+
});
|
|
244
|
+
});
|