@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a
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 +19 -35
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +1128 -119
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +6 -0
- package/src/__tests__/approval-routes-http.test.ts +128 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compaction-events.test.ts +6 -0
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-process-callsite.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-queue.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-slash-queue.test.ts +6 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +6 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-pressure-tools.test.ts +41 -1
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/document-tool-security.test.ts +9 -6
- package/src/__tests__/document-update-default-surface.test.ts +202 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/gemini-count-tokens.test.ts +70 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-disk-pressure.test.ts +10 -5
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/secret-response-routing.test.ts +35 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
- package/src/__tests__/skill-execute-input.test.ts +177 -1
- package/src/__tests__/skill-load-tool.test.ts +72 -0
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +75 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/db/status.ts +37 -3
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +400 -14
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +128 -19
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-marketplace.ts +11 -0
- package/src/cli/lib/plugin-pin-history.ts +257 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +53 -19
- package/src/daemon/conversation.ts +131 -20
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/memory-v2-startup.test.ts +131 -0
- package/src/daemon/memory-v2-startup.ts +100 -1
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/daemon/wake-conversation-ops.ts +1 -0
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-async-query.ts +20 -2
- package/src/memory/db-connection.ts +49 -5
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/permissions/secret-prompter.ts +15 -1
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/compaction/window-manager.ts +18 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/anthropic/client.ts +230 -175
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/call-site-routing.ts +10 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/gemini/client.ts +43 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/ratelimit.ts +9 -0
- package/src/providers/retry.ts +62 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +16 -0
- package/src/providers/usage-tracking.ts +7 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +227 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +71 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +35 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-routes.ts +40 -9
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +366 -11
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +15 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +30 -6
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +99 -2
- package/src/tools/skills/load.ts +43 -2
- package/src/tools/subagent/spawn.ts +64 -15
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +10 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/logs-db-path.ts +22 -0
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { BadRequestError } from "../runtime/routes/errors.js";
|
|
4
|
+
import type { CredentialMetadata } from "../tools/credentials/metadata-store.js";
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Mutable mock state (closed over by the mock factories below)
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
let secureStore: Map<string, string>;
|
|
11
|
+
let metadataStore: Map<string, CredentialMetadata>;
|
|
12
|
+
let syncedServices: string[];
|
|
13
|
+
let disconnectedProviders: string[];
|
|
14
|
+
let credentialIdCounter: number;
|
|
15
|
+
|
|
16
|
+
function metaKey(service: string, field: string): string {
|
|
17
|
+
return `${service}:${field}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Mocks for the routes' collaborators
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
mock.module("../runtime/auth/route-policy.js", () => ({
|
|
25
|
+
ACTOR_PRINCIPALS: [],
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
mock.module("../security/credential-key.js", () => ({
|
|
29
|
+
credentialKey: (service: string, field: string) => `${service}:${field}`,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
33
|
+
setSecureKeyAsync: mock(async (key: string, value: string) => {
|
|
34
|
+
secureStore.set(key, value);
|
|
35
|
+
return true;
|
|
36
|
+
}),
|
|
37
|
+
getSecureKeyAsync: mock(async (key: string) => secureStore.get(key)),
|
|
38
|
+
getSecureKeyResultAsync: mock(async (key: string) => ({
|
|
39
|
+
value: secureStore.get(key),
|
|
40
|
+
unreachable: false,
|
|
41
|
+
})),
|
|
42
|
+
deleteSecureKeyAsync: mock(async (key: string) =>
|
|
43
|
+
secureStore.delete(key) ? "deleted" : "not-found",
|
|
44
|
+
),
|
|
45
|
+
getActiveBackendName: () => "encrypted-store",
|
|
46
|
+
getActiveBackendInfoAsync: mock(async () => ({ backend: "encrypted-store" })),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
50
|
+
assertMetadataWritable: () => {},
|
|
51
|
+
listCredentialMetadata: mock(() => Array.from(metadataStore.values())),
|
|
52
|
+
getCredentialMetadata: mock((service: string, field: string) =>
|
|
53
|
+
metadataStore.get(metaKey(service, field)),
|
|
54
|
+
),
|
|
55
|
+
getCredentialMetadataById: mock((id: string) =>
|
|
56
|
+
Array.from(metadataStore.values()).find((m) => m.credentialId === id),
|
|
57
|
+
),
|
|
58
|
+
upsertCredentialMetadata: mock(
|
|
59
|
+
(
|
|
60
|
+
service: string,
|
|
61
|
+
field: string,
|
|
62
|
+
policy?: {
|
|
63
|
+
allowedTools?: string[];
|
|
64
|
+
usageDescription?: string;
|
|
65
|
+
alias?: string | null;
|
|
66
|
+
},
|
|
67
|
+
): CredentialMetadata => {
|
|
68
|
+
const key = metaKey(service, field);
|
|
69
|
+
const existing = metadataStore.get(key);
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
const meta: CredentialMetadata = {
|
|
72
|
+
credentialId: existing?.credentialId ?? `cred-${++credentialIdCounter}`,
|
|
73
|
+
service,
|
|
74
|
+
field,
|
|
75
|
+
allowedTools: policy?.allowedTools ?? existing?.allowedTools ?? [],
|
|
76
|
+
allowedDomains: existing?.allowedDomains ?? [],
|
|
77
|
+
usageDescription:
|
|
78
|
+
policy?.usageDescription ?? existing?.usageDescription,
|
|
79
|
+
alias: policy?.alias ?? existing?.alias ?? undefined,
|
|
80
|
+
createdAt: existing?.createdAt ?? now,
|
|
81
|
+
updatedAt: now,
|
|
82
|
+
};
|
|
83
|
+
metadataStore.set(key, meta);
|
|
84
|
+
return meta;
|
|
85
|
+
},
|
|
86
|
+
),
|
|
87
|
+
deleteCredentialMetadata: mock((service: string, field: string) =>
|
|
88
|
+
metadataStore.delete(metaKey(service, field)),
|
|
89
|
+
),
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
mock.module("../oauth/manual-token-connection.js", () => ({
|
|
93
|
+
syncManualTokenConnection: mock(async (service: string) => {
|
|
94
|
+
syncedServices.push(service);
|
|
95
|
+
}),
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
mock.module("../oauth/oauth-store.js", () => ({
|
|
99
|
+
listConnections: mock(() => []),
|
|
100
|
+
getConnectionByProvider: mock(() => undefined),
|
|
101
|
+
disconnectOAuthProvider: mock(async (provider: string) => {
|
|
102
|
+
disconnectedProviders.push(provider);
|
|
103
|
+
return "not-found";
|
|
104
|
+
}),
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
mock.module("../credential-execution/managed-catalog.js", () => ({
|
|
108
|
+
fetchManagedCatalog: mock(async () => ({ ok: true, descriptors: [] })),
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
import { ROUTES } from "../runtime/routes/credential-routes.js";
|
|
112
|
+
|
|
113
|
+
const setRoute = ROUTES.find((r) => r.operationId === "credentials_set");
|
|
114
|
+
const listRoute = ROUTES.find((r) => r.operationId === "credentials_list");
|
|
115
|
+
const deleteRoute = ROUTES.find((r) => r.operationId === "credentials_delete");
|
|
116
|
+
|
|
117
|
+
type SetResponse = { credentialId: string; service: string; field: string };
|
|
118
|
+
type ListResponse = {
|
|
119
|
+
credentials: Array<{
|
|
120
|
+
service: string;
|
|
121
|
+
field: string;
|
|
122
|
+
scrubbedValue: string;
|
|
123
|
+
hasSecret: boolean;
|
|
124
|
+
}>;
|
|
125
|
+
managedCredentials: unknown[];
|
|
126
|
+
};
|
|
127
|
+
type DeleteResponse = { service: string; field: string };
|
|
128
|
+
|
|
129
|
+
const SECRET_VALUE = "super-secret-token-value";
|
|
130
|
+
|
|
131
|
+
describe("credentials routes", () => {
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
secureStore = new Map();
|
|
134
|
+
metadataStore = new Map();
|
|
135
|
+
syncedServices = [];
|
|
136
|
+
disconnectedProviders = [];
|
|
137
|
+
credentialIdCounter = 0;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("credentials_set", () => {
|
|
141
|
+
test("stores the secret and returns identifiers without leaking the value", async () => {
|
|
142
|
+
/**
|
|
143
|
+
* Storing a credential via the route persists the plaintext to secure
|
|
144
|
+
* storage and creates metadata, but the response surfaces only the
|
|
145
|
+
* credential identifiers — never the secret itself.
|
|
146
|
+
*/
|
|
147
|
+
// GIVEN the credentials_set route is registered
|
|
148
|
+
expect(setRoute).toBeDefined();
|
|
149
|
+
|
|
150
|
+
// WHEN a credential is stored with a label, description, and allowed tools
|
|
151
|
+
const result = (await setRoute!.handler({
|
|
152
|
+
body: {
|
|
153
|
+
service: "vercel",
|
|
154
|
+
field: "api_token",
|
|
155
|
+
value: SECRET_VALUE,
|
|
156
|
+
label: "Vercel API Token",
|
|
157
|
+
description: "Used to deploy pages",
|
|
158
|
+
allowedTools: ["publish_page"],
|
|
159
|
+
},
|
|
160
|
+
})) as SetResponse;
|
|
161
|
+
|
|
162
|
+
// THEN the response carries the credential identifiers only
|
|
163
|
+
expect(result.service).toBe("vercel");
|
|
164
|
+
expect(result.field).toBe("api_token");
|
|
165
|
+
expect(result.credentialId).toBe("cred-1");
|
|
166
|
+
|
|
167
|
+
// AND the secret is persisted to secure storage
|
|
168
|
+
expect(secureStore.get("vercel:api_token")).toBe(SECRET_VALUE);
|
|
169
|
+
|
|
170
|
+
// AND a manual-token connection sync is triggered for the service
|
|
171
|
+
expect(syncedServices).toContain("vercel");
|
|
172
|
+
|
|
173
|
+
// AND the plaintext value never appears in the response
|
|
174
|
+
expect(JSON.stringify(result)).not.toContain(SECRET_VALUE);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("rejects a request missing the value", async () => {
|
|
178
|
+
/**
|
|
179
|
+
* The route validates required fields and rejects a store request that
|
|
180
|
+
* omits the secret value before touching storage.
|
|
181
|
+
*/
|
|
182
|
+
// GIVEN a store request with no value
|
|
183
|
+
const body = { service: "vercel", field: "api_token" };
|
|
184
|
+
|
|
185
|
+
// WHEN the handler runs
|
|
186
|
+
const call = setRoute!.handler({ body });
|
|
187
|
+
|
|
188
|
+
// THEN it rejects with a BadRequestError and stores nothing
|
|
189
|
+
await expect(call).rejects.toBeInstanceOf(BadRequestError);
|
|
190
|
+
expect(secureStore.size).toBe(0);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("credentials_list", () => {
|
|
195
|
+
test("returns masked metadata without exposing stored secrets", async () => {
|
|
196
|
+
/**
|
|
197
|
+
* Listing credentials returns metadata and a scrubbed preview for each
|
|
198
|
+
* stored secret; the raw plaintext is never included in the output.
|
|
199
|
+
*/
|
|
200
|
+
// GIVEN a stored credential
|
|
201
|
+
await setRoute!.handler({
|
|
202
|
+
body: {
|
|
203
|
+
service: "vercel",
|
|
204
|
+
field: "api_token",
|
|
205
|
+
value: SECRET_VALUE,
|
|
206
|
+
label: "Vercel API Token",
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// WHEN the credentials are listed
|
|
211
|
+
const result = (await listRoute!.handler({ body: {} })) as ListResponse;
|
|
212
|
+
|
|
213
|
+
// THEN the listing includes the credential with a masked value
|
|
214
|
+
expect(result.credentials).toHaveLength(1);
|
|
215
|
+
const entry = result.credentials[0];
|
|
216
|
+
expect(entry.service).toBe("vercel");
|
|
217
|
+
expect(entry.field).toBe("api_token");
|
|
218
|
+
expect(entry.hasSecret).toBe(true);
|
|
219
|
+
expect(entry.scrubbedValue).toBe("****alue");
|
|
220
|
+
|
|
221
|
+
// AND the raw secret never appears anywhere in the response
|
|
222
|
+
expect(JSON.stringify(result)).not.toContain(SECRET_VALUE);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("filters credentials by search substring", async () => {
|
|
226
|
+
/**
|
|
227
|
+
* The optional `search` filter restricts the listing to credentials
|
|
228
|
+
* whose service, field, alias, or description match the query.
|
|
229
|
+
*/
|
|
230
|
+
// GIVEN two stored credentials in different services
|
|
231
|
+
await setRoute!.handler({
|
|
232
|
+
body: { service: "vercel", field: "api_token", value: "v-secret" },
|
|
233
|
+
});
|
|
234
|
+
await setRoute!.handler({
|
|
235
|
+
body: { service: "github", field: "token", value: "g-secret" },
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// WHEN the listing is filtered by a service substring
|
|
239
|
+
const result = (await listRoute!.handler({
|
|
240
|
+
body: { search: "git" },
|
|
241
|
+
})) as ListResponse;
|
|
242
|
+
|
|
243
|
+
// THEN only the matching credential is returned
|
|
244
|
+
expect(result.credentials).toHaveLength(1);
|
|
245
|
+
expect(result.credentials[0].service).toBe("github");
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("credentials_delete", () => {
|
|
250
|
+
test("removes the secret and metadata and echoes the identifiers", async () => {
|
|
251
|
+
/**
|
|
252
|
+
* Deleting a credential removes both the stored secret and its metadata,
|
|
253
|
+
* returning the service and field that were deleted.
|
|
254
|
+
*/
|
|
255
|
+
// GIVEN a stored credential
|
|
256
|
+
await setRoute!.handler({
|
|
257
|
+
body: { service: "vercel", field: "api_token", value: SECRET_VALUE },
|
|
258
|
+
});
|
|
259
|
+
expect(secureStore.has("vercel:api_token")).toBe(true);
|
|
260
|
+
|
|
261
|
+
// WHEN the credential is deleted
|
|
262
|
+
const result = (await deleteRoute!.handler({
|
|
263
|
+
body: { service: "vercel", field: "api_token" },
|
|
264
|
+
})) as DeleteResponse;
|
|
265
|
+
|
|
266
|
+
// THEN the response echoes the identifiers
|
|
267
|
+
expect(result).toEqual({ service: "vercel", field: "api_token" });
|
|
268
|
+
|
|
269
|
+
// AND the secret and metadata are gone
|
|
270
|
+
expect(secureStore.has("vercel:api_token")).toBe(false);
|
|
271
|
+
expect(metadataStore.has("vercel:api_token")).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("deletes the Slack user_token surgically, preserving the OAuth connection", async () => {
|
|
275
|
+
/**
|
|
276
|
+
* The Slack user_token grants only read access to channels the bot isn't
|
|
277
|
+
* a member of; Socket Mode runs on the bot + app tokens. Deleting just the
|
|
278
|
+
* user_token removes that secret without disconnecting the provider, so
|
|
279
|
+
* the integration's connected state does not flap.
|
|
280
|
+
*/
|
|
281
|
+
// GIVEN a connected Slack channel with bot, app, and user tokens
|
|
282
|
+
secureStore.set("slack_channel:bot_token", "xoxb-bot");
|
|
283
|
+
secureStore.set("slack_channel:app_token", "xapp-app");
|
|
284
|
+
secureStore.set("slack_channel:user_token", "xoxp-user");
|
|
285
|
+
|
|
286
|
+
// WHEN only the user_token is deleted
|
|
287
|
+
const result = (await deleteRoute!.handler({
|
|
288
|
+
body: { service: "slack_channel", field: "user_token" },
|
|
289
|
+
})) as DeleteResponse;
|
|
290
|
+
|
|
291
|
+
// THEN the response echoes the identifiers
|
|
292
|
+
expect(result).toEqual({ service: "slack_channel", field: "user_token" });
|
|
293
|
+
|
|
294
|
+
// AND only the user_token is removed; bot + app tokens remain
|
|
295
|
+
expect(secureStore.has("slack_channel:user_token")).toBe(false);
|
|
296
|
+
expect(secureStore.has("slack_channel:bot_token")).toBe(true);
|
|
297
|
+
expect(secureStore.has("slack_channel:app_token")).toBe(true);
|
|
298
|
+
|
|
299
|
+
// AND the OAuth provider is never disconnected
|
|
300
|
+
expect(disconnectedProviders).not.toContain("slack_channel");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("rejects deleting an absent Slack user_token without disconnecting the provider", async () => {
|
|
304
|
+
/**
|
|
305
|
+
* Deleting a Slack user_token that was never stored surfaces the same
|
|
306
|
+
* not-found rejection as any other missing credential — it must not be
|
|
307
|
+
* reported as an internal storage error, and it must still skip the
|
|
308
|
+
* OAuth teardown so a connected channel's bot + app tokens are untouched.
|
|
309
|
+
*/
|
|
310
|
+
// GIVEN a connected Slack channel with only bot + app tokens (no user_token)
|
|
311
|
+
secureStore.set("slack_channel:bot_token", "xoxb-bot");
|
|
312
|
+
secureStore.set("slack_channel:app_token", "xapp-app");
|
|
313
|
+
|
|
314
|
+
// WHEN the absent user_token is deleted
|
|
315
|
+
const call = deleteRoute!.handler({
|
|
316
|
+
body: { service: "slack_channel", field: "user_token" },
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// THEN it rejects with a BadRequestError (not an InternalError)
|
|
320
|
+
await expect(call).rejects.toBeInstanceOf(BadRequestError);
|
|
321
|
+
|
|
322
|
+
// AND the bot + app tokens remain and the provider is never disconnected
|
|
323
|
+
expect(secureStore.has("slack_channel:bot_token")).toBe(true);
|
|
324
|
+
expect(secureStore.has("slack_channel:app_token")).toBe(true);
|
|
325
|
+
expect(disconnectedProviders).not.toContain("slack_channel");
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("disconnects the provider when a connection-critical Slack token is deleted", async () => {
|
|
329
|
+
/**
|
|
330
|
+
* Deleting a token that powers the Socket Mode connection (bot_token)
|
|
331
|
+
* follows the generic path, which tears down the OAuth connection.
|
|
332
|
+
*/
|
|
333
|
+
// GIVEN a stored Slack bot_token
|
|
334
|
+
secureStore.set("slack_channel:bot_token", "xoxb-bot");
|
|
335
|
+
|
|
336
|
+
// WHEN the bot_token is deleted
|
|
337
|
+
await deleteRoute!.handler({
|
|
338
|
+
body: { service: "slack_channel", field: "bot_token" },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// THEN the generic path disconnects the provider
|
|
342
|
+
expect(disconnectedProviders).toContain("slack_channel");
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("rejects deleting a credential that does not exist", async () => {
|
|
346
|
+
/**
|
|
347
|
+
* Deleting a credential with no stored secret, metadata, or OAuth
|
|
348
|
+
* connection rejects with a not-found error.
|
|
349
|
+
*/
|
|
350
|
+
// GIVEN no stored credential for the service+field
|
|
351
|
+
const body = { service: "ghost", field: "token" };
|
|
352
|
+
|
|
353
|
+
// WHEN the handler runs
|
|
354
|
+
const call = deleteRoute!.handler({ body });
|
|
355
|
+
|
|
356
|
+
// THEN it rejects with a BadRequestError
|
|
357
|
+
await expect(call).rejects.toBeInstanceOf(BadRequestError);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
@@ -95,17 +95,7 @@ import {
|
|
|
95
95
|
|
|
96
96
|
describe("Invariant 1: secrets never enter LLM context", () => {
|
|
97
97
|
for (const tc of contextInjectionCases) {
|
|
98
|
-
if (
|
|
99
|
-
tc.vector === "tool_output" &&
|
|
100
|
-
tc.tool === "credential_store" &&
|
|
101
|
-
tc.input.action === "store"
|
|
102
|
-
) {
|
|
103
|
-
// Store output never includes the value
|
|
104
|
-
test(`${tc.label}: secret not in output`, () => {
|
|
105
|
-
expect(tc.forbiddenValue).toBeTruthy();
|
|
106
|
-
// Actual assertion is in credential-vault.test.ts baseline section
|
|
107
|
-
});
|
|
108
|
-
} else if (tc.vector === "confirmation_payload") {
|
|
98
|
+
if (tc.vector === "confirmation_payload") {
|
|
109
99
|
// PR 23 added redaction to confirmation_request payloads via redactSensitiveFields
|
|
110
100
|
test(`${tc.label}: secret redacted from confirmation payload`, () => {
|
|
111
101
|
const payload = { ...tc.input };
|
|
@@ -132,7 +122,7 @@ describe("Invariant 1: secrets never enter LLM context", () => {
|
|
|
132
122
|
}
|
|
133
123
|
});
|
|
134
124
|
} else {
|
|
135
|
-
// tool_output cases
|
|
125
|
+
// tool_output cases (browser_fill_credential) — already passing via baselines
|
|
136
126
|
test(`${tc.label}: secret not in output`, () => {
|
|
137
127
|
expect(tc.forbiddenValue).toBeTruthy();
|
|
138
128
|
});
|
|
@@ -165,10 +155,10 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
165
155
|
// Hard boundary: only these production files may import from secure-keys.
|
|
166
156
|
// Any new import must be reviewed for secret-leak risk and added here.
|
|
167
157
|
const ALLOWED_IMPORTERS = new Set([
|
|
168
|
-
"tools/credentials/vault.ts", // credential store tool
|
|
169
158
|
"credential-execution/prompted-credential.ts", // shared prompt-action persistence (stores secret via setSecureKeyAsync)
|
|
170
159
|
"tools/credentials/broker.ts", // brokered credential access
|
|
171
160
|
"tools/network/web-search.ts", // web search API key lookup
|
|
161
|
+
"tools/network/web-fetch.ts", // web fetch provider (Firecrawl) API key lookup
|
|
172
162
|
"daemon/handlers/config-telegram.ts", // Telegram bot token management
|
|
173
163
|
"daemon/handlers/config-vercel.ts", // Vercel API token management
|
|
174
164
|
"runtime/routes/integrations/twilio.ts", // Twilio credential management (HTTP control-plane)
|
|
@@ -248,6 +238,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
248
238
|
"tools/credential-execution/make-authenticated-request.ts", // resolves the CES RPC client via getCesClient
|
|
249
239
|
"tools/credential-execution/manage-secure-command-tool.ts", // resolves the CES RPC client via getCesClient
|
|
250
240
|
"tools/executor.ts", // CES approval bridge resolves the CES RPC client via getCesClient
|
|
241
|
+
"tools/network/web-fetch.ts", // Firecrawl /scrape BYOK fetch provider API key lookup (firecrawl provider key)
|
|
251
242
|
]);
|
|
252
243
|
|
|
253
244
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -108,6 +108,18 @@ describe("classifyDiskPressureTurnPolicy", () => {
|
|
|
108
108
|
},
|
|
109
109
|
expected: { action: "block", reason: "trusted-contact" },
|
|
110
110
|
},
|
|
111
|
+
{
|
|
112
|
+
name: "unverified contact is blocked",
|
|
113
|
+
status: status(),
|
|
114
|
+
metadata: {
|
|
115
|
+
...guardianTurn,
|
|
116
|
+
trustContext: {
|
|
117
|
+
sourceChannel: "telegram",
|
|
118
|
+
trustClass: "unverified_contact",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
expected: { action: "block", reason: "trusted-contact" },
|
|
122
|
+
},
|
|
111
123
|
{
|
|
112
124
|
name: "non-guardian contact is blocked",
|
|
113
125
|
status: status(),
|
|
@@ -96,6 +96,9 @@ const {
|
|
|
96
96
|
} = await import("../tools/background-tool-registry.js");
|
|
97
97
|
const { shellTool } = await import("../tools/terminal/shell.js");
|
|
98
98
|
const { hostShellTool } = await import("../tools/host-terminal/host-shell.js");
|
|
99
|
+
const { __clearRegistryForTesting: clearToolRegistryForTesting, registerTool } =
|
|
100
|
+
await import("../tools/registry.js");
|
|
101
|
+
const { skillLoadTool } = await import("../tools/skills/load.js");
|
|
99
102
|
|
|
100
103
|
function makeToolDef(name: string): ToolDefinition {
|
|
101
104
|
return { name, description: `${name} tool`, input_schema: {} };
|
|
@@ -124,12 +127,15 @@ function setDiskUsage(usedMb: number, totalMb = 100): void {
|
|
|
124
127
|
|
|
125
128
|
beforeEach(() => {
|
|
126
129
|
_clearRegistryForTesting();
|
|
130
|
+
clearToolRegistryForTesting();
|
|
131
|
+
registerTool(skillLoadTool);
|
|
127
132
|
__resetDiskPressureGuardForTests();
|
|
128
133
|
setDiskUsage(10);
|
|
129
134
|
});
|
|
130
135
|
|
|
131
136
|
afterEach(() => {
|
|
132
137
|
_clearRegistryForTesting();
|
|
138
|
+
clearToolRegistryForTesting();
|
|
133
139
|
__resetDiskPressureGuardForTests();
|
|
134
140
|
diskSample = null;
|
|
135
141
|
});
|
|
@@ -140,6 +146,7 @@ describe("disk pressure cleanup tool restrictions", () => {
|
|
|
140
146
|
makeToolDef("bash"),
|
|
141
147
|
makeToolDef("host_bash"),
|
|
142
148
|
makeToolDef("file_read"),
|
|
149
|
+
makeToolDef("skill_load"),
|
|
143
150
|
makeToolDef("file_write"),
|
|
144
151
|
makeToolDef("skill_execute"),
|
|
145
152
|
makeToolDef("web_fetch"),
|
|
@@ -150,8 +157,14 @@ describe("disk pressure cleanup tool restrictions", () => {
|
|
|
150
157
|
const cleanupTools = resolve([]);
|
|
151
158
|
const cleanupNames = cleanupTools.map((tool) => tool.name).sort();
|
|
152
159
|
|
|
153
|
-
expect(cleanupNames).toEqual([
|
|
160
|
+
expect(cleanupNames).toEqual([
|
|
161
|
+
"bash",
|
|
162
|
+
"file_read",
|
|
163
|
+
"host_bash",
|
|
164
|
+
"skill_load",
|
|
165
|
+
]);
|
|
154
166
|
expect(ctx.allowedToolNames?.has("bash")).toBe(true);
|
|
167
|
+
expect(ctx.allowedToolNames?.has("skill_load")).toBe(true);
|
|
155
168
|
expect(ctx.allowedToolNames?.has("file_write")).toBe(false);
|
|
156
169
|
expect(ctx.allowedToolNames?.has("skill_execute")).toBe(false);
|
|
157
170
|
|
|
@@ -192,6 +205,33 @@ describe("disk pressure cleanup tool restrictions", () => {
|
|
|
192
205
|
);
|
|
193
206
|
});
|
|
194
207
|
|
|
208
|
+
test("executor fallback allows normal skill_load during cleanup mode", async () => {
|
|
209
|
+
const handler = new ToolApprovalHandler();
|
|
210
|
+
const result = await handler.checkPreExecutionGates(
|
|
211
|
+
"skill_load",
|
|
212
|
+
{ skill: "system-storage-cleanup" },
|
|
213
|
+
{
|
|
214
|
+
workingDir: "/workspace",
|
|
215
|
+
conversationId: "conv-cleanup",
|
|
216
|
+
trustClass: "guardian",
|
|
217
|
+
allowedToolNames: new Set(["skill_load"]),
|
|
218
|
+
diskPressureCleanupModeActive: true,
|
|
219
|
+
},
|
|
220
|
+
"sandbox",
|
|
221
|
+
"low",
|
|
222
|
+
Date.now(),
|
|
223
|
+
() => undefined,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
expect(result.allowed).toBe(true);
|
|
227
|
+
if (!result.allowed) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
"Expected disk pressure cleanup mode to allow skill_load",
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
expect(result.tool.name).toBe("skill_load");
|
|
233
|
+
});
|
|
234
|
+
|
|
195
235
|
test("locking cancels registered terminal background tools with disk pressure reason", () => {
|
|
196
236
|
const bashCancel = mock((_reason?: string) => undefined);
|
|
197
237
|
const hostCancel = mock((_reason?: string) => undefined);
|
|
@@ -6,6 +6,8 @@ const GIB = 1024 * MIB;
|
|
|
6
6
|
let existingPaths = new Set<string>();
|
|
7
7
|
const workspaceDir = "/workspace";
|
|
8
8
|
let minikubeStorageSize: string | undefined;
|
|
9
|
+
let isContainerized = false;
|
|
10
|
+
let isPlatform = false;
|
|
9
11
|
let statfsResult = {
|
|
10
12
|
bsize: 4096,
|
|
11
13
|
blocks: 0,
|
|
@@ -34,6 +36,8 @@ mock.module("node:child_process", () => ({
|
|
|
34
36
|
|
|
35
37
|
mock.module("../config/env-registry.js", () => ({
|
|
36
38
|
getMinikubeStorageSize: () => minikubeStorageSize,
|
|
39
|
+
getIsContainerized: () => isContainerized,
|
|
40
|
+
getIsPlatform: () => isPlatform,
|
|
37
41
|
}));
|
|
38
42
|
|
|
39
43
|
mock.module("../util/platform.js", () => ({
|
|
@@ -55,6 +59,8 @@ describe("disk usage sampler", () => {
|
|
|
55
59
|
beforeEach(() => {
|
|
56
60
|
existingPaths = new Set([workspaceDir]);
|
|
57
61
|
minikubeStorageSize = undefined;
|
|
62
|
+
isContainerized = false;
|
|
63
|
+
isPlatform = false;
|
|
58
64
|
statfsResult = statfsFor(100 * MIB, 25 * MIB);
|
|
59
65
|
spawnResult = { status: 0, stdout: "" };
|
|
60
66
|
spawnCalls = [];
|
|
@@ -119,6 +125,65 @@ describe("disk usage sampler", () => {
|
|
|
119
125
|
]);
|
|
120
126
|
});
|
|
121
127
|
|
|
128
|
+
test("measures workspace du usage on a local Docker hatch", () => {
|
|
129
|
+
isContainerized = true;
|
|
130
|
+
isPlatform = false;
|
|
131
|
+
statfsResult = statfsFor(100 * GIB, 40 * GIB);
|
|
132
|
+
spawnResult = {
|
|
133
|
+
status: 0,
|
|
134
|
+
stdout: `${2 * GIB}\t/workspace\n`,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const usage = getDiskUsageInfo();
|
|
138
|
+
|
|
139
|
+
// Used reflects only the workspace (du), free reflects the host headroom
|
|
140
|
+
// the volume can grow into, and total is their sum.
|
|
141
|
+
expect(usage).toEqual({
|
|
142
|
+
path: "/workspace",
|
|
143
|
+
totalMb: 2048 + 40960,
|
|
144
|
+
usedMb: 2048,
|
|
145
|
+
freeMb: 40960,
|
|
146
|
+
});
|
|
147
|
+
expect(spawnCalls).toEqual([
|
|
148
|
+
{ command: "du", args: ["-sb", "/workspace"] },
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("does not use du for platform-managed containerized instances", () => {
|
|
153
|
+
isContainerized = true;
|
|
154
|
+
isPlatform = true;
|
|
155
|
+
statfsResult = statfsFor(10 * GIB, 6 * GIB);
|
|
156
|
+
|
|
157
|
+
const usage = getDiskUsageInfo();
|
|
158
|
+
|
|
159
|
+
expect(usage).toEqual({
|
|
160
|
+
path: "/workspace",
|
|
161
|
+
totalMb: 10240,
|
|
162
|
+
usedMb: 4096,
|
|
163
|
+
freeMb: 6144,
|
|
164
|
+
});
|
|
165
|
+
expect(spawnCalls).toHaveLength(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("falls back to statfs when du fails on a local Docker hatch", () => {
|
|
169
|
+
isContainerized = true;
|
|
170
|
+
isPlatform = false;
|
|
171
|
+
statfsResult = statfsFor(100 * GIB, 40 * GIB);
|
|
172
|
+
spawnResult = { status: 1, stdout: "" };
|
|
173
|
+
|
|
174
|
+
const usage = getDiskUsageInfo();
|
|
175
|
+
|
|
176
|
+
expect(usage).toEqual({
|
|
177
|
+
path: "/workspace",
|
|
178
|
+
totalMb: 102400,
|
|
179
|
+
usedMb: 61440,
|
|
180
|
+
freeMb: 40960,
|
|
181
|
+
});
|
|
182
|
+
expect(spawnCalls).toEqual([
|
|
183
|
+
{ command: "du", args: ["-sb", "/workspace"] },
|
|
184
|
+
]);
|
|
185
|
+
});
|
|
186
|
+
|
|
122
187
|
test("returns null for malformed Kubernetes memory strings", () => {
|
|
123
188
|
expect(parseK8sMemoryBytes("")).toBeNull();
|
|
124
189
|
expect(parseK8sMemoryBytes("abc")).toBeNull();
|
|
@@ -264,12 +264,15 @@ describe("executeDocumentUpdate — input validation", () => {
|
|
|
264
264
|
seedFixtureDocuments();
|
|
265
265
|
});
|
|
266
266
|
|
|
267
|
-
test("
|
|
268
|
-
const result = executeDocumentUpdate(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
expect(
|
|
267
|
+
test("resolves to the conversation's document when surface_id is omitted", () => {
|
|
268
|
+
const result = executeDocumentUpdate(
|
|
269
|
+
{ content: "appended chunk" },
|
|
270
|
+
makeContext({ sendToClient: () => {} }),
|
|
271
|
+
);
|
|
272
|
+
expect(result.isError).toBe(false);
|
|
273
|
+
const body = parseResult<{ surface_id: string; success: boolean }>(result);
|
|
274
|
+
expect(body.success).toBe(true);
|
|
275
|
+
expect(body.surface_id).toBe("doc-current");
|
|
273
276
|
});
|
|
274
277
|
|
|
275
278
|
test("returns Invalid input when content is missing", () => {
|