@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -52,9 +52,16 @@ interface FakeManagedSubagent {
|
|
|
52
52
|
estimatedCost: number;
|
|
53
53
|
};
|
|
54
54
|
subagentDeniedToolNames: Set<string>;
|
|
55
|
+
subagentToolStats: {
|
|
56
|
+
calls: number;
|
|
57
|
+
succeeded: number;
|
|
58
|
+
filesWritten: Set<string>;
|
|
59
|
+
};
|
|
55
60
|
} | null;
|
|
56
61
|
state: SubagentState;
|
|
57
62
|
parentSendToClient: (msg: AssistantEvent) => void;
|
|
63
|
+
/** Sticky marker that a follow-up turn was queued during the run. */
|
|
64
|
+
hadEnqueuedMessages?: boolean;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
/** Type-safe accessor for SubagentManager's private internals via bracket notation. */
|
|
@@ -62,6 +69,7 @@ interface ManagerInternals {
|
|
|
62
69
|
subagents: Map<string, FakeManagedSubagent>;
|
|
63
70
|
parentToChildren: Map<string, Set<string>>;
|
|
64
71
|
runSubagent: (subagentId: string, objective: string) => Promise<void>;
|
|
72
|
+
releaseConversation: (managed: FakeManagedSubagent) => void;
|
|
65
73
|
stopSweep: () => void;
|
|
66
74
|
}
|
|
67
75
|
|
|
@@ -86,6 +94,11 @@ function injectFakeSubagent(
|
|
|
86
94
|
sendToClient: () => {},
|
|
87
95
|
usageStats: { inputTokens: 100, outputTokens: 50, estimatedCost: 0.005 },
|
|
88
96
|
subagentDeniedToolNames: new Set<string>(),
|
|
97
|
+
subagentToolStats: {
|
|
98
|
+
calls: 0,
|
|
99
|
+
succeeded: 0,
|
|
100
|
+
filesWritten: new Set<string>(),
|
|
101
|
+
},
|
|
89
102
|
};
|
|
90
103
|
|
|
91
104
|
const internals = asInternals(manager);
|
|
@@ -359,6 +372,141 @@ describe("SubagentManager notifyParent (via runSubagent)", () => {
|
|
|
359
372
|
asInternals(manager).stopSweep();
|
|
360
373
|
});
|
|
361
374
|
|
|
375
|
+
test("a settled run's notification carries what it actually ran", async () => {
|
|
376
|
+
clearCaptured();
|
|
377
|
+
const manager = new SubagentManager();
|
|
378
|
+
const subagentId = "sub-stats";
|
|
379
|
+
const state = makeState(subagentId);
|
|
380
|
+
injectFakeSubagent(manager, subagentId, state);
|
|
381
|
+
|
|
382
|
+
const managed = asInternals(manager).subagents.get(subagentId)!;
|
|
383
|
+
managed.conversation!.persistUserMessage = () => ({
|
|
384
|
+
id: "msg-1",
|
|
385
|
+
deduplicated: false,
|
|
386
|
+
});
|
|
387
|
+
managed.conversation!.runAgentLoop = async () => {
|
|
388
|
+
const counters = managed.conversation!.subagentToolStats;
|
|
389
|
+
counters.calls += 2;
|
|
390
|
+
counters.succeeded += 2;
|
|
391
|
+
counters.filesWritten.add("/report.md");
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
await asInternals(manager).runSubagent(subagentId, "Do something");
|
|
395
|
+
|
|
396
|
+
expect(capturedNotifications[0].message).toContain(
|
|
397
|
+
"[stats: 2 tool calls, 2 succeeded, files written via file_write/file_edit: 1]",
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
asInternals(manager).stopSweep();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("a queued follow-up turn's tool calls reach the reported stats", async () => {
|
|
404
|
+
clearCaptured();
|
|
405
|
+
const manager = new SubagentManager();
|
|
406
|
+
const subagentId = "sub-queued-stats";
|
|
407
|
+
const state = makeState(subagentId);
|
|
408
|
+
injectFakeSubagent(manager, subagentId, state);
|
|
409
|
+
|
|
410
|
+
const managed = asInternals(manager).subagents.get(subagentId)!;
|
|
411
|
+
managed.conversation!.persistUserMessage = () => ({
|
|
412
|
+
id: "msg-1",
|
|
413
|
+
deduplicated: false,
|
|
414
|
+
});
|
|
415
|
+
managed.conversation!.runAgentLoop = async () => {
|
|
416
|
+
const counters = managed.conversation!.subagentToolStats;
|
|
417
|
+
counters.calls += 2;
|
|
418
|
+
counters.succeeded += 2;
|
|
419
|
+
};
|
|
420
|
+
// Guidance arrived while the run was processing: the conversation is
|
|
421
|
+
// retained past the run so it can drain that turn afterwards.
|
|
422
|
+
managed.hadEnqueuedMessages = true;
|
|
423
|
+
|
|
424
|
+
await asInternals(manager).runSubagent(subagentId, "Do something");
|
|
425
|
+
|
|
426
|
+
// The run's own harvest only ever sees its own calls.
|
|
427
|
+
expect(state.stats).toEqual({ calls: 2, succeeded: 2, filesWritten: 0 });
|
|
428
|
+
// Quoting it in a message that is never rewritten would under-report the
|
|
429
|
+
// queued turn permanently, so the deferred notification quotes nothing and
|
|
430
|
+
// sends the parent to subagent_read instead.
|
|
431
|
+
expect(capturedNotifications[0].message).toContain(
|
|
432
|
+
"Queued follow-up guidance is still being processed",
|
|
433
|
+
);
|
|
434
|
+
expect(capturedNotifications[0].message).not.toContain("[stats:");
|
|
435
|
+
|
|
436
|
+
// The queued turn now drains, into the same retained conversation.
|
|
437
|
+
const counters = managed.conversation!.subagentToolStats;
|
|
438
|
+
counters.calls += 3;
|
|
439
|
+
counters.succeeded += 2;
|
|
440
|
+
counters.filesWritten.add("/queued-turn.md");
|
|
441
|
+
|
|
442
|
+
expect(manager.currentToolStats(subagentId)).toEqual({
|
|
443
|
+
kind: "counted",
|
|
444
|
+
stats: { calls: 5, succeeded: 4, filesWritten: 1 },
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
asInternals(manager).stopSweep();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test("releasing the conversation freezes the settled counters", async () => {
|
|
451
|
+
clearCaptured();
|
|
452
|
+
const manager = new SubagentManager();
|
|
453
|
+
const subagentId = "sub-release-stats";
|
|
454
|
+
const state = makeState(subagentId);
|
|
455
|
+
injectFakeSubagent(manager, subagentId, state);
|
|
456
|
+
|
|
457
|
+
const managed = asInternals(manager).subagents.get(subagentId)!;
|
|
458
|
+
managed.conversation!.persistUserMessage = () => ({
|
|
459
|
+
id: "msg-1",
|
|
460
|
+
deduplicated: false,
|
|
461
|
+
});
|
|
462
|
+
managed.conversation!.runAgentLoop = async () => {
|
|
463
|
+
managed.conversation!.subagentToolStats.calls += 1;
|
|
464
|
+
managed.conversation!.subagentToolStats.succeeded += 1;
|
|
465
|
+
};
|
|
466
|
+
managed.hadEnqueuedMessages = true;
|
|
467
|
+
|
|
468
|
+
await asInternals(manager).runSubagent(subagentId, "Do something");
|
|
469
|
+
|
|
470
|
+
const counters = managed.conversation!.subagentToolStats;
|
|
471
|
+
counters.calls += 4;
|
|
472
|
+
counters.succeeded += 3;
|
|
473
|
+
|
|
474
|
+
// The sweep releases the retained conversation once the drain has had its
|
|
475
|
+
// window; the last reading is taken on the way out and stands afterwards.
|
|
476
|
+
asInternals(manager).releaseConversation(managed);
|
|
477
|
+
|
|
478
|
+
expect(managed.conversation).toBeNull();
|
|
479
|
+
expect(manager.currentToolStats(subagentId)).toEqual({
|
|
480
|
+
kind: "counted",
|
|
481
|
+
stats: { calls: 5, succeeded: 4, filesWritten: 0 },
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
asInternals(manager).stopSweep();
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("a run that never harvested reports nothing rather than a zero", () => {
|
|
488
|
+
const manager = new SubagentManager();
|
|
489
|
+
const subagentId = "sub-unharvested";
|
|
490
|
+
injectFakeSubagent(manager, subagentId, makeState(subagentId));
|
|
491
|
+
|
|
492
|
+
// Still running: its counters are mid-flight, and a zero read off them now
|
|
493
|
+
// would read as "this subagent used no tools".
|
|
494
|
+
expect(manager.currentToolStats(subagentId)).toEqual({
|
|
495
|
+
kind: "unmeasured",
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test("an id the manager never held reports its counters unrecoverable", () => {
|
|
500
|
+
const manager = new SubagentManager();
|
|
501
|
+
|
|
502
|
+
// The counters exist nowhere but memory, so a caller holding a
|
|
503
|
+
// record-derived state for this id can never get them, and reporting zero
|
|
504
|
+
// calls would read as "this subagent did nothing".
|
|
505
|
+
expect(manager.currentToolStats("sub-not-in-manager")).toEqual({
|
|
506
|
+
kind: "unrecoverable",
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
|
|
362
510
|
test("failed subagent does not notify if already aborted", async () => {
|
|
363
511
|
clearCaptured();
|
|
364
512
|
const manager = new SubagentManager();
|
|
@@ -5,10 +5,12 @@ import { migrateCreateSubagentsTable } from "../persistence/migrations/311-creat
|
|
|
5
5
|
import { migrateAddSubagentParentToolUseId } from "../persistence/migrations/356-add-subagent-parent-tool-use-id.js";
|
|
6
6
|
import { resetTestTables } from "../persistence/raw-query.js";
|
|
7
7
|
import {
|
|
8
|
+
countRecentSimilarSpawns,
|
|
8
9
|
deleteSubagentRecordsByParent,
|
|
9
10
|
getSubagentRecordById,
|
|
10
11
|
getSubagentRecordByLabel,
|
|
11
12
|
loadAllSubagentRecords,
|
|
13
|
+
normalizeSpawnObjective,
|
|
12
14
|
type SubagentRecord,
|
|
13
15
|
upsertSubagentRecord,
|
|
14
16
|
} from "../persistence/subagent-store.js";
|
|
@@ -134,6 +136,145 @@ describe("subagent-store", () => {
|
|
|
134
136
|
});
|
|
135
137
|
});
|
|
136
138
|
|
|
139
|
+
describe("recent similar spawn tallies", () => {
|
|
140
|
+
const OBJECTIVE = "Audit the billing pipeline for drift";
|
|
141
|
+
|
|
142
|
+
/** A completed row for `OBJECTIVE`, spelled however the caller wants it. */
|
|
143
|
+
function spawn(
|
|
144
|
+
id: string,
|
|
145
|
+
over: Partial<SubagentRecord> = {},
|
|
146
|
+
): SubagentRecord {
|
|
147
|
+
return record({
|
|
148
|
+
id,
|
|
149
|
+
conversationId: `conv-${id}`,
|
|
150
|
+
objective: OBJECTIVE,
|
|
151
|
+
status: "completed",
|
|
152
|
+
createdAt: Date.now(),
|
|
153
|
+
completedAt: Date.now(),
|
|
154
|
+
estimatedCost: 0.5,
|
|
155
|
+
...over,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function tally(parentConversationId = "parent-1") {
|
|
160
|
+
return countRecentSimilarSpawns({
|
|
161
|
+
parentConversationId,
|
|
162
|
+
normalizedObjective: normalizeSpawnObjective(OBJECTIVE),
|
|
163
|
+
sinceMs: Date.now() - 24 * 60 * 60 * 1000,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
test("normalization folds case and whitespace over the whole objective", () => {
|
|
168
|
+
expect(normalizeSpawnObjective(" Audit the\nBILLING pipeline ")).toBe(
|
|
169
|
+
"audit the billing pipeline",
|
|
170
|
+
);
|
|
171
|
+
expect(normalizeSpawnObjective("x".repeat(200))).toHaveLength(200);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("counts both scopes and sums their cost", () => {
|
|
175
|
+
upsertSubagentRecord(spawn("a"));
|
|
176
|
+
upsertSubagentRecord(
|
|
177
|
+
spawn("b", { objective: " AUDIT the Billing\tpipeline for drift " }),
|
|
178
|
+
);
|
|
179
|
+
upsertSubagentRecord(spawn("c", { parentConversationId: "parent-2" }));
|
|
180
|
+
|
|
181
|
+
expect(tally()).toEqual({
|
|
182
|
+
conversation: { count: 2, estimatedCost: 1, inFlight: 0 },
|
|
183
|
+
assistant: { count: 3, estimatedCost: 1.5, inFlight: 0 },
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("a genuinely different objective is not a match", () => {
|
|
188
|
+
upsertSubagentRecord(spawn("a"));
|
|
189
|
+
upsertSubagentRecord(
|
|
190
|
+
spawn("b", { objective: "Audit the payouts pipeline for drift" }),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
expect(tally().conversation.count).toBe(1);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("a shared boilerplate prefix does not fold distinct objectives together", () => {
|
|
197
|
+
const preamble =
|
|
198
|
+
"Follow the audit checklist to the letter and report ".repeat(3);
|
|
199
|
+
upsertSubagentRecord(
|
|
200
|
+
spawn("a", { objective: `${preamble} on the billing service` }),
|
|
201
|
+
);
|
|
202
|
+
upsertSubagentRecord(
|
|
203
|
+
spawn("b", { objective: `${preamble} on the payouts service` }),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const forBilling = countRecentSimilarSpawns({
|
|
207
|
+
parentConversationId: "parent-1",
|
|
208
|
+
normalizedObjective: normalizeSpawnObjective(
|
|
209
|
+
`${preamble} on the billing service`,
|
|
210
|
+
),
|
|
211
|
+
sinceMs: Date.now() - 24 * 60 * 60 * 1000,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(forBilling.conversation.count).toBe(1);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("the same objective still matches however long its preamble", () => {
|
|
218
|
+
const objective = `${"e".repeat(200)} tail`;
|
|
219
|
+
upsertSubagentRecord(spawn("a", { objective }));
|
|
220
|
+
upsertSubagentRecord(spawn("b", { objective: ` ${objective} ` }));
|
|
221
|
+
|
|
222
|
+
expect(
|
|
223
|
+
countRecentSimilarSpawns({
|
|
224
|
+
parentConversationId: "parent-1",
|
|
225
|
+
normalizedObjective: normalizeSpawnObjective(objective),
|
|
226
|
+
sinceMs: Date.now() - 24 * 60 * 60 * 1000,
|
|
227
|
+
}).conversation.count,
|
|
228
|
+
).toBe(2);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("spawns older than the cutoff and advisor consults are left out", () => {
|
|
232
|
+
upsertSubagentRecord(spawn("fresh"));
|
|
233
|
+
upsertSubagentRecord(
|
|
234
|
+
spawn("stale", { createdAt: Date.now() - 90_000_000 }),
|
|
235
|
+
);
|
|
236
|
+
upsertSubagentRecord(spawn("consult", { role: "advisor" }));
|
|
237
|
+
|
|
238
|
+
expect(tally().assistant.count).toBe(1);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("runs that ended without an answer are left out of both scopes", () => {
|
|
242
|
+
upsertSubagentRecord(spawn("done"));
|
|
243
|
+
for (const status of ["failed", "aborted", "interrupted"]) {
|
|
244
|
+
upsertSubagentRecord(
|
|
245
|
+
spawn(`ended-${status}`, { status, completedAt: Date.now() }),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
expect(tally()).toEqual({
|
|
250
|
+
conversation: { count: 1, estimatedCost: 0.5, inFlight: 0 },
|
|
251
|
+
assistant: { count: 1, estimatedCost: 0.5, inFlight: 0 },
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("runs still under way are tallied apart from completed ones", () => {
|
|
256
|
+
// The burst shape the loop guard exists for: copies fired faster than any
|
|
257
|
+
// of them can finish are invisible to a completed-only count.
|
|
258
|
+
upsertSubagentRecord(spawn("done"));
|
|
259
|
+
upsertSubagentRecord(spawn("running", { status: "running" }));
|
|
260
|
+
upsertSubagentRecord(spawn("queued", { status: "pending" }));
|
|
261
|
+
upsertSubagentRecord(spawn("asking", { status: "awaiting_input" }));
|
|
262
|
+
upsertSubagentRecord(
|
|
263
|
+
spawn("running-elsewhere", {
|
|
264
|
+
status: "running",
|
|
265
|
+
parentConversationId: "parent-2",
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
expect(tally()).toEqual({
|
|
270
|
+
// An unfinished run has no cost recorded yet, so it adds nothing to the
|
|
271
|
+
// completed tally's spend.
|
|
272
|
+
conversation: { count: 1, estimatedCost: 0.5, inFlight: 3 },
|
|
273
|
+
assistant: { count: 1, estimatedCost: 0.5, inFlight: 4 },
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
137
278
|
describe("subagentStateFromRecord", () => {
|
|
138
279
|
test("maps the recorded status verbatim, leaving the settle to the caller", () => {
|
|
139
280
|
upsertSubagentRecord(record({ id: "active", status: "running" }));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { READ_ONLY_ALLOWED_TOOLS } from "../daemon/conversation-tool-setup.js";
|
|
3
4
|
import {
|
|
4
5
|
mergeSkillIds,
|
|
5
6
|
SUBAGENT_ROLE_REGISTRY,
|
|
@@ -9,14 +10,7 @@ import { buildSubagentSystemPrompt } from "../subagent/manager.js";
|
|
|
9
10
|
import type { SubagentConfig } from "../subagent/types.js";
|
|
10
11
|
|
|
11
12
|
/** All roles defined in the SubagentRole union. */
|
|
12
|
-
const ALL_ROLES: SubagentRole[] = [
|
|
13
|
-
"general",
|
|
14
|
-
"researcher",
|
|
15
|
-
"coder",
|
|
16
|
-
"planner",
|
|
17
|
-
"investigator",
|
|
18
|
-
"advisor",
|
|
19
|
-
];
|
|
13
|
+
const ALL_ROLES: SubagentRole[] = ["researcher", "builder", "advisor"];
|
|
20
14
|
|
|
21
15
|
describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
22
16
|
test("covers all values in the SubagentRole union", () => {
|
|
@@ -31,25 +25,84 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
|
31
25
|
}
|
|
32
26
|
});
|
|
33
27
|
|
|
34
|
-
test("
|
|
35
|
-
|
|
28
|
+
test("the read-only roles declare an explicit allowlist", () => {
|
|
29
|
+
for (const role of ["researcher", "advisor"] as const) {
|
|
30
|
+
expect(Array.isArray(SUBAGENT_ROLE_REGISTRY[role].allowedTools)).toBe(
|
|
31
|
+
true,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
expect(
|
|
35
|
+
SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!.length,
|
|
36
|
+
).toBeGreaterThan(0);
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
test("
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
test("builder declares no allowlist, so it keeps the parent's whole tool surface", () => {
|
|
40
|
+
// The regression this guards: an explicit list is a ceiling. A builder
|
|
41
|
+
// scoped to file/shell/web tools silently loses connectors, MCP tools,
|
|
42
|
+
// browser and computer use, and everything else the parent can reach.
|
|
43
|
+
expect(SUBAGENT_ROLE_REGISTRY.builder.allowedTools).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("advisor is scoped to exactly the three read-only fact-checking tools", () => {
|
|
47
|
+
expect(SUBAGENT_ROLE_REGISTRY.advisor.allowedTools).toEqual([
|
|
48
|
+
"file_read",
|
|
49
|
+
"file_list",
|
|
50
|
+
"code_search",
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("advisor cannot search memory or other conversations", () => {
|
|
55
|
+
// `recall` searches memory, the personal knowledge base, and prior
|
|
56
|
+
// conversations. The consult's documented contract is this conversation
|
|
57
|
+
// plus what the advisor reads in the workspace, so the one tool that
|
|
58
|
+
// reaches past it stays out.
|
|
59
|
+
expect(SUBAGENT_ROLE_REGISTRY.advisor.allowedTools).not.toContain("recall");
|
|
60
|
+
expect(SUBAGENT_ROLE_REGISTRY.advisor.systemPromptPreamble).toContain(
|
|
61
|
+
"cannot see other conversations",
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("every advisor tool is inside the runtime's owner-gated read-only set", () => {
|
|
66
|
+
// The advisor spawn sets `denySideEffectTools`, so a name outside
|
|
67
|
+
// READ_ONLY_ALLOWED_TOOLS is admitted by the role and then refused at
|
|
68
|
+
// dispatch: the model would be shown a tool it cannot run. Keeping the
|
|
69
|
+
// allowlist a subset is what makes the two gates agree.
|
|
70
|
+
for (const name of SUBAGENT_ROLE_REGISTRY.advisor.allowedTools!) {
|
|
71
|
+
expect(READ_ONLY_ALLOWED_TOOLS.has(name)).toBe(true);
|
|
47
72
|
}
|
|
48
73
|
});
|
|
49
74
|
|
|
50
|
-
test("advisor is
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
test("advisor is read-only: nothing write-capable, no shell, no skill execution", () => {
|
|
76
|
+
// The consult is a judgment call the parent blocks on, so the advisor must
|
|
77
|
+
// never be able to act on the workspace or persist output of its own.
|
|
78
|
+
const tools = SUBAGENT_ROLE_REGISTRY.advisor.allowedTools!;
|
|
79
|
+
for (const forbidden of [
|
|
80
|
+
"bash",
|
|
81
|
+
"host_bash",
|
|
82
|
+
"file_write",
|
|
83
|
+
"file_edit",
|
|
84
|
+
"skill_execute",
|
|
85
|
+
"web_fetch",
|
|
86
|
+
"recall",
|
|
87
|
+
]) {
|
|
88
|
+
expect(tools).not.toContain(forbidden);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("researcher preamble discourages slicing without instructing whole-file reads", () => {
|
|
93
|
+
const preamble = SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble;
|
|
94
|
+
// `file_read` takes no default line limit and its results skip result-time
|
|
95
|
+
// spooling, so a whole-file instruction puts full bodies on every LLM call
|
|
96
|
+
// for the rest of the turn. Keep the anti-slicing guidance without it.
|
|
97
|
+
expect(preamble.toLowerCase()).not.toContain("read whole files");
|
|
98
|
+
expect(preamble).toContain("one pass rather than many small slices");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("advisor preamble states it can verify facts but cannot change anything", () => {
|
|
102
|
+
const preamble = SUBAGENT_ROLE_REGISTRY.advisor.systemPromptPreamble;
|
|
103
|
+
expect(preamble).toContain("read and search the files");
|
|
104
|
+
expect(preamble).toContain("cannot change anything");
|
|
105
|
+
expect(preamble).not.toContain("no tools");
|
|
53
106
|
});
|
|
54
107
|
|
|
55
108
|
test("SubagentRole type includes advisor", () => {
|
|
@@ -57,21 +110,25 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
|
57
110
|
expect(SUBAGENT_ROLE_REGISTRY[advisor]).toBeDefined();
|
|
58
111
|
});
|
|
59
112
|
|
|
60
|
-
test('every
|
|
61
|
-
for
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
113
|
+
test('every allowlisted background role includes "notify_parent"', () => {
|
|
114
|
+
// Mid-run reporting only means something for a role the parent does not
|
|
115
|
+
// wait on. The advisor blocks the parent turn and returns its guidance as
|
|
116
|
+
// the tool result, so it intentionally has no notify_parent.
|
|
117
|
+
expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
|
|
118
|
+
"notify_parent",
|
|
119
|
+
);
|
|
120
|
+
expect(SUBAGENT_ROLE_REGISTRY.advisor.allowedTools).not.toContain(
|
|
121
|
+
"notify_parent",
|
|
122
|
+
);
|
|
67
123
|
});
|
|
68
124
|
|
|
69
|
-
test('
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
125
|
+
test('the scoped tool-using role includes "skill_execute" so preactivated skills work', () => {
|
|
126
|
+
// Without it, `preactivatedSkillIds` is a silent no-op for the role: the
|
|
127
|
+
// skill loads and none of its tools can be reached. Only an allowlisted
|
|
128
|
+
// role can lose it.
|
|
129
|
+
expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
|
|
130
|
+
"skill_execute",
|
|
131
|
+
);
|
|
75
132
|
});
|
|
76
133
|
|
|
77
134
|
test('researcher includes "recall" for local information access', () => {
|
|
@@ -79,34 +136,42 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
|
79
136
|
expect(tools).toContain("recall");
|
|
80
137
|
});
|
|
81
138
|
|
|
82
|
-
test(
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('planner includes "recall" for local information access', () => {
|
|
87
|
-
expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools!).toContain("recall");
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("investigator is shell-free and uses code_search for code/log investigation", () => {
|
|
91
|
-
const tools = SUBAGENT_ROLE_REGISTRY.investigator.allowedTools!;
|
|
139
|
+
test("researcher is read-only: no shell, no file writes", () => {
|
|
140
|
+
const tools = SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!;
|
|
92
141
|
expect(tools).not.toContain("bash");
|
|
93
142
|
expect(tools).not.toContain("host_bash");
|
|
94
|
-
expect(tools).toContain("code_search");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("investigator excludes file write tools (read-only investigation)", () => {
|
|
98
|
-
const tools = SUBAGENT_ROLE_REGISTRY.investigator.allowedTools!;
|
|
99
143
|
expect(tools).not.toContain("file_write");
|
|
100
144
|
expect(tools).not.toContain("file_edit");
|
|
101
145
|
});
|
|
102
146
|
|
|
103
|
-
test("
|
|
104
|
-
const
|
|
147
|
+
test("researcher can search and enumerate as well as read", () => {
|
|
148
|
+
const tools = SUBAGENT_ROLE_REGISTRY.researcher.allowedTools!;
|
|
149
|
+
expect(tools).toContain("code_search");
|
|
150
|
+
expect(tools).toContain("file_list");
|
|
151
|
+
expect(tools).toContain("web_search");
|
|
152
|
+
expect(tools).toContain("web_fetch");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("researcher preamble carries the read-only investigation contract", () => {
|
|
156
|
+
const preamble = SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble;
|
|
105
157
|
expect(preamble).toContain("Root cause");
|
|
106
158
|
expect(preamble).toContain("Evidence");
|
|
107
159
|
expect(preamble).toContain("notify_parent");
|
|
108
|
-
expect(preamble).not.toContain("shell access");
|
|
109
160
|
expect(preamble).toContain("code_search");
|
|
161
|
+
expect(preamble).toContain("no shell");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("builder preamble states it can write, run commands, and reach the parent's other tools", () => {
|
|
165
|
+
const preamble = SUBAGENT_ROLE_REGISTRY.builder.systemPromptPreamble;
|
|
166
|
+
expect(preamble).toContain("edit files");
|
|
167
|
+
expect(preamble).toContain("shell commands");
|
|
168
|
+
expect(preamble).toContain("any other tool the parent conversation can");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("builder preamble requires self-verification and a change report", () => {
|
|
172
|
+
const preamble = SUBAGENT_ROLE_REGISTRY.builder.systemPromptPreamble;
|
|
173
|
+
expect(preamble).toContain("verify");
|
|
174
|
+
expect(preamble).toContain("files you touched");
|
|
110
175
|
});
|
|
111
176
|
|
|
112
177
|
test("no role references the old memory_recall tool name", () => {
|
|
@@ -122,21 +187,18 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
|
122
187
|
expect(config.skillIds).toEqual([]);
|
|
123
188
|
}
|
|
124
189
|
});
|
|
125
|
-
|
|
126
|
-
test('researcher and planner include "file_list"', () => {
|
|
127
|
-
expect(SUBAGENT_ROLE_REGISTRY.researcher.allowedTools).toContain(
|
|
128
|
-
"file_list",
|
|
129
|
-
);
|
|
130
|
-
expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools).toContain("file_list");
|
|
131
|
-
});
|
|
132
190
|
});
|
|
133
191
|
|
|
134
|
-
describe("buildSubagentSystemPrompt
|
|
135
|
-
const cfg = (
|
|
192
|
+
describe("buildSubagentSystemPrompt: framing and constraints", () => {
|
|
193
|
+
const cfg = (
|
|
194
|
+
objective: string,
|
|
195
|
+
extra: Partial<SubagentConfig> = {},
|
|
196
|
+
): SubagentConfig => ({
|
|
136
197
|
id: "sub-1",
|
|
137
198
|
parentConversationId: "conv-1",
|
|
138
199
|
label: "task",
|
|
139
200
|
objective,
|
|
201
|
+
...extra,
|
|
140
202
|
});
|
|
141
203
|
|
|
142
204
|
test("every role's constructed prompt tells it to signal blocked instead of fabricating a result", () => {
|
|
@@ -156,7 +218,7 @@ describe("buildSubagentSystemPrompt — blocked-signal constraint", () => {
|
|
|
156
218
|
expect(
|
|
157
219
|
buildSubagentSystemPrompt(cfg("save output"), "researcher"),
|
|
158
220
|
).toContain("do NOT fabricate");
|
|
159
|
-
expect(buildSubagentSystemPrompt(cfg("save output"), "
|
|
221
|
+
expect(buildSubagentSystemPrompt(cfg("save output"), "builder")).toContain(
|
|
160
222
|
"do NOT fabricate",
|
|
161
223
|
);
|
|
162
224
|
// It is NOT duplicated into the researcher role's own preamble.
|
|
@@ -164,6 +226,53 @@ describe("buildSubagentSystemPrompt — blocked-signal constraint", () => {
|
|
|
164
226
|
SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble,
|
|
165
227
|
).not.toContain("do NOT fabricate");
|
|
166
228
|
});
|
|
229
|
+
|
|
230
|
+
test("every role is also told not to invent output for a tool that failed or was missing", () => {
|
|
231
|
+
// The blocked-signal line above only covers a capability the ROLE lacks.
|
|
232
|
+
// This one covers the runtime cases: the call failed, or the tool was not
|
|
233
|
+
// there at all.
|
|
234
|
+
for (const role of ALL_ROLES) {
|
|
235
|
+
const prompt = buildSubagentSystemPrompt(cfg("run the build"), role);
|
|
236
|
+
expect(prompt).toContain(
|
|
237
|
+
"If a tool call fails, or a tool you expected is unavailable, report the failure verbatim and stop that line of work.",
|
|
238
|
+
);
|
|
239
|
+
expect(prompt).toContain(
|
|
240
|
+
"Never simulate, reconstruct, or invent tool output you did not actually receive.",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("a persona renders as a line under the role preamble", () => {
|
|
246
|
+
const prompt = buildSubagentSystemPrompt(
|
|
247
|
+
cfg("assess the filing", { persona: "financial journalist" }),
|
|
248
|
+
"researcher",
|
|
249
|
+
);
|
|
250
|
+
expect(prompt).toContain(
|
|
251
|
+
"- Persona: act as financial journalist for this task.",
|
|
252
|
+
);
|
|
253
|
+
// Under the preamble, above the task.
|
|
254
|
+
expect(prompt.indexOf("- Persona:")).toBeGreaterThan(
|
|
255
|
+
prompt.indexOf(SUBAGENT_ROLE_REGISTRY.researcher.systemPromptPreamble),
|
|
256
|
+
);
|
|
257
|
+
expect(prompt.indexOf("- Persona:")).toBeLessThan(
|
|
258
|
+
prompt.indexOf("## Your Task"),
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("no persona line when the spawn carried none", () => {
|
|
263
|
+
expect(
|
|
264
|
+
buildSubagentSystemPrompt(cfg("do the work"), "builder"),
|
|
265
|
+
).not.toContain("- Persona:");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("the prompt names the type that ran", () => {
|
|
269
|
+
const prompt = buildSubagentSystemPrompt(
|
|
270
|
+
cfg("continue the work"),
|
|
271
|
+
"builder",
|
|
272
|
+
);
|
|
273
|
+
expect(prompt).toContain("## Your Task");
|
|
274
|
+
expect(prompt).toContain("- Role: builder");
|
|
275
|
+
});
|
|
167
276
|
});
|
|
168
277
|
|
|
169
278
|
describe("mergeSkillIds", () => {
|