@vellumai/assistant 0.11.4 → 0.11.5-dev.202608220019.a778cf0
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/AGENTS.md +6 -2
- package/ARCHITECTURE.md +29 -4
- package/README.md +1 -1
- package/docs/architecture/memory.md +17 -5
- package/docs/architecture/security.md +96 -152
- package/docs/guardian-request-flow.md +13 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +7 -14
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +30 -32
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +487 -11
- package/package.json +1 -1
- package/scripts/sync-web-search-catalog.ts +6 -0
- package/src/__tests__/anthropic-provider.test.ts +10 -3
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +4 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -6
- package/src/__tests__/channel-readiness-service.test.ts +126 -0
- package/src/__tests__/channel-reply-delivery.test.ts +30 -17
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/client-os-metadata-persistence.test.ts +16 -0
- package/src/__tests__/compactor-retained-image-validation.test.ts +142 -0
- package/src/__tests__/config-loader-backfill.test.ts +19 -10
- package/src/__tests__/container-cpu-sampler.test.ts +153 -0
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-delete-watch-timeline.test.ts +231 -0
- package/src/__tests__/conversation-error.test.ts +17 -0
- package/src/__tests__/conversation-lifecycle.test.ts +20 -26
- package/src/__tests__/conversation-pairing.test.ts +186 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
- package/src/__tests__/credential-broker-server-use.test.ts +24 -5
- package/src/__tests__/credential-routes.test.ts +22 -3
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/daemon-credential-client.test.ts +88 -0
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/dm-backfill.test.ts +63 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -4
- package/src/__tests__/events-client-registration.test.ts +28 -0
- package/src/__tests__/file-write-tool.test.ts +4 -2
- package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/host-proxy-interface.test.ts +11 -1
- package/src/__tests__/host-shell-tool.test.ts +23 -4
- package/src/__tests__/image-conversion.test.ts +143 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/input-repairs.test.ts +207 -0
- package/src/__tests__/live-workspace-guard.test.ts +75 -0
- package/src/__tests__/managed-profile-guard.test.ts +4 -2
- package/src/__tests__/mcp-abort-signal.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
- package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
- package/src/__tests__/mtime-cache.test.ts +2 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
- package/src/__tests__/openai-provider.test.ts +40 -0
- package/src/__tests__/openai-responses-provider.test.ts +18 -0
- package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/platform.test.ts +16 -1
- package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
- package/src/__tests__/plugin-api-shim.test.ts +5 -0
- package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
- package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/resource-pressure-guard.test.ts +428 -0
- package/src/__tests__/resource-pressure-routes.test.ts +113 -0
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
- package/src/__tests__/sanitize-config-for-transfer.test.ts +28 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
- package/src/__tests__/settings-routes.test.ts +85 -3
- package/src/__tests__/skill-tool-factory.test.ts +161 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +63 -38
- package/src/__tests__/tool-policy.test.ts +97 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
- package/src/__tests__/user-plugin-loader.test.ts +2 -0
- package/src/__tests__/validate-input.test.ts +243 -7
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -0
- package/src/__tests__/voice-session-bridge.test.ts +42 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +8 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
- package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
- package/src/acp/acp-claude-oauth.ts +38 -21
- package/src/acp/prepare-agent-env.ts +146 -55
- package/src/agent/loop-tool-dedup.test.ts +161 -0
- package/src/agent/loop.ts +68 -22
- package/src/api/constants/app-tools.ts +34 -0
- package/src/api/constants/profile-config-validation.ts +60 -0
- package/src/api/events/resource-pressure-status-changed.ts +53 -0
- package/src/api/events/tool-result.ts +6 -1
- package/src/api/events/watch-retro-completed.ts +52 -0
- package/src/api/index.ts +30 -0
- package/src/api/responses/resource-pressure-status.ts +25 -0
- package/src/approvals/guardian-channel-delivery.ts +70 -6
- package/src/approvals/guardian-request-resolvers.ts +32 -66
- package/src/calls/__tests__/voice-session-bridge.test.ts +114 -0
- package/src/calls/voice-session-bridge.ts +60 -6
- package/src/channels/__tests__/message-audience.test.ts +49 -0
- package/src/channels/__tests__/types.test.ts +17 -3
- package/src/channels/message-audience.ts +40 -0
- package/src/channels/types.ts +16 -12
- package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
- package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +2 -1
- package/src/cli/commands/__tests__/inference-profiles.test.ts +16 -4
- package/src/cli/commands/__tests__/inference-providers.test.ts +135 -2
- package/src/cli/commands/__tests__/keys.test.ts +89 -8
- package/src/cli/commands/__tests__/plugins.test.ts +57 -1
- package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
- package/src/cli/commands/channel-verification-sessions.ts +11 -21
- package/src/cli/commands/conversations.help.ts +34 -0
- package/src/cli/commands/conversations.ts +125 -0
- package/src/cli/commands/inference-profiles.ts +56 -3
- package/src/cli/commands/inference-providers.ts +37 -6
- package/src/cli/commands/inference.help.ts +9 -4
- package/src/cli/commands/keys.help.ts +13 -1
- package/src/cli/commands/keys.ts +26 -15
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/index.help.ts +41 -27
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/monitoring.ts +1 -0
- package/src/cli/commands/oauth/request.test.ts +93 -0
- package/src/cli/commands/oauth/request.ts +23 -9
- package/src/cli/commands/plugins.help.ts +28 -31
- package/src/cli/commands/plugins.ts +10 -0
- package/src/cli/commands/skills.help.ts +13 -16
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
- package/src/cli/lib/bundled-marketplace.json +26 -0
- package/src/cli/lib/daemon-credential-client.ts +25 -3
- package/src/cli/lib/install-from-github.ts +35 -24
- package/src/cli/lib/merge-plugin-tree.ts +22 -4
- package/src/cli/lib/open-browser.test.ts +67 -0
- package/src/cli/lib/open-browser.ts +24 -5
- package/src/cli/lib/plugin-surfaces.ts +27 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +7 -7
- package/src/config/__tests__/default-profile-catalog.test.ts +3 -3
- package/src/config/__tests__/profile-materialization.test.ts +26 -0
- package/src/config/default-profile-catalog.ts +5 -6
- package/src/config/feature-flag-registry.json +36 -28
- package/src/config/profile-materialization.ts +29 -0
- package/src/config/sanitize-for-transfer.ts +16 -0
- package/src/config/schemas/llm.ts +7 -0
- package/src/config/schemas/services.ts +6 -0
- package/src/config/webhook-routing.ts +8 -0
- package/src/context/compactor.ts +31 -2
- package/src/context/outbound-sanitize.ts +6 -0
- package/src/daemon/__tests__/lifecycle-watch-timeline-sweep.test.ts +98 -0
- package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
- package/src/daemon/conversation-error.ts +19 -2
- package/src/daemon/conversation-messaging.ts +22 -2
- package/src/daemon/conversation-process.ts +11 -0
- package/src/daemon/conversation-store.ts +10 -0
- package/src/daemon/conversation-surfaces.ts +21 -8
- package/src/daemon/conversation.ts +12 -10
- package/src/daemon/daemon-control.ts +1 -0
- package/src/daemon/handlers/__tests__/config-ingress-tunnel-records.test.ts +208 -0
- package/src/daemon/handlers/config-ingress.ts +115 -5
- package/src/daemon/lifecycle.ts +30 -2
- package/src/daemon/message-types/conversations.ts +5 -7
- package/src/daemon/message-types/web-activity.ts +3 -2
- package/src/daemon/provider-rejection-log-fields.ts +124 -0
- package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
- package/src/daemon/resource-pressure-guard.ts +387 -0
- package/src/daemon/shutdown-handlers.ts +2 -0
- package/src/daemon/startup-error.ts +16 -0
- package/src/daemon/trust-context.ts +14 -10
- package/src/daemon/unsendable-image-notice.ts +76 -0
- package/src/hooks/registry.ts +65 -51
- package/src/inbound/__tests__/tunnel-probe.test.ts +448 -0
- package/src/inbound/tunnel-probe.ts +261 -0
- package/src/ipc/__tests__/socket-path.test.ts +15 -7
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/socket-cleanup.ts +2 -11
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +128 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +138 -73
- package/src/messaging/providers/channel-transport.ts +100 -16
- package/src/messaging/providers/discord/send.ts +16 -0
- package/src/messaging/providers/discord/transport.ts +23 -1
- package/src/messaging/providers/index.ts +84 -16
- package/src/messaging/providers/slack/message-metadata.ts +45 -0
- package/src/messaging/providers/slack/render-transcript.ts +1 -4
- package/src/messaging/providers/slack/send.test.ts +88 -34
- package/src/messaging/providers/slack/send.ts +99 -50
- package/src/messaging/providers/slack/transport.ts +35 -37
- package/src/messaging/providers/telegram-bot/send.test.ts +88 -18
- package/src/messaging/providers/telegram-bot/send.ts +36 -0
- package/src/messaging/providers/telegram-bot/transport.ts +22 -8
- package/src/messaging/read-provider-metadata.test.ts +157 -0
- package/src/messaging/read-provider-metadata.ts +51 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
- package/src/notifications/__tests__/edit-notification.test.ts +174 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
- package/src/notifications/adapters/macos.ts +55 -0
- package/src/notifications/adapters/slack.ts +10 -5
- package/src/notifications/assistant-reply-producer.ts +37 -4
- package/src/notifications/conversation-pairing.ts +111 -21
- package/src/notifications/edit-notification.ts +38 -8
- package/src/notifications/emit-signal.ts +12 -14
- package/src/notifications/home-feed-side-effect.ts +218 -15
- package/src/notifications/types.ts +5 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +28 -3
- package/src/permissions/confirmation-guardian-request.ts +5 -1
- package/src/persistence/conversation-crud.ts +69 -20
- package/src/persistence/conversation-queries.ts +14 -1
- package/src/persistence/conversation-types.ts +22 -0
- package/src/persistence/db-async-query.ts +2 -1
- package/src/persistence/db-maintenance.ts +15 -0
- package/src/persistence/delivery-crud.ts +122 -15
- package/src/persistence/embeddings/qdrant-manager.ts +1 -0
- package/src/persistence/migrations/367-create-watch-timeline-entries.ts +46 -0
- package/src/persistence/migrations/368-watch-timeline-screenshot-blob.ts +33 -0
- package/src/persistence/migrations/__tests__/367-create-watch-timeline-entries.test.ts +98 -0
- package/src/persistence/migrations/__tests__/368-watch-timeline-screenshot-blob.test.ts +98 -0
- package/src/persistence/schema/index.ts +1 -0
- package/src/persistence/schema/watch.ts +29 -0
- package/src/persistence/steps.ts +4 -0
- package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
- package/src/plugin-api/conversation-turn.ts +27 -0
- package/src/plugin-api/credential-scope.test.ts +15 -0
- package/src/plugin-api/credential-scope.ts +14 -0
- package/src/plugin-api/index.ts +19 -1
- package/src/plugin-api/resolve-credential.ts +15 -10
- package/src/plugin-api/store-credential.ts +148 -0
- package/src/plugin-api/system-card.ts +36 -0
- package/src/plugin-api/vision-support.test.ts +82 -0
- package/src/plugin-api/vision-support.ts +14 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
- package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
- package/src/plugins/defaults/image-recovery/detect.ts +24 -1
- package/src/plugins/defaults/main.ts +15 -0
- package/src/plugins/defaults/memory/AGENTS.md +11 -3
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +1 -61
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/static-context.ts +0 -29
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/mtime-cache.ts +1 -1
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/plugin-tree-walk.ts +63 -4
- package/src/providers/__tests__/retry-network-error.test.ts +84 -0
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/connection-resolution.ts +25 -2
- package/src/providers/content-blocks.ts +22 -0
- package/src/providers/fetch-provider-catalog.ts +19 -0
- package/src/providers/gemini/client.ts +5 -2
- package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
- package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
- package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +92 -0
- package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
- package/src/providers/inference/__tests__/profile-config-validation.test.ts +39 -0
- package/src/providers/inference/__tests__/profile-probe-classify.test.ts +66 -0
- package/src/providers/inference/adapter-factory.ts +33 -2
- package/src/providers/inference/auth.ts +13 -2
- package/src/providers/inference/credential-usage.ts +37 -0
- package/src/providers/inference/endpoint-probe.ts +115 -0
- package/src/providers/inference/missing-credential-guard.ts +110 -0
- package/src/providers/inference/profile-probe.ts +256 -0
- package/src/providers/inference/resolve-auth.ts +7 -7
- package/src/providers/media-resolve.ts +176 -15
- package/src/providers/model-catalog.ts +10 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +17 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
- package/src/providers/openai/__tests__/connection-error-wrap.test.ts +44 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/api-error-normalization.ts +16 -2
- package/src/providers/openai/chat-completions-provider.ts +326 -44
- package/src/providers/openai/responses-provider.ts +5 -2
- package/src/providers/provider-send-message.ts +11 -0
- package/src/providers/retry.ts +6 -0
- package/src/providers/search-provider-catalog.ts +20 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +31 -10
- package/src/runtime/AGENTS.md +36 -0
- package/src/runtime/__tests__/host-observe.test.ts +302 -0
- package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
- package/src/runtime/__tests__/web-presence.test.ts +234 -0
- package/src/runtime/assistant-event-hub.ts +38 -0
- package/src/runtime/channel-readiness-service.ts +39 -13
- package/src/runtime/channel-readiness-types.ts +49 -2
- package/src/runtime/channel-reply-delivery.ts +55 -32
- package/src/runtime/channel-retry-sweep.ts +6 -4
- package/src/runtime/effective-capabilities.test.ts +19 -0
- package/src/runtime/effective-capabilities.ts +25 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/host-observe.ts +246 -0
- package/src/runtime/http-errors.ts +1 -0
- package/src/runtime/http-server.ts +185 -6
- package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +80 -0
- package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +118 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +72 -1
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +508 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +103 -71
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
- package/src/runtime/routes/__tests__/watch-routes-guardian-cache.test.ts +139 -0
- package/src/runtime/routes/__tests__/watch-routes.test.ts +598 -0
- package/src/runtime/routes/approval-routes.ts +37 -4
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
- package/src/runtime/routes/canned-message-complete.ts +68 -29
- package/src/runtime/routes/channel-readiness-routes.ts +14 -2
- package/src/runtime/routes/client-routes.ts +113 -0
- package/src/runtime/routes/conversation-list-routes.ts +22 -7
- package/src/runtime/routes/conversation-management-routes.ts +15 -9
- package/src/runtime/routes/conversation-query-routes.ts +10 -0
- package/src/runtime/routes/conversation-routes.ts +36 -22
- package/src/runtime/routes/credential-in-use.ts +85 -0
- package/src/runtime/routes/credential-routes.ts +55 -85
- package/src/runtime/routes/guardian-approval-interception.ts +44 -53
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
- package/src/runtime/routes/host-cu-routes.ts +18 -0
- package/src/runtime/routes/identity-routes.ts +5 -121
- package/src/runtime/routes/inbound-message-handler.ts +74 -30
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +182 -297
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +173 -386
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/inference-profiles-routes.ts +232 -31
- package/src/runtime/routes/inference-provider-connection-routes.ts +33 -11
- package/src/runtime/routes/ingress-status-routes.ts +180 -0
- package/src/runtime/routes/monitoring-routes.ts +9 -1
- package/src/runtime/routes/oauth-commands-routes.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +28 -32
- package/src/runtime/routes/resource-pressure-routes.ts +22 -0
- package/src/runtime/routes/secret-routes.ts +39 -8
- package/src/runtime/routes/settings-routes.ts +15 -12
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/user-route-dispatcher.ts +31 -2
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/watch-routes.ts +807 -0
- package/src/runtime/slack-reply-session.test.ts +23 -13
- package/src/runtime/slack-reply-session.ts +38 -55
- package/src/runtime/web-presence.ts +90 -0
- package/src/security/__tests__/untrusted-content.test.ts +42 -0
- package/src/security/untrusted-content.ts +28 -9
- package/src/skills/validate-input.ts +177 -40
- package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
- package/src/subagent/consult-context.ts +7 -23
- package/src/tools/credentials/broker.ts +24 -79
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/credentials/tool-policy.ts +67 -0
- package/src/tools/executor.ts +53 -15
- package/src/tools/network/__tests__/firecrawl-compat.test.ts +77 -0
- package/src/tools/network/__tests__/web-fetch-fastcrw.test.ts +169 -0
- package/src/tools/network/__tests__/web-search.test.ts +138 -3
- package/src/tools/network/firecrawl-compat.ts +90 -0
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/network/web-fetch.ts +142 -62
- package/src/tools/network/web-search.ts +162 -58
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/schema-transforms.ts +40 -5
- package/src/tools/shared/input-repairs.ts +160 -0
- package/src/tools/skills/skill-tool-factory.ts +18 -4
- package/src/tools/subagent/spawn.ts +0 -1
- package/src/tools/tool-approval-handler.ts +16 -10
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/util/__tests__/cgroup-memory.test.ts +3 -0
- package/src/util/cgroup-memory.ts +3 -0
- package/src/util/container-cpu-sampler.ts +250 -0
- package/src/util/image-conversion.ts +178 -14
- package/src/util/platform.ts +93 -10
- package/src/util/worker-process.ts +1 -0
- package/src/watch/__tests__/watch-retro.test.ts +665 -0
- package/src/watch/__tests__/watch-session-manager.test.ts +566 -0
- package/src/watch/__tests__/watch-timeline.test.ts +670 -0
- package/src/watch/watch-retro.ts +480 -0
- package/src/watch/watch-session-manager.ts +575 -0
- package/src/watch/watch-timeline.ts +848 -0
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
|
@@ -39,6 +39,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
39
39
|
|
|
40
40
|
import {
|
|
41
41
|
installIpcMock,
|
|
42
|
+
lastIpcParams,
|
|
42
43
|
mockIpcResponse,
|
|
43
44
|
} from "./helpers/gateway-classify-mock.js";
|
|
44
45
|
installIpcMock();
|
|
@@ -53,7 +54,8 @@ function mockRisk(
|
|
|
53
54
|
mockIpcResponse("classify_risk", {
|
|
54
55
|
risk,
|
|
55
56
|
reason: "test fixture",
|
|
56
|
-
matchType: "
|
|
57
|
+
matchType: "registry",
|
|
58
|
+
scopeOptions: [],
|
|
57
59
|
...extras,
|
|
58
60
|
});
|
|
59
61
|
}
|
|
@@ -82,8 +84,6 @@ import * as envRegistry from "../config/env-registry.js";
|
|
|
82
84
|
import {
|
|
83
85
|
check,
|
|
84
86
|
classifyRisk,
|
|
85
|
-
clearRiskCache,
|
|
86
|
-
generateAllowlistOptions,
|
|
87
87
|
generateScopeOptions,
|
|
88
88
|
SCOPE_AWARE_TOOLS,
|
|
89
89
|
} from "../permissions/checker.js";
|
|
@@ -180,8 +180,6 @@ describe("Permission Checker", () => {
|
|
|
180
180
|
mockIpcResponse("get_global_thresholds", DEFAULT_GATEWAY_THRESHOLDS);
|
|
181
181
|
// Clear the gateway threshold cache so each test gets a fresh threshold read
|
|
182
182
|
_clearGlobalCacheForTesting();
|
|
183
|
-
// Reset trust-store state and risk classification cache between tests
|
|
184
|
-
clearRiskCache();
|
|
185
183
|
// Reset guardian persona mock so each test opts in explicitly
|
|
186
184
|
mockGuardianPersonaPath = null;
|
|
187
185
|
loggerWarnCalls.length = 0;
|
|
@@ -447,268 +445,6 @@ describe("Permission Checker", () => {
|
|
|
447
445
|
});
|
|
448
446
|
});
|
|
449
447
|
|
|
450
|
-
// ── generateAllowlistOptions ───────────────────────────────────
|
|
451
|
-
|
|
452
|
-
describe("generateAllowlistOptions", () => {
|
|
453
|
-
test("file_write: generates prefixed file, ancestor directory wildcards, and tool wildcard", async () => {
|
|
454
|
-
const options = await generateAllowlistOptions("file_write", {
|
|
455
|
-
path: "/home/user/project/file.ts",
|
|
456
|
-
});
|
|
457
|
-
expect(options).toHaveLength(5);
|
|
458
|
-
// Patterns are prefixed with tool name to match check()'s "tool:path" format
|
|
459
|
-
expect(options[0].pattern).toBe("file_write:/home/user/project/file.ts");
|
|
460
|
-
expect(options[1].pattern).toBe("file_write:/home/user/project/**");
|
|
461
|
-
expect(options[2].pattern).toBe("file_write:/home/user/**");
|
|
462
|
-
expect(options[3].pattern).toBe("file_write:/home/**");
|
|
463
|
-
expect(options[4].pattern).toBe("file_write:*");
|
|
464
|
-
// Labels stay user-friendly
|
|
465
|
-
expect(options[0].label).toBe("/home/user/project/file.ts");
|
|
466
|
-
expect(options[1].label).toBe("/home/user/project/**");
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test("file_read: generates prefixed file, directory, and tool wildcard", async () => {
|
|
470
|
-
const options = await generateAllowlistOptions("file_read", {
|
|
471
|
-
path: "/tmp/data.json",
|
|
472
|
-
});
|
|
473
|
-
expect(options).toHaveLength(3);
|
|
474
|
-
expect(options[0].pattern).toBe("file_read:/tmp/data.json");
|
|
475
|
-
expect(options[1].pattern).toBe("file_read:/tmp/**");
|
|
476
|
-
expect(options[2].pattern).toBe("file_read:*");
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
test("host_file_read: generates prefixed file, directory, and tool wildcard", async () => {
|
|
480
|
-
const options = await generateAllowlistOptions("host_file_read", {
|
|
481
|
-
path: "/etc/hosts",
|
|
482
|
-
});
|
|
483
|
-
expect(options).toHaveLength(3);
|
|
484
|
-
expect(options[0].pattern).toBe("host_file_read:/etc/hosts");
|
|
485
|
-
expect(options[1].pattern).toBe("host_file_read:/etc/**");
|
|
486
|
-
expect(options[2].pattern).toBe("host_file_read:*");
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
test("host_file_write with file_path key", async () => {
|
|
490
|
-
const options = await generateAllowlistOptions("host_file_write", {
|
|
491
|
-
file_path: "/tmp/out.txt",
|
|
492
|
-
});
|
|
493
|
-
expect(options[0].pattern).toBe("host_file_write:/tmp/out.txt");
|
|
494
|
-
expect(options[1].pattern).toBe("host_file_write:/tmp/**");
|
|
495
|
-
expect(options[2].pattern).toBe("host_file_write:*");
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
test("file_write with file_path key", async () => {
|
|
499
|
-
const options = await generateAllowlistOptions("file_write", {
|
|
500
|
-
file_path: "/tmp/out.txt",
|
|
501
|
-
});
|
|
502
|
-
expect(options[0].pattern).toBe("file_write:/tmp/out.txt");
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
test("unknown tool returns wildcard", async () => {
|
|
506
|
-
const options = await generateAllowlistOptions("other_tool", {
|
|
507
|
-
foo: "bar",
|
|
508
|
-
});
|
|
509
|
-
expect(options).toHaveLength(1);
|
|
510
|
-
expect(options[0].pattern).toBe("*");
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
test("web_fetch: generates exact url, origin wildcard, and tool wildcard", async () => {
|
|
514
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
515
|
-
url: "https://example.com/docs/page",
|
|
516
|
-
});
|
|
517
|
-
expect(options).toHaveLength(3);
|
|
518
|
-
expect(options[0].pattern).toBe(
|
|
519
|
-
"web_fetch:https://example.com/docs/page",
|
|
520
|
-
);
|
|
521
|
-
expect(options[1].pattern).toBe("web_fetch:https://example.com/*");
|
|
522
|
-
expect(options[2].pattern).toBe("**");
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
test("web_fetch: strips fragments when generating allowlist options", async () => {
|
|
526
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
527
|
-
url: "https://example.com/docs/page#section-1",
|
|
528
|
-
});
|
|
529
|
-
expect(options).toHaveLength(3);
|
|
530
|
-
expect(options[0].pattern).toBe(
|
|
531
|
-
"web_fetch:https://example.com/docs/page",
|
|
532
|
-
);
|
|
533
|
-
expect(options[1].pattern).toBe("web_fetch:https://example.com/*");
|
|
534
|
-
expect(options[2].pattern).toBe("**");
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
test("web_fetch: strips trailing-dot hostnames when generating allowlist options", async () => {
|
|
538
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
539
|
-
url: "https://example.com./docs/page",
|
|
540
|
-
});
|
|
541
|
-
expect(options).toHaveLength(3);
|
|
542
|
-
expect(options[0].pattern).toBe(
|
|
543
|
-
"web_fetch:https://example.com/docs/page",
|
|
544
|
-
);
|
|
545
|
-
expect(options[1].pattern).toBe("web_fetch:https://example.com/*");
|
|
546
|
-
expect(options[2].pattern).toBe("**");
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
test("web_fetch: strips userinfo when generating allowlist options", async () => {
|
|
550
|
-
const username = "demo";
|
|
551
|
-
const credential = ["c", "r", "e", "d", "1", "2", "3"].join("");
|
|
552
|
-
const credentialedUrl = new URL("https://example.com/docs/page");
|
|
553
|
-
credentialedUrl.username = username;
|
|
554
|
-
credentialedUrl.password = credential;
|
|
555
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
556
|
-
url: credentialedUrl.href,
|
|
557
|
-
});
|
|
558
|
-
expect(options).toHaveLength(3);
|
|
559
|
-
expect(options[0].pattern).toBe(
|
|
560
|
-
"web_fetch:https://example.com/docs/page",
|
|
561
|
-
);
|
|
562
|
-
expect(options[1].pattern).toBe("web_fetch:https://example.com/*");
|
|
563
|
-
expect(options[2].pattern).toBe("**");
|
|
564
|
-
expect(options[0].pattern).not.toContain("demo:cred123@");
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
test("web_fetch: normalizes scheme-less host:port for allowlist options", async () => {
|
|
568
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
569
|
-
url: "example.com:8443/docs/page",
|
|
570
|
-
});
|
|
571
|
-
expect(options).toHaveLength(3);
|
|
572
|
-
expect(options[0].pattern).toBe(
|
|
573
|
-
"web_fetch:https://example.com:8443/docs/page",
|
|
574
|
-
);
|
|
575
|
-
expect(options[1].pattern).toBe("web_fetch:https://example.com:8443/*");
|
|
576
|
-
expect(options[2].pattern).toBe("**");
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
test("web_fetch: does not coerce path-only urls to https hostnames in allowlist options", async () => {
|
|
580
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
581
|
-
url: "/docs/getting-started",
|
|
582
|
-
});
|
|
583
|
-
expect(options).toHaveLength(2);
|
|
584
|
-
expect(options[0].pattern).toBe("web_fetch:/docs/getting-started");
|
|
585
|
-
expect(options[1].pattern).toBe("**");
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
test("scaffold_managed_skill: generates per-skill and wildcard options", async () => {
|
|
589
|
-
const options = await generateAllowlistOptions("scaffold_managed_skill", {
|
|
590
|
-
skill_id: "my-tool",
|
|
591
|
-
});
|
|
592
|
-
expect(options).toHaveLength(2);
|
|
593
|
-
expect(options[0].label).toBe("my-tool");
|
|
594
|
-
expect(options[0].pattern).toBe("scaffold_managed_skill:my-tool");
|
|
595
|
-
expect(options[0].description).toBe("This skill only");
|
|
596
|
-
expect(options[1].label).toBe("scaffold_managed_skill:*");
|
|
597
|
-
expect(options[1].pattern).toBe("scaffold_managed_skill:*");
|
|
598
|
-
expect(options[1].description).toBe("All managed skill scaffolds");
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
test("delete_managed_skill: generates per-skill and wildcard options", async () => {
|
|
602
|
-
const options = await generateAllowlistOptions("delete_managed_skill", {
|
|
603
|
-
skill_id: "doomed",
|
|
604
|
-
});
|
|
605
|
-
expect(options).toHaveLength(2);
|
|
606
|
-
expect(options[0].pattern).toBe("delete_managed_skill:doomed");
|
|
607
|
-
expect(options[1].pattern).toBe("delete_managed_skill:*");
|
|
608
|
-
expect(options[1].description).toBe("All managed skill deletes");
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
test("scaffold_managed_skill with empty skill_id: only wildcard option", async () => {
|
|
612
|
-
const options = await generateAllowlistOptions("scaffold_managed_skill", {
|
|
613
|
-
skill_id: "",
|
|
614
|
-
});
|
|
615
|
-
expect(options).toHaveLength(1);
|
|
616
|
-
expect(options[0].pattern).toBe("scaffold_managed_skill:*");
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
test("web_fetch: escapes minimatch metacharacters in generated exact and origin patterns", async () => {
|
|
620
|
-
const options = await generateAllowlistOptions("web_fetch", {
|
|
621
|
-
url: "https://[2001:db8::1]/search?q=test",
|
|
622
|
-
});
|
|
623
|
-
expect(options).toHaveLength(3);
|
|
624
|
-
expect(options[0].label).toBe("https://[2001:db8::1]/search?q=test");
|
|
625
|
-
expect(options[0].pattern).toBe(
|
|
626
|
-
"web_fetch:https://\\[2001:db8::1\\]/search\\?q=test",
|
|
627
|
-
);
|
|
628
|
-
expect(options[1].pattern).toBe("web_fetch:https://\\[2001:db8::1\\]/*");
|
|
629
|
-
expect(options[2].pattern).toBe("**");
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
// ── network_request allowlist options ─────────────────────────
|
|
633
|
-
|
|
634
|
-
test("network_request: generates exact url, origin wildcard, and tool wildcard", async () => {
|
|
635
|
-
const options = await generateAllowlistOptions("network_request", {
|
|
636
|
-
url: "https://api.example.com/v1/data",
|
|
637
|
-
});
|
|
638
|
-
expect(options).toHaveLength(3);
|
|
639
|
-
expect(options[0].pattern).toBe(
|
|
640
|
-
"network_request:https://api.example.com/v1/data",
|
|
641
|
-
);
|
|
642
|
-
expect(options[1].pattern).toBe(
|
|
643
|
-
"network_request:https://api.example.com/*",
|
|
644
|
-
);
|
|
645
|
-
expect(options[2].pattern).toBe("**");
|
|
646
|
-
expect(options[2].label).toBe("network_request:*");
|
|
647
|
-
expect(options[2].description).toBe("All network requests");
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
test("network_request: origin wildcard uses friendly hostname", async () => {
|
|
651
|
-
const options = await generateAllowlistOptions("network_request", {
|
|
652
|
-
url: "https://www.example.com/path",
|
|
653
|
-
});
|
|
654
|
-
expect(options[1].description).toBe("Any page on example.com");
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
test("network_request: normalizes scheme-less host:port input", async () => {
|
|
658
|
-
const options = await generateAllowlistOptions("network_request", {
|
|
659
|
-
url: "api.example.com:8443/v1/data",
|
|
660
|
-
});
|
|
661
|
-
expect(options).toHaveLength(3);
|
|
662
|
-
expect(options[0].pattern).toBe(
|
|
663
|
-
"network_request:https://api.example.com:8443/v1/data",
|
|
664
|
-
);
|
|
665
|
-
expect(options[1].pattern).toBe(
|
|
666
|
-
"network_request:https://api.example.com:8443/*",
|
|
667
|
-
);
|
|
668
|
-
expect(options[2].pattern).toBe("**");
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
test("network_request: strips fragments and userinfo", async () => {
|
|
672
|
-
const username = "demo";
|
|
673
|
-
const credential = ["c", "r", "e", "d", "1", "2", "3"].join("");
|
|
674
|
-
const credentialedUrl = new URL(
|
|
675
|
-
"https://api.example.com/v1/data#section",
|
|
676
|
-
);
|
|
677
|
-
credentialedUrl.username = username;
|
|
678
|
-
credentialedUrl.password = credential;
|
|
679
|
-
const options = await generateAllowlistOptions("network_request", {
|
|
680
|
-
url: credentialedUrl.href,
|
|
681
|
-
});
|
|
682
|
-
expect(options).toHaveLength(3);
|
|
683
|
-
expect(options[0].pattern).toBe(
|
|
684
|
-
"network_request:https://api.example.com/v1/data",
|
|
685
|
-
);
|
|
686
|
-
expect(options[0].pattern).not.toContain("demo:cred123@");
|
|
687
|
-
expect(options[0].pattern).not.toContain("#section");
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
test("network_request: escapes minimatch metacharacters", async () => {
|
|
691
|
-
const options = await generateAllowlistOptions("network_request", {
|
|
692
|
-
url: "https://[2001:db8::1]/api?key=val",
|
|
693
|
-
});
|
|
694
|
-
expect(options).toHaveLength(3);
|
|
695
|
-
expect(options[0].pattern).toBe(
|
|
696
|
-
"network_request:https://\\[2001:db8::1\\]/api\\?key=val",
|
|
697
|
-
);
|
|
698
|
-
expect(options[1].pattern).toBe(
|
|
699
|
-
"network_request:https://\\[2001:db8::1\\]/*",
|
|
700
|
-
);
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
test("network_request: empty url produces only tool wildcard", async () => {
|
|
704
|
-
const options = await generateAllowlistOptions("network_request", {
|
|
705
|
-
url: "",
|
|
706
|
-
});
|
|
707
|
-
expect(options).toHaveLength(1);
|
|
708
|
-
expect(options[0].pattern).toBe("**");
|
|
709
|
-
});
|
|
710
|
-
});
|
|
711
|
-
|
|
712
448
|
// ── generateScopeOptions ───────────────────────────────────────
|
|
713
449
|
|
|
714
450
|
describe("generateScopeOptions", () => {
|
|
@@ -1287,65 +1023,39 @@ describe("Permission Checker", () => {
|
|
|
1287
1023
|
// pattern (skillId@hash) and an any-version pattern (bare skillId).
|
|
1288
1024
|
// Input-supplied version_hash is always ignored to prevent spoofing.
|
|
1289
1025
|
|
|
1290
|
-
describe("
|
|
1026
|
+
describe("skill metadata sent to the gateway", () => {
|
|
1291
1027
|
function ensureSkillsDir(): void {
|
|
1292
1028
|
mkdirSync(join(checkerTestDir, "skills"), { recursive: true });
|
|
1293
1029
|
}
|
|
1294
1030
|
|
|
1295
|
-
test("
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
const options = await generateAllowlistOptions("skill_load", {
|
|
1300
|
-
skill: "test-opts-skill",
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
// Should have only the version-specific option
|
|
1304
|
-
expect(options).toHaveLength(1);
|
|
1305
|
-
expect(options[0].pattern).toMatch(/^skill_load:test-opts-skill@v1:/);
|
|
1306
|
-
expect(options[0].description).toBe("This exact version");
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
test("allowlist options ignore input version_hash and use disk-computed hash (regression)", async () => {
|
|
1031
|
+
test("the version hash comes from disk, never from the model's input", async () => {
|
|
1032
|
+
// The gateway builds the version-pinned trust-rule option from the hash
|
|
1033
|
+
// the daemon sends; taking it from the invocation would let a caller pin
|
|
1034
|
+
// a rule to a version that was never on disk.
|
|
1310
1035
|
ensureSkillsDir();
|
|
1311
1036
|
writeSkill("test-opts-explicit", "Test Opts Explicit");
|
|
1312
1037
|
|
|
1313
|
-
|
|
1314
|
-
// options must use the disk-computed hash, not the input value.
|
|
1315
|
-
const options = await generateAllowlistOptions("skill_load", {
|
|
1038
|
+
await classifyRisk("skill_load", {
|
|
1316
1039
|
skill: "test-opts-explicit",
|
|
1317
1040
|
version_hash: "v1:customhash123",
|
|
1318
1041
|
});
|
|
1319
1042
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
expect(
|
|
1324
|
-
|
|
1325
|
-
);
|
|
1326
|
-
expect(options[0].description).toBe("This exact version");
|
|
1043
|
+
const metadata = lastIpcParams("classify_risk")?.skillMetadata as
|
|
1044
|
+
| { skillId: string; versionHash: string }
|
|
1045
|
+
| undefined;
|
|
1046
|
+
expect(metadata?.skillId).toBe("test-opts-explicit");
|
|
1047
|
+
expect(metadata?.versionHash).toMatch(/^v1:/);
|
|
1048
|
+
expect(metadata?.versionHash).not.toBe("v1:customhash123");
|
|
1327
1049
|
});
|
|
1328
1050
|
|
|
1329
|
-
test("
|
|
1051
|
+
test("an unresolvable selector is sent with no metadata", async () => {
|
|
1330
1052
|
ensureSkillsDir();
|
|
1331
1053
|
|
|
1332
|
-
|
|
1333
|
-
skill: "no-such-skill",
|
|
1334
|
-
});
|
|
1335
|
-
|
|
1336
|
-
// Should have only the raw selector
|
|
1337
|
-
expect(options).toHaveLength(1);
|
|
1338
|
-
expect(options[0].pattern).toBe("skill_load:no-such-skill");
|
|
1339
|
-
expect(options[0].description).toBe("This skill");
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
|
-
test("allowlist options for empty skill selector only has wildcard", async () => {
|
|
1343
|
-
const options = await generateAllowlistOptions("skill_load", {
|
|
1344
|
-
skill: "",
|
|
1345
|
-
});
|
|
1054
|
+
await classifyRisk("skill_load", { skill: "no-such-skill" });
|
|
1346
1055
|
|
|
1347
|
-
|
|
1348
|
-
expect(
|
|
1056
|
+
const params = lastIpcParams("classify_risk");
|
|
1057
|
+
expect(params?.skill).toBe("no-such-skill");
|
|
1058
|
+
expect(params?.skillMetadata).toBeUndefined();
|
|
1349
1059
|
});
|
|
1350
1060
|
});
|
|
1351
1061
|
|
|
@@ -1514,7 +1224,6 @@ describe("bash network_mode=proxied — risk capped at medium", () => {
|
|
|
1514
1224
|
mockRisk("low");
|
|
1515
1225
|
mockIpcResponse("get_global_thresholds", DEFAULT_GATEWAY_THRESHOLDS);
|
|
1516
1226
|
_clearGlobalCacheForTesting();
|
|
1517
|
-
clearRiskCache();
|
|
1518
1227
|
});
|
|
1519
1228
|
|
|
1520
1229
|
test("proxied bash follows risk-based policy (medium risk → prompt outside container)", async () => {
|
|
@@ -1562,7 +1271,6 @@ describe("credentialed proxied bash — high risk escalation", () => {
|
|
|
1562
1271
|
mockRisk("low");
|
|
1563
1272
|
mockIpcResponse("get_global_thresholds", DEFAULT_GATEWAY_THRESHOLDS);
|
|
1564
1273
|
_clearGlobalCacheForTesting();
|
|
1565
|
-
clearRiskCache();
|
|
1566
1274
|
});
|
|
1567
1275
|
|
|
1568
1276
|
test("proxied bash with credential_ids sends credentialRefCount in IPC params", async () => {
|
|
@@ -1652,7 +1360,6 @@ describe("workspace mode — auto-allow workspace-scoped operations", () => {
|
|
|
1652
1360
|
mockRisk("low");
|
|
1653
1361
|
mockIpcResponse("get_global_thresholds", DEFAULT_GATEWAY_THRESHOLDS);
|
|
1654
1362
|
_clearGlobalCacheForTesting();
|
|
1655
|
-
clearRiskCache();
|
|
1656
1363
|
try {
|
|
1657
1364
|
rmSync(join(checkerTestDir, "protected", "trust.json"));
|
|
1658
1365
|
} catch {
|
|
@@ -1796,7 +1503,6 @@ describe("integration regressions (PR 11)", () => {
|
|
|
1796
1503
|
mockRisk("low");
|
|
1797
1504
|
mockIpcResponse("get_global_thresholds", DEFAULT_GATEWAY_THRESHOLDS);
|
|
1798
1505
|
_clearGlobalCacheForTesting();
|
|
1799
|
-
clearRiskCache();
|
|
1800
1506
|
// Delete the trust file to prevent stale default rules from prior tests
|
|
1801
1507
|
try {
|
|
1802
1508
|
rmSync(join(checkerTestDir, "protected", "trust.json"));
|
|
@@ -45,6 +45,10 @@ mock.module("../plugins/defaults/memory/substrate/skill-store.js", () => ({
|
|
|
45
45
|
seedV2SkillEntries: async () => {
|
|
46
46
|
seedCallCount += 1;
|
|
47
47
|
},
|
|
48
|
+
// The validate route builds the page index, which reads the seeded
|
|
49
|
+
// skill catalog; an empty catalog keeps that walk inert here.
|
|
50
|
+
listSkillEntries: () => [],
|
|
51
|
+
SKILL_SLUG_PREFIX: "skills/",
|
|
48
52
|
}));
|
|
49
53
|
|
|
50
54
|
// ---------------------------------------------------------------------------
|
|
@@ -254,7 +258,7 @@ describe("all memory v2 routes — MEMORY_V2_DISABLED gate", () => {
|
|
|
254
258
|
const result = (await route!.handler({ body: {} })) as {
|
|
255
259
|
pageCount: number;
|
|
256
260
|
edgeCount: number;
|
|
257
|
-
|
|
261
|
+
danglingLinks: unknown[];
|
|
258
262
|
oversizedPages: unknown[];
|
|
259
263
|
parseFailures: unknown[];
|
|
260
264
|
};
|
|
@@ -263,7 +267,7 @@ describe("all memory v2 routes — MEMORY_V2_DISABLED gate", () => {
|
|
|
263
267
|
// returned at all (no MEMORY_V2_DISABLED throw).
|
|
264
268
|
expect(result.pageCount).toBe(0);
|
|
265
269
|
expect(result.edgeCount).toBe(0);
|
|
266
|
-
expect(result.
|
|
270
|
+
expect(result.danglingLinks).toEqual([]);
|
|
267
271
|
expect(result.oversizedPages).toEqual([]);
|
|
268
272
|
expect(result.parseFailures).toEqual([]);
|
|
269
273
|
});
|
|
@@ -201,6 +201,22 @@ describe("client OS per-row evidence marker", () => {
|
|
|
201
201
|
expect(isMacOriginatedUserMessage(lastUserMetadata())).toBe(true);
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
+
// Mirrors the Mac-originated case above: a plain browser tab reports
|
|
205
|
+
// `client.os: "web"`, while the Electron desktop renderer resolves its own
|
|
206
|
+
// host OS first and persists `macos`/`windows` instead.
|
|
207
|
+
test("marks an OS this row's own transport reported (web)", async () => {
|
|
208
|
+
const ctx = createWebTurnContext("web");
|
|
209
|
+
await persistQueuedMessageBody(ctx, {
|
|
210
|
+
content: "hello",
|
|
211
|
+
requestId: "req-transport-os-web",
|
|
212
|
+
requestClientOs: "web",
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
expect(lastUserMetadata().client).toEqual({ os: "web" });
|
|
216
|
+
expect(lastUserMetadata().clientOsFromRequest).toBe(true);
|
|
217
|
+
expect(isMacOriginatedUserMessage(lastUserMetadata())).toBe(false);
|
|
218
|
+
});
|
|
219
|
+
|
|
204
220
|
test("marks an OS this row's own request headers reported", async () => {
|
|
205
221
|
const ctx = createWebTurnContext(undefined);
|
|
206
222
|
await persistQueuedMessageBody(ctx, {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compaction must never bake invalid image bytes into the rebuilt history.
|
|
3
|
+
*
|
|
4
|
+
* Retained images are re-hydrated from the attachment store and re-optimized
|
|
5
|
+
* at compaction time. If that pipeline yields bytes that no longer parse as an
|
|
6
|
+
* image (e.g. a corrupt conversion-cache entry), embedding them would make the
|
|
7
|
+
* provider reject EVERY subsequent turn with a 400: the conversation wedges
|
|
8
|
+
* until the block is manually removed. `buildRetainedImageBlocks` drops such
|
|
9
|
+
* payloads instead.
|
|
10
|
+
*/
|
|
11
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
buildRetainedImageBlocks,
|
|
15
|
+
collectImageManifest,
|
|
16
|
+
} from "../context/compactor.js";
|
|
17
|
+
import { attachInlineAttachmentToMessage } from "../persistence/attachments-store.js";
|
|
18
|
+
import {
|
|
19
|
+
addMessage,
|
|
20
|
+
createConversation,
|
|
21
|
+
} from "../persistence/conversation-crud.js";
|
|
22
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
23
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
24
|
+
|
|
25
|
+
await initializeDb();
|
|
26
|
+
|
|
27
|
+
// 1x1 transparent PNG.
|
|
28
|
+
const PNG_1X1_BASE64 =
|
|
29
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
|
|
30
|
+
|
|
31
|
+
// Declared image/png, but the bytes are not any known image format:
|
|
32
|
+
// the stand-in for a corrupted payload surviving in the pipeline.
|
|
33
|
+
const GARBAGE_BASE64 = Buffer.from("definitely not an image").toString(
|
|
34
|
+
"base64",
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function resetTables(): void {
|
|
38
|
+
const db = getDb();
|
|
39
|
+
db.run("DELETE FROM message_attachments");
|
|
40
|
+
db.run("DELETE FROM attachments");
|
|
41
|
+
db.run("DELETE FROM messages");
|
|
42
|
+
db.run("DELETE FROM conversations");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function addImageMessage(
|
|
46
|
+
conversationId: string,
|
|
47
|
+
filename: string,
|
|
48
|
+
dataBase64: string,
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
const inserted = await addMessage(
|
|
51
|
+
conversationId,
|
|
52
|
+
"user",
|
|
53
|
+
JSON.stringify([{ type: "text", text: filename }]),
|
|
54
|
+
{
|
|
55
|
+
metadata: { provenanceTrustClass: "guardian" },
|
|
56
|
+
skipIndexing: true,
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
await attachInlineAttachmentToMessage(
|
|
60
|
+
inserted.id,
|
|
61
|
+
0,
|
|
62
|
+
filename,
|
|
63
|
+
"image/png",
|
|
64
|
+
dataBase64,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("buildRetainedImageBlocks corrupt-bytes gate", () => {
|
|
69
|
+
beforeEach(resetTables);
|
|
70
|
+
|
|
71
|
+
test("valid image bytes are retained", async () => {
|
|
72
|
+
const conv = createConversation();
|
|
73
|
+
await addImageMessage(conv.id, "good.png", PNG_1X1_BASE64);
|
|
74
|
+
const manifest = collectImageManifest(conv.id, "guardian");
|
|
75
|
+
|
|
76
|
+
const { blocks, resolved, missing } = await buildRetainedImageBlocks(
|
|
77
|
+
["good.png"],
|
|
78
|
+
manifest,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(resolved).toEqual(["good.png"]);
|
|
82
|
+
expect(missing).toEqual([]);
|
|
83
|
+
expect(blocks).toHaveLength(1);
|
|
84
|
+
expect(blocks[0].source.type).toBe("base64");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("bytes that do not parse as an image are dropped, not embedded", async () => {
|
|
88
|
+
const conv = createConversation();
|
|
89
|
+
await addImageMessage(conv.id, "corrupt.png", GARBAGE_BASE64);
|
|
90
|
+
await addImageMessage(conv.id, "good.png", PNG_1X1_BASE64);
|
|
91
|
+
const manifest = collectImageManifest(conv.id, "guardian");
|
|
92
|
+
|
|
93
|
+
const { blocks, resolved, missing } = await buildRetainedImageBlocks(
|
|
94
|
+
["corrupt.png", "good.png"],
|
|
95
|
+
manifest,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// The corrupt payload is dropped (not listed as manifest-missing, since
|
|
99
|
+
// it resolved fine; its bytes were the problem) and the valid one
|
|
100
|
+
// survives.
|
|
101
|
+
expect(resolved).toEqual(["good.png"]);
|
|
102
|
+
expect(missing).toEqual([]);
|
|
103
|
+
expect(blocks).toHaveLength(1);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("a truncated JPEG (valid SOI header, no EOI) is dropped", async () => {
|
|
107
|
+
// A JPEG torn mid-write keeps its SOI header, so a format sniff alone
|
|
108
|
+
// accepts it; the gate must also require an EOI marker.
|
|
109
|
+
const tornJpeg = Buffer.concat([
|
|
110
|
+
Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10]),
|
|
111
|
+
Buffer.from("JFIF\0"),
|
|
112
|
+
Buffer.alloc(64),
|
|
113
|
+
]).toString("base64");
|
|
114
|
+
const conv = createConversation();
|
|
115
|
+
await addImageMessage(conv.id, "torn.jpg", tornJpeg);
|
|
116
|
+
const manifest = collectImageManifest(conv.id, "guardian");
|
|
117
|
+
|
|
118
|
+
const { blocks, resolved, missing } = await buildRetainedImageBlocks(
|
|
119
|
+
["torn.jpg"],
|
|
120
|
+
manifest,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(resolved).toEqual([]);
|
|
124
|
+
expect(missing).toEqual([]);
|
|
125
|
+
expect(blocks).toHaveLength(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("unresolvable filenames still land in missing", async () => {
|
|
129
|
+
const conv = createConversation();
|
|
130
|
+
await addImageMessage(conv.id, "good.png", PNG_1X1_BASE64);
|
|
131
|
+
const manifest = collectImageManifest(conv.id, "guardian");
|
|
132
|
+
|
|
133
|
+
const { blocks, resolved, missing } = await buildRetainedImageBlocks(
|
|
134
|
+
["never-uploaded.png", "good.png"],
|
|
135
|
+
manifest,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
expect(missing).toEqual(["never-uploaded.png"]);
|
|
139
|
+
expect(resolved).toEqual(["good.png"]);
|
|
140
|
+
expect(blocks).toHaveLength(1);
|
|
141
|
+
});
|
|
142
|
+
});
|