@vellumai/assistant 0.11.2 → 0.11.3-staging.1
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-queue.test.ts +265 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -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-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-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-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 +216 -132
- 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/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 +296 -145
- 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 +85 -42
- package/src/daemon/conversation-process.ts +76 -28
- 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 +105 -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.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/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 +56 -8
- 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 +350 -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 +295 -42
- 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/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/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
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"
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* conversions of the same image (or daemon restarts) skip the sips call.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { execFileSync } from "node:child_process";
|
|
17
16
|
import { createHash } from "node:crypto";
|
|
18
17
|
import {
|
|
19
18
|
closeSync,
|
|
@@ -101,10 +100,10 @@ export interface ConvertToJpegOptions {
|
|
|
101
100
|
quality?: number;
|
|
102
101
|
}
|
|
103
102
|
|
|
104
|
-
function runSips(
|
|
103
|
+
async function runSips(
|
|
105
104
|
inputBytes: Uint8Array,
|
|
106
105
|
options: ConvertToJpegOptions,
|
|
107
|
-
): Buffer | null {
|
|
106
|
+
): Promise<Buffer | null> {
|
|
108
107
|
const stamp = `${Date.now()}-${uuid().slice(0, 8)}`;
|
|
109
108
|
const srcPath = join(tmpdir(), `vellum-img-opt-${stamp}-src`);
|
|
110
109
|
const outPath = join(tmpdir(), `vellum-img-opt-${stamp}-out.jpg`);
|
|
@@ -133,7 +132,15 @@ function runSips(
|
|
|
133
132
|
"--out",
|
|
134
133
|
outPath,
|
|
135
134
|
);
|
|
136
|
-
|
|
135
|
+
const proc = Bun.spawn(["sips", ...args], {
|
|
136
|
+
stdout: "ignore",
|
|
137
|
+
stderr: "ignore",
|
|
138
|
+
timeout: 15_000,
|
|
139
|
+
});
|
|
140
|
+
await proc.exited;
|
|
141
|
+
if (proc.exitCode !== 0) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
137
144
|
return readFileSync(outPath) as Buffer;
|
|
138
145
|
} catch {
|
|
139
146
|
return null;
|
|
@@ -155,10 +162,10 @@ function runSips(
|
|
|
155
162
|
* Convert an image to JPEG, optionally downscaling. Returns null when
|
|
156
163
|
* conversion is unavailable (non-macOS) or fails; callers keep the original.
|
|
157
164
|
*/
|
|
158
|
-
export function convertImageToJpeg(
|
|
165
|
+
export async function convertImageToJpeg(
|
|
159
166
|
bytes: Uint8Array,
|
|
160
167
|
options: ConvertToJpegOptions = {},
|
|
161
|
-
): Buffer | null {
|
|
168
|
+
): Promise<Buffer | null> {
|
|
162
169
|
const hash = createHash("sha256").update(bytes).digest("hex").slice(0, 16);
|
|
163
170
|
// Options qualify the key so full-resolution storage conversions and
|
|
164
171
|
// resized transport conversions of the same source never collide.
|
|
@@ -173,7 +180,7 @@ export function convertImageToJpeg(
|
|
|
173
180
|
return cached;
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
const converted = runSips(bytes, options);
|
|
183
|
+
const converted = await runSips(bytes, options);
|
|
177
184
|
if (!converted) {
|
|
178
185
|
return null;
|
|
179
186
|
}
|
|
@@ -346,12 +353,12 @@ export interface NormalizedImageBytes {
|
|
|
346
353
|
* {@link jpegFilenameFor} when `converted` is true — a MIME-only correction
|
|
347
354
|
* sets `converted: false` and keeps the filename.
|
|
348
355
|
*/
|
|
349
|
-
export function normalizeImageBytes(
|
|
356
|
+
export async function normalizeImageBytes(
|
|
350
357
|
mimeType: string,
|
|
351
358
|
bytes: Uint8Array,
|
|
352
|
-
): NormalizedImageBytes {
|
|
359
|
+
): Promise<NormalizedImageBytes> {
|
|
353
360
|
if (isHeifImage(bytes)) {
|
|
354
|
-
const converted = convertImageToJpeg(bytes, {
|
|
361
|
+
const converted = await convertImageToJpeg(bytes, {
|
|
355
362
|
quality: STORAGE_JPEG_QUALITY,
|
|
356
363
|
});
|
|
357
364
|
if (!converted) {
|
|
@@ -376,14 +383,14 @@ export interface NormalizedImageBase64 {
|
|
|
376
383
|
* Base64 variant of {@link normalizeImageBytes}. Sniffs the decoded head
|
|
377
384
|
* first so non-HEIF payloads skip the full decode.
|
|
378
385
|
*/
|
|
379
|
-
export function normalizeImageBase64(
|
|
386
|
+
export async function normalizeImageBase64(
|
|
380
387
|
mimeType: string,
|
|
381
388
|
dataBase64: string,
|
|
382
|
-
): NormalizedImageBase64 {
|
|
389
|
+
): Promise<NormalizedImageBase64> {
|
|
383
390
|
// 16 base64 chars decode to the 12 bytes the ftyp/signature sniffs need.
|
|
384
391
|
const head = Buffer.from(dataBase64.slice(0, 16), "base64");
|
|
385
392
|
if (isHeifImage(head)) {
|
|
386
|
-
const normalized = normalizeImageBytes(
|
|
393
|
+
const normalized = await normalizeImageBytes(
|
|
387
394
|
mimeType,
|
|
388
395
|
Buffer.from(dataBase64, "base64"),
|
|
389
396
|
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin-declared schedules record their provenance in the `source_key`
|
|
3
|
+
* column as `plugin:<pluginName>/<scheduleName>`. The format is minted by
|
|
4
|
+
* `pluginScheduleSourceKey` in schedule/plugin-schedule-declarations.ts;
|
|
5
|
+
* this leaf parser lets display surfaces (CLI tables, tool output) derive
|
|
6
|
+
* the owning plugin without importing the daemon's schedule modules.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const PLUGIN_SOURCE_KEY_RE = /^plugin:([^/]+)\//;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Display attribution for a schedule row: the owning plugin's name, the raw
|
|
13
|
+
* key when it does not match the format so provenance is never silently
|
|
14
|
+
* dropped, or null when the row is imperative.
|
|
15
|
+
*/
|
|
16
|
+
export function describeScheduleSource(
|
|
17
|
+
sourceKey: string | null | undefined,
|
|
18
|
+
): string | null {
|
|
19
|
+
if (!sourceKey) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return PLUGIN_SOURCE_KEY_RE.exec(sourceKey)?.[1] ?? sourceKey;
|
|
23
|
+
}
|