@vellumai/assistant 0.6.5 → 0.6.6
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the plugin-driven runtime-injection chain (PR 21 of the
|
|
3
|
+
* `agent-plugin-system` plan).
|
|
4
|
+
*
|
|
5
|
+
* Covers:
|
|
6
|
+
*
|
|
7
|
+
* 1. The eight default injectors registered by `defaultInjectorsPlugin` come
|
|
8
|
+
* back from `getInjectors()` in the documented order (workspace-context →
|
|
9
|
+
* unified-turn-context → pkb-context → pkb-reminder → now-md →
|
|
10
|
+
* subagent-status → slack-messages → thread-focus).
|
|
11
|
+
* 2. A third-party-registered injector at `order: 25` slots between
|
|
12
|
+
* `unified-turn-context` (order 20) and `pkb` (order 30), proving the
|
|
13
|
+
* extensibility contract.
|
|
14
|
+
* 3. `composeInjectorChain` concatenates non-null blocks with a blank-line
|
|
15
|
+
* separator and yields an empty string when every injector opts out — the
|
|
16
|
+
* latter matches pre-PR behavior for the golden-path conversation state
|
|
17
|
+
* (all defaults return `null` in this PR).
|
|
18
|
+
* 4. `applyRuntimeInjections` with an empty `turnContext` chain leaves
|
|
19
|
+
* `blocks.injectorChainBlock` undefined, preserving the existing snapshot
|
|
20
|
+
* for conversations that don't opt into the chain.
|
|
21
|
+
* 5. `applyRuntimeInjections` surfaces the composed chain output on
|
|
22
|
+
* `blocks.injectorChainBlock` when a third-party injector contributes
|
|
23
|
+
* content.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
applyRuntimeInjections,
|
|
30
|
+
composeInjectorChain,
|
|
31
|
+
} from "../daemon/conversation-runtime-assembly.js";
|
|
32
|
+
import {
|
|
33
|
+
DEFAULT_INJECTOR_ORDER,
|
|
34
|
+
defaultInjectorsPlugin,
|
|
35
|
+
} from "../plugins/defaults/injectors.js";
|
|
36
|
+
import {
|
|
37
|
+
getInjectors,
|
|
38
|
+
registerPlugin,
|
|
39
|
+
resetPluginRegistryForTests,
|
|
40
|
+
} from "../plugins/registry.js";
|
|
41
|
+
import type {
|
|
42
|
+
InjectionBlock,
|
|
43
|
+
Injector,
|
|
44
|
+
Plugin,
|
|
45
|
+
TurnContext,
|
|
46
|
+
} from "../plugins/types.js";
|
|
47
|
+
import type { Message } from "../providers/types.js";
|
|
48
|
+
|
|
49
|
+
/** A fake TurnContext sufficient for driving `composeInjectorChain`. */
|
|
50
|
+
function makeTurnContext(): TurnContext {
|
|
51
|
+
return {
|
|
52
|
+
requestId: "req-test-1",
|
|
53
|
+
conversationId: "conv-test-1",
|
|
54
|
+
turnIndex: 0,
|
|
55
|
+
trust: {
|
|
56
|
+
sourceChannel: "vellum",
|
|
57
|
+
trustClass: "guardian",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Build a tiny valid plugin wrapping an array of injectors. */
|
|
63
|
+
function wrapInPlugin(name: string, injectors: Injector[]): Plugin {
|
|
64
|
+
return {
|
|
65
|
+
manifest: {
|
|
66
|
+
name,
|
|
67
|
+
version: "0.0.1",
|
|
68
|
+
requires: { pluginRuntime: "v1" },
|
|
69
|
+
},
|
|
70
|
+
injectors,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe("injector chain", () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
resetPluginRegistryForTests();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("defaultInjectorsPlugin registers the eight defaults in the documented order", () => {
|
|
80
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
81
|
+
|
|
82
|
+
const names = getInjectors().map((i) => i.name);
|
|
83
|
+
expect(names).toEqual([
|
|
84
|
+
"workspace-context",
|
|
85
|
+
"unified-turn-context",
|
|
86
|
+
"pkb-context",
|
|
87
|
+
"pkb-reminder",
|
|
88
|
+
"now-md",
|
|
89
|
+
"subagent-status",
|
|
90
|
+
"slack-messages",
|
|
91
|
+
"thread-focus",
|
|
92
|
+
]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("default injector order constants match the registered order values", () => {
|
|
96
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
97
|
+
|
|
98
|
+
const byName = new Map(getInjectors().map((i) => [i.name, i.order]));
|
|
99
|
+
expect(byName.get("workspace-context")).toBe(
|
|
100
|
+
DEFAULT_INJECTOR_ORDER.workspaceContext,
|
|
101
|
+
);
|
|
102
|
+
expect(byName.get("unified-turn-context")).toBe(
|
|
103
|
+
DEFAULT_INJECTOR_ORDER.unifiedTurnContext,
|
|
104
|
+
);
|
|
105
|
+
expect(byName.get("pkb-context")).toBe(DEFAULT_INJECTOR_ORDER.pkbContext);
|
|
106
|
+
expect(byName.get("pkb-reminder")).toBe(DEFAULT_INJECTOR_ORDER.pkbReminder);
|
|
107
|
+
expect(byName.get("now-md")).toBe(DEFAULT_INJECTOR_ORDER.nowMd);
|
|
108
|
+
expect(byName.get("subagent-status")).toBe(
|
|
109
|
+
DEFAULT_INJECTOR_ORDER.subagentStatus,
|
|
110
|
+
);
|
|
111
|
+
expect(byName.get("slack-messages")).toBe(
|
|
112
|
+
DEFAULT_INJECTOR_ORDER.slackMessages,
|
|
113
|
+
);
|
|
114
|
+
expect(byName.get("thread-focus")).toBe(DEFAULT_INJECTOR_ORDER.threadFocus);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("a third-party injector at order 25 slots between unified-turn-context (20) and pkb-context (30)", () => {
|
|
118
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
119
|
+
|
|
120
|
+
const middleInjector: Injector = {
|
|
121
|
+
name: "plugin-25",
|
|
122
|
+
order: 25,
|
|
123
|
+
async produce() {
|
|
124
|
+
return null;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
registerPlugin(wrapInPlugin("third-party", [middleInjector]));
|
|
128
|
+
|
|
129
|
+
const names = getInjectors().map((i) => i.name);
|
|
130
|
+
expect(names).toEqual([
|
|
131
|
+
"workspace-context", // 10
|
|
132
|
+
"unified-turn-context", // 20
|
|
133
|
+
"plugin-25", // 25 — slots in
|
|
134
|
+
"pkb-context", // 30
|
|
135
|
+
"pkb-reminder", // 35
|
|
136
|
+
"now-md", // 40
|
|
137
|
+
"subagent-status", // 50
|
|
138
|
+
"slack-messages", // 60
|
|
139
|
+
"thread-focus", // 70
|
|
140
|
+
]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("composeInjectorChain returns empty string when every injector opts out", async () => {
|
|
144
|
+
// The default chain is the golden-path: all seven defaults return `null`
|
|
145
|
+
// in this PR, so the composed block is an empty string, matching the
|
|
146
|
+
// pre-PR behavior where no chain existed at all.
|
|
147
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
148
|
+
|
|
149
|
+
const composed = await composeInjectorChain(makeTurnContext());
|
|
150
|
+
expect(composed).toBe("");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("composeInjectorChain returns empty string when registry is empty", async () => {
|
|
154
|
+
// No plugins registered — the chain is a no-op and must return an empty
|
|
155
|
+
// string (not throw, not undefined). Callers rely on this to treat the
|
|
156
|
+
// chain as purely additive.
|
|
157
|
+
const composed = await composeInjectorChain(makeTurnContext());
|
|
158
|
+
expect(composed).toBe("");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("composeInjectorChain concatenates non-null blocks in order with blank-line separators", async () => {
|
|
162
|
+
const first: Injector = {
|
|
163
|
+
name: "a",
|
|
164
|
+
order: 5,
|
|
165
|
+
async produce(): Promise<InjectionBlock> {
|
|
166
|
+
return { id: "a", text: "BLOCK_A" };
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
const second: Injector = {
|
|
170
|
+
name: "b",
|
|
171
|
+
order: 15,
|
|
172
|
+
async produce(): Promise<InjectionBlock> {
|
|
173
|
+
return { id: "b", text: "BLOCK_B" };
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
const skipped: Injector = {
|
|
177
|
+
name: "c",
|
|
178
|
+
order: 25,
|
|
179
|
+
async produce() {
|
|
180
|
+
return null;
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
// Register the higher-order one first to prove the chain sorts by `order`
|
|
184
|
+
// rather than registration order.
|
|
185
|
+
registerPlugin(wrapInPlugin("higher", [second]));
|
|
186
|
+
registerPlugin(wrapInPlugin("lower", [first]));
|
|
187
|
+
registerPlugin(wrapInPlugin("opts-out", [skipped]));
|
|
188
|
+
|
|
189
|
+
const composed = await composeInjectorChain(makeTurnContext());
|
|
190
|
+
expect(composed).toBe("BLOCK_A\n\nBLOCK_B");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("composeInjectorChain skips blocks with empty text", async () => {
|
|
194
|
+
const emitEmpty: Injector = {
|
|
195
|
+
name: "empty",
|
|
196
|
+
order: 10,
|
|
197
|
+
async produce(): Promise<InjectionBlock> {
|
|
198
|
+
return { id: "empty", text: "" };
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
const emitReal: Injector = {
|
|
202
|
+
name: "real",
|
|
203
|
+
order: 20,
|
|
204
|
+
async produce(): Promise<InjectionBlock> {
|
|
205
|
+
return { id: "real", text: "CONTENT" };
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
registerPlugin(wrapInPlugin("plugin", [emitEmpty, emitReal]));
|
|
209
|
+
|
|
210
|
+
const composed = await composeInjectorChain(makeTurnContext());
|
|
211
|
+
expect(composed).toBe("CONTENT");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("applyRuntimeInjections leaves injectorChainBlock undefined when defaults opt out", async () => {
|
|
215
|
+
// Golden-path snapshot: with only default injectors (all returning
|
|
216
|
+
// `null`), `applyRuntimeInjections` reports no chain output, so the
|
|
217
|
+
// historical `blocks` shape is preserved byte-for-byte for any
|
|
218
|
+
// conversation that doesn't involve third-party injectors.
|
|
219
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
220
|
+
|
|
221
|
+
const runMessages: Message[] = [
|
|
222
|
+
{ role: "user", content: [{ type: "text", text: "hello" }] },
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
226
|
+
turnContext: makeTurnContext(),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(result.blocks.injectorChainBlock).toBeUndefined();
|
|
230
|
+
// Sanity: the message array is untouched when no options fire (no
|
|
231
|
+
// hardcoded branches apply, and the chain contributed nothing).
|
|
232
|
+
expect(result.messages).toEqual(runMessages);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("applyRuntimeInjections surfaces third-party injector output on blocks.injectorChainBlock", async () => {
|
|
236
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
237
|
+
registerPlugin(
|
|
238
|
+
wrapInPlugin("third-party-25", [
|
|
239
|
+
{
|
|
240
|
+
name: "plugin-25",
|
|
241
|
+
order: 25,
|
|
242
|
+
async produce(): Promise<InjectionBlock> {
|
|
243
|
+
return { id: "plugin-25", text: "THIRD_PARTY_BLOCK" };
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
]),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const runMessages: Message[] = [
|
|
250
|
+
{ role: "user", content: [{ type: "text", text: "hi" }] },
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
254
|
+
turnContext: makeTurnContext(),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(result.blocks.injectorChainBlock).toBe("THIRD_PARTY_BLOCK");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("applyRuntimeInjections without turnContext still runs the chain under a synthesized context", async () => {
|
|
261
|
+
// Post-G2.1 semantics: the default chain is the canonical injection
|
|
262
|
+
// path, so `applyRuntimeInjections` must drive it even when the caller
|
|
263
|
+
// doesn't pass a `turnContext`. Test/legacy call sites that rely on
|
|
264
|
+
// option fields to opt into injections continue to work because the
|
|
265
|
+
// synthesized fallback exposes `injectionInputs` built from `options`.
|
|
266
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
267
|
+
registerPlugin(
|
|
268
|
+
wrapInPlugin("third-party-25", [
|
|
269
|
+
{
|
|
270
|
+
name: "plugin-25",
|
|
271
|
+
order: 25,
|
|
272
|
+
async produce(): Promise<InjectionBlock> {
|
|
273
|
+
return { id: "plugin-25", text: "THIRD_PARTY_BLOCK" };
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
]),
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const runMessages: Message[] = [
|
|
280
|
+
{ role: "user", content: [{ type: "text", text: "hi" }] },
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
const result = await applyRuntimeInjections(runMessages, {});
|
|
284
|
+
|
|
285
|
+
// Third-party injector runs even without a caller-supplied turnContext.
|
|
286
|
+
expect(result.blocks.injectorChainBlock).toBe("THIRD_PARTY_BLOCK");
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ── G2.1 migration integration tests ────────────────────────────────
|
|
290
|
+
//
|
|
291
|
+
// These assertions exercise the real per-turn injection pipeline with
|
|
292
|
+
// the default chain active, verifying that each ported injector emits
|
|
293
|
+
// byte-identical content to the pre-migration output and that a
|
|
294
|
+
// third-party injector registered at a fractional `order` slots into
|
|
295
|
+
// the correct position in the final user-tail content.
|
|
296
|
+
|
|
297
|
+
test("golden-path: default chain injects workspace + unified-turn + PKB + NOW + subagent in the correct positions", async () => {
|
|
298
|
+
// Canonical golden-path conversation state: full mode, non-Slack
|
|
299
|
+
// channel, workspace context + unified-turn + PKB + NOW + subagent
|
|
300
|
+
// all active. The expected final tail content ordering is:
|
|
301
|
+
//
|
|
302
|
+
// [workspace] ← prepend order 10 (topmost)
|
|
303
|
+
// [unified-turn] ← prepend order 20
|
|
304
|
+
// [now-md] ← after-memory-prefix order 40 (highest order, closest to memory)
|
|
305
|
+
// [pkb-reminder] ← after-memory-prefix order 35 (skipped when pkbActive=false)
|
|
306
|
+
// [pkb-context] ← after-memory-prefix order 30
|
|
307
|
+
// [user text]
|
|
308
|
+
// [subagent] ← append order 50
|
|
309
|
+
//
|
|
310
|
+
// No memory prefix blocks in this scenario, so after-memory-prefix
|
|
311
|
+
// lands right at the head of the user-text cluster.
|
|
312
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
313
|
+
|
|
314
|
+
const runMessages: Message[] = [
|
|
315
|
+
{ role: "user", content: [{ type: "text", text: "What next?" }] },
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
const workspaceText =
|
|
319
|
+
"<workspace>\nRoot: /sandbox\nDirectories: src, lib\n</workspace>";
|
|
320
|
+
const unifiedTurn =
|
|
321
|
+
"<turn_context>\ncurrent_time: 2026-04-22\ninterface: macos\n</turn_context>";
|
|
322
|
+
const pkbContent = "essentials of the project";
|
|
323
|
+
const nowContent = "Current focus: shipping G2.1";
|
|
324
|
+
const subagentBlock =
|
|
325
|
+
'<active_subagents>\n- [running] "worker" (sub-1) | elapsed: 5s\n</active_subagents>';
|
|
326
|
+
|
|
327
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
328
|
+
turnContext: makeTurnContext(),
|
|
329
|
+
workspaceTopLevelContext: workspaceText,
|
|
330
|
+
unifiedTurnContext: unifiedTurn,
|
|
331
|
+
pkbContext: pkbContent,
|
|
332
|
+
pkbActive: false, // disable reminder-branch to keep the snapshot small
|
|
333
|
+
nowScratchpad: nowContent,
|
|
334
|
+
subagentStatusBlock: subagentBlock,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// Extract the tail user message content as a list of text strings.
|
|
338
|
+
const tail = result.messages[result.messages.length - 1];
|
|
339
|
+
expect(tail.role).toBe("user");
|
|
340
|
+
const texts = tail.content
|
|
341
|
+
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
342
|
+
.map((b) => b.text);
|
|
343
|
+
|
|
344
|
+
// Positional assertions — each block lands where the injector's
|
|
345
|
+
// placement says it does.
|
|
346
|
+
expect(texts[0]).toBe(workspaceText); // prepend order 10
|
|
347
|
+
expect(texts[1]).toBe(unifiedTurn); // prepend order 20
|
|
348
|
+
// NOW and PKB are both after-memory-prefix; NOW runs later so sits above PKB.
|
|
349
|
+
expect(texts[2]).toBe(
|
|
350
|
+
`<NOW.md Always keep this up to date; keep under 10 lines>\n${nowContent}\n</NOW.md>`,
|
|
351
|
+
);
|
|
352
|
+
expect(texts[3]).toBe(`<knowledge_base>\n${pkbContent}\n</knowledge_base>`);
|
|
353
|
+
expect(texts[4]).toBe("What next?"); // user's typed text
|
|
354
|
+
expect(texts[5]).toBe(subagentBlock); // append order 50
|
|
355
|
+
expect(texts).toHaveLength(6);
|
|
356
|
+
|
|
357
|
+
// Block metadata captures for DB persistence — one field per default
|
|
358
|
+
// injector whose output the loader rehydrates from message metadata.
|
|
359
|
+
expect(result.blocks.workspaceBlock).toBe(workspaceText);
|
|
360
|
+
expect(result.blocks.unifiedTurnContext).toBe(unifiedTurn);
|
|
361
|
+
expect(result.blocks.nowScratchpadBlock).toBe(
|
|
362
|
+
`<NOW.md Always keep this up to date; keep under 10 lines>\n${nowContent}\n</NOW.md>`,
|
|
363
|
+
);
|
|
364
|
+
expect(result.blocks.pkbContextBlock).toBe(
|
|
365
|
+
`<knowledge_base>\n${pkbContent}\n</knowledge_base>`,
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("third-party injector at order 25 lands between unified-turn-context (20) and pkb-context (30) in the final message", async () => {
|
|
370
|
+
// Proves the extensibility contract end-to-end: a plugin-registered
|
|
371
|
+
// injector at `order: 25` with `placement: "prepend-user-tail"` slots
|
|
372
|
+
// between the unified-turn prepend (order 20, executes just before
|
|
373
|
+
// workspace) and the PKB after-memory splice (order 30). Because it
|
|
374
|
+
// uses prepend-user-tail placement it becomes a third prepend,
|
|
375
|
+
// landing between workspace (topmost) and unified-turn.
|
|
376
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
377
|
+
registerPlugin(
|
|
378
|
+
wrapInPlugin("third-party-25-prepend", [
|
|
379
|
+
{
|
|
380
|
+
name: "plugin-25",
|
|
381
|
+
order: 15, // between workspace (10) and unified-turn (20)
|
|
382
|
+
async produce(): Promise<InjectionBlock> {
|
|
383
|
+
return {
|
|
384
|
+
id: "plugin-25",
|
|
385
|
+
text: "<plugin_block_15/>",
|
|
386
|
+
placement: "prepend-user-tail",
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
]),
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const runMessages: Message[] = [
|
|
394
|
+
{ role: "user", content: [{ type: "text", text: "hi" }] },
|
|
395
|
+
];
|
|
396
|
+
|
|
397
|
+
const workspaceText = "<workspace>\nRoot: /sandbox\n</workspace>";
|
|
398
|
+
const unifiedTurn =
|
|
399
|
+
"<turn_context>\ncurrent_time: 2026-04-22\n</turn_context>";
|
|
400
|
+
|
|
401
|
+
const result = await applyRuntimeInjections(runMessages, {
|
|
402
|
+
turnContext: makeTurnContext(),
|
|
403
|
+
workspaceTopLevelContext: workspaceText,
|
|
404
|
+
unifiedTurnContext: unifiedTurn,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const tail = result.messages[result.messages.length - 1];
|
|
408
|
+
expect(tail.role).toBe("user");
|
|
409
|
+
const texts = tail.content
|
|
410
|
+
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
411
|
+
.map((b) => b.text);
|
|
412
|
+
|
|
413
|
+
// Descending-order application for prepends puts the lowest-`order`
|
|
414
|
+
// injector topmost, so order 10 (workspace) ends up on top, then
|
|
415
|
+
// plugin@15 below it, then unified-turn (order 20) below that.
|
|
416
|
+
expect(texts[0]).toBe(workspaceText);
|
|
417
|
+
expect(texts[1]).toBe("<plugin_block_15/>");
|
|
418
|
+
expect(texts[2]).toBe(unifiedTurn);
|
|
419
|
+
expect(texts[3]).toBe("hi");
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("slack-messages injector replaces runMessages when a chronological transcript is provided", async () => {
|
|
423
|
+
// End-to-end verification for the `replace-run-messages` placement:
|
|
424
|
+
// a Slack channel turn with a pre-rendered chronological transcript
|
|
425
|
+
// swaps the incoming `runMessages` for the transcript before the
|
|
426
|
+
// after-memory/append placements run. Memory-prefix blocks from the
|
|
427
|
+
// original tail are re-prepended onto the new tail so PKB / NOW
|
|
428
|
+
// splices still find them.
|
|
429
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
430
|
+
|
|
431
|
+
const originalRun: Message[] = [
|
|
432
|
+
{
|
|
433
|
+
role: "user",
|
|
434
|
+
content: [
|
|
435
|
+
// A memory prefix block that must be carried over to the Slack
|
|
436
|
+
// transcript's tail so after-memory splices still fire.
|
|
437
|
+
{
|
|
438
|
+
type: "text",
|
|
439
|
+
text: "<memory __injected>\nrecalled fact\n</memory>",
|
|
440
|
+
},
|
|
441
|
+
{ type: "text", text: "What's happening?" },
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
];
|
|
445
|
+
const slackTranscript: Message[] = [
|
|
446
|
+
{
|
|
447
|
+
role: "user",
|
|
448
|
+
content: [{ type: "text", text: "[12:00 alice]: kickoff" }],
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
role: "user",
|
|
452
|
+
content: [{ type: "text", text: "[12:05 @user]: What's happening?" }],
|
|
453
|
+
},
|
|
454
|
+
];
|
|
455
|
+
|
|
456
|
+
const result = await applyRuntimeInjections(originalRun, {
|
|
457
|
+
turnContext: makeTurnContext(),
|
|
458
|
+
channelCapabilities: {
|
|
459
|
+
channel: "slack",
|
|
460
|
+
dashboardCapable: false,
|
|
461
|
+
supportsDynamicUi: false,
|
|
462
|
+
supportsVoiceInput: false,
|
|
463
|
+
chatType: "channel",
|
|
464
|
+
},
|
|
465
|
+
slackChronologicalMessages: slackTranscript,
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// The swap replaced the run-messages wholesale but preserved the
|
|
469
|
+
// memory-prefix blocks onto the new tail user message.
|
|
470
|
+
expect(result.messages).toHaveLength(2);
|
|
471
|
+
const slackTail = result.messages[result.messages.length - 1];
|
|
472
|
+
expect(slackTail.role).toBe("user");
|
|
473
|
+
const texts = slackTail.content
|
|
474
|
+
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
475
|
+
.map((b) => b.text);
|
|
476
|
+
// Hardcoded channelCapabilities injection prepends first (Slack is a
|
|
477
|
+
// constrained channel), then the carried memory-prefix blocks, then
|
|
478
|
+
// the slack transcript's original user text.
|
|
479
|
+
expect(texts.some((t) => t.startsWith("<channel_capabilities>"))).toBe(
|
|
480
|
+
true,
|
|
481
|
+
);
|
|
482
|
+
expect(texts).toContain("<memory __injected>\nrecalled fact\n</memory>");
|
|
483
|
+
expect(texts[texts.length - 1]).toBe("[12:05 @user]: What's happening?");
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("minimal mode: only unified-turn-context survives; workspace/PKB/NOW/subagent are skipped", async () => {
|
|
487
|
+
// Validates the `minimal` injection-mode gating. Every default
|
|
488
|
+
// injector except `unified-turn-context` checks `mode === "full"` and
|
|
489
|
+
// opts out in minimal mode, so the tail should carry only the turn
|
|
490
|
+
// context prepend plus any non-injector hardcoded content (none
|
|
491
|
+
// here).
|
|
492
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
493
|
+
|
|
494
|
+
const result = await applyRuntimeInjections(
|
|
495
|
+
[
|
|
496
|
+
{
|
|
497
|
+
role: "user",
|
|
498
|
+
content: [{ type: "text", text: "hi" }],
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
{
|
|
502
|
+
turnContext: makeTurnContext(),
|
|
503
|
+
mode: "minimal",
|
|
504
|
+
workspaceTopLevelContext: "<workspace>...</workspace>",
|
|
505
|
+
unifiedTurnContext: "<turn_context>...</turn_context>",
|
|
506
|
+
pkbContext: "kbody",
|
|
507
|
+
pkbActive: true,
|
|
508
|
+
nowScratchpad: "nowbody",
|
|
509
|
+
subagentStatusBlock: "<active_subagents>...</active_subagents>",
|
|
510
|
+
},
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
const tail = result.messages[result.messages.length - 1];
|
|
514
|
+
const texts = tail.content
|
|
515
|
+
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
516
|
+
.map((b) => b.text);
|
|
517
|
+
|
|
518
|
+
expect(texts).toEqual(["<turn_context>...</turn_context>", "hi"]);
|
|
519
|
+
expect(result.blocks.unifiedTurnContext).toBe(
|
|
520
|
+
"<turn_context>...</turn_context>",
|
|
521
|
+
);
|
|
522
|
+
expect(result.blocks.workspaceBlock).toBeUndefined();
|
|
523
|
+
expect(result.blocks.pkbContextBlock).toBeUndefined();
|
|
524
|
+
expect(result.blocks.nowScratchpadBlock).toBeUndefined();
|
|
525
|
+
});
|
|
526
|
+
});
|
|
@@ -57,20 +57,6 @@ const scheduleCreateDef = scheduleToolsJson.tools.find(
|
|
|
57
57
|
(t: { name: string }) => t.name === "schedule_create",
|
|
58
58
|
);
|
|
59
59
|
|
|
60
|
-
// Load send_notification description from the bundled skill TOOLS.json
|
|
61
|
-
const notifToolsJson = JSON.parse(
|
|
62
|
-
readFileSync(
|
|
63
|
-
join(
|
|
64
|
-
import.meta.dirname,
|
|
65
|
-
"../config/bundled-skills/notifications/TOOLS.json",
|
|
66
|
-
),
|
|
67
|
-
"utf-8",
|
|
68
|
-
),
|
|
69
|
-
);
|
|
70
|
-
const sendNotificationDef = notifToolsJson.tools.find(
|
|
71
|
-
(t: { name: string }) => t.name === "send_notification",
|
|
72
|
-
);
|
|
73
|
-
|
|
74
60
|
// =====================================================================
|
|
75
61
|
// 1. Routing section removed from system prompt — guidance in tool descriptions
|
|
76
62
|
// =====================================================================
|
|
@@ -114,18 +100,6 @@ describe("schedule_create tool description", () => {
|
|
|
114
100
|
});
|
|
115
101
|
});
|
|
116
102
|
|
|
117
|
-
describe("send_notification tool description", () => {
|
|
118
|
-
test("states it fires immediately with no delay", () => {
|
|
119
|
-
expect(sendNotificationDef).toBeDefined();
|
|
120
|
-
expect(sendNotificationDef.description).toContain("immediate");
|
|
121
|
-
expect(sendNotificationDef.description).toContain("no delay");
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("redirects to schedule_create for future alerts", () => {
|
|
125
|
-
expect(sendNotificationDef.description).toContain("schedule_create");
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
103
|
// =====================================================================
|
|
130
104
|
// 3. Cross-tool consistency: schedule and task tools agree on routing boundaries
|
|
131
105
|
// =====================================================================
|