@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
|
@@ -55,7 +55,7 @@ mock.module("../providers/registry.js", () => ({
|
|
|
55
55
|
mock.module("../config/loader.js", () => ({
|
|
56
56
|
getConfig: () => ({
|
|
57
57
|
ui: {},
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
llm: {
|
|
60
60
|
default: {
|
|
61
61
|
provider: "mock-provider",
|
|
@@ -330,7 +330,9 @@ interface PendingRun {
|
|
|
330
330
|
reject: (err: Error) => void;
|
|
331
331
|
messages: Message[];
|
|
332
332
|
onEvent: (event: AgentEvent) => void;
|
|
333
|
-
onCheckpoint?: (
|
|
333
|
+
onCheckpoint?: (
|
|
334
|
+
checkpoint: CheckpointInfo,
|
|
335
|
+
) => CheckpointDecision | Promise<CheckpointDecision>;
|
|
334
336
|
}
|
|
335
337
|
|
|
336
338
|
let pendingRuns: PendingRun[] = [];
|
|
@@ -341,6 +343,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
341
343
|
getToolTokenBudget() {
|
|
342
344
|
return 0;
|
|
343
345
|
}
|
|
346
|
+
getResolvedTools() {
|
|
347
|
+
return [];
|
|
348
|
+
}
|
|
344
349
|
getActiveModel() {
|
|
345
350
|
return undefined;
|
|
346
351
|
}
|
|
@@ -349,7 +354,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
349
354
|
onEvent: (event: AgentEvent) => void,
|
|
350
355
|
_signal?: AbortSignal,
|
|
351
356
|
_requestId?: string,
|
|
352
|
-
onCheckpoint?: (
|
|
357
|
+
onCheckpoint?: (
|
|
358
|
+
checkpoint: CheckpointInfo,
|
|
359
|
+
) => CheckpointDecision | Promise<CheckpointDecision>,
|
|
353
360
|
): Promise<Message[]> {
|
|
354
361
|
return new Promise<Message[]>((resolve, reject) => {
|
|
355
362
|
pendingRuns.push({ resolve, reject, messages, onEvent, onCheckpoint });
|
|
@@ -1728,7 +1735,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1728
1735
|
// Simulate the agent loop calling it at a turn boundary.
|
|
1729
1736
|
const run = pendingRuns[0];
|
|
1730
1737
|
expect(run.onCheckpoint).toBeDefined();
|
|
1731
|
-
const decision = run.onCheckpoint!({
|
|
1738
|
+
const decision = await run.onCheckpoint!({
|
|
1732
1739
|
turnIndex: 0,
|
|
1733
1740
|
toolCount: 1,
|
|
1734
1741
|
hasToolUse: true,
|
|
@@ -1771,7 +1778,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1771
1778
|
// The pending run should have an onCheckpoint callback
|
|
1772
1779
|
const run = pendingRuns[0];
|
|
1773
1780
|
expect(run.onCheckpoint).toBeDefined();
|
|
1774
|
-
const decision = run.onCheckpoint!({
|
|
1781
|
+
const decision = await run.onCheckpoint!({
|
|
1775
1782
|
turnIndex: 0,
|
|
1776
1783
|
toolCount: 1,
|
|
1777
1784
|
hasToolUse: true,
|
|
@@ -1813,7 +1820,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1813
1820
|
// Simulate the agent loop yielding at the checkpoint (first run is mid-tool-use)
|
|
1814
1821
|
const run0 = pendingRuns[0];
|
|
1815
1822
|
expect(run0.onCheckpoint).toBeDefined();
|
|
1816
|
-
const decision = run0.onCheckpoint!({
|
|
1823
|
+
const decision = await run0.onCheckpoint!({
|
|
1817
1824
|
turnIndex: 0,
|
|
1818
1825
|
toolCount: 1,
|
|
1819
1826
|
hasToolUse: true,
|
|
@@ -1871,7 +1878,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1871
1878
|
|
|
1872
1879
|
// Simulate multiple tool-use turns before the checkpoint fires
|
|
1873
1880
|
// Turn 0 — checkpoint yields because msg-2 is waiting
|
|
1874
|
-
const decision = run.onCheckpoint!({
|
|
1881
|
+
const decision = await run.onCheckpoint!({
|
|
1875
1882
|
turnIndex: 0,
|
|
1876
1883
|
toolCount: 1,
|
|
1877
1884
|
hasToolUse: true,
|
|
@@ -1966,7 +1973,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1966
1973
|
const runA = pendingRuns[0];
|
|
1967
1974
|
expect(runA.onCheckpoint).toBeDefined();
|
|
1968
1975
|
expect(
|
|
1969
|
-
runA.onCheckpoint!({
|
|
1976
|
+
await runA.onCheckpoint!({
|
|
1970
1977
|
turnIndex: 0,
|
|
1971
1978
|
toolCount: 1,
|
|
1972
1979
|
hasToolUse: true,
|
|
@@ -1983,7 +1990,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1983
1990
|
const runB = pendingRuns[1];
|
|
1984
1991
|
expect(runB.onCheckpoint).toBeDefined();
|
|
1985
1992
|
expect(
|
|
1986
|
-
runB.onCheckpoint!({
|
|
1993
|
+
await runB.onCheckpoint!({
|
|
1987
1994
|
turnIndex: 0,
|
|
1988
1995
|
toolCount: 1,
|
|
1989
1996
|
hasToolUse: true,
|
|
@@ -1998,7 +2005,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
1998
2005
|
expect(runC.onCheckpoint).toBeDefined();
|
|
1999
2006
|
// Only D remains, still should yield
|
|
2000
2007
|
expect(
|
|
2001
|
-
runC.onCheckpoint!({
|
|
2008
|
+
await runC.onCheckpoint!({
|
|
2002
2009
|
turnIndex: 0,
|
|
2003
2010
|
toolCount: 1,
|
|
2004
2011
|
hasToolUse: true,
|
|
@@ -2012,7 +2019,7 @@ describe("Conversation checkpoint handoff", () => {
|
|
|
2012
2019
|
const runD = pendingRuns[3];
|
|
2013
2020
|
expect(runD.onCheckpoint).toBeDefined();
|
|
2014
2021
|
expect(
|
|
2015
|
-
runD.onCheckpoint!({
|
|
2022
|
+
await runD.onCheckpoint!({
|
|
2016
2023
|
turnIndex: 0,
|
|
2017
2024
|
toolCount: 1,
|
|
2018
2025
|
hasToolUse: true,
|
|
@@ -2411,7 +2418,12 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2411
2418
|
model: "mock",
|
|
2412
2419
|
providerDurationMs: 100,
|
|
2413
2420
|
});
|
|
2414
|
-
|
|
2421
|
+
// Await the message_complete dispatch so the async persistence pipeline
|
|
2422
|
+
// (which sets `state.lastAssistantMessageId`) finishes before the mock
|
|
2423
|
+
// resolves `agentLoop.run()` and downstream post-processing runs. The
|
|
2424
|
+
// real agent loop in `agent/loop.ts` awaits onEvent before returning,
|
|
2425
|
+
// so awaiting here keeps the mock faithful to production semantics.
|
|
2426
|
+
await run.onEvent({ type: "message_complete", message: assistantMsg });
|
|
2415
2427
|
run.resolve([...run.messages, assistantMsg]);
|
|
2416
2428
|
|
|
2417
2429
|
await p1;
|
|
@@ -2450,7 +2462,7 @@ describe("Conversation attachment event payloads", () => {
|
|
|
2450
2462
|
const run = pendingRuns[0];
|
|
2451
2463
|
expect(run.onCheckpoint).toBeDefined();
|
|
2452
2464
|
expect(
|
|
2453
|
-
run.onCheckpoint!({
|
|
2465
|
+
await run.onCheckpoint!({
|
|
2454
2466
|
turnIndex: 0,
|
|
2455
2467
|
toolCount: 1,
|
|
2456
2468
|
hasToolUse: true,
|
|
@@ -2585,7 +2597,10 @@ describe("Regression: cancel semantics and error channel split", () => {
|
|
|
2585
2597
|
model: "mock",
|
|
2586
2598
|
providerDurationMs: 100,
|
|
2587
2599
|
});
|
|
2588
|
-
|
|
2600
|
+
// Await the message_complete dispatch so the async persistence pipeline
|
|
2601
|
+
// finishes before the mock resolves `agentLoop.run()`. See the matching
|
|
2602
|
+
// comment in "message_complete includes assistant attachments".
|
|
2603
|
+
await run.onEvent({ type: "message_complete", message: assistantMsg });
|
|
2589
2604
|
run.resolve([...run.messages, assistantMsg]);
|
|
2590
2605
|
await p1;
|
|
2591
2606
|
|
|
@@ -432,15 +432,16 @@ describe("macOS browser backend fallback (no extension, no cdp-inspect)", () =>
|
|
|
432
432
|
expect(interfaceCtx!.userMessageInterface).toBe("macos");
|
|
433
433
|
expect(interfaceCtx!.assistantMessageInterface).toBe("macos");
|
|
434
434
|
|
|
435
|
-
//
|
|
436
|
-
// hostBrowserProxy
|
|
437
|
-
//
|
|
438
|
-
// the
|
|
439
|
-
//
|
|
435
|
+
// macOS now natively supports host_browser, so the conversation gets a
|
|
436
|
+
// hostBrowserProxy provisioned via the SSE sender path even without an
|
|
437
|
+
// extension connection. The proxy routes host_browser_request frames to
|
|
438
|
+
// the macOS desktop client over SSE. The hostBrowserSenderOverride
|
|
439
|
+
// remains undefined because no registry-routed extension connection is
|
|
440
|
+
// present — the proxy uses the default SSE sender.
|
|
440
441
|
expect(
|
|
441
442
|
(capturedConversation as unknown as Record<string, unknown>)
|
|
442
443
|
.hostBrowserProxy,
|
|
443
|
-
).
|
|
444
|
+
).toBeDefined();
|
|
444
445
|
expect(
|
|
445
446
|
(capturedConversation as unknown as Record<string, unknown>)
|
|
446
447
|
.hostBrowserSenderOverride,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
// PKB search is mocked so the reminder-hints tests can assert behavior
|
|
4
4
|
// without standing up Qdrant. The mock returns whatever is staged in
|
|
@@ -31,8 +31,6 @@ import {
|
|
|
31
31
|
findLastInjectedNowContent,
|
|
32
32
|
injectChannelCapabilityContext,
|
|
33
33
|
injectChannelCommandContext,
|
|
34
|
-
injectNowScratchpad,
|
|
35
|
-
injectSubagentStatus,
|
|
36
34
|
isGroupChatType,
|
|
37
35
|
isSlackChannelConversation,
|
|
38
36
|
loadSlackActiveThreadFocusBlock,
|
|
@@ -49,9 +47,24 @@ import {
|
|
|
49
47
|
writeSlackMetadata,
|
|
50
48
|
} from "../messaging/providers/slack/message-metadata.js";
|
|
51
49
|
import { parentAlias } from "../messaging/providers/slack/render-transcript.js";
|
|
50
|
+
import { defaultInjectorsPlugin } from "../plugins/defaults/injectors.js";
|
|
51
|
+
import {
|
|
52
|
+
registerPlugin,
|
|
53
|
+
resetPluginRegistryForTests,
|
|
54
|
+
} from "../plugins/registry.js";
|
|
52
55
|
import type { Message } from "../providers/types.js";
|
|
53
56
|
import type { SubagentState } from "../subagent/types.js";
|
|
54
57
|
|
|
58
|
+
// `applyRuntimeInjections` is now driven by the default injector chain
|
|
59
|
+
// (PR G2.1). The default-injectors plugin must be registered for the chain
|
|
60
|
+
// to emit workspace, PKB, NOW.md, subagent, Slack, and thread-focus blocks.
|
|
61
|
+
// Each test gets a clean registry so a test that registers its own plugin
|
|
62
|
+
// doesn't leak into the next one.
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
resetPluginRegistryForTests();
|
|
65
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
66
|
+
});
|
|
67
|
+
|
|
55
68
|
// ---------------------------------------------------------------------------
|
|
56
69
|
// resolveChannelCapabilities
|
|
57
70
|
// ---------------------------------------------------------------------------
|
|
@@ -797,86 +810,12 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
797
810
|
});
|
|
798
811
|
});
|
|
799
812
|
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
role: "user",
|
|
807
|
-
content: [{ type: "text", text: "What should I work on?" }],
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
test("inserts NOW.md before user content", () => {
|
|
811
|
-
const result = injectNowScratchpad(
|
|
812
|
-
baseUserMessage,
|
|
813
|
-
"Current focus: shipping PR 3",
|
|
814
|
-
);
|
|
815
|
-
expect(result.content.length).toBe(2);
|
|
816
|
-
// Scratchpad comes first (before user content)
|
|
817
|
-
const injected = result.content[0];
|
|
818
|
-
expect(injected.type).toBe("text");
|
|
819
|
-
const text = (injected as { type: "text"; text: string }).text;
|
|
820
|
-
expect(text).toBe(
|
|
821
|
-
"<NOW.md Always keep this up to date; keep under 10 lines>\nCurrent focus: shipping PR 3\n</NOW.md>",
|
|
822
|
-
);
|
|
823
|
-
// Original content comes last
|
|
824
|
-
expect((result.content[1] as { type: "text"; text: string }).text).toBe(
|
|
825
|
-
"What should I work on?",
|
|
826
|
-
);
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
test("inserts after memory_context but before user content", () => {
|
|
830
|
-
const messageWithMemory: Message = {
|
|
831
|
-
role: "user",
|
|
832
|
-
content: [
|
|
833
|
-
{
|
|
834
|
-
type: "text",
|
|
835
|
-
text: "<memory_context __injected>\nrecalled notes\n</memory_context>",
|
|
836
|
-
},
|
|
837
|
-
{ type: "text", text: "What should I work on?" },
|
|
838
|
-
],
|
|
839
|
-
};
|
|
840
|
-
|
|
841
|
-
const result = injectNowScratchpad(messageWithMemory, "scratchpad notes");
|
|
842
|
-
expect(result.content.length).toBe(3);
|
|
843
|
-
// Memory context stays first
|
|
844
|
-
expect(
|
|
845
|
-
(result.content[0] as { type: "text"; text: string }).text,
|
|
846
|
-
).toContain("<memory_context");
|
|
847
|
-
// Scratchpad inserted after memory
|
|
848
|
-
expect(
|
|
849
|
-
(result.content[1] as { type: "text"; text: string }).text,
|
|
850
|
-
).toContain("<NOW.md");
|
|
851
|
-
// User content is last
|
|
852
|
-
expect((result.content[2] as { type: "text"; text: string }).text).toBe(
|
|
853
|
-
"What should I work on?",
|
|
854
|
-
);
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
test("preserves existing multi-block content with scratchpad before it", () => {
|
|
858
|
-
const multiBlockMessage: Message = {
|
|
859
|
-
role: "user",
|
|
860
|
-
content: [
|
|
861
|
-
{ type: "text", text: "First block" },
|
|
862
|
-
{ type: "text", text: "Second block" },
|
|
863
|
-
],
|
|
864
|
-
};
|
|
865
|
-
|
|
866
|
-
const result = injectNowScratchpad(multiBlockMessage, "scratchpad notes");
|
|
867
|
-
expect(result.content.length).toBe(3);
|
|
868
|
-
// Scratchpad is first (no memory_context to skip)
|
|
869
|
-
expect(
|
|
870
|
-
(result.content[0] as { type: "text"; text: string }).text,
|
|
871
|
-
).toContain("<NOW.md");
|
|
872
|
-
expect((result.content[1] as { type: "text"; text: string }).text).toBe(
|
|
873
|
-
"First block",
|
|
874
|
-
);
|
|
875
|
-
expect((result.content[2] as { type: "text"; text: string }).text).toBe(
|
|
876
|
-
"Second block",
|
|
877
|
-
);
|
|
878
|
-
});
|
|
879
|
-
});
|
|
813
|
+
// The standalone `injectNowScratchpad` helper was removed in G2.1. The
|
|
814
|
+
// now-md default injector (registered by `defaultInjectorsPlugin`) emits
|
|
815
|
+
// the `<NOW.md>` block as an `after-memory-prefix` placement during
|
|
816
|
+
// `applyRuntimeInjections`. The suites below (`applyRuntimeInjections with
|
|
817
|
+
// nowScratchpad` and the injection-mode tests) cover that behaviour
|
|
818
|
+
// end-to-end.
|
|
880
819
|
|
|
881
820
|
// ---------------------------------------------------------------------------
|
|
882
821
|
// stripNowScratchpad
|
|
@@ -1837,22 +1776,9 @@ describe("buildSubagentStatusBlock", () => {
|
|
|
1837
1776
|
});
|
|
1838
1777
|
});
|
|
1839
1778
|
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
role: "user",
|
|
1844
|
-
content: [{ type: "text", text: "hello" }],
|
|
1845
|
-
};
|
|
1846
|
-
const result = injectSubagentStatus(
|
|
1847
|
-
msg,
|
|
1848
|
-
"<active_subagents>\ntest\n</active_subagents>",
|
|
1849
|
-
);
|
|
1850
|
-
expect(result.content).toHaveLength(2);
|
|
1851
|
-
expect(
|
|
1852
|
-
(result.content[1] as { type: string; text: string }).text,
|
|
1853
|
-
).toContain("<active_subagents>");
|
|
1854
|
-
});
|
|
1855
|
-
});
|
|
1779
|
+
// `injectSubagentStatus` was removed in G2.1 — coverage of the append
|
|
1780
|
+
// placement lives in the `applyRuntimeInjections — subagent status` suite
|
|
1781
|
+
// below, which exercises the subagent-status default injector end-to-end.
|
|
1856
1782
|
|
|
1857
1783
|
describe("applyRuntimeInjections — subagent status", () => {
|
|
1858
1784
|
const userMsg: Message = {
|
|
@@ -2702,7 +2628,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2702
2628
|
},
|
|
2703
2629
|
{
|
|
2704
2630
|
role: "assistant",
|
|
2705
|
-
content: [{ type: "text", text: "
|
|
2631
|
+
content: [{ type: "text", text: "prior reply" }],
|
|
2706
2632
|
},
|
|
2707
2633
|
],
|
|
2708
2634
|
},
|
|
@@ -3666,6 +3592,125 @@ describe("assembleSlackActiveThreadFocusBlock", () => {
|
|
|
3666
3592
|
expect(result!).toContain("@user");
|
|
3667
3593
|
});
|
|
3668
3594
|
|
|
3595
|
+
test("assistant reactions are not double-attributed (`@assistant: [... @assistant reacted ...]`)", () => {
|
|
3596
|
+
// `renderReaction` bakes `@assistant` into the reaction tag line
|
|
3597
|
+
// (`[11/14/23 14:28 @assistant reacted 👍 to Mxxxxxx]`). The
|
|
3598
|
+
// post-render step that prepends `@assistant: ` to assistant content
|
|
3599
|
+
// lines must skip reaction lines, otherwise the flattened block
|
|
3600
|
+
// produces `@assistant: [... @assistant reacted ...]` — two
|
|
3601
|
+
// attributions for one event.
|
|
3602
|
+
const rows: SlackTranscriptInputRow[] = [
|
|
3603
|
+
buildRow(
|
|
3604
|
+
"user",
|
|
3605
|
+
"Parent",
|
|
3606
|
+
1_000,
|
|
3607
|
+
buildMeta({ channelTs: PARENT_TS, displayName: "@alice" }),
|
|
3608
|
+
),
|
|
3609
|
+
// Assistant reply in the thread — gets an `@assistant:` prefix.
|
|
3610
|
+
buildRow(
|
|
3611
|
+
"assistant",
|
|
3612
|
+
"Assistant reply",
|
|
3613
|
+
2_000,
|
|
3614
|
+
buildMeta({
|
|
3615
|
+
channelTs: "1700000005.000001",
|
|
3616
|
+
threadTs: PARENT_TS,
|
|
3617
|
+
}),
|
|
3618
|
+
),
|
|
3619
|
+
// Assistant reaction on the parent — must NOT get a second prefix.
|
|
3620
|
+
buildRow(
|
|
3621
|
+
"assistant",
|
|
3622
|
+
"[reaction]",
|
|
3623
|
+
3_000,
|
|
3624
|
+
buildMeta({
|
|
3625
|
+
channelTs: "1700000008.000002",
|
|
3626
|
+
eventKind: "reaction",
|
|
3627
|
+
reaction: {
|
|
3628
|
+
emoji: "👍",
|
|
3629
|
+
targetChannelTs: PARENT_TS,
|
|
3630
|
+
op: "added",
|
|
3631
|
+
},
|
|
3632
|
+
}),
|
|
3633
|
+
),
|
|
3634
|
+
// Latest user row in the thread — required for `detectActiveThreadTs`
|
|
3635
|
+
// to lock onto PARENT_TS (the latest user turn is the anchor).
|
|
3636
|
+
buildRow(
|
|
3637
|
+
"user",
|
|
3638
|
+
"User follow-up",
|
|
3639
|
+
4_000,
|
|
3640
|
+
buildMeta({
|
|
3641
|
+
channelTs: REPLY_TS,
|
|
3642
|
+
threadTs: PARENT_TS,
|
|
3643
|
+
displayName: "@alice",
|
|
3644
|
+
}),
|
|
3645
|
+
),
|
|
3646
|
+
];
|
|
3647
|
+
const result = assembleSlackActiveThreadFocusBlock(rows, SLACK_CAPS);
|
|
3648
|
+
expect(result).not.toBeNull();
|
|
3649
|
+
// Double-attribution anti-pattern must NOT appear anywhere.
|
|
3650
|
+
expect(result!).not.toContain("@assistant: [");
|
|
3651
|
+
// Both the reaction attribution and the reply prefix are still present.
|
|
3652
|
+
expect(result!).toContain("@assistant reacted 👍");
|
|
3653
|
+
expect(result!).toContain("@assistant: Assistant reply");
|
|
3654
|
+
});
|
|
3655
|
+
|
|
3656
|
+
test("assistant reaction overflow trailer is not double-attributed", () => {
|
|
3657
|
+
// When assistant reactions overflow the per-target cap, `renderSlackTranscript`
|
|
3658
|
+
// emits a trailer line (`[…and N more reactions to Mxxxxxx]`) whose role
|
|
3659
|
+
// is inherited from the first overflowing reaction — i.e. `assistant`. The
|
|
3660
|
+
// trailer embeds no actor attribution but ends with the parent alias and
|
|
3661
|
+
// shares the same `M<hex>]` signature as a real reaction line, so it must
|
|
3662
|
+
// be detected by `isReactionTagLine` and skipped by the prefix step.
|
|
3663
|
+
const PARENT_ALIAS_TS = PARENT_TS;
|
|
3664
|
+
const buildAssistantReaction = (ts: string, emoji: string) =>
|
|
3665
|
+
buildRow(
|
|
3666
|
+
"assistant",
|
|
3667
|
+
"[reaction]",
|
|
3668
|
+
Number.parseFloat(ts) * 1000,
|
|
3669
|
+
buildMeta({
|
|
3670
|
+
channelTs: ts,
|
|
3671
|
+
eventKind: "reaction",
|
|
3672
|
+
reaction: {
|
|
3673
|
+
emoji,
|
|
3674
|
+
targetChannelTs: PARENT_ALIAS_TS,
|
|
3675
|
+
op: "added",
|
|
3676
|
+
},
|
|
3677
|
+
}),
|
|
3678
|
+
);
|
|
3679
|
+
const rows: SlackTranscriptInputRow[] = [
|
|
3680
|
+
buildRow(
|
|
3681
|
+
"user",
|
|
3682
|
+
"Parent",
|
|
3683
|
+
1_000,
|
|
3684
|
+
buildMeta({ channelTs: PARENT_TS, displayName: "@alice" }),
|
|
3685
|
+
),
|
|
3686
|
+
// Overflow the default per-target cap (5) with 7 reactions so the
|
|
3687
|
+
// trailer line is emitted with 2 excess.
|
|
3688
|
+
buildAssistantReaction("1700000100.000001", "👍"),
|
|
3689
|
+
buildAssistantReaction("1700000100.000002", "🎉"),
|
|
3690
|
+
buildAssistantReaction("1700000100.000003", "🔥"),
|
|
3691
|
+
buildAssistantReaction("1700000100.000004", "💯"),
|
|
3692
|
+
buildAssistantReaction("1700000100.000005", "👏"),
|
|
3693
|
+
buildAssistantReaction("1700000100.000006", "👀"),
|
|
3694
|
+
buildAssistantReaction("1700000100.000007", "🚀"),
|
|
3695
|
+
// Latest user row in the thread — required for `detectActiveThreadTs`.
|
|
3696
|
+
buildRow(
|
|
3697
|
+
"user",
|
|
3698
|
+
"Follow-up",
|
|
3699
|
+
2_000_000,
|
|
3700
|
+
buildMeta({
|
|
3701
|
+
channelTs: REPLY_TS,
|
|
3702
|
+
threadTs: PARENT_TS,
|
|
3703
|
+
displayName: "@alice",
|
|
3704
|
+
}),
|
|
3705
|
+
),
|
|
3706
|
+
];
|
|
3707
|
+
const result = assembleSlackActiveThreadFocusBlock(rows, SLACK_CAPS);
|
|
3708
|
+
expect(result).not.toBeNull();
|
|
3709
|
+
expect(result!).toContain("more reactions");
|
|
3710
|
+
// The trailer line must not be double-attributed.
|
|
3711
|
+
expect(result!).not.toMatch(/@assistant: \[…and \d+ more reaction/);
|
|
3712
|
+
});
|
|
3713
|
+
|
|
3669
3714
|
test("emits a block even when the parent has not been backfilled yet", () => {
|
|
3670
3715
|
// The inbound reply detects an `activeThreadTs` from its own
|
|
3671
3716
|
// `threadTs`, but the parent (`channelTs === activeThreadTs`) has not
|
|
@@ -3826,7 +3871,7 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
3826
3871
|
},
|
|
3827
3872
|
{
|
|
3828
3873
|
role: "assistant",
|
|
3829
|
-
content: [{ type: "text", text: "
|
|
3874
|
+
content: [{ type: "text", text: "hi back!" }],
|
|
3830
3875
|
},
|
|
3831
3876
|
{
|
|
3832
3877
|
role: "user",
|
|
@@ -3877,9 +3922,9 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
3877
3922
|
expect(result!.map((m) => (m.content[0] as { text: string }).text)).toEqual(
|
|
3878
3923
|
[
|
|
3879
3924
|
"[11/14/23 14:25]: old hi",
|
|
3880
|
-
"
|
|
3925
|
+
"old reply",
|
|
3881
3926
|
"[11/14/23 14:28 @alice]: fresh hi",
|
|
3882
|
-
"
|
|
3927
|
+
"fresh reply",
|
|
3883
3928
|
],
|
|
3884
3929
|
);
|
|
3885
3930
|
expect(result!.map((m) => m.role)).toEqual([
|
|
@@ -4035,7 +4080,7 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4035
4080
|
expect(rendered[1]!).toEqual({
|
|
4036
4081
|
role: "assistant",
|
|
4037
4082
|
content: [
|
|
4038
|
-
{ type: "text", text: "
|
|
4083
|
+
{ type: "text", text: "looking it up" },
|
|
4039
4084
|
{
|
|
4040
4085
|
type: "tool_use",
|
|
4041
4086
|
id: "tu_1",
|
|
@@ -4357,11 +4402,11 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4357
4402
|
role: "user",
|
|
4358
4403
|
content: [{ type: "text", text: "[11/14/23 23:03 @alice]: hi" }],
|
|
4359
4404
|
});
|
|
4360
|
-
// Row 2: assistant
|
|
4405
|
+
// Row 2: assistant content + tool_use(abc) — no tag line.
|
|
4361
4406
|
expect(result![1]).toEqual({
|
|
4362
4407
|
role: "assistant",
|
|
4363
4408
|
content: [
|
|
4364
|
-
{ type: "text", text: "
|
|
4409
|
+
{ type: "text", text: "checking..." },
|
|
4365
4410
|
{
|
|
4366
4411
|
type: "tool_use",
|
|
4367
4412
|
id: "tu_abc",
|
|
@@ -4377,11 +4422,11 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4377
4422
|
{ type: "tool_result", tool_use_id: "tu_abc", content: "result 1" },
|
|
4378
4423
|
],
|
|
4379
4424
|
});
|
|
4380
|
-
// Row 4: assistant
|
|
4425
|
+
// Row 4: assistant content + tool_use(def) — no tag line.
|
|
4381
4426
|
expect(result![3]).toEqual({
|
|
4382
4427
|
role: "assistant",
|
|
4383
4428
|
content: [
|
|
4384
|
-
{ type: "text", text: "
|
|
4429
|
+
{ type: "text", text: "one more lookup..." },
|
|
4385
4430
|
{
|
|
4386
4431
|
type: "tool_use",
|
|
4387
4432
|
id: "tu_def",
|
|
@@ -4397,10 +4442,10 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4397
4442
|
{ type: "tool_result", tool_use_id: "tu_def", content: "result 2" },
|
|
4398
4443
|
],
|
|
4399
4444
|
});
|
|
4400
|
-
// Row 6: assistant final text-only answer,
|
|
4445
|
+
// Row 6: assistant final text-only answer, content-only (no tag line).
|
|
4401
4446
|
expect(result![5]).toEqual({
|
|
4402
4447
|
role: "assistant",
|
|
4403
|
-
content: [{ type: "text", text: "
|
|
4448
|
+
content: [{ type: "text", text: "all done" }],
|
|
4404
4449
|
});
|
|
4405
4450
|
// Row 7: user follow-up tag line.
|
|
4406
4451
|
expect(result![6]).toEqual({
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { applyRuntimeInjections } from "../daemon/conversation-runtime-assembly.js";
|
|
4
|
+
import { defaultInjectorsPlugin } from "../plugins/defaults/injectors.js";
|
|
3
5
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "../
|
|
6
|
+
registerPlugin,
|
|
7
|
+
resetPluginRegistryForTests,
|
|
8
|
+
} from "../plugins/registry.js";
|
|
7
9
|
import type { Message } from "../providers/types.js";
|
|
8
10
|
|
|
9
11
|
// ---------------------------------------------------------------------------
|
|
@@ -21,43 +23,21 @@ function userMsg(text: string): Message {
|
|
|
21
23
|
const sampleContext =
|
|
22
24
|
"<workspace>\nRoot: /sandbox\nDirectories: src, lib, tests\n</workspace>";
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// The standalone `injectWorkspaceTopLevelContext` helper was removed in
|
|
27
|
+
// G2.1. The workspace-context default injector (registered by
|
|
28
|
+
// `defaultInjectorsPlugin`) now emits the workspace block as a
|
|
29
|
+
// `prepend-user-tail` placement during `applyRuntimeInjections`. The suite
|
|
30
|
+
// below exercises that end-to-end path instead.
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
role: "user",
|
|
37
|
-
content: [
|
|
38
|
-
{ type: "text", text: "First" },
|
|
39
|
-
{ type: "text", text: "Second" },
|
|
40
|
-
],
|
|
41
|
-
};
|
|
42
|
-
const injected = injectWorkspaceTopLevelContext(original, sampleContext);
|
|
43
|
-
|
|
44
|
-
expect(injected.content).toHaveLength(3);
|
|
45
|
-
expect(injected.content[0].type).toBe("text");
|
|
46
|
-
expect((injected.content[0] as { text: string }).text).toBe(sampleContext);
|
|
47
|
-
expect((injected.content[1] as { text: string }).text).toBe("First");
|
|
48
|
-
expect((injected.content[2] as { text: string }).text).toBe("Second");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("does not mutate original message", () => {
|
|
52
|
-
const original = userMsg("Hello");
|
|
53
|
-
const originalContentLength = original.content.length;
|
|
54
|
-
injectWorkspaceTopLevelContext(original, sampleContext);
|
|
55
|
-
|
|
56
|
-
expect(original.content).toHaveLength(originalContentLength);
|
|
32
|
+
describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
// Post-G2.1: workspace injection is driven by the `workspace-context`
|
|
35
|
+
// default injector, so the plugin must be registered for the chain to
|
|
36
|
+
// produce a block. Each test gets a clean registry.
|
|
37
|
+
resetPluginRegistryForTests();
|
|
38
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
57
39
|
});
|
|
58
|
-
});
|
|
59
40
|
|
|
60
|
-
describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
61
41
|
test("injects workspace context when provided", async () => {
|
|
62
42
|
const messages: Message[] = [userMsg("Hello")];
|
|
63
43
|
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
@@ -100,6 +80,11 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
|
100
80
|
});
|
|
101
81
|
|
|
102
82
|
describe("applyRuntimeInjections — minimal mode skips workspace blocks", () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
resetPluginRegistryForTests();
|
|
85
|
+
registerPlugin(defaultInjectorsPlugin);
|
|
86
|
+
});
|
|
87
|
+
|
|
103
88
|
test("minimal mode skips workspace top-level context", async () => {
|
|
104
89
|
const messages: Message[] = [userMsg("Hello")];
|
|
105
90
|
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
@@ -411,14 +411,14 @@ describe("composeConversationSeed", () => {
|
|
|
411
411
|
});
|
|
412
412
|
|
|
413
413
|
test('falls back to event name when title is "Notification" and body is empty', () => {
|
|
414
|
-
const signal = makeSignal({ sourceEventName: "
|
|
414
|
+
const signal = makeSignal({ sourceEventName: "watcher.notification" });
|
|
415
415
|
const copy = makeCopy({ title: "Notification", body: "" });
|
|
416
416
|
const seed = composeConversationSeed(
|
|
417
417
|
signal,
|
|
418
418
|
"vellum" as NotificationChannel,
|
|
419
419
|
copy,
|
|
420
420
|
);
|
|
421
|
-
expect(seed).toBe("
|
|
421
|
+
expect(seed).toBe("Watcher notification");
|
|
422
422
|
});
|
|
423
423
|
|
|
424
424
|
test('uses context payload "message" field in fallback when available', () => {
|
|
@@ -30,7 +30,7 @@ mock.module("../providers/registry.js", () => ({
|
|
|
30
30
|
mock.module("../config/loader.js", () => ({
|
|
31
31
|
getConfig: () => ({
|
|
32
32
|
ui: {},
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
llm: {
|
|
35
35
|
default: {
|
|
36
36
|
provider: "mock-provider",
|
|
@@ -207,6 +207,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
207
207
|
getToolTokenBudget() {
|
|
208
208
|
return 0;
|
|
209
209
|
}
|
|
210
|
+
getResolvedTools() {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
210
213
|
getActiveModel() {
|
|
211
214
|
return undefined;
|
|
212
215
|
}
|
|
@@ -215,7 +218,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
215
218
|
onEvent: (event: AgentEvent) => void,
|
|
216
219
|
_signal?: AbortSignal,
|
|
217
220
|
_requestId?: string,
|
|
218
|
-
_onCheckpoint?: (
|
|
221
|
+
_onCheckpoint?: (
|
|
222
|
+
checkpoint: CheckpointInfo,
|
|
223
|
+
) => CheckpointDecision | Promise<CheckpointDecision>,
|
|
219
224
|
): Promise<Message[]> {
|
|
220
225
|
return new Promise<Message[]>((resolve) => {
|
|
221
226
|
pendingRuns.push({ resolve, messages, onEvent });
|