@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
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution of the `role` a spawn asks for into one of the three subagent
|
|
3
|
+
* types.
|
|
4
|
+
*
|
|
5
|
+
* The model writes this field freely: it names a type, an older type name, a
|
|
6
|
+
* job title, or a whole sentence of framing. Every one of those has to land on
|
|
7
|
+
* a defined capability envelope, so resolution is total and never rejects.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { truncate } from "../util/truncate.js";
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_SUBAGENT_ROLE,
|
|
13
|
+
SUBAGENT_ROLE_REGISTRY,
|
|
14
|
+
type SubagentRole,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
|
|
17
|
+
export interface ResolvedSubagentRole {
|
|
18
|
+
/** The type the child actually runs as. */
|
|
19
|
+
role: SubagentRole;
|
|
20
|
+
/**
|
|
21
|
+
* The requested text, when it named no type and is carried into the child's
|
|
22
|
+
* framing as a persona instead.
|
|
23
|
+
*/
|
|
24
|
+
personaText?: string;
|
|
25
|
+
/** The legacy name the caller used, when it resolved through an alias. */
|
|
26
|
+
alias?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The role names that predate the three types. They stay accepted because
|
|
31
|
+
* every prompt, skill, and habit that names one is still in circulation, and a
|
|
32
|
+
* spawn is a poor place to learn a taxonomy changed.
|
|
33
|
+
*/
|
|
34
|
+
const ROLE_ALIASES: Record<string, SubagentRole> = {
|
|
35
|
+
planner: "researcher",
|
|
36
|
+
investigator: "researcher",
|
|
37
|
+
coder: "builder",
|
|
38
|
+
general: "builder",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* How much of a free-text role survives into the persona line. A role value
|
|
43
|
+
* can arrive as an entire sentence of instructions; the persona line is a
|
|
44
|
+
* framing hint, and the objective is where the task belongs.
|
|
45
|
+
*/
|
|
46
|
+
const MAX_PERSONA_LENGTH = 120;
|
|
47
|
+
|
|
48
|
+
/** Single-line, length-bounded form of a free-text role. */
|
|
49
|
+
function toPersonaText(raw: string): string {
|
|
50
|
+
return truncate(raw.replace(/\s+/g, " ").trim(), MAX_PERSONA_LENGTH);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a requested role into the type the child runs as.
|
|
55
|
+
*
|
|
56
|
+
* - Absent (or blank) resolves to `builder`, which imposes no tool allowlist.
|
|
57
|
+
* A spawn that names no role has always run on the parent's full surface,
|
|
58
|
+
* and a task delegated without a stated shape is as likely to need a file
|
|
59
|
+
* written as read.
|
|
60
|
+
* - The three type names pass through, case-insensitively.
|
|
61
|
+
* - A legacy name resolves to its successor type and is reported in `alias`.
|
|
62
|
+
* - Anything else resolves to `researcher` with the text kept as
|
|
63
|
+
* `personaText`. This is least privilege for a value nobody defined: a typo
|
|
64
|
+
* or an invented role must not silently hand out write access. When the task
|
|
65
|
+
* really did need to write, the child reports that it cannot and the parent
|
|
66
|
+
* re-spawns as `builder`, which is a visible and cheap correction.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveSubagentRole(
|
|
69
|
+
raw: string | undefined,
|
|
70
|
+
): ResolvedSubagentRole {
|
|
71
|
+
const trimmed = raw?.trim() ?? "";
|
|
72
|
+
if (trimmed.length === 0) {
|
|
73
|
+
return { role: DEFAULT_SUBAGENT_ROLE };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// `Object.hasOwn` rather than `in`: a role of "constructor" or "toString"
|
|
77
|
+
// reaches inherited members and would resolve to something that is not a
|
|
78
|
+
// role at all.
|
|
79
|
+
const key = trimmed.toLowerCase();
|
|
80
|
+
if (Object.hasOwn(SUBAGENT_ROLE_REGISTRY, key)) {
|
|
81
|
+
return { role: key as SubagentRole };
|
|
82
|
+
}
|
|
83
|
+
if (Object.hasOwn(ROLE_ALIASES, key)) {
|
|
84
|
+
return { role: ROLE_ALIASES[key], alias: key };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { role: "researcher", personaText: toPersonaText(trimmed) };
|
|
88
|
+
}
|
package/src/subagent/types.ts
CHANGED
|
@@ -92,15 +92,47 @@ export interface SubagentConfig {
|
|
|
92
92
|
preactivatedSkillIds?: string[];
|
|
93
93
|
/** Whether the parent should present the result to the user. Defaults to true. */
|
|
94
94
|
sendResultToUser?: boolean;
|
|
95
|
-
/**
|
|
95
|
+
/** The subagent's type. Omitted spawns run as {@link DEFAULT_SUBAGENT_ROLE}. */
|
|
96
96
|
role?: SubagentRole;
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
* Free-text framing rendered as a persona line under the role preamble, for
|
|
99
|
+
* a spawn whose requested role names a character rather than a type ("staff
|
|
100
|
+
* security engineer", "financial journalist"). The role still decides what
|
|
101
|
+
* the child may do; the persona only decides how it reads the task.
|
|
102
|
+
*/
|
|
103
|
+
persona?: string;
|
|
104
|
+
/**
|
|
105
|
+
* The shape the child's final message has to take. Omitted spawns run as
|
|
106
|
+
* `report`, which asks for nothing beyond the role preamble's own reporting
|
|
107
|
+
* guidance.
|
|
108
|
+
*/
|
|
109
|
+
outputContract?: SubagentOutputContract;
|
|
110
|
+
/**
|
|
111
|
+
* How this subagent was spawned: the call site and its context/lifecycle
|
|
112
|
+
* shape. Stamped onto the child conversation row and emitted as
|
|
113
|
+
* `subagent_spawn_mode` on `llm_usage` telemetry so delegated spend is
|
|
114
|
+
* separable per variety.
|
|
115
|
+
*
|
|
116
|
+
* Set by the spawning call site, which is the only layer that knows: the
|
|
117
|
+
* manager cannot tell an advisor consult from a plain fork, nor a live-voice
|
|
118
|
+
* continuation from a tool-initiated one. Omitting it falls back to the
|
|
119
|
+
* mechanical `fork ? "fork" : "regular"`, so a future call site that forgets
|
|
120
|
+
* still lands on an honest value rather than NULL.
|
|
121
|
+
*/
|
|
122
|
+
spawnMode?: SubagentSpawnMode;
|
|
123
|
+
/**
|
|
124
|
+
* When true, this subagent may run only the runtime's own read-only built-ins:
|
|
125
|
+
* anything else, including a workspace/plugin/skill/MCP tool registered UNDER
|
|
126
|
+
* a built-in's name, is kept off the model's tool surface and refused by the
|
|
127
|
+
* executor regardless of trust class (see `isRefusedInReadOnlyPass`). Because
|
|
128
|
+
* the check is on the tool's registered OWNER and not its name, it is the only
|
|
129
|
+
* durable read-only guarantee available; a role allowlist alone is a set of
|
|
130
|
+
* names any extension can claim.
|
|
131
|
+
*
|
|
132
|
+
* Set by callers whose contract is that nothing changes: an unattended pass
|
|
133
|
+
* that must not take an unapproved action while the user isn't watching (the
|
|
134
|
+
* subagent surfaces the intended action for the user to approve instead), and
|
|
135
|
+
* the advisor consult, whose whole product is judgment rather than action.
|
|
104
136
|
*/
|
|
105
137
|
denySideEffectTools?: boolean;
|
|
106
138
|
/**
|
|
@@ -143,6 +175,13 @@ export interface SubagentConfig {
|
|
|
143
175
|
* leave this unset so existing call-site precedence stays intact.
|
|
144
176
|
*/
|
|
145
177
|
forceOverrideProfile?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* The `cron_runs.id` of the schedule firing that spawned this subagent.
|
|
180
|
+
* Passed into the subagent's `runAgentLoop` so its usage rows carry the same
|
|
181
|
+
* stamp as the parent turn's and schedule cost reporting sees the delegated
|
|
182
|
+
* spend. Unset for spawns that no schedule triggered.
|
|
183
|
+
*/
|
|
184
|
+
cronRunId?: string | null;
|
|
146
185
|
/**
|
|
147
186
|
* Tool-use id of the `skill_execute` call that spawned this subagent.
|
|
148
187
|
* Forwarded into the `subagent_spawned` event so the client can anchor the
|
|
@@ -168,8 +207,90 @@ export interface SubagentState {
|
|
|
168
207
|
completedAt?: number;
|
|
169
208
|
/** Cumulative token usage. */
|
|
170
209
|
usage: UsageStats;
|
|
210
|
+
/**
|
|
211
|
+
* What the subagent actually ran, harvested from the child conversation when
|
|
212
|
+
* the run ends and re-read while that conversation is still retained (a
|
|
213
|
+
* follow-up turn queued during the run drains after the run returns). In
|
|
214
|
+
* memory only, so a state rebuilt from the durable row never has it: the
|
|
215
|
+
* manager answers that case with {@link SubagentToolStatsReading}.
|
|
216
|
+
*/
|
|
217
|
+
stats?: SubagentToolStatsSummary;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Harvested form of the conversation's live `SubagentToolStats`: the
|
|
222
|
+
* `filesWritten` Set is collapsed to its size, so the state carries counts
|
|
223
|
+
* rather than a reference into a conversation that is about to be released.
|
|
224
|
+
*/
|
|
225
|
+
export interface SubagentToolStatsSummary {
|
|
226
|
+
calls: number;
|
|
227
|
+
succeeded: number;
|
|
228
|
+
/**
|
|
229
|
+
* Distinct paths the child passed to `file_write` / `file_edit`. Those two
|
|
230
|
+
* tools are the only writes the executor can attribute to a path, so a
|
|
231
|
+
* builder that writes through the shell, a document tool, or an MCP tool
|
|
232
|
+
* adds nothing here. {@link formatSubagentToolStats} names the two tools for
|
|
233
|
+
* that reason.
|
|
234
|
+
*/
|
|
235
|
+
filesWritten: number;
|
|
171
236
|
}
|
|
172
237
|
|
|
238
|
+
/**
|
|
239
|
+
* What the manager can say about a subagent's tool-call counters, which live in
|
|
240
|
+
* memory only.
|
|
241
|
+
*
|
|
242
|
+
* - `counted`: the run was harvested, so the numbers are real.
|
|
243
|
+
* - `unmeasured`: a live run that never reached its harvest. Nothing was
|
|
244
|
+
* measured and nothing was lost, so a reader claims neither.
|
|
245
|
+
* - `unrecoverable`: the state was rebuilt from the durable row (the startup
|
|
246
|
+
* rehydration, or a subagent the manager's window no longer holds). The row
|
|
247
|
+
* carries no counters, so they can never be produced for this subagent, and
|
|
248
|
+
* reporting zero calls would read as "this subagent did nothing".
|
|
249
|
+
*
|
|
250
|
+
* A rehydrated entry sits in the manager exactly like a live one, so manager
|
|
251
|
+
* membership alone cannot separate the last two; the manager tracks which of
|
|
252
|
+
* its entries it rebuilt and answers with this instead.
|
|
253
|
+
*/
|
|
254
|
+
export type SubagentToolStatsReading =
|
|
255
|
+
| { kind: "counted"; stats: SubagentToolStatsSummary }
|
|
256
|
+
| { kind: "unmeasured" }
|
|
257
|
+
| { kind: "unrecoverable" };
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* The machine half of a subagent's result, appended to the parent's completion
|
|
261
|
+
* notification and to `subagent_read`. The child's prose is its own account of
|
|
262
|
+
* what it did; this line is the measured one, so a report of executed work by a
|
|
263
|
+
* subagent that called nothing is visible instead of taken on trust.
|
|
264
|
+
*
|
|
265
|
+
* The file count names the two tools it comes from. A builder runs on the
|
|
266
|
+
* parent's whole tool surface and can write through the shell, a document tool,
|
|
267
|
+
* or an MCP tool, none of which the counter sees; an unqualified "files
|
|
268
|
+
* written: 0" would read as fabricated work for a subagent that really did
|
|
269
|
+
* write, which is the exact misreading this line exists to prevent.
|
|
270
|
+
*/
|
|
271
|
+
export function formatSubagentToolStats(
|
|
272
|
+
stats: SubagentToolStatsSummary,
|
|
273
|
+
): string {
|
|
274
|
+
if (stats.calls === 0) {
|
|
275
|
+
return "[stats: no tools were used by this subagent; treat any claims of executed work as unverified]";
|
|
276
|
+
}
|
|
277
|
+
const plural = stats.calls === 1 ? "" : "s";
|
|
278
|
+
return `[stats: ${stats.calls} tool call${plural}, ${stats.succeeded} succeeded, files written via file_write/file_edit: ${stats.filesWritten}]`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Stats footer for a subagent whose in-memory counters no longer exist. */
|
|
282
|
+
export const SUBAGENT_STATS_UNAVAILABLE =
|
|
283
|
+
"[stats: unavailable (tool counters are not retained for this subagent)]";
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Appended to a read taken while the subagent is still working through
|
|
287
|
+
* guidance queued during its run. Its own run is over by then, which is what
|
|
288
|
+
* makes it terminal, but the queued turn adds output and tool calls after
|
|
289
|
+
* that, so such a read is a progress report rather than the final one.
|
|
290
|
+
*/
|
|
291
|
+
export const SUBAGENT_READ_STILL_PROCESSING =
|
|
292
|
+
"[note: this subagent is still processing queued follow-up guidance. The output above and the counts below stop at its last finished turn; read again for the rest.]";
|
|
293
|
+
|
|
173
294
|
// ── Bounded listing ─────────────────────────────────────────────────────
|
|
174
295
|
|
|
175
296
|
/** Recency key matching the durable query's `COALESCE(completed_at, created_at)`. */
|
|
@@ -209,13 +330,119 @@ export const SUBAGENT_LIMITS = {
|
|
|
209
330
|
|
|
210
331
|
// ── Roles ───────────────────────────────────────────────────────────────
|
|
211
332
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
333
|
+
/**
|
|
334
|
+
* What a subagent is, derived from two questions: can it change things, and
|
|
335
|
+
* does the parent wait for it.
|
|
336
|
+
*
|
|
337
|
+
* - `researcher`: read-only, runs in the background, scoped to a fixed
|
|
338
|
+
* allowlist.
|
|
339
|
+
* - `builder`: write-capable, runs in the background, on the parent's whole
|
|
340
|
+
* tool surface.
|
|
341
|
+
* - `advisor`: read-only, the parent turn blocks on its guidance.
|
|
342
|
+
*
|
|
343
|
+
* Write-plus-blocking is deliberately absent: a parent that waits on a change
|
|
344
|
+
* has no reason to delegate it. Model tier is a separate knob
|
|
345
|
+
* (`inference_profile`), and a persona is free text
|
|
346
|
+
* (`SubagentConfig.persona`), so neither is a type.
|
|
347
|
+
*
|
|
348
|
+
* The legacy names (`general`, `coder`, `planner`, `investigator`) remain
|
|
349
|
+
* accepted at the tool boundary as aliases; see ./role-resolution.ts.
|
|
350
|
+
*/
|
|
351
|
+
export type SubagentRole = "researcher" | "builder" | "advisor";
|
|
352
|
+
|
|
353
|
+
// ── Output contracts ─────────────────────────────────────────────────────
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Every contract the tool boundary accepts, in advertised order. `report` is
|
|
357
|
+
* first because it is what a spawn that names none runs under.
|
|
358
|
+
*/
|
|
359
|
+
export const SUBAGENT_OUTPUT_CONTRACTS = [
|
|
360
|
+
"report",
|
|
361
|
+
"verdict",
|
|
362
|
+
"artifact",
|
|
363
|
+
] as const;
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* The shape a subagent's final message has to take. Orthogonal to
|
|
367
|
+
* {@link SubagentRole}, which decides what the child may do: the contract
|
|
368
|
+
* decides what it owes back, so the same read-only researcher can return an
|
|
369
|
+
* investigation report or an evidence-backed pass/fail list.
|
|
370
|
+
*
|
|
371
|
+
* - `report`: prose answering the objective, which is what every role preamble
|
|
372
|
+
* already asks for.
|
|
373
|
+
* - `verdict`: per-criterion PASS/FAIL with the evidence behind each call.
|
|
374
|
+
* Researcher-only, since a verdict is a claim about what is already there.
|
|
375
|
+
* Checking work this way is mechanical rather than exploratory, so the spawn
|
|
376
|
+
* also defaults to the cheap model tier.
|
|
377
|
+
* - `artifact`: the deliverable is the thing produced, not the write-up.
|
|
378
|
+
* Builder-only, since nothing else can produce one.
|
|
379
|
+
*
|
|
380
|
+
* "Verifier" is deliberately not a fourth role: verification is a researcher
|
|
381
|
+
* under the `verdict` contract, and a role would have implied a separate tool
|
|
382
|
+
* envelope it does not need.
|
|
383
|
+
*/
|
|
384
|
+
export type SubagentOutputContract = (typeof SUBAGENT_OUTPUT_CONTRACTS)[number];
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* The instruction a contract adds to the child's framing, or `undefined` for
|
|
388
|
+
* `report`, whose expectations the role preamble already states.
|
|
389
|
+
*
|
|
390
|
+
* Rendered into the built system prompt for a regular spawn and into the fork
|
|
391
|
+
* task framing for a fork, which inherits the parent's prompt verbatim and so
|
|
392
|
+
* never sees a built one.
|
|
393
|
+
*/
|
|
394
|
+
export function subagentOutputContractText(
|
|
395
|
+
contract: SubagentOutputContract | undefined,
|
|
396
|
+
): string | undefined {
|
|
397
|
+
switch (contract) {
|
|
398
|
+
case "verdict":
|
|
399
|
+
return (
|
|
400
|
+
"For each criterion in the objective return PASS or FAIL plus the exact evidence (file path, line, value, or quote). " +
|
|
401
|
+
"If evidence is unavailable say CANNOT VERIFY and state what is missing. No prose beyond the verdict list."
|
|
402
|
+
);
|
|
403
|
+
case "artifact":
|
|
404
|
+
return "Your deliverable is the artifact itself. End by listing the exact files you created or modified.";
|
|
405
|
+
default:
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ── Spawn modes ──────────────────────────────────────────────────────────
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* How a subagent was spawned. Orthogonal to {@link SubagentRole}: the role
|
|
414
|
+
* selects the tool allowlist and system-prompt preamble (what the child may
|
|
415
|
+
* do), the spawn mode selects context inheritance and lifecycle (how many
|
|
416
|
+
* input tokens the child starts with, and whether the parent blocks on it).
|
|
417
|
+
* A fork's inherited parent transcript is the dominant input-token driver and
|
|
418
|
+
* is independent of which role ran, which is why neither field subsumes the
|
|
419
|
+
* other.
|
|
420
|
+
*
|
|
421
|
+
* - `regular`: fire-and-forget `subagent_spawn`, fresh objective-only context.
|
|
422
|
+
* - `fork`: `subagent_spawn` with `fork: true`, inherits the parent transcript.
|
|
423
|
+
* - `advisor_consult`: synchronous, read-only advisor consult on the advisor
|
|
424
|
+
* profile; the parent turn blocks on it and returns its guidance inline.
|
|
425
|
+
* - `voice_continuation`: live-voice background continuation of an interrupted
|
|
426
|
+
* turn, spawned as a fork with no role and therefore WRITE-CAPABLE: it runs
|
|
427
|
+
* as {@link DEFAULT_SUBAGENT_ROLE} on the parent's full tool surface, with
|
|
428
|
+
* side effects governed by the standard non-interactive permission path
|
|
429
|
+
* under the trust the foreground voice turn ran under. It is silent only in
|
|
430
|
+
* the sense that the run makes no sound of its own, a later session turn
|
|
431
|
+
* speaking its answer; that is a statement about sound, not blast radius.
|
|
432
|
+
*
|
|
433
|
+
* This block is the single description of the modes. The persistence and
|
|
434
|
+
* telemetry layers that carry the same values point here rather than restate
|
|
435
|
+
* them, so a correction lands once.
|
|
436
|
+
*
|
|
437
|
+
* Mirrored on the wire as `llm_usage.subagent_spawn_mode`, which is an OPEN
|
|
438
|
+
* string set on the platform side: adding a value here needs no coordinated
|
|
439
|
+
* platform release.
|
|
440
|
+
*/
|
|
441
|
+
export type SubagentSpawnMode =
|
|
442
|
+
| "regular"
|
|
443
|
+
| "fork"
|
|
444
|
+
| "advisor_consult"
|
|
445
|
+
| "voice_continuation";
|
|
219
446
|
|
|
220
447
|
export interface SubagentRoleConfig {
|
|
221
448
|
/**
|
|
@@ -223,6 +450,17 @@ export interface SubagentRoleConfig {
|
|
|
223
450
|
* `undefined` means no filter (all tools available).
|
|
224
451
|
*/
|
|
225
452
|
allowedTools?: string[];
|
|
453
|
+
/**
|
|
454
|
+
* When true, the role refuses side-effecting tools regardless of trust
|
|
455
|
+
* class: a name on {@link allowedTools} still has to resolve to a
|
|
456
|
+
* first-party built-in, so a workspace or plugin tool that claims an
|
|
457
|
+
* allowed name is kept off the surface and refused at dispatch.
|
|
458
|
+
*
|
|
459
|
+
* This lives on the role rather than at a spawn site so every path that
|
|
460
|
+
* projects the role, the live spawn and the `tools list --agent` preview
|
|
461
|
+
* alike, applies the same gate.
|
|
462
|
+
*/
|
|
463
|
+
denySideEffects?: boolean;
|
|
226
464
|
/** Skill IDs to pre-activate on the subagent conversation. */
|
|
227
465
|
skillIds: string[];
|
|
228
466
|
/** Role-specific text prepended to the subagent system prompt. */
|
|
@@ -231,76 +469,73 @@ export interface SubagentRoleConfig {
|
|
|
231
469
|
|
|
232
470
|
export const SUBAGENT_ROLE_REGISTRY: Record<SubagentRole, SubagentRoleConfig> =
|
|
233
471
|
{
|
|
234
|
-
general: {
|
|
235
|
-
allowedTools: undefined,
|
|
236
|
-
skillIds: [],
|
|
237
|
-
systemPromptPreamble:
|
|
238
|
-
"You are a general-purpose subagent. Complete the delegated task thoroughly and concisely.",
|
|
239
|
-
},
|
|
240
472
|
researcher: {
|
|
241
473
|
allowedTools: [
|
|
242
474
|
"web_search",
|
|
243
475
|
"web_fetch",
|
|
244
476
|
"file_read",
|
|
245
477
|
"file_list",
|
|
246
|
-
"recall",
|
|
247
|
-
"notify_parent",
|
|
248
|
-
],
|
|
249
|
-
skillIds: [],
|
|
250
|
-
systemPromptPreamble:
|
|
251
|
-
"You are a research-focused subagent with read-only access. Search the web, read files, and recall memories. You cannot write files or run shell commands.",
|
|
252
|
-
},
|
|
253
|
-
coder: {
|
|
254
|
-
allowedTools: [
|
|
255
|
-
"bash",
|
|
256
|
-
"file_read",
|
|
257
|
-
"file_write",
|
|
258
|
-
"file_edit",
|
|
259
|
-
"web_search",
|
|
260
|
-
"recall",
|
|
261
|
-
"notify_parent",
|
|
262
|
-
],
|
|
263
|
-
skillIds: [],
|
|
264
|
-
systemPromptPreamble:
|
|
265
|
-
"You are a code-focused subagent with file and shell access. Read, write, and edit files, and run shell commands.",
|
|
266
|
-
},
|
|
267
|
-
planner: {
|
|
268
|
-
allowedTools: [
|
|
269
|
-
"file_read",
|
|
270
|
-
"file_list",
|
|
271
|
-
"web_search",
|
|
272
|
-
"web_fetch",
|
|
273
|
-
"recall",
|
|
274
|
-
"notify_parent",
|
|
275
|
-
],
|
|
276
|
-
skillIds: [],
|
|
277
|
-
systemPromptPreamble:
|
|
278
|
-
"You are an analysis-focused subagent with read-only access. Read files, search the web, and synthesize findings. You cannot write files or run shell commands.",
|
|
279
|
-
},
|
|
280
|
-
investigator: {
|
|
281
|
-
allowedTools: [
|
|
282
478
|
"code_search",
|
|
283
|
-
"file_read",
|
|
284
|
-
"file_list",
|
|
285
|
-
"web_search",
|
|
286
|
-
"web_fetch",
|
|
287
479
|
"recall",
|
|
480
|
+
"skill_execute",
|
|
288
481
|
"notify_parent",
|
|
289
482
|
],
|
|
290
483
|
skillIds: [],
|
|
291
484
|
systemPromptPreamble: [
|
|
292
|
-
"You are
|
|
293
|
-
|
|
294
|
-
|
|
485
|
+
"You are a research subagent with read-only access: search the web, read and search files, and recall memories. There is no shell, and you cannot write or edit files.",
|
|
486
|
+
// `file_read` takes no default line limit and its results are exempt
|
|
487
|
+
// from result-time spooling, so a full body rides every LLM call until
|
|
488
|
+
// the turn ends. Telling the role to read whole files makes that the
|
|
489
|
+
// default shape of a research turn, and this role now covers the
|
|
490
|
+
// traffic three roles used to. The anti-slicing intent stays: one pass
|
|
491
|
+
// over the range that is needed rather than many small ones.
|
|
492
|
+
"Working method: use code_search to search file contents across directories, file_list to enumerate paths, and file_read to read files and logs. Prefer broad code_search queries across a directory over one-symbol-at-a-time queries, and read the range you need in one pass rather than many small slices.",
|
|
295
493
|
"Send notify_parent (urgency 'important') as soon as each finding is confirmed, so progress survives interruption.",
|
|
296
|
-
"Your final message
|
|
297
|
-
"If you approach context limits, stop investigating and
|
|
494
|
+
"Your final message is the deliverable: a compact report that answers the objective, gives the evidence behind each claim (file:line references, URLs, or quotes), and names what you could not determine. For a root-cause investigation, use the sections Symptom, Root cause, Evidence, Suggested fix, Open questions.",
|
|
495
|
+
"If you approach context limits, stop investigating and write the report from what you have. A partial report delivered is worth more than a complete investigation lost.",
|
|
496
|
+
].join(" "),
|
|
497
|
+
},
|
|
498
|
+
builder: {
|
|
499
|
+
// No allowlist: a builder projects the same tool surface its parent
|
|
500
|
+
// conversation does, connectors, MCP tools, browser and computer use
|
|
501
|
+
// included. A fixed list would be a ceiling on what "can change things"
|
|
502
|
+
// means, and the tools a build task needs are not enumerable in advance:
|
|
503
|
+
// the work that has to file the ticket, send the message, or drive the
|
|
504
|
+
// browser is exactly the work a parent delegates.
|
|
505
|
+
skillIds: [],
|
|
506
|
+
systemPromptPreamble: [
|
|
507
|
+
"You are a build subagent with the parent's full tool surface: read, write, and edit files, run shell commands, search code, search the web, and use any other tool the parent conversation can reach.",
|
|
508
|
+
"Carry the task through end to end, then verify it yourself with the command that proves it (a build, a test run, a re-read of what you wrote) before reporting.",
|
|
509
|
+
"Send notify_parent (urgency 'important') when a milestone lands or a decision only the parent can make blocks you.",
|
|
510
|
+
"Your final message must state what you changed, the exact files you touched, and the result of the verification you ran.",
|
|
298
511
|
].join(" "),
|
|
299
512
|
},
|
|
300
513
|
advisor: {
|
|
301
|
-
|
|
514
|
+
// Read-only fact checking, deliberately narrower than the researcher's
|
|
515
|
+
// list: no web fetch, no skill execution, no memory search, nothing that
|
|
516
|
+
// persists. The advisor answers from the inherited conversation and opens
|
|
517
|
+
// a file only when a specific fact would change the advice.
|
|
518
|
+
//
|
|
519
|
+
// Names alone are not the guarantee. The advisor spawn also sets
|
|
520
|
+
// `denySideEffectTools`, so each name must additionally resolve to the
|
|
521
|
+
// first-party built-in implementation (`READ_ONLY_ALLOWED_TOOLS` plus the
|
|
522
|
+
// `ownerKind === "default"` check in `isRefusedInReadOnlyPass`) before the
|
|
523
|
+
// tool reaches the wire or the executor. Every entry here must stay inside
|
|
524
|
+
// that read-only set, or it is admitted by the role and then refused at
|
|
525
|
+
// dispatch. `subagent-role-registry.test.ts` asserts the subset.
|
|
526
|
+
allowedTools: ["file_read", "file_list", "code_search"],
|
|
527
|
+
denySideEffects: true,
|
|
302
528
|
skillIds: [],
|
|
303
529
|
systemPromptPreamble:
|
|
304
|
-
"You are a read-only senior advisor consulted for a one-shot strategic review. Read the inherited conversation, then return focused, high-leverage guidance in a single response. You
|
|
530
|
+
"You are a read-only senior advisor consulted for a one-shot strategic review. Read the inherited conversation, then return focused, high-leverage guidance in a single response. You may read and search the files in the workspace to verify a decisive fact, but you cannot change anything and you cannot see other conversations.",
|
|
305
531
|
},
|
|
306
532
|
};
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* The type a spawn that names no role runs as. `builder` imposes no tool
|
|
536
|
+
* allowlist, so a caller that omits the field keeps the full surface its
|
|
537
|
+
* conversation projects: the internal context-inheriting callers (the
|
|
538
|
+
* live-voice continuation) continue the parent's turn with the tools that turn
|
|
539
|
+
* had, and a delegated task whose shape was never stated can still write.
|
|
540
|
+
*/
|
|
541
|
+
export const DEFAULT_SUBAGENT_ROLE: SubagentRole = "builder";
|
|
@@ -25,10 +25,9 @@ export interface UnknownAllowlistTool {
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Pure core: given the set of registered tool names, return every allowlist
|
|
28
|
-
* entry that has no matching registered tool.
|
|
29
|
-
* (`allowedTools: undefined
|
|
30
|
-
*
|
|
31
|
-
* registry.
|
|
28
|
+
* entry that has no matching registered tool. A role with no allowlist
|
|
29
|
+
* (`allowedTools: undefined`) imposes no filter and contributes nothing.
|
|
30
|
+
* Exported for direct unit testing without standing up the real tool registry.
|
|
32
31
|
*/
|
|
33
32
|
export function findUnknownAllowlistTools(
|
|
34
33
|
registeredToolNames: ReadonlySet<string>,
|
|
@@ -45,12 +45,22 @@ mock.module("../bot-username.js", () => ({
|
|
|
45
45
|
|
|
46
46
|
let apiBaseUrl = "https://api.telegram.org";
|
|
47
47
|
|
|
48
|
+
// What the gateway reconciler recorded on its last successful setWebhook.
|
|
49
|
+
// Defaults to the URL the fixtures report from getWebhookInfo, so the common
|
|
50
|
+
// case is a verified match; tests override it to exercise a stale registration
|
|
51
|
+
// and a deployment that never recorded one.
|
|
52
|
+
let registeredWebhookUrl: string | undefined =
|
|
53
|
+
"https://example.test/webhooks/telegram";
|
|
54
|
+
|
|
48
55
|
// Spread the real modules below: these are broad barrels shared with peer test
|
|
49
56
|
// files, and replacing one wholesale drops the exports those files import.
|
|
50
57
|
const actualLoader = await import("../../config/loader.js");
|
|
51
58
|
mock.module("../../config/loader.js", () => ({
|
|
52
59
|
...actualLoader,
|
|
53
|
-
getConfig: () => ({
|
|
60
|
+
getConfig: () => ({
|
|
61
|
+
telegram: { apiBaseUrl, registeredWebhookUrl },
|
|
62
|
+
ingress: ingressConfig,
|
|
63
|
+
}),
|
|
54
64
|
loadRawConfig: () => ({ ingress: ingressConfig }),
|
|
55
65
|
}));
|
|
56
66
|
|
|
@@ -152,6 +162,7 @@ beforeEach(() => {
|
|
|
152
162
|
fetchCallCount = 0;
|
|
153
163
|
fetchedUrls.length = 0;
|
|
154
164
|
apiBaseUrl = "https://api.telegram.org";
|
|
165
|
+
registeredWebhookUrl = "https://example.test/webhooks/telegram";
|
|
155
166
|
emittedSignals.length = 0;
|
|
156
167
|
emitFails = false;
|
|
157
168
|
_resetTelegramWebhookHealthState();
|
|
@@ -317,6 +328,67 @@ describe("detection", () => {
|
|
|
317
328
|
expect(result.status).toBe("unknown");
|
|
318
329
|
});
|
|
319
330
|
|
|
331
|
+
test("reports url_mismatch when Telegram holds an address we did not register", async () => {
|
|
332
|
+
// The quiet-channel case the module header names as a known limitation:
|
|
333
|
+
// Telegram only produces delivery errors when it has something to deliver,
|
|
334
|
+
// so a stale tunnel address never errors and, before this comparison, read
|
|
335
|
+
// as healthy. No error is set here on purpose.
|
|
336
|
+
registeredWebhookUrl = "https://current.test/webhooks/telegram";
|
|
337
|
+
setWebhookInfo({
|
|
338
|
+
url: "https://stale.test/webhooks/telegram",
|
|
339
|
+
pending_update_count: 0,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const result = await checkTelegramWebhookHealth();
|
|
343
|
+
|
|
344
|
+
expect(result.status).toBe("url_mismatch");
|
|
345
|
+
expect(result.registeredUrl).toBe("https://stale.test/webhooks/telegram");
|
|
346
|
+
expect(result.expectedUrl).toBe("https://current.test/webhooks/telegram");
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("reports url_mismatch ahead of a recent delivery error", async () => {
|
|
350
|
+
// Both conditions hold. The mismatch is the cause and the error is its
|
|
351
|
+
// symptom, and they have different fixes, so the mismatch must win.
|
|
352
|
+
registeredWebhookUrl = "https://current.test/webhooks/telegram";
|
|
353
|
+
setWebhookInfo({
|
|
354
|
+
url: "https://stale.test/webhooks/telegram",
|
|
355
|
+
pending_update_count: 3,
|
|
356
|
+
last_error_date: Math.floor(Date.now() / 1000),
|
|
357
|
+
last_error_message: "Connection refused",
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect((await checkTelegramWebhookHealth()).status).toBe("url_mismatch");
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("reports unverified when nothing recorded which URL we registered", async () => {
|
|
364
|
+
// Reconciliation has not completed, or predates URL recording. Registered
|
|
365
|
+
// and quiet, but unproven: this must not report healthy.
|
|
366
|
+
registeredWebhookUrl = undefined;
|
|
367
|
+
setWebhookInfo({
|
|
368
|
+
url: "https://example.test/webhooks/telegram",
|
|
369
|
+
pending_update_count: 0,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const result = await checkTelegramWebhookHealth();
|
|
373
|
+
|
|
374
|
+
expect(result.status).toBe("unverified");
|
|
375
|
+
expect(result.expectedUrl).toBeUndefined();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test("an unverified webhook does not alert the guardian", async () => {
|
|
379
|
+
// Unproven is not broken. Alerting here would page every install whose
|
|
380
|
+
// last reconciliation predates recording.
|
|
381
|
+
registeredWebhookUrl = undefined;
|
|
382
|
+
setWebhookInfo({
|
|
383
|
+
url: "https://example.test/webhooks/telegram",
|
|
384
|
+
pending_update_count: 0,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
await runTelegramWebhookHealthCheck();
|
|
388
|
+
|
|
389
|
+
expect(emittedSignals).toHaveLength(0);
|
|
390
|
+
});
|
|
391
|
+
|
|
320
392
|
test("honors a configured telegram.apiBaseUrl", async () => {
|
|
321
393
|
apiBaseUrl = "https://telegram-proxy.internal/";
|
|
322
394
|
|