@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
|
@@ -39,6 +39,7 @@ import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
|
|
|
39
39
|
import { recordToolDenied, recordToolError } from "../telemetry/tool-audit.js";
|
|
40
40
|
import { getLogger } from "../util/logger.js";
|
|
41
41
|
import { resolveExecutionTarget } from "./execution-target.js";
|
|
42
|
+
import { safeTimeoutMs } from "./execution-timeout.js";
|
|
42
43
|
import { channelCoordinatesFromToolContext } from "./policy-context.js";
|
|
43
44
|
import { getAllTools, getTool, getToolOwner } from "./registry.js";
|
|
44
45
|
import { isSideEffectTool } from "./side-effects.js";
|
|
@@ -127,9 +128,39 @@ export function buildInactiveToolMessage(args: {
|
|
|
127
128
|
|
|
128
129
|
/** Default polling interval for inline grant wait (ms). */
|
|
129
130
|
const TC_GRANT_WAIT_INTERVAL_MS = 500;
|
|
130
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Fallback maximum wait for the inline grant wait (ms), used only when the
|
|
133
|
+
* deployed config cannot be read. The governing budget is
|
|
134
|
+
* `timeouts.permissionTimeoutSec` (see {@link resolveInlineGrantWaitMs}).
|
|
135
|
+
*/
|
|
131
136
|
export const TC_GRANT_WAIT_MAX_MS = 60_000;
|
|
132
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the wait budget for an escalated tool grant.
|
|
140
|
+
*
|
|
141
|
+
* A guardian answering an escalated tool call is making the same decision as a
|
|
142
|
+
* local user answering a permission prompt, so both paths spend the same
|
|
143
|
+
* budget: `timeouts.permissionTimeoutSec` (read by `permissions/prompter.ts`).
|
|
144
|
+
* If anything, this path needs the larger share of it: the prompter's user
|
|
145
|
+
* already has the prompt on screen, while the guardian is notified
|
|
146
|
+
* out-of-band and has to context-switch before deciding.
|
|
147
|
+
*
|
|
148
|
+
* Falls back to {@link TC_GRANT_WAIT_MAX_MS} rather than the tool-execution
|
|
149
|
+
* default on a non-positive value, so a bad config can never collapse the
|
|
150
|
+
* window to zero and auto-deny every escalation.
|
|
151
|
+
*
|
|
152
|
+
* Exported because the grant resolver sizes its `inline_wait_active` staleness
|
|
153
|
+
* threshold off this same budget: if the two drift, an approval arriving while
|
|
154
|
+
* a waiter is still live gets misread as a dead waiter and the requester is
|
|
155
|
+
* told to retry a call that is about to resume on its own.
|
|
156
|
+
*/
|
|
157
|
+
export function resolveInlineGrantWaitMs(): number {
|
|
158
|
+
return safeTimeoutMs(
|
|
159
|
+
getConfig().timeouts.permissionTimeoutSec,
|
|
160
|
+
TC_GRANT_WAIT_MAX_MS,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
133
164
|
/**
|
|
134
165
|
* Inline wait result for trusted-contact grant polling.
|
|
135
166
|
* - `granted`: a grant was minted and consumed within the wait window.
|
|
@@ -152,13 +183,16 @@ export type InlineGrantWaitOutcome =
|
|
|
152
183
|
* and atomically consume the grant).
|
|
153
184
|
*
|
|
154
185
|
* Only called for trusted_contact actors with valid guardian bindings.
|
|
186
|
+
*
|
|
187
|
+
* `options.maxWaitMs` overrides the wait budget; omitting it spends the
|
|
188
|
+
* configured one from {@link resolveInlineGrantWaitMs}.
|
|
155
189
|
*/
|
|
156
190
|
export async function waitForInlineGrant(
|
|
157
191
|
escalationRequestId: string,
|
|
158
192
|
consumeParams: Parameters<typeof consumeGrantForInvocation>[0],
|
|
159
193
|
options?: { maxWaitMs?: number; intervalMs?: number; signal?: AbortSignal },
|
|
160
194
|
): Promise<InlineGrantWaitOutcome> {
|
|
161
|
-
const maxWait = options?.maxWaitMs ??
|
|
195
|
+
const maxWait = options?.maxWaitMs ?? resolveInlineGrantWaitMs();
|
|
162
196
|
const interval = options?.intervalMs ?? TC_GRANT_WAIT_INTERVAL_MS;
|
|
163
197
|
const signal = options?.signal;
|
|
164
198
|
const deadline = Date.now() + maxWait;
|
|
@@ -592,9 +626,16 @@ export type PreExecutionGateResult =
|
|
|
592
626
|
}
|
|
593
627
|
| { allowed: false; result: ToolExecutionResult };
|
|
594
628
|
|
|
595
|
-
/**
|
|
629
|
+
/**
|
|
630
|
+
* Overrides for the inline grant wait behavior. Production leaves this empty
|
|
631
|
+
* so the wait spends the configured budget; tests inject short waits to keep
|
|
632
|
+
* escalation cases fast.
|
|
633
|
+
*/
|
|
596
634
|
export interface InlineGrantWaitConfig {
|
|
597
|
-
/**
|
|
635
|
+
/**
|
|
636
|
+
* Maximum time to wait for guardian approval (ms). Defaults to the budget
|
|
637
|
+
* from {@link resolveInlineGrantWaitMs}.
|
|
638
|
+
*/
|
|
598
639
|
maxWaitMs?: number;
|
|
599
640
|
/** Polling interval during the wait (ms). Defaults to TC_GRANT_WAIT_INTERVAL_MS. */
|
|
600
641
|
intervalMs?: number;
|
package/src/tools/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import type { AnsweredQuestion } from "../api/events/question-answered.js";
|
|
3
4
|
import type { InterfaceId } from "../channels/types.js";
|
|
4
5
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
5
6
|
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
@@ -133,6 +134,14 @@ export interface ToolExecutionResult {
|
|
|
133
134
|
/** Structured activity metadata for client rendering (web search, web fetch, etc).
|
|
134
135
|
* Populated by daemon-internal tools; plugins must not set this. */
|
|
135
136
|
activityMetadata?: ToolActivityMetadata;
|
|
137
|
+
/**
|
|
138
|
+
* Typed side channel from the `ask_question` executor to the agent loop: the
|
|
139
|
+
* questions asked and the answers the user gave. The loop forwards it on the
|
|
140
|
+
* `tool_result` event and persists it on the tool_use block, so the answered
|
|
141
|
+
* card renders live and survives a history reopen instead of the decision
|
|
142
|
+
* disappearing with the interactive prompt. Set only by `ask_question`.
|
|
143
|
+
*/
|
|
144
|
+
answeredQuestion?: AnsweredQuestion;
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
export type ProxyToolResolver = (
|
|
@@ -407,6 +416,14 @@ export interface ToolContext {
|
|
|
407
416
|
* @legacy
|
|
408
417
|
*/
|
|
409
418
|
overrideProfile?: string;
|
|
419
|
+
/**
|
|
420
|
+
* The firing's `cron_runs.id` when a schedule triggered this turn, `null`
|
|
421
|
+
* otherwise. Tools that spawn further LLM work (`subagent_spawn`,
|
|
422
|
+
* `subagent_message`) forward it so the delegated usage rows carry the same
|
|
423
|
+
* stamp and attribute to the firing rather than dropping out of schedule
|
|
424
|
+
* cost reporting.
|
|
425
|
+
*/
|
|
426
|
+
cronRunId?: string | null;
|
|
410
427
|
/**
|
|
411
428
|
* The LLM call site of the turn currently executing this tool (`mainAgent`,
|
|
412
429
|
* `heartbeatAgent`, scheduled work, etc.). `subagent_spawn` reads it to
|
|
@@ -418,10 +435,12 @@ export interface ToolContext {
|
|
|
418
435
|
invokingCallSite?: LLMCallSite;
|
|
419
436
|
/**
|
|
420
437
|
* Canonical principal ID of the actor on whose behalf this tool invocation
|
|
421
|
-
* is running
|
|
438
|
+
* is running — the turn's actor principal, falling back to the trust
|
|
439
|
+
* context's `guardianPrincipalId` (see `resolveTurnActorPrincipalId`).
|
|
422
440
|
* Used by host proxies to bind cross-client targeted execution to the same
|
|
423
|
-
* authenticated user identity
|
|
424
|
-
* with
|
|
441
|
+
* authenticated user identity, so it must resolve to the SAME principal a
|
|
442
|
+
* desktop client registers with on its SSE stream. May be undefined for
|
|
443
|
+
* legacy/internal flows with no resolved actor identity.
|
|
425
444
|
* @legacy
|
|
426
445
|
*/
|
|
427
446
|
sourceActorPrincipalId?: string;
|
|
@@ -132,7 +132,7 @@ export const SURFACE_SHAPE_DOCS: Record<string, SurfaceShapeDoc> = {
|
|
|
132
132
|
oauth_connect: {
|
|
133
133
|
purpose: "managed OAuth connection button for an integration account",
|
|
134
134
|
shape:
|
|
135
|
-
"{ providerKey, displayName?, description?, logoUrl? }
|
|
135
|
+
"{ providerKey, requestedScopes?, displayName?, description?, logoUrl? }: managed OAuth connection CTA; use when the task needs a managed integration account (Google, Linear, GitHub, ...) instead of settings or shell OAuth. requestedScopes is an optional FULL replacement set of OAuth scopes: when set, the connection is granted exactly these scopes instead of the platform defaults, so include every scope the connection should keep; omit it to use the platform's default scopes",
|
|
136
136
|
missingContent: (data) =>
|
|
137
137
|
isNonEmptyString(data.providerKey)
|
|
138
138
|
? null
|
|
@@ -79,8 +79,10 @@ export const WIDGET_TOKEN_PROPERTIES: readonly string[] = [
|
|
|
79
79
|
// System / status
|
|
80
80
|
"--system-positive-strong",
|
|
81
81
|
"--system-positive-weak",
|
|
82
|
+
"--system-positive-on-weak",
|
|
82
83
|
"--system-negative-strong",
|
|
83
84
|
"--system-negative-weak",
|
|
85
|
+
"--system-negative-on-weak",
|
|
84
86
|
"--system-negative-hover",
|
|
85
87
|
"--system-mid-strong",
|
|
86
88
|
"--system-mid-weak",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { getDb } from "../../persistence/db-connection.js";
|
|
4
|
+
import { initializeDb } from "../../persistence/db-init.js";
|
|
5
|
+
import {
|
|
6
|
+
resetSubagentAttributionCacheForTests,
|
|
7
|
+
resolveSubagentAttribution,
|
|
8
|
+
} from "../subagent-attribution.js";
|
|
9
|
+
|
|
10
|
+
await initializeDb();
|
|
11
|
+
|
|
12
|
+
let counter = 0;
|
|
13
|
+
|
|
14
|
+
function insertConversation(opts: {
|
|
15
|
+
subagentRole?: string;
|
|
16
|
+
subagentSpawnMode?: string;
|
|
17
|
+
}): string {
|
|
18
|
+
counter += 1;
|
|
19
|
+
const id = `conv-attr-${counter}`;
|
|
20
|
+
const db = getDb();
|
|
21
|
+
db.run(
|
|
22
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at, subagent_role, subagent_spawn_mode)
|
|
23
|
+
VALUES ('${id}', 'background', 1000, 1000, ${
|
|
24
|
+
opts.subagentRole === undefined ? "NULL" : `'${opts.subagentRole}'`
|
|
25
|
+
}, ${
|
|
26
|
+
opts.subagentSpawnMode === undefined
|
|
27
|
+
? "NULL"
|
|
28
|
+
: `'${opts.subagentSpawnMode}'`
|
|
29
|
+
})`,
|
|
30
|
+
);
|
|
31
|
+
return id;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe("resolveSubagentAttribution", () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
resetSubagentAttributionCacheForTests();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("returns the stamped role and spawn mode for a subagent conversation", () => {
|
|
40
|
+
const id = insertConversation({
|
|
41
|
+
subagentRole: "advisor",
|
|
42
|
+
subagentSpawnMode: "advisor_consult",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(resolveSubagentAttribution(id)).toEqual({
|
|
46
|
+
subagentRole: "advisor",
|
|
47
|
+
subagentSpawnMode: "advisor_consult",
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("returns nulls for an ordinary conversation", () => {
|
|
52
|
+
const id = insertConversation({});
|
|
53
|
+
|
|
54
|
+
expect(resolveSubagentAttribution(id)).toEqual({
|
|
55
|
+
subagentRole: null,
|
|
56
|
+
subagentSpawnMode: null,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("returns nulls without a conversation id", () => {
|
|
61
|
+
expect(resolveSubagentAttribution(undefined)).toEqual({
|
|
62
|
+
subagentRole: null,
|
|
63
|
+
subagentSpawnMode: null,
|
|
64
|
+
});
|
|
65
|
+
expect(resolveSubagentAttribution("")).toEqual({
|
|
66
|
+
subagentRole: null,
|
|
67
|
+
subagentSpawnMode: null,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("returns nulls for an unknown conversation and does not cache the miss", () => {
|
|
72
|
+
// The columns are stamped at conversation creation, so a miss means the
|
|
73
|
+
// row has not landed yet, and caching it would pin an empty result.
|
|
74
|
+
expect(resolveSubagentAttribution("conv-not-yet")).toEqual({
|
|
75
|
+
subagentRole: null,
|
|
76
|
+
subagentSpawnMode: null,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const db = getDb();
|
|
80
|
+
db.run(
|
|
81
|
+
`INSERT INTO conversations (id, conversation_type, created_at, updated_at, subagent_role, subagent_spawn_mode)
|
|
82
|
+
VALUES ('conv-not-yet', 'background', 1000, 1000, 'coder', 'regular')`,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
expect(resolveSubagentAttribution("conv-not-yet")).toEqual({
|
|
86
|
+
subagentRole: "coder",
|
|
87
|
+
subagentSpawnMode: "regular",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("memoizes a resolved conversation", () => {
|
|
92
|
+
const id = insertConversation({
|
|
93
|
+
subagentRole: "general",
|
|
94
|
+
subagentSpawnMode: "fork",
|
|
95
|
+
});
|
|
96
|
+
expect(resolveSubagentAttribution(id).subagentSpawnMode).toBe("fork");
|
|
97
|
+
|
|
98
|
+
// The columns are immutable after creation, so a later write must not be
|
|
99
|
+
// observable, proving the second call did not hit the database.
|
|
100
|
+
const db = getDb();
|
|
101
|
+
db.run(
|
|
102
|
+
`UPDATE conversations SET subagent_spawn_mode = 'regular' WHERE id = '${id}'`,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(resolveSubagentAttribution(id).subagentSpawnMode).toBe("fork");
|
|
106
|
+
resetSubagentAttributionCacheForTests();
|
|
107
|
+
expect(resolveSubagentAttribution(id).subagentSpawnMode).toBe("regular");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { rawGet } from "../persistence/raw-query.js";
|
|
2
|
+
import { getLogger } from "../util/logger.js";
|
|
3
|
+
|
|
4
|
+
const log = getLogger("subagent-attribution");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The two delegated-work attribution dimensions carried on a conversation:
|
|
8
|
+
* the subagent role it was spawned with, and how it was spawned. Both null
|
|
9
|
+
* for a conversation that is not a subagent.
|
|
10
|
+
*/
|
|
11
|
+
export interface SubagentAttribution {
|
|
12
|
+
subagentRole: string | null;
|
|
13
|
+
subagentSpawnMode: string | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const NOT_A_SUBAGENT: SubagentAttribution = {
|
|
17
|
+
subagentRole: null,
|
|
18
|
+
subagentSpawnMode: null,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Bound on the memo below. Both columns are stamped once at conversation
|
|
23
|
+
* creation and never updated, so a hit is always correct and the only cost of
|
|
24
|
+
* a bounded cache is an occasional re-read. Sized to comfortably cover the
|
|
25
|
+
* conversations resident in one process without becoming a memory concern.
|
|
26
|
+
*/
|
|
27
|
+
const MAX_CACHED_CONVERSATIONS = 2048;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Insertion-ordered memo. `Map` iteration order is insertion order, so
|
|
31
|
+
* dropping the first key evicts the oldest entry, sufficient for a lookup
|
|
32
|
+
* whose values are immutable and whose miss cost is a single primary-key read.
|
|
33
|
+
*/
|
|
34
|
+
const cache = new Map<string, SubagentAttribution>();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the delegated-work attribution for a conversation, for forwarding on
|
|
38
|
+
* the runtime proxy's `X-Vellum-Subagent-*` headers.
|
|
39
|
+
*
|
|
40
|
+
* Reads the columns stamped at spawn time (migration 362) rather than the
|
|
41
|
+
* `subagents` table: `subagents` rows are deleted on dispose, and the billing
|
|
42
|
+
* path must not depend on a row that may already be gone.
|
|
43
|
+
*
|
|
44
|
+
* Never throws. This runs on the provider dispatch hot path and attribution
|
|
45
|
+
* must never be able to take a model request down. Any failure degrades to
|
|
46
|
+
* "no attribution", which is exactly how the platform treats a missing header.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveSubagentAttribution(
|
|
49
|
+
conversationId: string | undefined,
|
|
50
|
+
): SubagentAttribution {
|
|
51
|
+
if (conversationId === undefined || conversationId.length === 0) {
|
|
52
|
+
return NOT_A_SUBAGENT;
|
|
53
|
+
}
|
|
54
|
+
const cached = cache.get(conversationId);
|
|
55
|
+
if (cached !== undefined) {
|
|
56
|
+
return cached;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const row = readRow(conversationId);
|
|
60
|
+
if (!row) {
|
|
61
|
+
// Either no row yet, or the read failed. Deliberately NOT cached: caching
|
|
62
|
+
// a miss would pin an empty result for a conversation whose row lands
|
|
63
|
+
// moments later, or for the whole process after one transient DB error.
|
|
64
|
+
return NOT_A_SUBAGENT;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const attribution: SubagentAttribution = {
|
|
68
|
+
subagentRole: row.subagent_role,
|
|
69
|
+
subagentSpawnMode: row.subagent_spawn_mode,
|
|
70
|
+
};
|
|
71
|
+
if (cache.size >= MAX_CACHED_CONVERSATIONS) {
|
|
72
|
+
const oldest = cache.keys().next();
|
|
73
|
+
if (!oldest.done) {
|
|
74
|
+
cache.delete(oldest.value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
cache.set(conversationId, attribution);
|
|
78
|
+
return attribution;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ConversationSubagentRow {
|
|
82
|
+
subagent_role: string | null;
|
|
83
|
+
subagent_spawn_mode: string | null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read the two columns, or `null` when the conversation has no row or the
|
|
88
|
+
* read fails. Failure covers the pre-migration case (columns absent) and any
|
|
89
|
+
* DB-unavailable context, such as a unit test exercising the provider stack
|
|
90
|
+
* alone. Attribution must never be able to take a model request down.
|
|
91
|
+
*/
|
|
92
|
+
function readRow(conversationId: string): ConversationSubagentRow | null {
|
|
93
|
+
try {
|
|
94
|
+
return rawGet<ConversationSubagentRow>(
|
|
95
|
+
"usage:subagentAttribution",
|
|
96
|
+
`SELECT subagent_role, subagent_spawn_mode FROM conversations WHERE id = ?`,
|
|
97
|
+
conversationId,
|
|
98
|
+
);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
log.debug({ err, conversationId }, "Subagent attribution lookup failed");
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Test seam: drops every memoized entry. */
|
|
106
|
+
export function resetSubagentAttributionCacheForTests(): void {
|
|
107
|
+
cache.clear();
|
|
108
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `withSqliteRetry()`, the shared retry wrapper for transient
|
|
3
|
+
* SQLite write contention (`SQLITE_BUSY*` / `SQLITE_IOERR*`).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from "bun:test";
|
|
7
|
+
|
|
8
|
+
import { isRetryableSqliteError, withSqliteRetry } from "../sqlite-retry.js";
|
|
9
|
+
|
|
10
|
+
function sqliteError(code: string): Error {
|
|
11
|
+
const err = new Error(code);
|
|
12
|
+
(err as Error & { code: string }).code = code;
|
|
13
|
+
return err;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("isRetryableSqliteError", () => {
|
|
17
|
+
test("matches the SQLITE_BUSY and SQLITE_IOERR families", () => {
|
|
18
|
+
expect(isRetryableSqliteError(sqliteError("SQLITE_BUSY"))).toBe(true);
|
|
19
|
+
expect(isRetryableSqliteError(sqliteError("SQLITE_BUSY_SNAPSHOT"))).toBe(
|
|
20
|
+
true,
|
|
21
|
+
);
|
|
22
|
+
expect(isRetryableSqliteError(sqliteError("SQLITE_IOERR"))).toBe(true);
|
|
23
|
+
expect(isRetryableSqliteError(sqliteError("SQLITE_IOERR_SHORT_READ"))).toBe(
|
|
24
|
+
true,
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("rejects non-transient errors", () => {
|
|
29
|
+
expect(isRetryableSqliteError(sqliteError("SQLITE_CONSTRAINT"))).toBe(
|
|
30
|
+
false,
|
|
31
|
+
);
|
|
32
|
+
expect(isRetryableSqliteError(new Error("database is locked"))).toBe(false);
|
|
33
|
+
expect(isRetryableSqliteError(null)).toBe(false);
|
|
34
|
+
expect(isRetryableSqliteError(undefined)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("withSqliteRetry", () => {
|
|
39
|
+
test("returns the wrapped value on first success", async () => {
|
|
40
|
+
let calls = 0;
|
|
41
|
+
const result = await withSqliteRetry(
|
|
42
|
+
() => {
|
|
43
|
+
calls += 1;
|
|
44
|
+
return "ok";
|
|
45
|
+
},
|
|
46
|
+
{ op: "test-op" },
|
|
47
|
+
);
|
|
48
|
+
expect(result).toBe("ok");
|
|
49
|
+
expect(calls).toBe(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("retries transient contention and returns the eventual success", async () => {
|
|
53
|
+
let calls = 0;
|
|
54
|
+
const result = await withSqliteRetry(
|
|
55
|
+
() => {
|
|
56
|
+
calls += 1;
|
|
57
|
+
if (calls <= 2) {
|
|
58
|
+
throw sqliteError("SQLITE_BUSY");
|
|
59
|
+
}
|
|
60
|
+
return "recovered";
|
|
61
|
+
},
|
|
62
|
+
{ op: "test-op", baseDelayMs: 1 },
|
|
63
|
+
);
|
|
64
|
+
expect(result).toBe("recovered");
|
|
65
|
+
expect(calls).toBe(3);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("awaits an async wrapped function", async () => {
|
|
69
|
+
let calls = 0;
|
|
70
|
+
const result = await withSqliteRetry(
|
|
71
|
+
async () => {
|
|
72
|
+
calls += 1;
|
|
73
|
+
if (calls === 1) {
|
|
74
|
+
throw sqliteError("SQLITE_IOERR");
|
|
75
|
+
}
|
|
76
|
+
return 42;
|
|
77
|
+
},
|
|
78
|
+
{ op: "test-op", baseDelayMs: 1 },
|
|
79
|
+
);
|
|
80
|
+
expect(result).toBe(42);
|
|
81
|
+
expect(calls).toBe(2);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("throws immediately on a non-retryable error", async () => {
|
|
85
|
+
let calls = 0;
|
|
86
|
+
await expect(
|
|
87
|
+
withSqliteRetry(
|
|
88
|
+
() => {
|
|
89
|
+
calls += 1;
|
|
90
|
+
throw sqliteError("SQLITE_CONSTRAINT_PRIMARYKEY");
|
|
91
|
+
},
|
|
92
|
+
{ op: "test-op", baseDelayMs: 1 },
|
|
93
|
+
),
|
|
94
|
+
).rejects.toThrow("SQLITE_CONSTRAINT_PRIMARYKEY");
|
|
95
|
+
expect(calls).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("rethrows after exhausting maxRetries", async () => {
|
|
99
|
+
let calls = 0;
|
|
100
|
+
await expect(
|
|
101
|
+
withSqliteRetry(
|
|
102
|
+
() => {
|
|
103
|
+
calls += 1;
|
|
104
|
+
throw sqliteError("SQLITE_BUSY");
|
|
105
|
+
},
|
|
106
|
+
{ op: "test-op", maxRetries: 2, baseDelayMs: 1 },
|
|
107
|
+
),
|
|
108
|
+
).rejects.toThrow("SQLITE_BUSY");
|
|
109
|
+
// Initial attempt + 2 retries.
|
|
110
|
+
expect(calls).toBe(3);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -46,6 +46,22 @@ export function createAbortReason(
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* True when the abort came from a person stopping the turn they are watching
|
|
51
|
+
* (the Stop button, Esc, or the CLI's cancel signal file) rather than from the
|
|
52
|
+
* conversation going away or another subsystem seizing it.
|
|
53
|
+
*
|
|
54
|
+
* The distinction matters for the message queue. A user interrupt ends the
|
|
55
|
+
* turn in flight; it says nothing about the messages the same user queued
|
|
56
|
+
* behind it, so those survive the abort and run on the interrupted turn's
|
|
57
|
+
* drain. Every other kind (dispose, eviction, voice supersession, subagent
|
|
58
|
+
* teardown) is the conversation or its owner disappearing, where a queued
|
|
59
|
+
* message has nothing left to run on and is discarded.
|
|
60
|
+
*/
|
|
61
|
+
export function isUserInterruptAbort(reason: AbortReason | undefined): boolean {
|
|
62
|
+
return reason?.kind === "user_cancel" || reason?.kind === "signal_cancel";
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
export function isAbortReason(value: unknown): value is AbortReason {
|
|
50
66
|
if (typeof value !== "object" || value === null) {
|
|
51
67
|
return false;
|
package/src/util/ansi.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripping of terminal escape sequences and control characters from
|
|
3
|
+
* untrusted strings before they reach a terminal or notification copy.
|
|
4
|
+
*
|
|
5
|
+
* One pattern serves every call site so coverage cannot drift between
|
|
6
|
+
* hand-rolled copies. CSI sequences carry the full parameter and
|
|
7
|
+
* intermediate byte ranges; OSC sequences match through their BEL/ST
|
|
8
|
+
* terminator, with the terminator optional so an unterminated sequence
|
|
9
|
+
* cannot smuggle its body past the strip.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Well-formed ANSI escape sequences: CSI (`ESC [ params intermediates final`,
|
|
14
|
+
* covering colors, cursor moves, erase) and OSC (`ESC ] payload BEL|ST`,
|
|
15
|
+
* covering window-title writes and hyperlinks).
|
|
16
|
+
*/
|
|
17
|
+
const ANSI_SEQUENCE_RE =
|
|
18
|
+
/\u001b\[[0-?]*[ -/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?/g;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Runs of C0/C1 control characters, including DEL and any bare ESC left by a
|
|
22
|
+
* malformed sequence. `\r` and `\n` are C0, so this also flattens newlines.
|
|
23
|
+
*/
|
|
24
|
+
const CONTROL_CHAR_RUN_RE = /[\u0000-\u001f\u007f-\u009f]+/g;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Remove ANSI CSI/OSC escape sequences, leaving other control characters
|
|
28
|
+
* (newlines, tabs) intact. For multi-line text whose line structure matters.
|
|
29
|
+
*/
|
|
30
|
+
export function stripAnsiSequences(value: string): string {
|
|
31
|
+
return value.replace(ANSI_SEQUENCE_RE, "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Remove ANSI CSI/OSC escape sequences, then replace every remaining run of
|
|
36
|
+
* C0/C1 control characters with `replacement` (removed by default). For
|
|
37
|
+
* single-line rendering where no control character may survive.
|
|
38
|
+
*/
|
|
39
|
+
export function stripAnsiAndControlChars(
|
|
40
|
+
value: string,
|
|
41
|
+
replacement = "",
|
|
42
|
+
): string {
|
|
43
|
+
return stripAnsiSequences(value).replace(CONTROL_CHAR_RUN_RE, replacement);
|
|
44
|
+
}
|
package/src/util/disk-usage.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
2
|
import { existsSync, statfsSync } from "node:fs";
|
|
3
|
+
import { promisify } from "node:util";
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
getIsContainerized,
|
|
@@ -8,6 +9,8 @@ import {
|
|
|
8
9
|
} from "../config/env-registry.js";
|
|
9
10
|
import { getWorkspaceDir } from "./platform.js";
|
|
10
11
|
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
|
|
11
14
|
export interface DiskUsageInfo {
|
|
12
15
|
path: string;
|
|
13
16
|
totalMb: number;
|
|
@@ -19,21 +22,17 @@ export interface DiskUsageInfo {
|
|
|
19
22
|
* Measure the on-disk usage of one or more directory paths using `du -sb`.
|
|
20
23
|
* Returns the sum of all paths in bytes, or null on failure.
|
|
21
24
|
*/
|
|
22
|
-
function getDirectorySizeBytes(paths: string[]): number | null {
|
|
25
|
+
async function getDirectorySizeBytes(paths: string[]): Promise<number | null> {
|
|
23
26
|
try {
|
|
24
27
|
const existing = paths.filter((p) => existsSync(p));
|
|
25
28
|
if (existing.length === 0) {
|
|
26
29
|
return null;
|
|
27
30
|
}
|
|
28
|
-
const
|
|
29
|
-
encoding: "utf-8",
|
|
31
|
+
const { stdout } = await execFileAsync("du", ["-sb", ...existing], {
|
|
30
32
|
timeout: 30_000,
|
|
31
33
|
});
|
|
32
|
-
if (result.status !== 0) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
34
|
let total = 0;
|
|
36
|
-
for (const line of
|
|
35
|
+
for (const line of stdout.trim().split("\n")) {
|
|
37
36
|
const size = parseInt(line.split("\t")[0], 10);
|
|
38
37
|
if (!isNaN(size) && size > 0) {
|
|
39
38
|
total += size;
|
|
@@ -46,14 +45,19 @@ function getDirectorySizeBytes(paths: string[]): number | null {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
const DU_CACHE_TTL_MS = 60_000;
|
|
49
|
-
|
|
48
|
+
// Caches the in-flight promise so concurrent callers share one `du` run.
|
|
49
|
+
let duCacheValue: Promise<number | null> | null = null;
|
|
50
50
|
let duCacheTime = 0;
|
|
51
51
|
let duCachePaths: string | null = null;
|
|
52
52
|
|
|
53
|
-
function getCachedDirectorySizeBytes(paths: string[]): number | null {
|
|
53
|
+
function getCachedDirectorySizeBytes(paths: string[]): Promise<number | null> {
|
|
54
54
|
const key = paths.join("\0");
|
|
55
55
|
const now = Date.now();
|
|
56
|
-
if (
|
|
56
|
+
if (
|
|
57
|
+
duCacheValue !== null &&
|
|
58
|
+
duCachePaths === key &&
|
|
59
|
+
now - duCacheTime < DU_CACHE_TTL_MS
|
|
60
|
+
) {
|
|
57
61
|
return duCacheValue;
|
|
58
62
|
}
|
|
59
63
|
duCacheValue = getDirectorySizeBytes(paths);
|
|
@@ -119,7 +123,7 @@ function classifyWorkspaceVolume(
|
|
|
119
123
|
return { kind: "none" };
|
|
120
124
|
}
|
|
121
125
|
|
|
122
|
-
export function getDiskUsageInfo(): DiskUsageInfo | null {
|
|
126
|
+
export async function getDiskUsageInfo(): Promise<DiskUsageInfo | null> {
|
|
123
127
|
try {
|
|
124
128
|
const wsDir = getWorkspaceDir();
|
|
125
129
|
const diskPath = existsSync(wsDir) ? wsDir : "/";
|
|
@@ -135,7 +139,7 @@ export function getDiskUsageInfo(): DiskUsageInfo | null {
|
|
|
135
139
|
if (diskPath !== "/data" && existsSync("/data")) {
|
|
136
140
|
volumePaths.push("/data");
|
|
137
141
|
}
|
|
138
|
-
const usedBytes = getCachedDirectorySizeBytes(volumePaths);
|
|
142
|
+
const usedBytes = await getCachedDirectorySizeBytes(volumePaths);
|
|
139
143
|
if (usedBytes !== null) {
|
|
140
144
|
const totalBytes =
|
|
141
145
|
reporting.kind === "fixed-cap"
|