@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
|
@@ -11,6 +11,7 @@ import { join } from "node:path";
|
|
|
11
11
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
+
DEFAULT_READ_LINE_LIMIT,
|
|
14
15
|
FileSystemOps,
|
|
15
16
|
type PathPolicy,
|
|
16
17
|
} from "../tools/shared/filesystem/file-ops-service.js";
|
|
@@ -44,12 +45,12 @@ function sandboxPolicyFor(boundary: string): PathPolicy {
|
|
|
44
45
|
// ---------------------------------------------------------------------------
|
|
45
46
|
|
|
46
47
|
describe("FileSystemOps.readFileSafe", () => {
|
|
47
|
-
test("reads a file successfully", () => {
|
|
48
|
+
test("reads a file successfully", async () => {
|
|
48
49
|
const dir = makeTempDir();
|
|
49
50
|
writeFileSync(join(dir, "hello.txt"), "line one\nline two\nline three\n");
|
|
50
51
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
51
52
|
|
|
52
|
-
const result = ops.readFileSafe({ path: "hello.txt" });
|
|
53
|
+
const result = await ops.readFileSafe({ path: "hello.txt" });
|
|
53
54
|
expect(result.ok).toBe(true);
|
|
54
55
|
if (!result.ok) {
|
|
55
56
|
return;
|
|
@@ -59,11 +60,11 @@ describe("FileSystemOps.readFileSafe", () => {
|
|
|
59
60
|
expect(result.value.content).toContain("line three");
|
|
60
61
|
});
|
|
61
62
|
|
|
62
|
-
test("returns NOT_FOUND for missing file", () => {
|
|
63
|
+
test("returns NOT_FOUND for missing file", async () => {
|
|
63
64
|
const dir = makeTempDir();
|
|
64
65
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
65
66
|
|
|
66
|
-
const result = ops.readFileSafe({ path: "nonexistent.txt" });
|
|
67
|
+
const result = await ops.readFileSafe({ path: "nonexistent.txt" });
|
|
67
68
|
expect(result.ok).toBe(false);
|
|
68
69
|
if (result.ok) {
|
|
69
70
|
return;
|
|
@@ -71,12 +72,12 @@ describe("FileSystemOps.readFileSafe", () => {
|
|
|
71
72
|
expect(result.error.code).toBe("NOT_FOUND");
|
|
72
73
|
});
|
|
73
74
|
|
|
74
|
-
test("returns NOT_A_FILE for a directory", () => {
|
|
75
|
+
test("returns NOT_A_FILE for a directory", async () => {
|
|
75
76
|
const dir = makeTempDir();
|
|
76
77
|
mkdirSync(join(dir, "subdir"));
|
|
77
78
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
78
79
|
|
|
79
|
-
const result = ops.readFileSafe({ path: "subdir" });
|
|
80
|
+
const result = await ops.readFileSafe({ path: "subdir" });
|
|
80
81
|
expect(result.ok).toBe(false);
|
|
81
82
|
if (result.ok) {
|
|
82
83
|
return;
|
|
@@ -84,13 +85,13 @@ describe("FileSystemOps.readFileSafe", () => {
|
|
|
84
85
|
expect(result.error.code).toBe("NOT_A_FILE");
|
|
85
86
|
});
|
|
86
87
|
|
|
87
|
-
test("returns SIZE_LIMIT_EXCEEDED for oversized file", () => {
|
|
88
|
+
test("returns SIZE_LIMIT_EXCEEDED for oversized file", async () => {
|
|
88
89
|
const dir = makeTempDir();
|
|
89
90
|
const filePath = join(dir, "big.txt");
|
|
90
91
|
writeFileSync(filePath, "x".repeat(200));
|
|
91
92
|
|
|
92
93
|
const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
|
|
93
|
-
const result = ops.readFileSafe({ path: "big.txt" });
|
|
94
|
+
const result = await ops.readFileSafe({ path: "big.txt" });
|
|
94
95
|
expect(result.ok).toBe(false);
|
|
95
96
|
if (result.ok) {
|
|
96
97
|
return;
|
|
@@ -98,11 +99,11 @@ describe("FileSystemOps.readFileSafe", () => {
|
|
|
98
99
|
expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
|
|
99
100
|
});
|
|
100
101
|
|
|
101
|
-
test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", () => {
|
|
102
|
+
test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", async () => {
|
|
102
103
|
const dir = makeTempDir();
|
|
103
104
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
104
105
|
|
|
105
|
-
const result = ops.readFileSafe({ path: "../../../etc/passwd" });
|
|
106
|
+
const result = await ops.readFileSafe({ path: "../../../etc/passwd" });
|
|
106
107
|
expect(result.ok).toBe(false);
|
|
107
108
|
if (result.ok) {
|
|
108
109
|
return;
|
|
@@ -110,20 +111,94 @@ describe("FileSystemOps.readFileSafe", () => {
|
|
|
110
111
|
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
111
112
|
});
|
|
112
113
|
|
|
113
|
-
test("respects offset and limit", () => {
|
|
114
|
+
test("respects offset and limit", async () => {
|
|
114
115
|
const dir = makeTempDir();
|
|
115
116
|
writeFileSync(join(dir, "lines.txt"), "a\nb\nc\nd\ne\n");
|
|
116
117
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
117
118
|
|
|
118
|
-
const result = ops.readFileSafe({
|
|
119
|
+
const result = await ops.readFileSafe({
|
|
120
|
+
path: "lines.txt",
|
|
121
|
+
offset: 2,
|
|
122
|
+
limit: 2,
|
|
123
|
+
});
|
|
124
|
+
expect(result.ok).toBe(true);
|
|
125
|
+
if (!result.ok) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Assert on the numbered lines rather than bare letters: the truncation
|
|
129
|
+
// notice is prose, so a bare `toContain("d")` matches its wording.
|
|
130
|
+
const [body] = result.value.content.split("\n\n[Truncated:");
|
|
131
|
+
expect(body).toContain(" 2 b");
|
|
132
|
+
expect(body).toContain(" 3 c");
|
|
133
|
+
expect(body).not.toContain(" 1 a");
|
|
134
|
+
expect(body).not.toContain(" 4 d");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("caps an unbounded read at the default line limit and says so", async () => {
|
|
138
|
+
const dir = makeTempDir();
|
|
139
|
+
const total = DEFAULT_READ_LINE_LIMIT + 500;
|
|
140
|
+
const lines = Array.from({ length: total }, (_, i) => `line${i + 1}`);
|
|
141
|
+
writeFileSync(join(dir, "big.txt"), lines.join("\n"));
|
|
142
|
+
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
143
|
+
|
|
144
|
+
const result = await ops.readFileSafe({ path: "big.txt" });
|
|
145
|
+
expect(result.ok).toBe(true);
|
|
146
|
+
if (!result.ok) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const content = result.value.content;
|
|
150
|
+
expect(content).toContain(` ${DEFAULT_READ_LINE_LIMIT} line2000`);
|
|
151
|
+
expect(content).not.toContain("line2001");
|
|
152
|
+
expect(content).toContain(
|
|
153
|
+
`[Truncated: showing through line ${DEFAULT_READ_LINE_LIMIT} of ${total}. Read on with offset=${DEFAULT_READ_LINE_LIMIT + 1}`,
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("an explicit limit is honored rather than replaced by the default", async () => {
|
|
158
|
+
const dir = makeTempDir();
|
|
159
|
+
const lines = Array.from(
|
|
160
|
+
{ length: DEFAULT_READ_LINE_LIMIT + 500 },
|
|
161
|
+
(_, i) => `line${i + 1}`,
|
|
162
|
+
);
|
|
163
|
+
writeFileSync(join(dir, "big.txt"), lines.join("\n"));
|
|
164
|
+
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
165
|
+
|
|
166
|
+
const result = await ops.readFileSafe({
|
|
167
|
+
path: "big.txt",
|
|
168
|
+
limit: DEFAULT_READ_LINE_LIMIT + 500,
|
|
169
|
+
});
|
|
170
|
+
expect(result.ok).toBe(true);
|
|
171
|
+
if (!result.ok) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
expect(result.value.content).toContain("line2500");
|
|
175
|
+
expect(result.value.content).not.toContain("[Truncated:");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("a read that reaches the last line carries no truncation notice", async () => {
|
|
179
|
+
const dir = makeTempDir();
|
|
180
|
+
writeFileSync(join(dir, "small.txt"), "a\nb\nc");
|
|
181
|
+
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
182
|
+
|
|
183
|
+
const result = await ops.readFileSafe({ path: "small.txt" });
|
|
184
|
+
expect(result.ok).toBe(true);
|
|
185
|
+
if (!result.ok) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
expect(result.value.content).not.toContain("[Truncated:");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("paging past the end is not reported as truncation", async () => {
|
|
192
|
+
const dir = makeTempDir();
|
|
193
|
+
writeFileSync(join(dir, "small.txt"), "a\nb\nc");
|
|
194
|
+
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
195
|
+
|
|
196
|
+
const result = await ops.readFileSafe({ path: "small.txt", offset: 100 });
|
|
119
197
|
expect(result.ok).toBe(true);
|
|
120
198
|
if (!result.ok) {
|
|
121
199
|
return;
|
|
122
200
|
}
|
|
123
|
-
expect(result.value.content).toContain("
|
|
124
|
-
expect(result.value.content).toContain("c");
|
|
125
|
-
expect(result.value.content).not.toContain(" 1");
|
|
126
|
-
expect(result.value.content).not.toContain("d");
|
|
201
|
+
expect(result.value.content).not.toContain("[Truncated:");
|
|
127
202
|
});
|
|
128
203
|
});
|
|
129
204
|
|
|
@@ -132,11 +207,11 @@ describe("FileSystemOps.readFileSafe", () => {
|
|
|
132
207
|
// ---------------------------------------------------------------------------
|
|
133
208
|
|
|
134
209
|
describe("FileSystemOps.writeFileSafe", () => {
|
|
135
|
-
test("writes a new file", () => {
|
|
210
|
+
test("writes a new file", async () => {
|
|
136
211
|
const dir = makeTempDir();
|
|
137
212
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
138
213
|
|
|
139
|
-
const result = ops.writeFileSafe({
|
|
214
|
+
const result = await ops.writeFileSafe({
|
|
140
215
|
path: "new.txt",
|
|
141
216
|
content: "hello world",
|
|
142
217
|
});
|
|
@@ -150,12 +225,12 @@ describe("FileSystemOps.writeFileSafe", () => {
|
|
|
150
225
|
expect(existsSync(join(dir, "new.txt"))).toBe(true);
|
|
151
226
|
});
|
|
152
227
|
|
|
153
|
-
test("overwrites an existing file and returns old content", () => {
|
|
228
|
+
test("overwrites an existing file and returns old content", async () => {
|
|
154
229
|
const dir = makeTempDir();
|
|
155
230
|
writeFileSync(join(dir, "existing.txt"), "old stuff");
|
|
156
231
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
157
232
|
|
|
158
|
-
const result = ops.writeFileSafe({
|
|
233
|
+
const result = await ops.writeFileSafe({
|
|
159
234
|
path: "existing.txt",
|
|
160
235
|
content: "new stuff",
|
|
161
236
|
});
|
|
@@ -168,11 +243,11 @@ describe("FileSystemOps.writeFileSafe", () => {
|
|
|
168
243
|
expect(result.value.newContent).toBe("new stuff");
|
|
169
244
|
});
|
|
170
245
|
|
|
171
|
-
test("creates parent directories when needed", () => {
|
|
246
|
+
test("creates parent directories when needed", async () => {
|
|
172
247
|
const dir = makeTempDir();
|
|
173
248
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
174
249
|
|
|
175
|
-
const result = ops.writeFileSafe({
|
|
250
|
+
const result = await ops.writeFileSafe({
|
|
176
251
|
path: "a/b/c/deep.txt",
|
|
177
252
|
content: "deep",
|
|
178
253
|
});
|
|
@@ -184,11 +259,11 @@ describe("FileSystemOps.writeFileSafe", () => {
|
|
|
184
259
|
expect(existsSync(join(dir, "a/b/c/deep.txt"))).toBe(true);
|
|
185
260
|
});
|
|
186
261
|
|
|
187
|
-
test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", () => {
|
|
262
|
+
test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", async () => {
|
|
188
263
|
const dir = makeTempDir();
|
|
189
264
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
190
265
|
|
|
191
|
-
const result = ops.writeFileSafe({
|
|
266
|
+
const result = await ops.writeFileSafe({
|
|
192
267
|
path: "../../../tmp/evil.txt",
|
|
193
268
|
content: "bad",
|
|
194
269
|
});
|
|
@@ -199,11 +274,11 @@ describe("FileSystemOps.writeFileSafe", () => {
|
|
|
199
274
|
expect(result.error.code).toBe("PATH_OUT_OF_BOUNDS");
|
|
200
275
|
});
|
|
201
276
|
|
|
202
|
-
test("returns SIZE_LIMIT_EXCEEDED for oversized content", () => {
|
|
277
|
+
test("returns SIZE_LIMIT_EXCEEDED for oversized content", async () => {
|
|
203
278
|
const dir = makeTempDir();
|
|
204
279
|
const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 10 });
|
|
205
280
|
|
|
206
|
-
const result = ops.writeFileSafe({
|
|
281
|
+
const result = await ops.writeFileSafe({
|
|
207
282
|
path: "big.txt",
|
|
208
283
|
content: "x".repeat(50),
|
|
209
284
|
});
|
|
@@ -220,11 +295,43 @@ describe("FileSystemOps.writeFileSafe", () => {
|
|
|
220
295
|
// ---------------------------------------------------------------------------
|
|
221
296
|
|
|
222
297
|
describe("FileSystemOps.editFileSafe", () => {
|
|
223
|
-
test("
|
|
298
|
+
test("concurrent edits of the same file both land", async () => {
|
|
299
|
+
const dir = makeTempDir();
|
|
300
|
+
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
301
|
+
writeFileSync(join(dir, "shared.txt"), "alpha\nbeta\n");
|
|
302
|
+
|
|
303
|
+
// Without per-path serialization, both edits read the same original
|
|
304
|
+
// content and the later write drops the earlier edit.
|
|
305
|
+
const [first, second] = await Promise.all([
|
|
306
|
+
ops.editFileSafe({
|
|
307
|
+
path: "shared.txt",
|
|
308
|
+
oldString: "alpha",
|
|
309
|
+
newString: "ALPHA",
|
|
310
|
+
replaceAll: false,
|
|
311
|
+
}),
|
|
312
|
+
ops.editFileSafe({
|
|
313
|
+
path: "shared.txt",
|
|
314
|
+
oldString: "beta",
|
|
315
|
+
newString: "BETA",
|
|
316
|
+
replaceAll: false,
|
|
317
|
+
}),
|
|
318
|
+
]);
|
|
319
|
+
|
|
320
|
+
expect(first.ok).toBe(true);
|
|
321
|
+
expect(second.ok).toBe(true);
|
|
322
|
+
const read = await ops.readFileSafe({ path: "shared.txt" });
|
|
323
|
+
expect(read.ok).toBe(true);
|
|
324
|
+
if (read.ok) {
|
|
325
|
+
expect(read.value.content).toContain("ALPHA");
|
|
326
|
+
expect(read.value.content).toContain("BETA");
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("returns NOT_FOUND for nonexistent file", async () => {
|
|
224
331
|
const dir = makeTempDir();
|
|
225
332
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
226
333
|
|
|
227
|
-
const result = ops.editFileSafe({
|
|
334
|
+
const result = await ops.editFileSafe({
|
|
228
335
|
path: "nope.txt",
|
|
229
336
|
oldString: "a",
|
|
230
337
|
newString: "b",
|
|
@@ -237,12 +344,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
237
344
|
expect(result.error.code).toBe("NOT_FOUND");
|
|
238
345
|
});
|
|
239
346
|
|
|
240
|
-
test("returns NOT_A_FILE when target is a directory", () => {
|
|
347
|
+
test("returns NOT_A_FILE when target is a directory", async () => {
|
|
241
348
|
const dir = makeTempDir();
|
|
242
349
|
mkdirSync(join(dir, "subdir"));
|
|
243
350
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
244
351
|
|
|
245
|
-
const result = ops.editFileSafe({
|
|
352
|
+
const result = await ops.editFileSafe({
|
|
246
353
|
path: "subdir",
|
|
247
354
|
oldString: "a",
|
|
248
355
|
newString: "b",
|
|
@@ -255,12 +362,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
255
362
|
expect(result.error.code).toBe("NOT_A_FILE");
|
|
256
363
|
});
|
|
257
364
|
|
|
258
|
-
test("returns MATCH_NOT_FOUND when old_string is absent", () => {
|
|
365
|
+
test("returns MATCH_NOT_FOUND when old_string is absent", async () => {
|
|
259
366
|
const dir = makeTempDir();
|
|
260
367
|
writeFileSync(join(dir, "file.txt"), "hello world");
|
|
261
368
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
262
369
|
|
|
263
|
-
const result = ops.editFileSafe({
|
|
370
|
+
const result = await ops.editFileSafe({
|
|
264
371
|
path: "file.txt",
|
|
265
372
|
oldString: "xyz",
|
|
266
373
|
newString: "abc",
|
|
@@ -273,12 +380,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
273
380
|
expect(result.error.code).toBe("MATCH_NOT_FOUND");
|
|
274
381
|
});
|
|
275
382
|
|
|
276
|
-
test("returns MATCH_AMBIGUOUS when old_string matches multiple times", () => {
|
|
383
|
+
test("returns MATCH_AMBIGUOUS when old_string matches multiple times", async () => {
|
|
277
384
|
const dir = makeTempDir();
|
|
278
385
|
writeFileSync(join(dir, "file.txt"), "foo bar foo baz foo");
|
|
279
386
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
280
387
|
|
|
281
|
-
const result = ops.editFileSafe({
|
|
388
|
+
const result = await ops.editFileSafe({
|
|
282
389
|
path: "file.txt",
|
|
283
390
|
oldString: "foo",
|
|
284
391
|
newString: "qux",
|
|
@@ -291,12 +398,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
291
398
|
expect(result.error.code).toBe("MATCH_AMBIGUOUS");
|
|
292
399
|
});
|
|
293
400
|
|
|
294
|
-
test("replaces all occurrences when replaceAll is true", () => {
|
|
401
|
+
test("replaces all occurrences when replaceAll is true", async () => {
|
|
295
402
|
const dir = makeTempDir();
|
|
296
403
|
writeFileSync(join(dir, "file.txt"), "foo bar foo baz foo");
|
|
297
404
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
298
405
|
|
|
299
|
-
const result = ops.editFileSafe({
|
|
406
|
+
const result = await ops.editFileSafe({
|
|
300
407
|
path: "file.txt",
|
|
301
408
|
oldString: "foo",
|
|
302
409
|
newString: "qux",
|
|
@@ -312,12 +419,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
312
419
|
expect(result.value.matchMethod).toBe("exact");
|
|
313
420
|
});
|
|
314
421
|
|
|
315
|
-
test("performs a unique edit successfully", () => {
|
|
422
|
+
test("performs a unique edit successfully", async () => {
|
|
316
423
|
const dir = makeTempDir();
|
|
317
424
|
writeFileSync(join(dir, "file.txt"), "one two three");
|
|
318
425
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
319
426
|
|
|
320
|
-
const result = ops.editFileSafe({
|
|
427
|
+
const result = await ops.editFileSafe({
|
|
321
428
|
path: "file.txt",
|
|
322
429
|
oldString: "two",
|
|
323
430
|
newString: "TWO",
|
|
@@ -333,12 +440,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
333
440
|
expect(result.value.filePath).toContain("file.txt");
|
|
334
441
|
});
|
|
335
442
|
|
|
336
|
-
test("returns MATCH_NOT_FOUND for empty oldString", () => {
|
|
443
|
+
test("returns MATCH_NOT_FOUND for empty oldString", async () => {
|
|
337
444
|
const dir = makeTempDir();
|
|
338
445
|
writeFileSync(join(dir, "file.txt"), "hello world");
|
|
339
446
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
340
447
|
|
|
341
|
-
const result = ops.editFileSafe({
|
|
448
|
+
const result = await ops.editFileSafe({
|
|
342
449
|
path: "file.txt",
|
|
343
450
|
oldString: "",
|
|
344
451
|
newString: "injected",
|
|
@@ -351,12 +458,12 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
351
458
|
expect(result.error.code).toBe("MATCH_NOT_FOUND");
|
|
352
459
|
});
|
|
353
460
|
|
|
354
|
-
test("returns SIZE_LIMIT_EXCEEDED for oversized file on edit", () => {
|
|
461
|
+
test("returns SIZE_LIMIT_EXCEEDED for oversized file on edit", async () => {
|
|
355
462
|
const dir = makeTempDir();
|
|
356
463
|
writeFileSync(join(dir, "big.txt"), "x".repeat(200));
|
|
357
464
|
const ops = new FileSystemOps(sandboxPolicyFor(dir), { sizeLimit: 100 });
|
|
358
465
|
|
|
359
|
-
const result = ops.editFileSafe({
|
|
466
|
+
const result = await ops.editFileSafe({
|
|
360
467
|
path: "big.txt",
|
|
361
468
|
oldString: "x",
|
|
362
469
|
newString: "y",
|
|
@@ -369,11 +476,11 @@ describe("FileSystemOps.editFileSafe", () => {
|
|
|
369
476
|
expect(result.error.code).toBe("SIZE_LIMIT_EXCEEDED");
|
|
370
477
|
});
|
|
371
478
|
|
|
372
|
-
test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", () => {
|
|
479
|
+
test("returns PATH_OUT_OF_BOUNDS for path outside sandbox", async () => {
|
|
373
480
|
const dir = makeTempDir();
|
|
374
481
|
const ops = new FileSystemOps(sandboxPolicyFor(dir));
|
|
375
482
|
|
|
376
|
-
const result = ops.editFileSafe({
|
|
483
|
+
const result = await ops.editFileSafe({
|
|
377
484
|
path: "../../../etc/passwd",
|
|
378
485
|
oldString: "root",
|
|
379
486
|
newString: "toor",
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import {
|
|
12
|
+
afterEach,
|
|
13
|
+
beforeAll,
|
|
14
|
+
beforeEach,
|
|
15
|
+
describe,
|
|
16
|
+
expect,
|
|
17
|
+
test,
|
|
18
|
+
} from "bun:test";
|
|
19
|
+
|
|
20
|
+
import { finalizeTool } from "../tools/tool-defaults.js";
|
|
21
|
+
import type { Tool, ToolContext } from "../tools/types.js";
|
|
22
|
+
|
|
23
|
+
let fileReadTool: Tool;
|
|
24
|
+
let fileWriteTool: Tool;
|
|
25
|
+
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
const [read, write] = await Promise.all([
|
|
28
|
+
import("../tools/filesystem/read.js"),
|
|
29
|
+
import("../tools/filesystem/write.js"),
|
|
30
|
+
]);
|
|
31
|
+
fileReadTool = finalizeTool(read.fileReadTool, "file_read");
|
|
32
|
+
fileWriteTool = finalizeTool(write.fileWriteTool, "file_write");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const testDirs: string[] = [];
|
|
36
|
+
|
|
37
|
+
function makeTempDir(prefix: string): string {
|
|
38
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), prefix)));
|
|
39
|
+
testDirs.push(dir);
|
|
40
|
+
return dir;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function makeContext(workingDir: string): ToolContext {
|
|
44
|
+
return {
|
|
45
|
+
workingDir,
|
|
46
|
+
conversationId: "test-conversation",
|
|
47
|
+
trustClass: "guardian",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A skill installed outside the workspace, mirroring a bundled skill in the
|
|
53
|
+
* container's install tree (`/app/assistant/src/config/bundled-skills/<id>`).
|
|
54
|
+
* Skill bodies point the model at these reference files by absolute path.
|
|
55
|
+
*/
|
|
56
|
+
function makeInstalledSkill(id: string): string {
|
|
57
|
+
const installTree = makeTempDir("container-install-");
|
|
58
|
+
const directoryPath = join(installTree, id);
|
|
59
|
+
mkdirSync(join(directoryPath, "references"), { recursive: true });
|
|
60
|
+
writeFileSync(join(directoryPath, "SKILL.md"), `# ${id}\n`);
|
|
61
|
+
return directoryPath;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
// The read allowance only changes behavior where the boundary is hard: on
|
|
66
|
+
// non-containerized installs out-of-workspace reads already fall back to
|
|
67
|
+
// host-style validation.
|
|
68
|
+
process.env.IS_CONTAINERIZED = "true";
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
delete process.env.IS_CONTAINERIZED;
|
|
73
|
+
delete process.env.GATEWAY_SECURITY_DIR;
|
|
74
|
+
for (const dir of testDirs.splice(0)) {
|
|
75
|
+
rmSync(dir, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("file_read outside the workspace in a container", () => {
|
|
80
|
+
test("reads a reference file from the install tree", async () => {
|
|
81
|
+
const workspace = makeTempDir("container-workspace-");
|
|
82
|
+
const skillDir = makeInstalledSkill("image-studio");
|
|
83
|
+
const referencePath = join(skillDir, "references", "guide.md");
|
|
84
|
+
writeFileSync(referencePath, "# Guide\nreference body\n");
|
|
85
|
+
|
|
86
|
+
const result = await fileReadTool.execute(
|
|
87
|
+
{ path: referencePath },
|
|
88
|
+
makeContext(workspace),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(result.isError).toBe(false);
|
|
92
|
+
expect(result.content).toContain("reference body");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("denies the service security directory", async () => {
|
|
96
|
+
const workspace = makeTempDir("container-workspace-");
|
|
97
|
+
const securityDir = makeTempDir("container-security-");
|
|
98
|
+
writeFileSync(join(securityDir, "trust.json"), "trust body\n");
|
|
99
|
+
process.env.GATEWAY_SECURITY_DIR = securityDir;
|
|
100
|
+
|
|
101
|
+
const result = await fileReadTool.execute(
|
|
102
|
+
{ path: join(securityDir, "trust.json") },
|
|
103
|
+
makeContext(workspace),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(result.isError).toBe(true);
|
|
107
|
+
expect(result.content).toContain("service security directory");
|
|
108
|
+
expect(result.content).not.toContain("trust body");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("denies the daemon's own process environment", async () => {
|
|
112
|
+
const workspace = makeTempDir("container-workspace-");
|
|
113
|
+
|
|
114
|
+
const result = await fileReadTool.execute(
|
|
115
|
+
{ path: "/proc/self/environ" },
|
|
116
|
+
makeContext(workspace),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(result.isError).toBe(true);
|
|
120
|
+
expect(result.content).toContain("process environment");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("denies file_write outside the workspace", async () => {
|
|
124
|
+
const workspace = makeTempDir("container-workspace-");
|
|
125
|
+
const skillDir = makeInstalledSkill("image-studio");
|
|
126
|
+
const targetPath = join(skillDir, "references", "injected.md");
|
|
127
|
+
|
|
128
|
+
const result = await fileWriteTool.execute(
|
|
129
|
+
{ path: targetPath, content: "injected body\n" },
|
|
130
|
+
makeContext(workspace),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(result.isError).toBe(true);
|
|
134
|
+
expect(result.content).toContain("outside the working directory");
|
|
135
|
+
expect(existsSync(targetPath)).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -103,16 +103,20 @@ describe("file_read tool (sandbox)", () => {
|
|
|
103
103
|
expect(result.content).toContain("outside content");
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
test("
|
|
106
|
+
test("reads a file outside the workspace boundary when containerized", async () => {
|
|
107
107
|
const dir = makeTempDir();
|
|
108
|
+
const outside = makeTempDir();
|
|
109
|
+
const target = join(outside, "reference.md");
|
|
110
|
+
writeFileSync(target, "install tree content");
|
|
108
111
|
process.env.IS_CONTAINERIZED = "true";
|
|
109
112
|
try {
|
|
110
113
|
const result = await fileReadTool.execute(
|
|
111
|
-
{ path:
|
|
114
|
+
{ path: target },
|
|
112
115
|
makeContext(dir),
|
|
113
116
|
);
|
|
114
|
-
|
|
115
|
-
expect(result.
|
|
117
|
+
|
|
118
|
+
expect(result.isError).toBe(false);
|
|
119
|
+
expect(result.content).toContain("install tree content");
|
|
116
120
|
} finally {
|
|
117
121
|
delete process.env.IS_CONTAINERIZED;
|
|
118
122
|
}
|
|
@@ -206,32 +210,40 @@ describe("file_read image support", () => {
|
|
|
206
210
|
expect(result.content).toContain("Image loaded");
|
|
207
211
|
});
|
|
208
212
|
|
|
209
|
-
test("
|
|
213
|
+
test("reads an image outside the workspace boundary when containerized", async () => {
|
|
210
214
|
const dir = makeTempDir();
|
|
215
|
+
const outside = makeTempDir();
|
|
216
|
+
writeFileSync(join(outside, "diagram.jpg"), JPEG_HEADER);
|
|
211
217
|
process.env.IS_CONTAINERIZED = "true";
|
|
212
218
|
try {
|
|
213
219
|
const result = await fileReadTool.execute(
|
|
214
|
-
{ path: "
|
|
220
|
+
{ path: join(outside, "diagram.jpg") },
|
|
215
221
|
makeContext(dir),
|
|
216
222
|
);
|
|
217
223
|
|
|
218
|
-
expect(result.isError).toBe(
|
|
219
|
-
expect(result.content).toContain("
|
|
224
|
+
expect(result.isError).toBe(false);
|
|
225
|
+
expect(result.content).toContain("Image loaded");
|
|
220
226
|
} finally {
|
|
221
227
|
delete process.env.IS_CONTAINERIZED;
|
|
222
228
|
}
|
|
223
229
|
});
|
|
224
230
|
});
|
|
225
231
|
|
|
226
|
-
// ── Out-of-bounds hint for host_file_read
|
|
232
|
+
// ── Out-of-bounds hint for host_file_read ─────────────────────────────
|
|
233
|
+
//
|
|
234
|
+
// Reads leave the workspace freely, so the boundary only holds when the
|
|
235
|
+
// service security deny set cannot be mirrored: a relative override resolves
|
|
236
|
+
// against the owning service's cwd, so the policy fails closed.
|
|
227
237
|
|
|
228
238
|
describe("file_read out-of-bounds hint", () => {
|
|
229
239
|
beforeEach(() => {
|
|
230
240
|
process.env.IS_CONTAINERIZED = "true";
|
|
241
|
+
process.env.GATEWAY_SECURITY_DIR = "relative/security-dir";
|
|
231
242
|
});
|
|
232
243
|
|
|
233
244
|
afterEach(() => {
|
|
234
245
|
delete process.env.IS_CONTAINERIZED;
|
|
246
|
+
delete process.env.GATEWAY_SECURITY_DIR;
|
|
235
247
|
});
|
|
236
248
|
|
|
237
249
|
test("suggests host_file_read for out-of-bounds text file path", async () => {
|