@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
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
*
|
|
5
5
|
* This module owns only the durable row shape and raw SQL. The mapping to and
|
|
6
6
|
* from the manager's `SubagentState` lives in `SubagentManager`, keeping this
|
|
7
|
-
* layer decoupled from the subagent domain types.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* layer decoupled from the subagent domain types. It reaches into that domain
|
|
8
|
+
* for exactly two things, both to keep a SQL predicate from drifting away from
|
|
9
|
+
* the definition the manager uses: `normalizeSubagentLabel`, because label
|
|
10
|
+
* lookups have to fold the way the in-memory index folds and SQLite has no
|
|
11
|
+
* equivalent (`lower()` is ASCII-only), and `TERMINAL_STATUSES`, which decides
|
|
12
|
+
* which statuses a similarity scan reads as finished.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
normalizeSubagentLabel,
|
|
17
|
+
TERMINAL_STATUSES,
|
|
18
|
+
} from "../subagent/types.js";
|
|
15
19
|
import { rawAll, rawGet, rawRun } from "./raw-query.js";
|
|
16
20
|
|
|
17
21
|
/** A durable subagent lifecycle record (camelCase mirror of the row). */
|
|
@@ -299,6 +303,144 @@ export function getSubagentRecordsByParent(
|
|
|
299
303
|
).map(rowToRecord);
|
|
300
304
|
}
|
|
301
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Rows the similarity scan reads before it stops. The scan is bounded so a
|
|
308
|
+
* long-lived assistant cannot turn every spawn into a full-table read; a
|
|
309
|
+
* truncated window can only under-count, which is the safe direction for a
|
|
310
|
+
* caller that rate-limits on the count.
|
|
311
|
+
*/
|
|
312
|
+
const RECENT_SPAWN_SCAN_LIMIT = 500;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* The status a run has to hold to count as a repeat of already-done work. A
|
|
316
|
+
* failed, aborted, or interrupted run left no answer behind, so spawning its
|
|
317
|
+
* objective again is recovery rather than repetition, and counting those runs
|
|
318
|
+
* would hold the retry that is exactly the right move.
|
|
319
|
+
*/
|
|
320
|
+
const REUSABLE_SPAWN_STATUS = "completed";
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Statuses the scan skips: the run ended without an answer, so it is neither
|
|
324
|
+
* work already done nor work still under way. Derived from the terminal set so
|
|
325
|
+
* a new terminal status cannot silently read as in flight.
|
|
326
|
+
*/
|
|
327
|
+
const DEAD_END_SPAWN_STATUSES: readonly string[] = [
|
|
328
|
+
...TERMINAL_STATUSES,
|
|
329
|
+
].filter((status) => status !== REUSABLE_SPAWN_STATUS);
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Fold an objective to the form two spawns are compared in: lowercased and
|
|
333
|
+
* whitespace-collapsed. Callers normalize the incoming objective with this same
|
|
334
|
+
* function, so both sides of the comparison are folded by one implementation.
|
|
335
|
+
*
|
|
336
|
+
* The whole objective is compared, never a prefix. Batch work routinely shares
|
|
337
|
+
* a long instruction preamble and diverges only in its tail (the file,
|
|
338
|
+
* component, or ticket each run targets), and comparing prefixes reads that
|
|
339
|
+
* batch as one objective repeated. Comparing in full costs only the re-run
|
|
340
|
+
* whose wording changed, which is a missed catch by an advisory guard, while a
|
|
341
|
+
* batch folded together holds work the caller genuinely meant to run. The
|
|
342
|
+
* runaway shape this guard exists for is a burst of the SAME objective, which
|
|
343
|
+
* folds identically either way, so full comparison gives up nothing on the
|
|
344
|
+
* case that matters and keeps a legitimate fan-out running.
|
|
345
|
+
*
|
|
346
|
+
* The separate `context` spawn argument is not part of the key: a subagent row
|
|
347
|
+
* does not carry it (context, prompts, and trust are deliberately left off the
|
|
348
|
+
* durable record), so two spawns with one objective and different contexts
|
|
349
|
+
* still fold together. That residue is bounded by the guard being advisory:
|
|
350
|
+
* the caller is told what already ran and spawns anyway if it meant to.
|
|
351
|
+
*/
|
|
352
|
+
export function normalizeSpawnObjective(objective: string): string {
|
|
353
|
+
return objective.toLowerCase().replace(/\s+/gu, " ").trim();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** How runs matching one normalized objective are distributed in a scope. */
|
|
357
|
+
export interface SimilarSpawnTally {
|
|
358
|
+
/** Runs that completed, so their answer exists and can be read. */
|
|
359
|
+
count: number;
|
|
360
|
+
/** Summed `estimated_cost` of the completed rows. */
|
|
361
|
+
estimatedCost: number;
|
|
362
|
+
/**
|
|
363
|
+
* Runs still under way (pending, running, or awaiting input). Kept apart
|
|
364
|
+
* from `count` because they have produced nothing to read yet: they are
|
|
365
|
+
* evidence of duplicated work in progress, not of an available answer.
|
|
366
|
+
*/
|
|
367
|
+
inFlight: number;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** The two scopes a repeat spawn is judged against. */
|
|
371
|
+
export interface RecentSimilarSpawns {
|
|
372
|
+
/** Matches spawned under the same parent conversation. */
|
|
373
|
+
conversation: SimilarSpawnTally;
|
|
374
|
+
/** Matches anywhere in this assistant; the table holds one assistant's rows. */
|
|
375
|
+
assistant: SimilarSpawnTally;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Tally the runs since `sinceMs` (an epoch cutoff) whose objective folds to
|
|
380
|
+
* `normalizedObjective`, both under `parentConversationId` and assistant-wide,
|
|
381
|
+
* split by whether each run finished with an answer or is still under way.
|
|
382
|
+
*
|
|
383
|
+
* Runs that ended without an answer are left out entirely: spawning their
|
|
384
|
+
* objective again is recovery rather than repetition. Advisor rows are left out
|
|
385
|
+
* too: an advisor consult blocks its caller and is not rate-limited, so its
|
|
386
|
+
* history must not count against a background spawn.
|
|
387
|
+
*
|
|
388
|
+
* Both scopes come from one bounded scan because the assistant-wide set
|
|
389
|
+
* contains the conversation's, so a second query would read the same rows
|
|
390
|
+
* twice.
|
|
391
|
+
*
|
|
392
|
+
* Rows hold the raw objective, so the fold has to happen on the column too.
|
|
393
|
+
* It runs in JS rather than SQL: SQLite's `lower()` folds ASCII only and has no
|
|
394
|
+
* whitespace-collapsing function, so a SQL predicate would disagree with
|
|
395
|
+
* {@link normalizeSpawnObjective} on exactly the objectives (accented, oddly
|
|
396
|
+
* spaced) a re-run is most likely to differ by.
|
|
397
|
+
*/
|
|
398
|
+
export function countRecentSimilarSpawns(args: {
|
|
399
|
+
parentConversationId: string;
|
|
400
|
+
normalizedObjective: string;
|
|
401
|
+
sinceMs: number;
|
|
402
|
+
}): RecentSimilarSpawns {
|
|
403
|
+
const rows = rawAll<{
|
|
404
|
+
parent_conversation_id: string;
|
|
405
|
+
objective: string;
|
|
406
|
+
status: string;
|
|
407
|
+
estimated_cost: number;
|
|
408
|
+
}>(
|
|
409
|
+
"subagent:countRecentSimilar",
|
|
410
|
+
`SELECT parent_conversation_id, objective, status, estimated_cost FROM subagents
|
|
411
|
+
WHERE created_at >= ? AND role <> 'advisor'
|
|
412
|
+
AND status NOT IN (${DEAD_END_SPAWN_STATUSES.map(() => "?").join(", ")})
|
|
413
|
+
ORDER BY created_at DESC
|
|
414
|
+
LIMIT ?`,
|
|
415
|
+
args.sinceMs,
|
|
416
|
+
...DEAD_END_SPAWN_STATUSES,
|
|
417
|
+
RECENT_SPAWN_SCAN_LIMIT,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const tally: RecentSimilarSpawns = {
|
|
421
|
+
conversation: { count: 0, estimatedCost: 0, inFlight: 0 },
|
|
422
|
+
assistant: { count: 0, estimatedCost: 0, inFlight: 0 },
|
|
423
|
+
};
|
|
424
|
+
const add = (scope: SimilarSpawnTally, row: (typeof rows)[number]): void => {
|
|
425
|
+
if (row.status === REUSABLE_SPAWN_STATUS) {
|
|
426
|
+
scope.count += 1;
|
|
427
|
+
scope.estimatedCost += row.estimated_cost;
|
|
428
|
+
} else {
|
|
429
|
+
scope.inFlight += 1;
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
for (const row of rows) {
|
|
433
|
+
if (normalizeSpawnObjective(row.objective) !== args.normalizedObjective) {
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
add(tally.assistant, row);
|
|
437
|
+
if (row.parent_conversation_id === args.parentConversationId) {
|
|
438
|
+
add(tally.conversation, row);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return tally;
|
|
442
|
+
}
|
|
443
|
+
|
|
302
444
|
/**
|
|
303
445
|
* Delete every subagent record spawned under `parentConversationId`. A row
|
|
304
446
|
* lives as long as its parent conversation, and the TTL sweep drops a child's
|
|
@@ -75,6 +75,26 @@ describe("managedSpeechTranscribe", () => {
|
|
|
75
75
|
expect(JSON.parse(init.body as string)).not.toHaveProperty("source");
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
+
test("sends the spoken language when one is given", async () => {
|
|
79
|
+
// The platform forwards this to Deepgram, which otherwise decodes the
|
|
80
|
+
// audio as English rather than detecting its language.
|
|
81
|
+
await managedSpeechTranscribe({
|
|
82
|
+
audio: AUDIO,
|
|
83
|
+
mimeType: "audio/wav",
|
|
84
|
+
language: "multi",
|
|
85
|
+
});
|
|
86
|
+
const [, init] = mockClient!.fetch.mock.calls[0] as [string, RequestInit];
|
|
87
|
+
expect(JSON.parse(init.body as string).language).toBe("multi");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("omits language when not provided", async () => {
|
|
91
|
+
// An absent field and an empty one are different requests upstream, and
|
|
92
|
+
// the empty one fails the platform serializer.
|
|
93
|
+
await managedSpeechTranscribe({ audio: AUDIO, mimeType: "audio/wav" });
|
|
94
|
+
const [, init] = mockClient!.fetch.mock.calls[0] as [string, RequestInit];
|
|
95
|
+
expect(JSON.parse(init.body as string)).not.toHaveProperty("language");
|
|
96
|
+
});
|
|
97
|
+
|
|
78
98
|
test("returns unavailable when no platform client resolves", async () => {
|
|
79
99
|
mockClient = null;
|
|
80
100
|
const result = await managedSpeechTranscribe({
|
|
@@ -90,6 +90,14 @@ export async function managedSpeechTranscribe(input: {
|
|
|
90
90
|
audio: Buffer;
|
|
91
91
|
mimeType: string;
|
|
92
92
|
source?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Spoken language to decode: a BCP-47 code, or `"multi"` for nova-3
|
|
95
|
+
* code-switching. Omitted means the platform sends none, which Deepgram
|
|
96
|
+
* decodes as English rather than detecting the language. Older platform
|
|
97
|
+
* builds that predate the field ignore it, so sending one is safe before
|
|
98
|
+
* the platform side deploys.
|
|
99
|
+
*/
|
|
100
|
+
language?: string;
|
|
93
101
|
signal?: AbortSignal;
|
|
94
102
|
}): Promise<ManagedSpeechResult<ManagedSpeechTranscription>> {
|
|
95
103
|
const resolved = await resolveClient();
|
|
@@ -107,6 +115,7 @@ export async function managedSpeechTranscribe(input: {
|
|
|
107
115
|
audioBase64: input.audio.toString("base64"),
|
|
108
116
|
mimeType: input.mimeType,
|
|
109
117
|
...(input.source !== undefined ? { source: input.source } : {}),
|
|
118
|
+
...(input.language ? { language: input.language } : {}),
|
|
110
119
|
}),
|
|
111
120
|
signal: input.signal,
|
|
112
121
|
},
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -173,6 +173,13 @@ export {
|
|
|
173
173
|
CredentialResolutionError,
|
|
174
174
|
resolveCredential,
|
|
175
175
|
} from "./resolve-credential.js";
|
|
176
|
+
// Resolve the public URL a third party should deliver to for one of the
|
|
177
|
+
// plugin's own ingress routes. Which URL is correct depends on how the
|
|
178
|
+
// assistant is reachable (a managed platform callback route, or a configured
|
|
179
|
+
// public ingress), and `ingress.publicBaseUrl` alone does not decide it. Uses
|
|
180
|
+
// the same resolution as `webhooks register`, and registers the callback route
|
|
181
|
+
// on the managed branches. The plugin defaults to the one in context.
|
|
182
|
+
export { resolveWebhookUrl, type WebhookUrlOptions } from "./webhook-url.js";
|
|
176
183
|
// Resolve a provider for a call site (optionally overriding the profile) so a
|
|
177
184
|
// plugin can run inference through the workspace's configured profiles and
|
|
178
185
|
// credentials — managed-proxy or BYOK — without supplying its own API key.
|
|
@@ -317,6 +324,7 @@ export {
|
|
|
317
324
|
deleteConversation,
|
|
318
325
|
getConversation,
|
|
319
326
|
getConversationDirPath,
|
|
327
|
+
getConversationProcessingStartedAt,
|
|
320
328
|
getMessages,
|
|
321
329
|
hasLexicalTokens,
|
|
322
330
|
isConversationProcessing,
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public URL resolution for a plugin's own ingress route.
|
|
3
|
+
*
|
|
4
|
+
* A plugin that receives third-party webhooks has to tell the vendor where to
|
|
5
|
+
* deliver. Which URL is correct depends on how the assistant is reachable:
|
|
6
|
+
* a platform pod and a platform-connected assistant are served through a
|
|
7
|
+
* managed callback route, while a self-hosted deployment is served through its
|
|
8
|
+
* configured public ingress. `ingress.publicBaseUrl` alone does not decide it,
|
|
9
|
+
* because on a platform-connected assistant that value holds the Velay tunnel
|
|
10
|
+
* address, which is not where plugin routes are served.
|
|
11
|
+
*
|
|
12
|
+
* `resolveCallbackUrl` owns that decision for every caller in the codebase
|
|
13
|
+
* (`webhooks register`, the Telegram webhook manager). This exposes it to
|
|
14
|
+
* plugins so there is one implementation rather than one per plugin.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
18
|
+
|
|
19
|
+
import { getConfig } from "../config/loader.js";
|
|
20
|
+
import { resolveCallbackUrl } from "../inbound/platform-callback-registration.js";
|
|
21
|
+
import { getPublicBaseUrl } from "../inbound/public-ingress-urls.js";
|
|
22
|
+
import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The namespace the gateway serves plugin ingress under. Fixed here rather
|
|
26
|
+
* than supplied by the caller, so a plugin cannot claim another's routes.
|
|
27
|
+
*/
|
|
28
|
+
const PLUGIN_WEBHOOK_PREFIX = "webhooks/plugins";
|
|
29
|
+
|
|
30
|
+
/** Plugin directory names usable as a URL path segment. */
|
|
31
|
+
const SAFE_PLUGIN_NAME = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Route paths the gateway serves, matching `IngressRouteSchema` in
|
|
35
|
+
* `gateway/src/channels/plugin-ingress.ts`: relative, free of query, fragment
|
|
36
|
+
* and whitespace, canonical under percent-decoding, and carrying no `.` or
|
|
37
|
+
* `..` segment.
|
|
38
|
+
*
|
|
39
|
+
* The two must agree. A path this rejects but the gateway serves is a route a
|
|
40
|
+
* plugin can install and then never resolve a URL for.
|
|
41
|
+
*/
|
|
42
|
+
function isServableRoutePath(value: string): boolean {
|
|
43
|
+
if (value.length === 0 || value.endsWith("/")) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (!/^[^/?#\s][^?#\s]*$/.test(value)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
let decoded: string;
|
|
50
|
+
try {
|
|
51
|
+
decoded = decodeURIComponent(value);
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (decoded !== value) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return !value.split("/").some((seg) => seg === "." || seg === "..");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface WebhookUrlOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Route path within the plugin's namespace, such as `"events-photon"`.
|
|
64
|
+
* Relative, with no leading slash.
|
|
65
|
+
*/
|
|
66
|
+
path: string;
|
|
67
|
+
/**
|
|
68
|
+
* Plugin whose namespace the route belongs to.
|
|
69
|
+
*
|
|
70
|
+
* Defaults to the plugin in context, which covers hooks and tools. Supply it
|
|
71
|
+
* explicitly from a plugin route handler, where no context is established.
|
|
72
|
+
*/
|
|
73
|
+
plugin?: string;
|
|
74
|
+
/** Human-readable label for the platform's admin display. */
|
|
75
|
+
sourceIdentifier?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Registration type for one route.
|
|
80
|
+
*
|
|
81
|
+
* The platform keys a callback route by type, so two routes of one plugin need
|
|
82
|
+
* two types or the second registration replaces the first. The slug keeps the
|
|
83
|
+
* type legible in the platform's admin display and the digest keeps it unique:
|
|
84
|
+
* `events-v1` and `events_v1` share a slug and differ here.
|
|
85
|
+
*/
|
|
86
|
+
function registrationType(plugin: string, route: string): string {
|
|
87
|
+
const slug = route.replace(/[^a-z0-9]+/gi, "_");
|
|
88
|
+
const digest = createHash("sha256").update(route).digest("hex").slice(0, 8);
|
|
89
|
+
return `plugin_${plugin}_${slug}_${digest}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Drop a trailing slash from a resolved callback URL.
|
|
94
|
+
*
|
|
95
|
+
* The platform appends one to the callback URLs it hands back, and a provider
|
|
96
|
+
* given `.../events-comms/` delivers to that spelling verbatim. The gateway
|
|
97
|
+
* matches a plugin route against its declared path, which may never end in a
|
|
98
|
+
* slash (see `IngressRouteSchema`), so the slash comes off once here rather
|
|
99
|
+
* than at each plugin that registers a URL.
|
|
100
|
+
*
|
|
101
|
+
* Only the path is ever trimmed: this resolver passes no query parameters, and
|
|
102
|
+
* a URL carrying one is left alone rather than truncated.
|
|
103
|
+
*/
|
|
104
|
+
function withoutTrailingSlash(url: string): string {
|
|
105
|
+
if (url.includes("?") || url.includes("#")) {
|
|
106
|
+
return url;
|
|
107
|
+
}
|
|
108
|
+
return url.replace(/\/+$/, "");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Resolve the public URL a third party should deliver to for `path`.
|
|
113
|
+
*
|
|
114
|
+
* On the managed branches this registers a callback route with the platform,
|
|
115
|
+
* which is idempotent and matches what `assistant webhooks register` does.
|
|
116
|
+
*
|
|
117
|
+
* @throws when the plugin cannot be determined, when `path` is not one the
|
|
118
|
+
* gateway would serve, or when no ingress is configured and the assistant is
|
|
119
|
+
* not connected to the platform. That last case has no URL that would work,
|
|
120
|
+
* and a plausible one would produce a vendor registration that silently
|
|
121
|
+
* receives nothing.
|
|
122
|
+
*/
|
|
123
|
+
export async function resolveWebhookUrl(
|
|
124
|
+
options: WebhookUrlOptions,
|
|
125
|
+
): Promise<string> {
|
|
126
|
+
const { path, sourceIdentifier } = options;
|
|
127
|
+
const plugin = options.plugin ?? getCurrentPluginName();
|
|
128
|
+
|
|
129
|
+
if (plugin === undefined) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"No plugin is in context; pass `plugin` to resolve a webhook URL.",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (!SAFE_PLUGIN_NAME.test(plugin)) {
|
|
135
|
+
throw new Error(`Invalid plugin name for a webhook URL: "${plugin}"`);
|
|
136
|
+
}
|
|
137
|
+
if (!isServableRoutePath(path)) {
|
|
138
|
+
throw new Error(`Invalid plugin webhook path: "${path}"`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const callbackPath = `${PLUGIN_WEBHOOK_PREFIX}/${plugin}/${path}`;
|
|
142
|
+
|
|
143
|
+
const resolved = await resolveCallbackUrl(
|
|
144
|
+
() => `${getPublicBaseUrl(getConfig())}/${callbackPath}`,
|
|
145
|
+
callbackPath,
|
|
146
|
+
registrationType(plugin, path),
|
|
147
|
+
undefined,
|
|
148
|
+
sourceIdentifier,
|
|
149
|
+
);
|
|
150
|
+
return withoutTrailingSlash(resolved);
|
|
151
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enumeration of installed plugin directories, including the realpath
|
|
3
|
+
* containment boundary that keeps a symlinked root pointing outside the
|
|
4
|
+
* plugins directory out of the list.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
mkdirSync,
|
|
9
|
+
mkdtempSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
symlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { afterAll, beforeEach, describe, expect, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
19
|
+
import { listInstalledPluginDirs } from "../installed-plugin-dirs.js";
|
|
20
|
+
|
|
21
|
+
const pluginsDir = getWorkspacePluginsDir();
|
|
22
|
+
const outsideRoot = mkdtempSync(join(tmpdir(), "installed-plugin-dirs-"));
|
|
23
|
+
|
|
24
|
+
afterAll(() => {
|
|
25
|
+
rmSync(outsideRoot, { recursive: true, force: true });
|
|
26
|
+
rmSync(pluginsDir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/** A directory carrying the `package.json` that marks it as an install. */
|
|
30
|
+
function makePluginDir(parent: string, name: string): string {
|
|
31
|
+
const dir = join(parent, name);
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
writeFileSync(
|
|
34
|
+
join(dir, "package.json"),
|
|
35
|
+
JSON.stringify({ name, version: "1.0.0" }),
|
|
36
|
+
);
|
|
37
|
+
return dir;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
rmSync(pluginsDir, { recursive: true, force: true });
|
|
42
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("listInstalledPluginDirs", () => {
|
|
46
|
+
test("lists real plugin directories and skips dot entries", () => {
|
|
47
|
+
makePluginDir(pluginsDir, "alpha");
|
|
48
|
+
makePluginDir(pluginsDir, ".hidden");
|
|
49
|
+
mkdirSync(join(pluginsDir, "no-manifest"), { recursive: true });
|
|
50
|
+
|
|
51
|
+
expect(listInstalledPluginDirs().map((p) => p.name)).toEqual(["alpha"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("skips a symlinked plugin root that points outside the plugins dir", () => {
|
|
55
|
+
const escapee = makePluginDir(outsideRoot, "escapee");
|
|
56
|
+
symlinkSync(escapee, join(pluginsDir, "escapee"));
|
|
57
|
+
makePluginDir(pluginsDir, "contained");
|
|
58
|
+
|
|
59
|
+
// The plugin loader refuses to import from a root that resolves outside
|
|
60
|
+
// the plugins directory, so enumeration must not report it as installed:
|
|
61
|
+
// a caller acting on this list would arm and run code the loader would
|
|
62
|
+
// never activate.
|
|
63
|
+
expect(listInstalledPluginDirs().map((p) => p.name)).toEqual(["contained"]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("skips a symlink that aliases the plugins directory itself", () => {
|
|
67
|
+
symlinkSync(pluginsDir, join(pluginsDir, "self"));
|
|
68
|
+
|
|
69
|
+
expect(listInstalledPluginDirs()).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("keeps a symlinked plugin root that stays inside the plugins dir", () => {
|
|
73
|
+
const real = makePluginDir(pluginsDir, "real");
|
|
74
|
+
symlinkSync(real, join(pluginsDir, "alias"));
|
|
75
|
+
|
|
76
|
+
expect(
|
|
77
|
+
listInstalledPluginDirs()
|
|
78
|
+
.map((p) => p.name)
|
|
79
|
+
.sort(),
|
|
80
|
+
).toEqual(["alias", "real"]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {
|
|
8
8
|
mkdirSync,
|
|
9
9
|
mkdtempSync,
|
|
10
|
+
realpathSync,
|
|
10
11
|
renameSync,
|
|
11
12
|
rmSync,
|
|
12
13
|
symlinkSync,
|
|
@@ -20,7 +21,10 @@ import { afterAll, describe, expect, test } from "bun:test";
|
|
|
20
21
|
|
|
21
22
|
import { snapshotPluginSource } from "../source-fingerprint.js";
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
// Resolved: snapshots report realpaths, and the platform temp directory can
|
|
25
|
+
// itself sit behind a link, so an unresolved fixture root would make every
|
|
26
|
+
// fixture path an alias of itself.
|
|
27
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "source-fingerprint-")));
|
|
24
28
|
|
|
25
29
|
afterAll(() => {
|
|
26
30
|
rmSync(root, { recursive: true, force: true });
|
|
@@ -12,13 +12,11 @@
|
|
|
12
12
|
* causes a spurious reload.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { existsSync
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
getWorkspacePluginsDir,
|
|
21
|
-
} from "../util/platform.js";
|
|
18
|
+
import { getWorkspaceHooksDir } from "../util/platform.js";
|
|
19
|
+
import { listInstalledPluginDirs } from "./installed-plugin-dirs.js";
|
|
22
20
|
import { snapshotPluginSource } from "./source-fingerprint.js";
|
|
23
21
|
import type { PluginSourceVersion } from "./source-versions.js";
|
|
24
22
|
|
|
@@ -33,28 +31,7 @@ import type { PluginSourceVersion } from "./source-versions.js";
|
|
|
33
31
|
export function collectSourceVersions(): Record<string, PluginSourceVersion> {
|
|
34
32
|
const out: Record<string, PluginSourceVersion> = {};
|
|
35
33
|
|
|
36
|
-
const
|
|
37
|
-
let entries: string[] = [];
|
|
38
|
-
try {
|
|
39
|
-
entries = readdirSync(pluginsDir);
|
|
40
|
-
} catch {
|
|
41
|
-
// No plugins directory yet — nothing to watch there.
|
|
42
|
-
}
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
if (entry.startsWith(".")) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
const dir = join(pluginsDir, entry);
|
|
48
|
-
try {
|
|
49
|
-
if (!statSync(dir).isDirectory()) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (!existsSync(join(dir, "package.json"))) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
34
|
+
for (const { dir } of listInstalledPluginDirs()) {
|
|
58
35
|
const snapshot = snapshotPluginSource(dir);
|
|
59
36
|
out[dir] = {
|
|
60
37
|
fingerprint: snapshot.fingerprint,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* the user, or the model re-issuing the exact same call — surface a notice
|
|
5
5
|
* via `additionalContext` that coaches it to (a) give the user a brief
|
|
6
6
|
* progress summary and (b) delegate the rest of the investigation to an
|
|
7
|
-
* `
|
|
7
|
+
* `researcher` subagent instead of continuing inline.
|
|
8
8
|
*
|
|
9
9
|
* Motivation: a root-cause request investigated inline ran 167 sequential
|
|
10
10
|
* bash calls in a single turn with no user-facing text, overflowed the
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* all observe identical history and compute the same streak). Entries are
|
|
49
49
|
* dropped when the streak restarts.
|
|
50
50
|
*
|
|
51
|
-
* Subagent conversations are exempt:
|
|
51
|
+
* Subagent conversations are exempt: a researcher is *supposed* to dig at
|
|
52
52
|
* length, and subagents cannot nest (`SUBAGENT_LIMITS.maxDepth`), so the
|
|
53
53
|
* delegation advice would be wrong there. Subagents run under the
|
|
54
54
|
* `subagentSpawn` call site, so the exemption is a cheap `ctx.callSite` read.
|
|
@@ -66,7 +66,7 @@ import type {
|
|
|
66
66
|
* provider-only context, never to the user.
|
|
67
67
|
*/
|
|
68
68
|
export const EXPLORATION_DRIFT_NUDGE_TEXT =
|
|
69
|
-
"<system_notice>You have made a long unbroken run of exploration tool calls (shell/file reads) without sending the user any text. Do two things now: (1) send the user a brief summary of what you have found so far
|
|
69
|
+
"<system_notice>You have made a long unbroken run of exploration tool calls (shell/file reads) without sending the user any text. Do two things now: (1) send the user a brief summary of what you have found so far, do not keep working silently; (2) if you are tracing a root cause or exploring code/logs at length, stop exploring inline and delegate the remainder to a subagent: call subagent_spawn with role 'researcher' and a precise objective. It will investigate in its own context window and return a compact root-cause report. Continuing inline floods this conversation's context and risks losing your findings before you can report them.</system_notice>";
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Canonical loop notice, parameterized on the repeated call. Firmer than the
|
|
@@ -77,7 +77,7 @@ export function explorationLoopNudgeText(
|
|
|
77
77
|
toolName: string,
|
|
78
78
|
repeatCount: number,
|
|
79
79
|
): string {
|
|
80
|
-
return `<system_notice>You have issued this exact ${toolName} call ${repeatCount} times in the current run of exploration tool calls. Repeating an identical read-only call yields no new information
|
|
80
|
+
return `<system_notice>You have issued this exact ${toolName} call ${repeatCount} times in the current run of exploration tool calls. Repeating an identical read-only call yields no new information, you are likely stuck. Do two things now: (1) send the user a brief summary of what you have found so far and what you are still missing, do not keep working silently; (2) stop exploring inline and delegate the remaining investigation to a subagent: call subagent_spawn with role 'researcher' and a precise objective that includes what you have already checked and ruled out. It will investigate in its own context window and return a compact root-cause report. Do not re-issue this call again.</system_notice>`;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
@@ -308,7 +308,7 @@ const postToolUse: HookFunction<PostToolUseContext> = async (ctx) => {
|
|
|
308
308
|
trigger: loopNudge ? "loop" : "long-dig",
|
|
309
309
|
...(loopNudge ? { repeatCount: loopRepeatCount } : {}),
|
|
310
310
|
},
|
|
311
|
-
"Exploration drift detected
|
|
311
|
+
"Exploration drift detected: nudging summary + researcher delegation",
|
|
312
312
|
);
|
|
313
313
|
ctx.additionalContext = ctx.additionalContext
|
|
314
314
|
? `${ctx.additionalContext}\n${nudgeText}`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "default-exploration-drift",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "First-party default plugin contributing a post-tool-use hook that nudges the model to summarize and delegate to
|
|
4
|
+
"description": "First-party default plugin contributing a post-tool-use hook that nudges the model to summarize and delegate to a researcher subagent when a turn accumulates a long run of exploration tool calls, or (on loop-prone models) re-issues an identical exploration call.",
|
|
5
5
|
"private": true,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -44,7 +44,7 @@ const postModelCall: HookFunction<PostModelCallContext> = async (ctx) => {
|
|
|
44
44
|
isRecoverableImageError(ctx.error.message) &&
|
|
45
45
|
!isImageRecoveryAttempted(ctx.conversationId)
|
|
46
46
|
) {
|
|
47
|
-
const { messages, changed } = recoverUnsendableImages(ctx.messages);
|
|
47
|
+
const { messages, changed } = await recoverUnsendableImages(ctx.messages);
|
|
48
48
|
// A rejection can be classified as recoverable yet leave nothing to fix —
|
|
49
49
|
// e.g. a media-type mismatch on a format the sniffer cannot identify. Retrying
|
|
50
50
|
// an unchanged history would resend the identical rejected image and surface a
|
|
@@ -59,7 +59,9 @@ const postModelCall: HookFunction<PostModelCallContext> = async (ctx) => {
|
|
|
59
59
|
// turns, so a persistence failure must never abort the retry that is about
|
|
60
60
|
// to run — log it and continue with the in-memory recovery.
|
|
61
61
|
try {
|
|
62
|
-
const rewritten = persistUnsendableImageDowngrades(
|
|
62
|
+
const rewritten = await persistUnsendableImageDowngrades(
|
|
63
|
+
ctx.conversationId,
|
|
64
|
+
);
|
|
63
65
|
if (rewritten > 0) {
|
|
64
66
|
ctx.logger.info(
|
|
65
67
|
{ plugin: "image-recovery", rewritten },
|