@vellumai/assistant 0.6.5 → 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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- 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/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- 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__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- 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__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- 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__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- 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 +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- 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 +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- 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__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -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 +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -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-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- 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/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- 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/messaging/SKILL.md +3 -3
- 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 +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- 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-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- 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/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- 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/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- 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/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- 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/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -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 +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- 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 +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- 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 +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- 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/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- 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/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- 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/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- 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/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- 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/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- 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/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- 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/041-backfill-google-gmail-settings-scope.ts +3 -4
- 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/registry.ts +12 -0
- 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__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- 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__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- 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 -88
- 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/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/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/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- 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
|
@@ -14,9 +14,11 @@ import type {
|
|
|
14
14
|
ParsedCommand,
|
|
15
15
|
} from "../tools/terminal/parser.js";
|
|
16
16
|
import { getLogger } from "../util/logger.js";
|
|
17
|
+
import { parseArgs } from "./arg-parser.js";
|
|
17
18
|
import { DEFAULT_COMMAND_REGISTRY } from "./command-registry.js";
|
|
18
19
|
import type {
|
|
19
20
|
ArgRule,
|
|
21
|
+
ArgSchema,
|
|
20
22
|
BashClassifierInput,
|
|
21
23
|
CommandRiskSpec,
|
|
22
24
|
Risk,
|
|
@@ -26,6 +28,7 @@ import type {
|
|
|
26
28
|
UserRule,
|
|
27
29
|
} from "./risk-types.js";
|
|
28
30
|
import { cachedParse } from "./shell-identity.js";
|
|
31
|
+
import type { AllowlistOption } from "./types.js";
|
|
29
32
|
|
|
30
33
|
const log = getLogger("bash-risk-classifier");
|
|
31
34
|
|
|
@@ -176,20 +179,17 @@ function getWrappedProgramWithArgs(seg: {
|
|
|
176
179
|
|
|
177
180
|
/**
|
|
178
181
|
* Extract the first positional (non-flag) arg, skipping value-consuming flags.
|
|
182
|
+
* Delegates to the shared `parseArgs()` utility for consistent arg parsing.
|
|
179
183
|
*/
|
|
180
184
|
function firstPositionalArg(
|
|
181
185
|
args: string[],
|
|
182
186
|
valueFlags?: Set<string>,
|
|
183
187
|
): string | undefined {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
return arg;
|
|
191
|
-
}
|
|
192
|
-
return undefined;
|
|
188
|
+
const schema: ArgSchema = valueFlags
|
|
189
|
+
? { valueFlags: [...valueFlags], positionals: "none" }
|
|
190
|
+
: { positionals: "none" };
|
|
191
|
+
const parsed = parseArgs(args, schema);
|
|
192
|
+
return parsed.positionals[0];
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// ── Safe-file downgrade for rm ────────────────────────────────────────────────
|
|
@@ -226,9 +226,8 @@ function resolveSubcommand(
|
|
|
226
226
|
return { spec, remainingArgs: args };
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
: undefined;
|
|
229
|
+
const valueFlagsList = spec.argSchema?.valueFlags;
|
|
230
|
+
const valueFlags = valueFlagsList ? new Set(valueFlagsList) : undefined;
|
|
232
231
|
const subcommandName = firstPositionalArg(args, valueFlags);
|
|
233
232
|
|
|
234
233
|
if (!subcommandName || !spec.subcommands[subcommandName]) {
|
|
@@ -367,12 +366,50 @@ export function classifySegment(
|
|
|
367
366
|
let argRuleReason = "";
|
|
368
367
|
|
|
369
368
|
const allArgs = segment.args;
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
369
|
+
|
|
370
|
+
// Parse args using the resolved spec's argSchema for structured lookups.
|
|
371
|
+
const schema = resolvedSpec.argSchema ?? {};
|
|
372
|
+
const parsed = parseArgs(allArgs, schema);
|
|
373
|
+
|
|
374
|
+
// Track which positionals have been covered by a rule.
|
|
375
|
+
const matchedPositionalIndices = new Set<number>();
|
|
376
|
+
|
|
377
|
+
for (const rule of argRules) {
|
|
378
|
+
if (rule.flags && rule.flags.length > 0 && rule.valuePattern) {
|
|
379
|
+
// ── Rules with flags + valuePattern ──────────────────────────────
|
|
380
|
+
// Look up each rule flag in parsed.flags. If the flag has a string
|
|
381
|
+
// value (consumed by parseArgs), test that value against the pattern.
|
|
382
|
+
// This replaces the manual next-token lookahead.
|
|
383
|
+
// Also check for --flag=value forms already handled by parseArgs.
|
|
384
|
+
//
|
|
385
|
+
// Known limitation: parseArgs stores flags in a Map (last value wins),
|
|
386
|
+
// so repeated flags like `curl -d @/etc/shadow -d safe` only check
|
|
387
|
+
// the last value. A future improvement could store flag values as
|
|
388
|
+
// arrays to catch all occurrences.
|
|
389
|
+
let flagValueMatched = false;
|
|
390
|
+
for (const flag of rule.flags) {
|
|
391
|
+
const flagVal = parsed.flags.get(flag);
|
|
392
|
+
if (typeof flagVal === "string") {
|
|
393
|
+
if (getCompiledPattern(rule.valuePattern).test(flagVal)) {
|
|
394
|
+
flagValueMatched = true;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Also check raw args for inline --flag=value forms where the flag
|
|
401
|
+
// name is NOT in the argSchema.valueFlags (parseArgs wouldn't split
|
|
402
|
+
// it). matchesArgRule handles this case.
|
|
403
|
+
if (!flagValueMatched) {
|
|
404
|
+
for (const arg of allArgs) {
|
|
405
|
+
if (matchesArgRule(rule, arg)) {
|
|
406
|
+
flagValueMatched = true;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (flagValueMatched) {
|
|
376
413
|
if (
|
|
377
414
|
!anyArgRuleMatched ||
|
|
378
415
|
riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
|
|
@@ -381,38 +418,125 @@ export function classifySegment(
|
|
|
381
418
|
argRuleReason = rule.reason;
|
|
382
419
|
}
|
|
383
420
|
anyArgRuleMatched = true;
|
|
384
|
-
matched = true;
|
|
385
|
-
break; // first match per arg wins
|
|
386
421
|
}
|
|
387
|
-
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
) {
|
|
397
|
-
const nextArg = allArgs[i + 1];
|
|
398
|
-
if (getCompiledPattern(rule.valuePattern).test(nextArg)) {
|
|
399
|
-
if (
|
|
400
|
-
!anyArgRuleMatched ||
|
|
401
|
-
riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
|
|
402
|
-
) {
|
|
403
|
-
argRuleMaxRisk = rule.risk;
|
|
404
|
-
argRuleReason = rule.reason;
|
|
405
|
-
}
|
|
406
|
-
anyArgRuleMatched = true;
|
|
407
|
-
matched = true;
|
|
422
|
+
} else if (rule.flags && rule.flags.length > 0) {
|
|
423
|
+
// ── Rules with flags only (no valuePattern) ──────────────────────
|
|
424
|
+
// Check flag presence in parsed.flags Map.
|
|
425
|
+
// Also scan raw allArgs for combined short flags like `-rf` that
|
|
426
|
+
// parseArgs treats as a single boolean flag token.
|
|
427
|
+
let flagMatched = false;
|
|
428
|
+
for (const flag of rule.flags) {
|
|
429
|
+
if (parsed.flags.has(flag)) {
|
|
430
|
+
flagMatched = true;
|
|
408
431
|
break;
|
|
409
432
|
}
|
|
410
433
|
}
|
|
434
|
+
|
|
435
|
+
// Fallback: scan raw args for combined short flags (e.g. `-rf`)
|
|
436
|
+
// and --flag=value forms (e.g. `--set=managed`) that parseArgs
|
|
437
|
+
// doesn't split when the flag isn't in argSchema.valueFlags.
|
|
438
|
+
// matchesArgRule handles both cases via its flag splitting logic.
|
|
439
|
+
if (!flagMatched) {
|
|
440
|
+
for (const arg of allArgs) {
|
|
441
|
+
if (matchesArgRule(rule, arg)) {
|
|
442
|
+
flagMatched = true;
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (flagMatched) {
|
|
449
|
+
if (
|
|
450
|
+
!anyArgRuleMatched ||
|
|
451
|
+
riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
|
|
452
|
+
) {
|
|
453
|
+
argRuleMaxRisk = rule.risk;
|
|
454
|
+
argRuleReason = rule.reason;
|
|
455
|
+
}
|
|
456
|
+
anyArgRuleMatched = true;
|
|
457
|
+
}
|
|
458
|
+
} else if (rule.valuePattern) {
|
|
459
|
+
// ── Rules with valuePattern only (no flags) ──────────────────────
|
|
460
|
+
// Test each positional against the pattern.
|
|
461
|
+
const re = getCompiledPattern(rule.valuePattern);
|
|
462
|
+
let positionalMatched = false;
|
|
463
|
+
for (let pi = 0; pi < parsed.positionals.length; pi++) {
|
|
464
|
+
if (re.test(parsed.positionals[pi])) {
|
|
465
|
+
positionalMatched = true;
|
|
466
|
+
matchedPositionalIndices.add(pi);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Also check raw allArgs for backward compatibility — some patterns
|
|
471
|
+
// may match flag-like tokens or args that parseArgs classified
|
|
472
|
+
// differently.
|
|
473
|
+
if (!positionalMatched) {
|
|
474
|
+
for (const arg of allArgs) {
|
|
475
|
+
if (re.test(arg)) {
|
|
476
|
+
positionalMatched = true;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (positionalMatched) {
|
|
483
|
+
if (
|
|
484
|
+
!anyArgRuleMatched ||
|
|
485
|
+
riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
|
|
486
|
+
) {
|
|
487
|
+
argRuleMaxRisk = rule.risk;
|
|
488
|
+
argRuleReason = rule.reason;
|
|
489
|
+
}
|
|
490
|
+
anyArgRuleMatched = true;
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
// No flags and no valuePattern — always matches (unusual but allowed)
|
|
494
|
+
if (
|
|
495
|
+
!anyArgRuleMatched ||
|
|
496
|
+
riskOrd(rule.risk) > riskOrd(argRuleMaxRisk)
|
|
497
|
+
) {
|
|
498
|
+
argRuleMaxRisk = rule.risk;
|
|
499
|
+
argRuleReason = rule.reason;
|
|
500
|
+
}
|
|
501
|
+
anyArgRuleMatched = true;
|
|
411
502
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Check for unmatched positionals — any positional not covered by a
|
|
506
|
+
// valuePattern-only rule prevents de-escalation.
|
|
507
|
+
for (let pi = 0; pi < parsed.positionals.length; pi++) {
|
|
508
|
+
if (!matchedPositionalIndices.has(pi)) {
|
|
415
509
|
hasUnmatchedNonFlagArg = true;
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Also check raw allArgs for non-flag args that parseArgs may have
|
|
515
|
+
// classified as flags (e.g. combined short flags like `-rf` are boolean
|
|
516
|
+
// flags in parseArgs but are non-flag args in the old iteration model).
|
|
517
|
+
// We only need to track unmatched non-flag args from the raw iteration
|
|
518
|
+
// perspective for backward compatibility.
|
|
519
|
+
if (!hasUnmatchedNonFlagArg) {
|
|
520
|
+
for (const arg of allArgs) {
|
|
521
|
+
if (arg.startsWith("-")) continue;
|
|
522
|
+
// Check if this positional was matched by any rule
|
|
523
|
+
let wasMatched = false;
|
|
524
|
+
for (const rule of argRules) {
|
|
525
|
+
if (matchesArgRule(rule, arg)) {
|
|
526
|
+
wasMatched = true;
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
// Check flag+value lookahead match (arg as a flag value)
|
|
530
|
+
if (rule.flags && rule.valuePattern && rule.flags.includes(arg)) {
|
|
531
|
+
// This arg is a flag that matched a rule flag — it's structural
|
|
532
|
+
wasMatched = true;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (!wasMatched) {
|
|
537
|
+
hasUnmatchedNonFlagArg = true;
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
416
540
|
}
|
|
417
541
|
}
|
|
418
542
|
|
|
@@ -500,10 +624,19 @@ export function generateScopeOptions(
|
|
|
500
624
|
options.push({ pattern, label });
|
|
501
625
|
}
|
|
502
626
|
|
|
503
|
-
// For multi-segment commands (pipelines), use the full command as
|
|
504
|
-
// and individual segment programs for broader options
|
|
627
|
+
// For multi-segment commands (pipelines, &&, etc.), use the full command as
|
|
628
|
+
// exact match and individual segment programs for broader options.
|
|
629
|
+
// Reconstruct using actual operators from the parsed segments (not hardcoded " | ").
|
|
505
630
|
if (parsed.segments.length > 1) {
|
|
506
|
-
const
|
|
631
|
+
const parts: string[] = [];
|
|
632
|
+
for (let i = 0; i < parsed.segments.length; i++) {
|
|
633
|
+
const seg = parsed.segments[i];
|
|
634
|
+
if (i > 0 && seg.operator) {
|
|
635
|
+
parts.push(seg.operator);
|
|
636
|
+
}
|
|
637
|
+
parts.push(seg.command);
|
|
638
|
+
}
|
|
639
|
+
const fullCommand = parts.join(" ");
|
|
507
640
|
addOption(`^${escapeRegex(fullCommand)}$`, fullCommand);
|
|
508
641
|
// Add command-level wildcards for each unique program
|
|
509
642
|
const programs = new Set(parsed.segments.map((s) => s.program));
|
|
@@ -530,14 +663,35 @@ export function generateScopeOptions(
|
|
|
530
663
|
return options;
|
|
531
664
|
}
|
|
532
665
|
|
|
533
|
-
// Separate args into flags and positionals
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
666
|
+
// Separate args into flags and positionals.
|
|
667
|
+
// When the command has an argSchema, use parseArgs for accurate flag/positional
|
|
668
|
+
// separation (correctly handles value-consuming flags like `find -name "*.ts"`).
|
|
669
|
+
// Otherwise, fall back to the naive `startsWith("-")` heuristic.
|
|
670
|
+
let flags: string[];
|
|
671
|
+
let positionals: string[];
|
|
672
|
+
|
|
673
|
+
if (spec?.argSchema) {
|
|
674
|
+
const parsedArgs = parseArgs(seg.args, spec.argSchema);
|
|
675
|
+
// Convert the flags Map to a flat string array: for value-consuming flags,
|
|
676
|
+
// include both the flag and its value as separate entries; for boolean flags,
|
|
677
|
+
// include just the flag.
|
|
678
|
+
flags = [];
|
|
679
|
+
for (const [flagName, flagValue] of parsedArgs.flags) {
|
|
680
|
+
flags.push(flagName);
|
|
681
|
+
if (typeof flagValue === "string") {
|
|
682
|
+
flags.push(flagValue);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
positionals = parsedArgs.positionals;
|
|
686
|
+
} else {
|
|
687
|
+
flags = [];
|
|
688
|
+
positionals = [];
|
|
689
|
+
for (const arg of seg.args) {
|
|
690
|
+
if (arg.startsWith("-")) {
|
|
691
|
+
flags.push(arg);
|
|
692
|
+
} else {
|
|
693
|
+
positionals.push(arg);
|
|
694
|
+
}
|
|
541
695
|
}
|
|
542
696
|
}
|
|
543
697
|
|
|
@@ -551,12 +705,19 @@ export function generateScopeOptions(
|
|
|
551
705
|
}
|
|
552
706
|
|
|
553
707
|
// 2. Wildcard positionals right-to-left
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
708
|
+
// When a subcommand is detected, exclude it from the positionals that get
|
|
709
|
+
// wildcarded — it's placed explicitly before flags in the label.
|
|
710
|
+
const wildcardPositionals = subcommand ? positionals.slice(1) : positionals;
|
|
711
|
+
if (wildcardPositionals.length > 1) {
|
|
712
|
+
for (let drop = 1; drop < wildcardPositionals.length; drop++) {
|
|
713
|
+
const kept = wildcardPositionals.slice(
|
|
714
|
+
0,
|
|
715
|
+
wildcardPositionals.length - drop,
|
|
716
|
+
);
|
|
717
|
+
const sub = subcommand ? [subcommand] : [];
|
|
718
|
+
const parts = [programName, ...sub, ...flags, ...kept].filter(Boolean);
|
|
558
719
|
const pattern = `^${parts.map(escapeRegex).join("\\s+")}\\s+.*$`;
|
|
559
|
-
const label = [programName, ...flags, ...kept, "*"].join(" ");
|
|
720
|
+
const label = [programName, ...sub, ...flags, ...kept, "*"].join(" ");
|
|
560
721
|
addOption(pattern, label);
|
|
561
722
|
}
|
|
562
723
|
}
|
|
@@ -589,6 +750,82 @@ function escapeRegex(s: string): string {
|
|
|
589
750
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
590
751
|
}
|
|
591
752
|
|
|
753
|
+
// ── Scope → Allowlist conversion ─────────────────────────────────────────────
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Extract stable tokens from a scope option label: program and subcommand
|
|
757
|
+
* words, skipping flags (starting with `-`) and wildcards (`*`).
|
|
758
|
+
* Returns an `action:` prefixed pattern that matches the action key
|
|
759
|
+
* candidates produced by `buildCommandCandidates()`.
|
|
760
|
+
*/
|
|
761
|
+
function labelToActionPattern(label: string): string {
|
|
762
|
+
const tokens = label
|
|
763
|
+
.split(/\s+/)
|
|
764
|
+
.filter((t) => !t.startsWith("-") && t !== "*");
|
|
765
|
+
return `action:${tokens.join(" ")}`;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Convert classifier-produced `ScopeOption[]` to `AllowlistOption[]` format.
|
|
770
|
+
*
|
|
771
|
+
* Patterns must be glob-compatible (not regex) because trust rules use
|
|
772
|
+
* Minimatch for matching against command candidates produced by
|
|
773
|
+
* `buildCommandCandidates()`. The format:
|
|
774
|
+
* - First option (exact match): raw command string
|
|
775
|
+
* - Intermediate options: `action:<program> <subcommand>` patterns that match
|
|
776
|
+
* action key candidates (labels reorder args so can't be used as globs directly)
|
|
777
|
+
* - Command-level wildcards: `action:<program>` matching the broadest action key
|
|
778
|
+
*
|
|
779
|
+
* Deduplicates by pattern to avoid redundant options when multiple scope levels
|
|
780
|
+
* collapse to the same action key.
|
|
781
|
+
*/
|
|
782
|
+
export function scopeOptionsToAllowlistOptions(
|
|
783
|
+
scopeOptions: ScopeOption[],
|
|
784
|
+
_parsed: ParsedCommand,
|
|
785
|
+
): AllowlistOption[] {
|
|
786
|
+
if (scopeOptions.length === 0) return [];
|
|
787
|
+
|
|
788
|
+
const results: AllowlistOption[] = [];
|
|
789
|
+
const seenPatterns = new Set<string>();
|
|
790
|
+
|
|
791
|
+
for (let i = 0; i < scopeOptions.length; i++) {
|
|
792
|
+
const opt = scopeOptions[i];
|
|
793
|
+
let description: string;
|
|
794
|
+
let pattern: string;
|
|
795
|
+
|
|
796
|
+
if (i === 0) {
|
|
797
|
+
// Exact match: raw command string (matches the raw candidate)
|
|
798
|
+
description = "This exact command";
|
|
799
|
+
pattern = opt.label;
|
|
800
|
+
} else if (
|
|
801
|
+
opt.label.endsWith(" *") &&
|
|
802
|
+
!opt.label.slice(0, -2).includes(" ")
|
|
803
|
+
) {
|
|
804
|
+
// Command-level wildcard (label is "<program> *"): use action: prefix
|
|
805
|
+
// to match action key candidates from buildCommandCandidates()
|
|
806
|
+
const prog = opt.label.slice(0, -2);
|
|
807
|
+
description = `Any ${prog} command`;
|
|
808
|
+
pattern = `action:${prog}`;
|
|
809
|
+
} else {
|
|
810
|
+
// Intermediate wildcard: use action:<tokens> pattern to match action key
|
|
811
|
+
// candidates. We can't use the label as a glob directly because the scope
|
|
812
|
+
// ladder reorders args (flags before positionals), but command candidates
|
|
813
|
+
// preserve user arg order.
|
|
814
|
+
const actionPattern = labelToActionPattern(opt.label);
|
|
815
|
+
description = "Commands matching this pattern";
|
|
816
|
+
pattern = actionPattern;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Deduplicate: skip options that produce the same pattern as a prior one
|
|
820
|
+
if (seenPatterns.has(pattern)) continue;
|
|
821
|
+
seenPatterns.add(pattern);
|
|
822
|
+
|
|
823
|
+
results.push({ label: opt.label, description, pattern });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return results;
|
|
827
|
+
}
|
|
828
|
+
|
|
592
829
|
// ── Main classifier ──────────────────────────────────────────────────────────
|
|
593
830
|
|
|
594
831
|
/**
|
|
@@ -619,6 +856,7 @@ export class BashRiskClassifier implements RiskClassifier<BashClassifierInput> {
|
|
|
619
856
|
reason: "Empty command",
|
|
620
857
|
scopeOptions: [],
|
|
621
858
|
matchType: "registry",
|
|
859
|
+
allowlistOptions: [],
|
|
622
860
|
};
|
|
623
861
|
}
|
|
624
862
|
|
|
@@ -678,12 +916,17 @@ export class BashRiskClassifier implements RiskClassifier<BashClassifierInput> {
|
|
|
678
916
|
}
|
|
679
917
|
|
|
680
918
|
const scopeOptions = generateScopeOptions(parsed, this.registry);
|
|
919
|
+
const allowlistOptions = scopeOptionsToAllowlistOptions(
|
|
920
|
+
scopeOptions,
|
|
921
|
+
parsed,
|
|
922
|
+
);
|
|
681
923
|
|
|
682
924
|
const assessment: RiskAssessment = {
|
|
683
925
|
riskLevel: maxRiskLevel,
|
|
684
926
|
reason: maxReason,
|
|
685
927
|
scopeOptions,
|
|
686
928
|
matchType,
|
|
929
|
+
allowlistOptions,
|
|
687
930
|
};
|
|
688
931
|
|
|
689
932
|
// Risk assessment analytics
|