@vellumai/assistant 0.6.4 → 0.6.5
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/.prettierignore +5 -0
- package/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/openapi.yaml +123 -11
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/approval-cascade.test.ts +31 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +318 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/heartbeat-service.ts +76 -28
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +73 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +230 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +23 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +501 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +76 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +52 -1
- package/src/runtime/http-types.ts +23 -1
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +912 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/migration-routes.ts +720 -124
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +28 -6
- package/src/schedule/scheduler.ts +8 -0
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +26 -7
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +12 -3
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +2 -2
- package/src/util/pricing.ts +15 -5
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ── Mocks ────────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
// Mock skill resolution so we can control version hashes and catalog lookups.
|
|
6
|
+
let mockResolvedSkill: {
|
|
7
|
+
id: string;
|
|
8
|
+
directoryPath: string;
|
|
9
|
+
} | null = null;
|
|
10
|
+
let mockVersionHash: string | undefined;
|
|
11
|
+
let mockTransitiveHash: string | undefined;
|
|
12
|
+
let mockInlineExpansions: string[] = [];
|
|
13
|
+
let mockInlineEnabled = false;
|
|
14
|
+
|
|
15
|
+
mock.module("../config/skills.js", () => ({
|
|
16
|
+
resolveSkillSelector: (_selector: string) => ({
|
|
17
|
+
skill: mockResolvedSkill,
|
|
18
|
+
}),
|
|
19
|
+
loadSkillCatalog: () =>
|
|
20
|
+
mockResolvedSkill
|
|
21
|
+
? [
|
|
22
|
+
{
|
|
23
|
+
id: mockResolvedSkill.id,
|
|
24
|
+
directoryPath: mockResolvedSkill.directoryPath,
|
|
25
|
+
inlineCommandExpansions:
|
|
26
|
+
mockInlineExpansions.length > 0
|
|
27
|
+
? mockInlineExpansions
|
|
28
|
+
: undefined,
|
|
29
|
+
},
|
|
30
|
+
]
|
|
31
|
+
: [],
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
mock.module("../skills/version-hash.js", () => ({
|
|
35
|
+
computeSkillVersionHash: () => {
|
|
36
|
+
if (mockVersionHash === undefined) throw new Error("no hash");
|
|
37
|
+
return mockVersionHash;
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
mock.module("../skills/transitive-version-hash.js", () => ({
|
|
42
|
+
computeTransitiveSkillVersionHash: () => {
|
|
43
|
+
if (mockTransitiveHash === undefined) throw new Error("no hash");
|
|
44
|
+
return mockTransitiveHash;
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
mock.module("../skills/include-graph.js", () => ({
|
|
49
|
+
indexCatalogById: () => new Map(),
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
mock.module("../config/loader.js", () => ({
|
|
53
|
+
getConfig: () => ({}),
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
57
|
+
isAssistantFeatureFlagEnabled: () => mockInlineEnabled,
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
import {
|
|
61
|
+
type SkillClassifierInput,
|
|
62
|
+
SkillLoadRiskClassifier,
|
|
63
|
+
skillLoadRiskClassifier,
|
|
64
|
+
} from "./skill-risk-classifier.js";
|
|
65
|
+
|
|
66
|
+
// ── Test helpers ─────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
function resetMocks(): void {
|
|
69
|
+
mockResolvedSkill = null;
|
|
70
|
+
mockVersionHash = undefined;
|
|
71
|
+
mockTransitiveHash = undefined;
|
|
72
|
+
mockInlineExpansions = [];
|
|
73
|
+
mockInlineEnabled = false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── SkillLoadRiskClassifier ──────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
describe("SkillLoadRiskClassifier", () => {
|
|
79
|
+
test("skill_load is always Low risk", async () => {
|
|
80
|
+
resetMocks();
|
|
81
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
82
|
+
const result = await classifier.classify({ toolName: "skill_load" });
|
|
83
|
+
expect(result.riskLevel).toBe("low");
|
|
84
|
+
expect(result.reason).toBe("Skill load (default)");
|
|
85
|
+
expect(result.scopeOptions).toEqual([]);
|
|
86
|
+
expect(result.matchType).toBe("registry");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("scaffold_managed_skill is always High risk", async () => {
|
|
90
|
+
resetMocks();
|
|
91
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
92
|
+
const result = await classifier.classify({
|
|
93
|
+
toolName: "scaffold_managed_skill",
|
|
94
|
+
});
|
|
95
|
+
expect(result.riskLevel).toBe("high");
|
|
96
|
+
expect(result.reason).toBe(
|
|
97
|
+
"Skill scaffold — writes persistent skill source code",
|
|
98
|
+
);
|
|
99
|
+
expect(result.scopeOptions).toEqual([]);
|
|
100
|
+
expect(result.matchType).toBe("registry");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("delete_managed_skill is always High risk", async () => {
|
|
104
|
+
resetMocks();
|
|
105
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
106
|
+
const result = await classifier.classify({
|
|
107
|
+
toolName: "delete_managed_skill",
|
|
108
|
+
});
|
|
109
|
+
expect(result.riskLevel).toBe("high");
|
|
110
|
+
expect(result.reason).toBe(
|
|
111
|
+
"Skill delete — removes persistent skill source code",
|
|
112
|
+
);
|
|
113
|
+
expect(result.scopeOptions).toEqual([]);
|
|
114
|
+
expect(result.matchType).toBe("registry");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("skill_load with skillSelector is still Low risk", async () => {
|
|
118
|
+
resetMocks();
|
|
119
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
120
|
+
const result = await classifier.classify({
|
|
121
|
+
toolName: "skill_load",
|
|
122
|
+
skillSelector: "my-custom-skill",
|
|
123
|
+
});
|
|
124
|
+
expect(result.riskLevel).toBe("low");
|
|
125
|
+
expect(result.reason).toBe("Skill load (default)");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("scaffold_managed_skill with skillSelector is still High risk", async () => {
|
|
129
|
+
resetMocks();
|
|
130
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
131
|
+
const result = await classifier.classify({
|
|
132
|
+
toolName: "scaffold_managed_skill",
|
|
133
|
+
skillSelector: "new-skill",
|
|
134
|
+
});
|
|
135
|
+
expect(result.riskLevel).toBe("high");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("delete_managed_skill with skillSelector is still High risk", async () => {
|
|
139
|
+
resetMocks();
|
|
140
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
141
|
+
const result = await classifier.classify({
|
|
142
|
+
toolName: "delete_managed_skill",
|
|
143
|
+
skillSelector: "old-skill",
|
|
144
|
+
});
|
|
145
|
+
expect(result.riskLevel).toBe("high");
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ── Allowlist options ────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
describe("allowlistOptions", () => {
|
|
152
|
+
test("skill_load without selector produces wildcard option", async () => {
|
|
153
|
+
resetMocks();
|
|
154
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
155
|
+
const result = await classifier.classify({ toolName: "skill_load" });
|
|
156
|
+
expect(result.allowlistOptions).toEqual([
|
|
157
|
+
{
|
|
158
|
+
label: "skill_load:*",
|
|
159
|
+
description: "All skill loads",
|
|
160
|
+
pattern: "skill_load:*",
|
|
161
|
+
},
|
|
162
|
+
]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("skill_load with unresolvable selector produces selector-based option", async () => {
|
|
166
|
+
resetMocks();
|
|
167
|
+
// mockResolvedSkill is null — skill not found
|
|
168
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
169
|
+
const result = await classifier.classify({
|
|
170
|
+
toolName: "skill_load",
|
|
171
|
+
skillSelector: "unknown-skill",
|
|
172
|
+
});
|
|
173
|
+
expect(result.allowlistOptions).toEqual([
|
|
174
|
+
{
|
|
175
|
+
label: "unknown-skill",
|
|
176
|
+
description: "This skill",
|
|
177
|
+
pattern: "skill_load:unknown-skill",
|
|
178
|
+
},
|
|
179
|
+
]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("skill_load with resolved skill + version hash produces version-pinned option", async () => {
|
|
183
|
+
resetMocks();
|
|
184
|
+
mockResolvedSkill = { id: "my-skill", directoryPath: "/skills/my-skill" };
|
|
185
|
+
mockVersionHash = "abc123";
|
|
186
|
+
|
|
187
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
188
|
+
const result = await classifier.classify({
|
|
189
|
+
toolName: "skill_load",
|
|
190
|
+
skillSelector: "my-skill",
|
|
191
|
+
});
|
|
192
|
+
expect(result.allowlistOptions).toEqual([
|
|
193
|
+
{
|
|
194
|
+
label: "my-skill@abc123",
|
|
195
|
+
description: "This exact version",
|
|
196
|
+
pattern: "skill_load:my-skill@abc123",
|
|
197
|
+
},
|
|
198
|
+
]);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("skill_load with dynamic skill produces version-pinned + any-version options", async () => {
|
|
202
|
+
resetMocks();
|
|
203
|
+
mockResolvedSkill = {
|
|
204
|
+
id: "dynamic-skill",
|
|
205
|
+
directoryPath: "/skills/dynamic-skill",
|
|
206
|
+
};
|
|
207
|
+
mockVersionHash = "def456";
|
|
208
|
+
mockTransitiveHash = "trans789";
|
|
209
|
+
mockInlineExpansions = ["some-command"];
|
|
210
|
+
mockInlineEnabled = true;
|
|
211
|
+
|
|
212
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
213
|
+
const result = await classifier.classify({
|
|
214
|
+
toolName: "skill_load",
|
|
215
|
+
skillSelector: "dynamic-skill",
|
|
216
|
+
});
|
|
217
|
+
expect(result.allowlistOptions).toEqual([
|
|
218
|
+
{
|
|
219
|
+
label: "dynamic-skill@trans789",
|
|
220
|
+
description: "This exact version (pinned)",
|
|
221
|
+
pattern: "skill_load_dynamic:dynamic-skill@trans789",
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
label: "dynamic-skill",
|
|
225
|
+
description: "This skill (any version)",
|
|
226
|
+
pattern: "skill_load_dynamic:dynamic-skill",
|
|
227
|
+
},
|
|
228
|
+
]);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("scaffold_managed_skill produces skill-specific + wildcard options", async () => {
|
|
232
|
+
resetMocks();
|
|
233
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
234
|
+
const result = await classifier.classify({
|
|
235
|
+
toolName: "scaffold_managed_skill",
|
|
236
|
+
skillSelector: "new-skill",
|
|
237
|
+
});
|
|
238
|
+
expect(result.allowlistOptions).toEqual([
|
|
239
|
+
{
|
|
240
|
+
label: "new-skill",
|
|
241
|
+
description: "This skill only",
|
|
242
|
+
pattern: "scaffold_managed_skill:new-skill",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
label: "scaffold_managed_skill:*",
|
|
246
|
+
description: "All managed skill scaffolds",
|
|
247
|
+
pattern: "scaffold_managed_skill:*",
|
|
248
|
+
},
|
|
249
|
+
]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("delete_managed_skill produces skill-specific + wildcard options", async () => {
|
|
253
|
+
resetMocks();
|
|
254
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
255
|
+
const result = await classifier.classify({
|
|
256
|
+
toolName: "delete_managed_skill",
|
|
257
|
+
skillSelector: "old-skill",
|
|
258
|
+
});
|
|
259
|
+
expect(result.allowlistOptions).toEqual([
|
|
260
|
+
{
|
|
261
|
+
label: "old-skill",
|
|
262
|
+
description: "This skill only",
|
|
263
|
+
pattern: "delete_managed_skill:old-skill",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
label: "delete_managed_skill:*",
|
|
267
|
+
description: "All managed skill deletes",
|
|
268
|
+
pattern: "delete_managed_skill:*",
|
|
269
|
+
},
|
|
270
|
+
]);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("scaffold_managed_skill without selector produces only wildcard", async () => {
|
|
274
|
+
resetMocks();
|
|
275
|
+
const classifier = new SkillLoadRiskClassifier();
|
|
276
|
+
const result = await classifier.classify({
|
|
277
|
+
toolName: "scaffold_managed_skill",
|
|
278
|
+
});
|
|
279
|
+
expect(result.allowlistOptions).toEqual([
|
|
280
|
+
{
|
|
281
|
+
label: "scaffold_managed_skill:*",
|
|
282
|
+
description: "All managed skill scaffolds",
|
|
283
|
+
pattern: "scaffold_managed_skill:*",
|
|
284
|
+
},
|
|
285
|
+
]);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ── Singleton export ─────────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
describe("singleton", () => {
|
|
292
|
+
test("skillLoadRiskClassifier is an instance of SkillLoadRiskClassifier", () => {
|
|
293
|
+
expect(skillLoadRiskClassifier).toBeInstanceOf(SkillLoadRiskClassifier);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("singleton produces same results as fresh instance", async () => {
|
|
297
|
+
resetMocks();
|
|
298
|
+
const inputs: SkillClassifierInput[] = [
|
|
299
|
+
{ toolName: "skill_load" },
|
|
300
|
+
{ toolName: "scaffold_managed_skill" },
|
|
301
|
+
{ toolName: "delete_managed_skill" },
|
|
302
|
+
];
|
|
303
|
+
|
|
304
|
+
const fresh = new SkillLoadRiskClassifier();
|
|
305
|
+
for (const input of inputs) {
|
|
306
|
+
const singletonResult = await skillLoadRiskClassifier.classify(input);
|
|
307
|
+
const freshResult = await fresh.classify(input);
|
|
308
|
+
expect(singletonResult).toEqual(freshResult);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill risk classifier — classifies skill tool invocations by risk level.
|
|
3
|
+
*
|
|
4
|
+
* Implements RiskClassifier<SkillClassifierInput> with constant risk levels
|
|
5
|
+
* for each skill tool type:
|
|
6
|
+
* - skill_load: Low (read-only skill loading)
|
|
7
|
+
* - scaffold_managed_skill: High (writes persistent skill source code)
|
|
8
|
+
* - delete_managed_skill: High (removes persistent skill source code)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
12
|
+
import { getConfig } from "../config/loader.js";
|
|
13
|
+
import { loadSkillCatalog, resolveSkillSelector } from "../config/skills.js";
|
|
14
|
+
import { indexCatalogById } from "../skills/include-graph.js";
|
|
15
|
+
import { computeTransitiveSkillVersionHash } from "../skills/transitive-version-hash.js";
|
|
16
|
+
import { computeSkillVersionHash } from "../skills/version-hash.js";
|
|
17
|
+
import type { RiskAssessment, RiskClassifier } from "./risk-types.js";
|
|
18
|
+
import type { AllowlistOption } from "./types.js";
|
|
19
|
+
|
|
20
|
+
// ── Input type ───────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/** Input to the skill risk classifier. */
|
|
23
|
+
export interface SkillClassifierInput {
|
|
24
|
+
/** Which skill tool is being invoked. */
|
|
25
|
+
toolName: "skill_load" | "scaffold_managed_skill" | "delete_managed_skill";
|
|
26
|
+
/** Optional skill selector (e.g. skill name or path). */
|
|
27
|
+
skillSelector?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── Allowlist option helpers ─────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve a skill selector to its id and version hash. The version hash
|
|
34
|
+
* is always computed from disk so that untrusted input cannot spoof a
|
|
35
|
+
* pre-approved hash.
|
|
36
|
+
*/
|
|
37
|
+
function resolveSkillIdAndHash(
|
|
38
|
+
selector: string,
|
|
39
|
+
): { id: string; versionHash?: string } | null {
|
|
40
|
+
const resolved = resolveSkillSelector(selector);
|
|
41
|
+
if (!resolved.skill) return null;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const hash = computeSkillVersionHash(resolved.skill.directoryPath);
|
|
45
|
+
return { id: resolved.skill.id, versionHash: hash };
|
|
46
|
+
} catch {
|
|
47
|
+
return { id: resolved.skill.id };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check whether a skill (by id) has parsed inline command expansions.
|
|
53
|
+
*/
|
|
54
|
+
function hasInlineExpansions(skillId: string): boolean {
|
|
55
|
+
const catalog = loadSkillCatalog();
|
|
56
|
+
const skill = catalog.find((s) => s.id === skillId);
|
|
57
|
+
return (
|
|
58
|
+
skill?.inlineCommandExpansions != null &&
|
|
59
|
+
skill.inlineCommandExpansions.length > 0
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compute the transitive version hash for a skill, returning `undefined`
|
|
65
|
+
* when computation fails (missing includes, cycle, etc.).
|
|
66
|
+
*/
|
|
67
|
+
function computeTransitiveHashSafe(skillId: string): string | undefined {
|
|
68
|
+
try {
|
|
69
|
+
const catalog = loadSkillCatalog();
|
|
70
|
+
const index = indexCatalogById(catalog);
|
|
71
|
+
return computeTransitiveSkillVersionHash(skillId, index);
|
|
72
|
+
} catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build allowlist options for a skill_load invocation, mirroring the logic
|
|
79
|
+
* in checker.ts `skillLoadAllowlistStrategy()`.
|
|
80
|
+
*/
|
|
81
|
+
function buildSkillLoadAllowlistOptions(
|
|
82
|
+
rawSelector?: string,
|
|
83
|
+
): AllowlistOption[] {
|
|
84
|
+
if (!rawSelector) {
|
|
85
|
+
return [
|
|
86
|
+
{
|
|
87
|
+
label: "skill_load:*",
|
|
88
|
+
description: "All skill loads",
|
|
89
|
+
pattern: "skill_load:*",
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const resolved = resolveSkillIdAndHash(rawSelector);
|
|
95
|
+
|
|
96
|
+
// Check whether this is a dynamic (inline-command) skill load
|
|
97
|
+
const config = getConfig();
|
|
98
|
+
const inlineEnabled = isAssistantFeatureFlagEnabled(
|
|
99
|
+
"inline-skill-commands",
|
|
100
|
+
config,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (resolved && inlineEnabled && hasInlineExpansions(resolved.id)) {
|
|
104
|
+
const transitiveHash = computeTransitiveHashSafe(resolved.id);
|
|
105
|
+
const options: AllowlistOption[] = [];
|
|
106
|
+
if (transitiveHash) {
|
|
107
|
+
options.push({
|
|
108
|
+
label: `${resolved.id}@${transitiveHash}`,
|
|
109
|
+
description: "This exact version (pinned)",
|
|
110
|
+
pattern: `skill_load_dynamic:${resolved.id}@${transitiveHash}`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
options.push({
|
|
114
|
+
label: resolved.id,
|
|
115
|
+
description: "This skill (any version)",
|
|
116
|
+
pattern: `skill_load_dynamic:${resolved.id}`,
|
|
117
|
+
});
|
|
118
|
+
return options;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (resolved && resolved.versionHash) {
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
label: `${resolved.id}@${resolved.versionHash}`,
|
|
125
|
+
description: "This exact version",
|
|
126
|
+
pattern: `skill_load:${resolved.id}@${resolved.versionHash}`,
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
label: rawSelector,
|
|
133
|
+
description: "This skill",
|
|
134
|
+
pattern: `skill_load:${rawSelector}`,
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build allowlist options for scaffold/delete managed skill tools,
|
|
141
|
+
* mirroring the logic in checker.ts `managedSkillAllowlistStrategy()`.
|
|
142
|
+
*/
|
|
143
|
+
function buildManagedSkillAllowlistOptions(
|
|
144
|
+
toolName: string,
|
|
145
|
+
skillId?: string,
|
|
146
|
+
): AllowlistOption[] {
|
|
147
|
+
const toolLabel =
|
|
148
|
+
toolName === "scaffold_managed_skill" ? "scaffold" : "delete";
|
|
149
|
+
const options: AllowlistOption[] = [];
|
|
150
|
+
if (skillId) {
|
|
151
|
+
options.push({
|
|
152
|
+
label: skillId,
|
|
153
|
+
description: "This skill only",
|
|
154
|
+
pattern: `${toolName}:${skillId}`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
options.push({
|
|
158
|
+
label: `${toolName}:*`,
|
|
159
|
+
description: `All managed skill ${toolLabel}s`,
|
|
160
|
+
pattern: `${toolName}:*`,
|
|
161
|
+
});
|
|
162
|
+
return options;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Classifier ───────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Skill risk classifier implementation.
|
|
169
|
+
*
|
|
170
|
+
* Classifies skill tool invocations with constant risk levels per tool type.
|
|
171
|
+
* checker.ts delegates to the singleton `skillLoadRiskClassifier` instance
|
|
172
|
+
* for all skill tool risk classification.
|
|
173
|
+
*/
|
|
174
|
+
export class SkillLoadRiskClassifier implements RiskClassifier<SkillClassifierInput> {
|
|
175
|
+
async classify(input: SkillClassifierInput): Promise<RiskAssessment> {
|
|
176
|
+
const { toolName, skillSelector } = input;
|
|
177
|
+
|
|
178
|
+
switch (toolName) {
|
|
179
|
+
case "skill_load":
|
|
180
|
+
return {
|
|
181
|
+
riskLevel: "low",
|
|
182
|
+
reason: "Skill load (default)",
|
|
183
|
+
scopeOptions: [],
|
|
184
|
+
matchType: "registry",
|
|
185
|
+
allowlistOptions: buildSkillLoadAllowlistOptions(skillSelector),
|
|
186
|
+
};
|
|
187
|
+
case "scaffold_managed_skill":
|
|
188
|
+
return {
|
|
189
|
+
riskLevel: "high",
|
|
190
|
+
reason: "Skill scaffold — writes persistent skill source code",
|
|
191
|
+
scopeOptions: [],
|
|
192
|
+
matchType: "registry",
|
|
193
|
+
allowlistOptions: buildManagedSkillAllowlistOptions(
|
|
194
|
+
toolName,
|
|
195
|
+
skillSelector,
|
|
196
|
+
),
|
|
197
|
+
};
|
|
198
|
+
case "delete_managed_skill":
|
|
199
|
+
return {
|
|
200
|
+
riskLevel: "high",
|
|
201
|
+
reason: "Skill delete — removes persistent skill source code",
|
|
202
|
+
scopeOptions: [],
|
|
203
|
+
matchType: "registry",
|
|
204
|
+
allowlistOptions: buildManagedSkillAllowlistOptions(
|
|
205
|
+
toolName,
|
|
206
|
+
skillSelector,
|
|
207
|
+
),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Singleton classifier instance. */
|
|
214
|
+
export const skillLoadRiskClassifier = new SkillLoadRiskClassifier();
|
|
@@ -8,9 +8,14 @@
|
|
|
8
8
|
* Both async and synchronous variants are exported. The sync variants
|
|
9
9
|
* use `Bun.spawnSync` + `curl` to make blocking HTTP calls — acceptable
|
|
10
10
|
* for user-initiated write operations that are infrequent.
|
|
11
|
+
*
|
|
12
|
+
* All rule-returning endpoints parse response payloads through the shared
|
|
13
|
+
* `parseTrustRule` canonical parser so the client never returns unparsed
|
|
14
|
+
* or untyped raw rule objects.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
import type { TrustRule } from "@vellumai/ces-contracts";
|
|
18
|
+
import { parseTrustRule } from "@vellumai/ces-contracts";
|
|
14
19
|
|
|
15
20
|
import { getGatewayInternalBaseUrl } from "../config/env.js";
|
|
16
21
|
import { mintEdgeRelayToken } from "../runtime/auth/token-service.js";
|
|
@@ -165,32 +170,58 @@ function requestSync<T>(method: string, path: string, body?: unknown): T {
|
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Response parsing helpers
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Parse a raw rule object from a gateway response through the shared
|
|
179
|
+
* canonical parser. Ensures the trust client never returns unparsed or
|
|
180
|
+
* untyped rule objects, regardless of what the gateway sends back.
|
|
181
|
+
*/
|
|
182
|
+
function parseRuleResponse(raw: unknown): TrustRule {
|
|
183
|
+
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
184
|
+
throw new Error("Trust rule response is not a valid object");
|
|
185
|
+
}
|
|
186
|
+
const { rule } = parseTrustRule(raw as Record<string, unknown>);
|
|
187
|
+
return rule as TrustRule;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Parse an array of raw rule objects from a gateway response.
|
|
192
|
+
*/
|
|
193
|
+
function parseRulesResponse(raw: unknown[]): TrustRule[] {
|
|
194
|
+
return raw.map((r) => parseRuleResponse(r));
|
|
195
|
+
}
|
|
196
|
+
|
|
168
197
|
// ---------------------------------------------------------------------------
|
|
169
198
|
// Public API
|
|
170
199
|
// ---------------------------------------------------------------------------
|
|
171
200
|
|
|
172
201
|
/** Fetch all trust rules from the gateway. */
|
|
173
202
|
export async function getAllRules(): Promise<TrustRule[]> {
|
|
174
|
-
const data = await request<{ rules:
|
|
175
|
-
return data.rules;
|
|
203
|
+
const data = await request<{ rules: unknown[] }>("GET", "/v1/trust-rules");
|
|
204
|
+
return parseRulesResponse(data.rules);
|
|
176
205
|
}
|
|
177
206
|
|
|
178
207
|
/** Create a new trust rule. */
|
|
179
208
|
export async function addRule(params: {
|
|
180
209
|
tool: string;
|
|
181
210
|
pattern: string;
|
|
182
|
-
scope
|
|
211
|
+
scope?: string;
|
|
183
212
|
decision?: TrustRule["decision"];
|
|
184
213
|
priority?: number;
|
|
185
|
-
allowHighRisk?: boolean;
|
|
186
214
|
executionTarget?: string;
|
|
187
215
|
}): Promise<TrustRule> {
|
|
188
|
-
|
|
216
|
+
// Only include scope in the request body if provided.
|
|
217
|
+
const { scope, ...rest } = params;
|
|
218
|
+
const body = scope != null ? { ...rest, scope } : rest;
|
|
219
|
+
const data = await request<{ rule: unknown }>(
|
|
189
220
|
"POST",
|
|
190
221
|
"/v1/trust-rules",
|
|
191
|
-
|
|
222
|
+
body,
|
|
192
223
|
);
|
|
193
|
-
return data.rule;
|
|
224
|
+
return parseRuleResponse(data.rule);
|
|
194
225
|
}
|
|
195
226
|
|
|
196
227
|
/** Update an existing trust rule by ID. */
|
|
@@ -202,16 +233,15 @@ export async function updateRule(
|
|
|
202
233
|
scope?: string;
|
|
203
234
|
decision?: TrustRule["decision"];
|
|
204
235
|
priority?: number;
|
|
205
|
-
allowHighRisk?: boolean;
|
|
206
236
|
executionTarget?: string;
|
|
207
237
|
},
|
|
208
238
|
): Promise<TrustRule> {
|
|
209
|
-
const data = await request<{ rule:
|
|
239
|
+
const data = await request<{ rule: unknown }>(
|
|
210
240
|
"PATCH",
|
|
211
241
|
`/v1/trust-rules/${encodeURIComponent(id)}`,
|
|
212
242
|
updates,
|
|
213
243
|
);
|
|
214
|
-
return data.rule;
|
|
244
|
+
return parseRuleResponse(data.rule);
|
|
215
245
|
}
|
|
216
246
|
|
|
217
247
|
/** Remove a trust rule by ID. Returns true if the rule was found and deleted. */
|
|
@@ -245,11 +275,11 @@ export async function findMatchingRule(
|
|
|
245
275
|
commands: candidates.join(","),
|
|
246
276
|
scope,
|
|
247
277
|
});
|
|
248
|
-
const data = await request<{ rule:
|
|
278
|
+
const data = await request<{ rule: unknown | null }>(
|
|
249
279
|
"GET",
|
|
250
280
|
`/v1/trust-rules/match?${params.toString()}`,
|
|
251
281
|
);
|
|
252
|
-
return data.rule;
|
|
282
|
+
return data.rule != null ? parseRuleResponse(data.rule) : null;
|
|
253
283
|
}
|
|
254
284
|
|
|
255
285
|
/** Accept the starter approval bundle, seeding common low-risk allow rules. */
|
|
@@ -266,26 +296,24 @@ export async function acceptStarterBundle(): Promise<AcceptStarterBundleResult>
|
|
|
266
296
|
|
|
267
297
|
/** Fetch all trust rules from the gateway (synchronous). */
|
|
268
298
|
export function getAllRulesSync(): TrustRule[] {
|
|
269
|
-
const data = requestSync<{ rules:
|
|
270
|
-
return data.rules;
|
|
299
|
+
const data = requestSync<{ rules: unknown[] }>("GET", "/v1/trust-rules");
|
|
300
|
+
return parseRulesResponse(data.rules);
|
|
271
301
|
}
|
|
272
302
|
|
|
273
303
|
/** Create a new trust rule (synchronous). */
|
|
274
304
|
export function addRuleSync(params: {
|
|
275
305
|
tool: string;
|
|
276
306
|
pattern: string;
|
|
277
|
-
scope
|
|
307
|
+
scope?: string;
|
|
278
308
|
decision?: TrustRule["decision"];
|
|
279
309
|
priority?: number;
|
|
280
|
-
allowHighRisk?: boolean;
|
|
281
310
|
executionTarget?: string;
|
|
282
311
|
}): TrustRule {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
);
|
|
288
|
-
return data.rule;
|
|
312
|
+
// Only include scope in the request body if provided.
|
|
313
|
+
const { scope, ...rest } = params;
|
|
314
|
+
const body = scope != null ? { ...rest, scope } : rest;
|
|
315
|
+
const data = requestSync<{ rule: unknown }>("POST", "/v1/trust-rules", body);
|
|
316
|
+
return parseRuleResponse(data.rule);
|
|
289
317
|
}
|
|
290
318
|
|
|
291
319
|
/** Update an existing trust rule by ID (synchronous). */
|
|
@@ -297,16 +325,15 @@ export function updateRuleSync(
|
|
|
297
325
|
scope?: string;
|
|
298
326
|
decision?: TrustRule["decision"];
|
|
299
327
|
priority?: number;
|
|
300
|
-
allowHighRisk?: boolean;
|
|
301
328
|
executionTarget?: string;
|
|
302
329
|
},
|
|
303
330
|
): TrustRule {
|
|
304
|
-
const data = requestSync<{ rule:
|
|
331
|
+
const data = requestSync<{ rule: unknown }>(
|
|
305
332
|
"PATCH",
|
|
306
333
|
`/v1/trust-rules/${encodeURIComponent(id)}`,
|
|
307
334
|
updates,
|
|
308
335
|
);
|
|
309
|
-
return data.rule;
|
|
336
|
+
return parseRuleResponse(data.rule);
|
|
310
337
|
}
|
|
311
338
|
|
|
312
339
|
/** Remove a trust rule by ID (synchronous). Returns true if deleted. */
|