@vellumai/assistant 0.6.1 → 0.6.3
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/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -8,7 +8,7 @@ import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
|
8
8
|
class HostFileWriteTool implements Tool {
|
|
9
9
|
name = "host_file_write";
|
|
10
10
|
description =
|
|
11
|
-
"Write content to a file on
|
|
11
|
+
"Write content to a file on your guardian's device, creating it if it does not exist. For files on your own machine, use file_write instead.";
|
|
12
12
|
category = "host-filesystem";
|
|
13
13
|
defaultRiskLevel = RiskLevel.Medium;
|
|
14
14
|
|
|
@@ -207,30 +207,35 @@ class HostShellTool implements Tool {
|
|
|
207
207
|
detached: true,
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
// Kill the entire process tree. Tries the process group first
|
|
211
|
+
// (negative PID), then falls back to killing the direct child if the
|
|
212
|
+
// PID is unavailable or the group kill fails.
|
|
213
|
+
const killTree = () => {
|
|
214
|
+
if (child.pid != null) {
|
|
215
|
+
try {
|
|
216
|
+
process.kill(-child.pid, "SIGKILL");
|
|
217
|
+
return;
|
|
218
|
+
} catch {
|
|
219
|
+
// Process group may have already exited — fall through.
|
|
220
|
+
}
|
|
221
|
+
}
|
|
212
222
|
try {
|
|
213
|
-
|
|
223
|
+
child.kill("SIGKILL");
|
|
214
224
|
} catch {
|
|
215
|
-
//
|
|
225
|
+
// Child may have already exited.
|
|
216
226
|
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const timer = setTimeout(() => {
|
|
230
|
+
timedOut = true;
|
|
231
|
+
killTree();
|
|
217
232
|
}, timeoutMs);
|
|
218
233
|
|
|
219
234
|
// Cooperative cancellation via AbortSignal
|
|
220
|
-
const onAbort = () =>
|
|
221
|
-
try {
|
|
222
|
-
process.kill(-child.pid!, "SIGKILL");
|
|
223
|
-
} catch {
|
|
224
|
-
// Process group may have already exited.
|
|
225
|
-
}
|
|
226
|
-
};
|
|
235
|
+
const onAbort = () => killTree();
|
|
227
236
|
if (context.signal) {
|
|
228
237
|
if (context.signal.aborted) {
|
|
229
|
-
|
|
230
|
-
process.kill(-child.pid!, "SIGKILL");
|
|
231
|
-
} catch {
|
|
232
|
-
// Process group may have already exited.
|
|
233
|
-
}
|
|
238
|
+
killTree();
|
|
234
239
|
} else {
|
|
235
240
|
context.signal.addEventListener("abort", onAbort, { once: true });
|
|
236
241
|
}
|
|
@@ -7,11 +7,14 @@ import {
|
|
|
7
7
|
generateAllowlistOptions,
|
|
8
8
|
generateScopeOptions,
|
|
9
9
|
} from "../permissions/checker.js";
|
|
10
|
-
import { getMode } from "../permissions/permission-mode-store.js";
|
|
11
10
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
12
11
|
import { addRule } from "../permissions/trust-store.js";
|
|
13
12
|
import { RiskLevel } from "../permissions/types.js";
|
|
14
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
CONVERSATION_HOST_ACCESS_PROMPT,
|
|
15
|
+
evaluateV2ConsentDisposition,
|
|
16
|
+
isConversationHostAccessDecision,
|
|
17
|
+
} from "../permissions/v2-consent-policy.js";
|
|
15
18
|
import {
|
|
16
19
|
getEffectiveMode,
|
|
17
20
|
setConversationMode,
|
|
@@ -20,7 +23,6 @@ import {
|
|
|
20
23
|
import { getLogger } from "../util/logger.js";
|
|
21
24
|
import { buildPolicyContext } from "./policy-context.js";
|
|
22
25
|
import { isSideEffectTool } from "./side-effects.js";
|
|
23
|
-
import { wrapCommand } from "./terminal/sandbox.js";
|
|
24
26
|
import type { ExecutionTarget } from "./types.js";
|
|
25
27
|
import type { Tool, ToolContext, ToolLifecycleEvent } from "./types.js";
|
|
26
28
|
|
|
@@ -68,50 +70,29 @@ export class PermissionChecker {
|
|
|
68
70
|
}
|
|
69
71
|
| undefined,
|
|
70
72
|
): Promise<PermissionDecision> {
|
|
71
|
-
// ── permission-controls-v2 early gate ──────────────────────────────
|
|
72
|
-
// When the v2 flag is enabled, replace the entire risk-classification
|
|
73
|
-
// path with a simple binary check: is it a host tool + is host access
|
|
74
|
-
// enabled? Certain security gates (requireFreshApproval,
|
|
75
|
-
// forcePromptSideEffects, hostAccess=false) fall through to the v1
|
|
76
|
-
// prompt flow so the interactive prompter is engaged.
|
|
77
|
-
const cfg = getConfig();
|
|
78
73
|
let v2ForcePrompt = false;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// interactive prompter is engaged (returning allowed:false here
|
|
101
|
-
// would surface an error string instead of a permission dialog).
|
|
102
|
-
// The v2ForcePrompt flag ensures check()'s allow decision is
|
|
103
|
-
// promoted to prompt so the user sees a permission dialog.
|
|
104
|
-
v2ForcePrompt = true;
|
|
105
|
-
} else {
|
|
106
|
-
// Non-host tools are auto-allowed when v2 is on
|
|
107
|
-
return {
|
|
108
|
-
allowed: true,
|
|
109
|
-
decision: "allow",
|
|
110
|
-
riskLevel: RiskLevel.Low,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
74
|
+
const cfg = getConfig();
|
|
75
|
+
const v2Enabled = isAssistantFeatureFlagEnabled(
|
|
76
|
+
"permission-controls-v2",
|
|
77
|
+
cfg,
|
|
78
|
+
);
|
|
79
|
+
if (v2Enabled) {
|
|
80
|
+
const v2Disposition = evaluateV2ConsentDisposition(name, input, context);
|
|
81
|
+
if (v2Disposition === "auto_allow") {
|
|
82
|
+
return {
|
|
83
|
+
allowed: true,
|
|
84
|
+
decision: "allow",
|
|
85
|
+
riskLevel: RiskLevel.Low,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (v2Disposition === "prompt_host_access") {
|
|
89
|
+
// Host tool with hostAccess disabled — fall through to v1 so the
|
|
90
|
+
// interactive prompter is engaged (returning allowed:false here
|
|
91
|
+
// would surface an error string instead of a permission dialog).
|
|
92
|
+
// The v2ForcePrompt flag ensures check()'s allow decision is
|
|
93
|
+
// promoted to prompt so the user sees a permission dialog.
|
|
94
|
+
v2ForcePrompt = true;
|
|
113
95
|
}
|
|
114
|
-
// Falls through to the v1 risk-classification + prompter path
|
|
115
96
|
}
|
|
116
97
|
|
|
117
98
|
const risk = await classifyRisk(
|
|
@@ -302,40 +283,34 @@ export class PermissionChecker {
|
|
|
302
283
|
return { allowed: true, decision: "temporary_override", riskLevel };
|
|
303
284
|
}
|
|
304
285
|
|
|
305
|
-
const allowlistOptions = await generateAllowlistOptions(
|
|
306
|
-
name,
|
|
307
|
-
input,
|
|
308
|
-
context.signal,
|
|
309
|
-
);
|
|
310
|
-
const scopeOptions = generateScopeOptions(context.workingDir, name);
|
|
311
286
|
const previewDiff = computePreviewDiff(name, input, context.workingDir);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
287
|
+
const promptOptions = v2ForcePrompt
|
|
288
|
+
? CONVERSATION_HOST_ACCESS_PROMPT
|
|
289
|
+
: v2Enabled
|
|
290
|
+
? {
|
|
291
|
+
allowlistOptions: [] as Awaited<
|
|
292
|
+
ReturnType<typeof generateAllowlistOptions>
|
|
293
|
+
>,
|
|
294
|
+
scopeOptions: [] as ReturnType<typeof generateScopeOptions>,
|
|
295
|
+
persistentDecisionsAllowed: false,
|
|
296
|
+
temporaryOptionsAvailable: undefined,
|
|
297
|
+
}
|
|
298
|
+
: {
|
|
299
|
+
allowlistOptions: await generateAllowlistOptions(
|
|
300
|
+
name,
|
|
301
|
+
input,
|
|
302
|
+
context.signal,
|
|
303
|
+
),
|
|
304
|
+
scopeOptions: generateScopeOptions(context.workingDir, name),
|
|
305
|
+
persistentDecisionsAllowed: !context.requireFreshApproval,
|
|
306
|
+
temporaryOptionsAvailable:
|
|
307
|
+
context.trustClass === "guardian" &&
|
|
308
|
+
!context.requireFreshApproval
|
|
309
|
+
? (["allow_10m", "allow_conversation"] as Array<
|
|
310
|
+
"allow_10m" | "allow_conversation"
|
|
311
|
+
>)
|
|
312
|
+
: undefined,
|
|
313
|
+
};
|
|
339
314
|
|
|
340
315
|
emitLifecycleEvent({
|
|
341
316
|
type: "permission_prompt",
|
|
@@ -347,11 +322,10 @@ export class PermissionChecker {
|
|
|
347
322
|
requestId: context.requestId,
|
|
348
323
|
riskLevel,
|
|
349
324
|
reason: result.reason,
|
|
350
|
-
allowlistOptions,
|
|
351
|
-
scopeOptions,
|
|
325
|
+
allowlistOptions: promptOptions.allowlistOptions,
|
|
326
|
+
scopeOptions: promptOptions.scopeOptions,
|
|
352
327
|
diff: previewDiff,
|
|
353
|
-
|
|
354
|
-
persistentDecisionsAllowed,
|
|
328
|
+
persistentDecisionsAllowed: promptOptions.persistentDecisionsAllowed,
|
|
355
329
|
});
|
|
356
330
|
|
|
357
331
|
await getHookManager().trigger("permission-request", {
|
|
@@ -365,28 +339,31 @@ export class PermissionChecker {
|
|
|
365
339
|
name,
|
|
366
340
|
input,
|
|
367
341
|
riskLevel,
|
|
368
|
-
allowlistOptions,
|
|
369
|
-
scopeOptions,
|
|
342
|
+
promptOptions.allowlistOptions,
|
|
343
|
+
promptOptions.scopeOptions,
|
|
370
344
|
previewDiff,
|
|
371
|
-
sandboxed,
|
|
372
345
|
context.conversationId,
|
|
373
346
|
executionTarget,
|
|
374
|
-
persistentDecisionsAllowed,
|
|
347
|
+
promptOptions.persistentDecisionsAllowed,
|
|
375
348
|
context.signal,
|
|
376
|
-
temporaryOptionsAvailable,
|
|
349
|
+
promptOptions.temporaryOptionsAvailable,
|
|
377
350
|
context.toolUseId,
|
|
351
|
+
v2ForcePrompt,
|
|
378
352
|
);
|
|
379
353
|
|
|
380
|
-
const decision =
|
|
354
|
+
const decision =
|
|
355
|
+
v2ForcePrompt && !isConversationHostAccessDecision(response.decision)
|
|
356
|
+
? "deny"
|
|
357
|
+
: response.decision;
|
|
381
358
|
|
|
382
359
|
await getHookManager().trigger("permission-resolve", {
|
|
383
360
|
toolName: name,
|
|
384
|
-
decision
|
|
361
|
+
decision,
|
|
385
362
|
riskLevel,
|
|
386
363
|
conversationId: context.conversationId,
|
|
387
364
|
});
|
|
388
365
|
|
|
389
|
-
if (
|
|
366
|
+
if (decision === "deny") {
|
|
390
367
|
const contextualDenial =
|
|
391
368
|
typeof response.decisionContext === "string"
|
|
392
369
|
? response.decisionContext.trim()
|
|
@@ -421,15 +398,15 @@ export class PermissionChecker {
|
|
|
421
398
|
};
|
|
422
399
|
}
|
|
423
400
|
|
|
424
|
-
if (
|
|
401
|
+
if (decision === "always_deny") {
|
|
425
402
|
// For non-scoped tools (empty scopeOptions), default to 'everywhere' since
|
|
426
403
|
// the client has no scope picker and will send undefined.
|
|
427
404
|
const effectiveDenyScope =
|
|
428
|
-
scopeOptions.length === 0
|
|
405
|
+
promptOptions.scopeOptions.length === 0
|
|
429
406
|
? (response.selectedScope ?? "everywhere")
|
|
430
407
|
: response.selectedScope;
|
|
431
408
|
const ruleSaved = !!(
|
|
432
|
-
persistentDecisionsAllowed &&
|
|
409
|
+
promptOptions.persistentDecisionsAllowed &&
|
|
433
410
|
response.selectedPattern &&
|
|
434
411
|
effectiveDenyScope
|
|
435
412
|
);
|
|
@@ -470,9 +447,9 @@ export class PermissionChecker {
|
|
|
470
447
|
}
|
|
471
448
|
|
|
472
449
|
if (
|
|
473
|
-
persistentDecisionsAllowed &&
|
|
474
|
-
(
|
|
475
|
-
|
|
450
|
+
promptOptions.persistentDecisionsAllowed &&
|
|
451
|
+
(decision === "always_allow" ||
|
|
452
|
+
decision === "always_allow_high_risk") &&
|
|
476
453
|
response.selectedPattern
|
|
477
454
|
) {
|
|
478
455
|
const ruleOptions: {
|
|
@@ -480,7 +457,7 @@ export class PermissionChecker {
|
|
|
480
457
|
executionTarget?: string;
|
|
481
458
|
} = {};
|
|
482
459
|
|
|
483
|
-
if (
|
|
460
|
+
if (decision === "always_allow_high_risk") {
|
|
484
461
|
ruleOptions.allowHighRisk = true;
|
|
485
462
|
}
|
|
486
463
|
|
|
@@ -492,7 +469,7 @@ export class PermissionChecker {
|
|
|
492
469
|
// Only default to 'everywhere' for non-scoped tools (empty scopeOptions).
|
|
493
470
|
// For scoped tools, require an explicit scope to prevent silent permission widening.
|
|
494
471
|
const effectiveScope =
|
|
495
|
-
scopeOptions.length === 0
|
|
472
|
+
promptOptions.scopeOptions.length === 0
|
|
496
473
|
? (response.selectedScope ?? "everywhere")
|
|
497
474
|
: response.selectedScope;
|
|
498
475
|
if (effectiveScope) {
|
|
@@ -511,13 +488,13 @@ export class PermissionChecker {
|
|
|
511
488
|
// time-limited or conversation-scoped override. Subsequent tool
|
|
512
489
|
// invocations in this conversation will auto-approve without
|
|
513
490
|
// prompting (checked above in the temporary override block).
|
|
514
|
-
if (
|
|
491
|
+
if (decision === "allow_10m") {
|
|
515
492
|
setTimedMode(context.conversationId);
|
|
516
493
|
log.info(
|
|
517
494
|
{ toolName: name, conversationId: context.conversationId },
|
|
518
495
|
"Activated timed (10m) temporary approval mode",
|
|
519
496
|
);
|
|
520
|
-
} else if (
|
|
497
|
+
} else if (decision === "allow_conversation") {
|
|
521
498
|
setConversationMode(context.conversationId);
|
|
522
499
|
log.info(
|
|
523
500
|
{ toolName: name, conversationId: context.conversationId },
|
package/src/tools/registry.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { hostFileReadTool } from "./host-filesystem/read.js";
|
|
|
8
8
|
import { hostFileWriteTool } from "./host-filesystem/write.js";
|
|
9
9
|
import { hostShellTool } from "./host-terminal/host-shell.js";
|
|
10
10
|
import { registerSystemTools } from "./system/register.js";
|
|
11
|
-
import { setPermissionModeTool } from "./system/set-permission-mode.js";
|
|
12
11
|
import type { Tool } from "./types.js";
|
|
13
12
|
import { allUiSurfaceTools } from "./ui-surface/definitions.js";
|
|
14
13
|
import { registerUiSurfaceTools } from "./ui-surface/registry.js";
|
|
@@ -285,7 +284,6 @@ export async function initializeTools(): Promise<void> {
|
|
|
285
284
|
...allComputerUseTools.map((t: Tool) => t.name),
|
|
286
285
|
...allUiSurfaceTools.map((t: Tool) => t.name),
|
|
287
286
|
...coreAppProxyTools.map((t: Tool) => t.name),
|
|
288
|
-
setPermissionModeTool.name,
|
|
289
287
|
]);
|
|
290
288
|
|
|
291
289
|
coreToolsSnapshot = new Map<string, Tool>();
|
|
@@ -2,6 +2,7 @@ import { getConfig } from "../config/loader.js";
|
|
|
2
2
|
import { getHookManager } from "../hooks/manager.js";
|
|
3
3
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
4
4
|
import { RiskLevel } from "../permissions/types.js";
|
|
5
|
+
import { isPermissionControlsV2Enabled } from "../permissions/v2-consent-policy.js";
|
|
5
6
|
import type { SecretPattern } from "../security/secret-scanner.js";
|
|
6
7
|
import {
|
|
7
8
|
compileCustomPatterns,
|
|
@@ -269,6 +270,39 @@ export class SecretDetectionHandler {
|
|
|
269
270
|
): Promise<{ result: ToolExecutionResult; earlyReturn: boolean }> {
|
|
270
271
|
const types = [...new Set(allMatches.map((m) => m.type))].join(", ");
|
|
271
272
|
|
|
273
|
+
if (isPermissionControlsV2Enabled()) {
|
|
274
|
+
const blockedContent = `Tool output blocked: detected ${allMatches.length} potential secret(s) (${types}). Secret-output approval cards are disabled under v2. Ask the user for confirmation conversationally before retrying.`;
|
|
275
|
+
const durationMs = Date.now() - startTime;
|
|
276
|
+
|
|
277
|
+
emitLifecycleEvent(context, {
|
|
278
|
+
type: "permission_denied",
|
|
279
|
+
toolName: name,
|
|
280
|
+
executionTarget,
|
|
281
|
+
input,
|
|
282
|
+
workingDir: context.workingDir,
|
|
283
|
+
conversationId: context.conversationId,
|
|
284
|
+
requestId: context.requestId,
|
|
285
|
+
riskLevel: RiskLevel.High,
|
|
286
|
+
decision: "deny",
|
|
287
|
+
reason: "Secret output blocked without deterministic prompt under v2",
|
|
288
|
+
durationMs,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
void getHookManager().trigger("post-tool-execute", {
|
|
292
|
+
toolName: name,
|
|
293
|
+
input: sanitizeToolInput(name, input),
|
|
294
|
+
riskLevel,
|
|
295
|
+
isError: true,
|
|
296
|
+
durationMs,
|
|
297
|
+
conversationId: context.conversationId,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
result: { content: blockedContent, isError: true },
|
|
302
|
+
earlyReturn: true,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
272
306
|
// Non-interactive sessions: auto-block secret output instead of waiting for prompt
|
|
273
307
|
if (context.isInteractive === false) {
|
|
274
308
|
const blockedContent = `Tool output blocked: detected ${allMatches.length} potential secret(s) (${types}). No interactive client available to approve.`;
|
|
@@ -331,7 +365,6 @@ export class SecretDetectionHandler {
|
|
|
331
365
|
[], // no allowlist options
|
|
332
366
|
[], // no scope options
|
|
333
367
|
undefined, // no diff
|
|
334
|
-
undefined, // not sandboxed
|
|
335
368
|
context.conversationId,
|
|
336
369
|
executionTarget,
|
|
337
370
|
false, // no persistent decisions
|
|
@@ -61,63 +61,37 @@ function detectMediaType(buf: Buffer): string | null {
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
*/
|
|
71
|
-
export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
72
|
-
let stat;
|
|
73
|
-
try {
|
|
74
|
-
stat = statSync(resolvedPath);
|
|
75
|
-
} catch {
|
|
76
|
-
return {
|
|
77
|
-
content: `Error: file not found: ${resolvedPath}`,
|
|
78
|
-
isError: true,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!stat.isFile()) {
|
|
83
|
-
return { content: `Error: ${resolvedPath} is not a file`, isError: true };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (stat.size > MAX_SOURCE_SIZE_BYTES) {
|
|
87
|
-
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
64
|
+
function buildImageToolResult(
|
|
65
|
+
buffer: Buffer,
|
|
66
|
+
sourceLabel: string,
|
|
67
|
+
): ToolExecutionResult {
|
|
68
|
+
if (buffer.length > MAX_SOURCE_SIZE_BYTES) {
|
|
69
|
+
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
88
70
|
return {
|
|
89
71
|
content: `Error: image too large (${sizeMB} MB). Maximum source file size is 100 MB.`,
|
|
90
72
|
isError: true,
|
|
91
73
|
};
|
|
92
74
|
}
|
|
93
75
|
|
|
94
|
-
let buffer: Buffer;
|
|
95
|
-
try {
|
|
96
|
-
buffer = readFileSync(resolvedPath) as Buffer;
|
|
97
|
-
} catch (err) {
|
|
98
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
-
return { content: `Error reading file: ${msg}`, isError: true };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
76
|
// Detect actual format from magic bytes - never trust the file extension
|
|
103
77
|
// alone, since sips converts to JPEG and files can be misnamed.
|
|
104
78
|
const detectedType = detectMediaType(buffer);
|
|
105
79
|
if (!detectedType) {
|
|
106
80
|
return {
|
|
107
|
-
content: `Error: could not detect image format for ${
|
|
81
|
+
content: `Error: could not detect image format for ${sourceLabel}. The file may be corrupt.`,
|
|
108
82
|
isError: true,
|
|
109
83
|
};
|
|
110
84
|
}
|
|
111
85
|
|
|
112
86
|
// Optimize before size-checking — oversized images may compress under the limit.
|
|
113
87
|
const rawBase64 = buffer.toString("base64");
|
|
114
|
-
const { data: base64Data, mediaType: finalType } =
|
|
115
|
-
|
|
88
|
+
const { data: base64Data, mediaType: finalType } = optimizeImageForTransport(
|
|
89
|
+
rawBase64,
|
|
90
|
+
detectedType,
|
|
91
|
+
);
|
|
116
92
|
const optimized = base64Data !== rawBase64;
|
|
117
93
|
|
|
118
|
-
const optimizedBytes =
|
|
119
|
-
? Math.ceil((base64Data.length * 3) / 4)
|
|
120
|
-
: buffer.length;
|
|
94
|
+
const optimizedBytes = Buffer.from(base64Data, "base64").length;
|
|
121
95
|
if (optimizedBytes > MAX_SIZE_BYTES) {
|
|
122
96
|
const sizeMB = (optimizedBytes / (1024 * 1024)).toFixed(1);
|
|
123
97
|
return {
|
|
@@ -136,14 +110,61 @@ export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
|
136
110
|
};
|
|
137
111
|
|
|
138
112
|
const sizeSuffix = optimized
|
|
139
|
-
? ` (optimized from ${(
|
|
113
|
+
? ` (optimized from ${(buffer.length / 1024).toFixed(0)} KB to ${(
|
|
140
114
|
optimizedBytes / 1024
|
|
141
115
|
).toFixed(0)} KB)`
|
|
142
116
|
: "";
|
|
143
117
|
|
|
144
118
|
return {
|
|
145
|
-
content: `Image loaded: ${
|
|
119
|
+
content: `Image loaded: ${sourceLabel} (${optimizedBytes} bytes, ${finalType})${sizeSuffix}`,
|
|
146
120
|
isError: false,
|
|
147
121
|
contentBlocks: [imageBlock],
|
|
148
122
|
};
|
|
149
123
|
}
|
|
124
|
+
|
|
125
|
+
export function readImageBase64(
|
|
126
|
+
base64Data: string,
|
|
127
|
+
sourceLabel: string,
|
|
128
|
+
): ToolExecutionResult {
|
|
129
|
+
return buildImageToolResult(Buffer.from(base64Data, "base64"), sourceLabel);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Read an image file from disk, optionally optimize it, and return a
|
|
134
|
+
* ToolExecutionResult with base64-encoded image content blocks.
|
|
135
|
+
*
|
|
136
|
+
* The caller is responsible for path resolution and sandbox enforcement -
|
|
137
|
+
* `resolvedPath` must be an already-validated absolute path.
|
|
138
|
+
*/
|
|
139
|
+
export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
140
|
+
let stat;
|
|
141
|
+
try {
|
|
142
|
+
stat = statSync(resolvedPath);
|
|
143
|
+
} catch {
|
|
144
|
+
return {
|
|
145
|
+
content: `Error: file not found: ${resolvedPath}`,
|
|
146
|
+
isError: true,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!stat.isFile()) {
|
|
151
|
+
return { content: `Error: ${resolvedPath} is not a file`, isError: true };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (stat.size > MAX_SOURCE_SIZE_BYTES) {
|
|
155
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
156
|
+
return {
|
|
157
|
+
content: `Error: image too large (${sizeMB} MB). Maximum source file size is 100 MB.`,
|
|
158
|
+
isError: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let buffer: Buffer;
|
|
163
|
+
try {
|
|
164
|
+
buffer = readFileSync(resolvedPath) as Buffer;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
167
|
+
return { content: `Error reading file: ${msg}`, isError: true };
|
|
168
|
+
}
|
|
169
|
+
return buildImageToolResult(buffer, resolvedPath);
|
|
170
|
+
}
|
|
@@ -3,7 +3,6 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
|
|
6
|
-
import { getConfig } from "../../config/loader.js";
|
|
7
6
|
import { computeSkillVersionHash } from "../../skills/version-hash.js";
|
|
8
7
|
import { buildSanitizedEnv } from "../terminal/safe-env.js";
|
|
9
8
|
import { wrapCommand } from "../terminal/sandbox.js";
|
|
@@ -138,11 +137,9 @@ function spawnRunner(
|
|
|
138
137
|
const stderrChunks: Buffer[] = [];
|
|
139
138
|
let timedOut = false;
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
? { ...config.sandbox, enabled: context.sandboxOverride }
|
|
145
|
-
: config.sandbox;
|
|
140
|
+
// The assistant runs exclusively in Docker or platform-managed
|
|
141
|
+
// environments where the container provides isolation.
|
|
142
|
+
const sandboxConfig = { enabled: false } as const;
|
|
146
143
|
|
|
147
144
|
const bunRunCmd = "bun run __skill_runner.ts";
|
|
148
145
|
const wrapped = wrapCommand(bunRunCmd, runDir, sandboxConfig);
|
|
@@ -8,9 +8,15 @@ export async function executeSubagentSpawn(
|
|
|
8
8
|
const label = input.label as string;
|
|
9
9
|
const objective = input.objective as string;
|
|
10
10
|
const extraContext = input.context as string | undefined;
|
|
11
|
-
const
|
|
11
|
+
const fork = input.fork === true;
|
|
12
12
|
const role = (input.role as string | undefined) ?? undefined;
|
|
13
13
|
|
|
14
|
+
// For fork mode, sendResultToUser defaults to false unless explicitly set to true.
|
|
15
|
+
// For regular mode, sendResultToUser defaults to true (existing behavior).
|
|
16
|
+
const sendResultToUser = fork
|
|
17
|
+
? input.send_result_to_user === true
|
|
18
|
+
: input.send_result_to_user !== false;
|
|
19
|
+
|
|
14
20
|
if (!label || !objective) {
|
|
15
21
|
return {
|
|
16
22
|
content: 'Both "label" and "objective" are required.',
|
|
@@ -29,6 +35,36 @@ export async function executeSubagentSpawn(
|
|
|
29
35
|
};
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
// ── Fork mode: resolve parent context ────────────────────────────
|
|
39
|
+
let forkFields: {
|
|
40
|
+
fork: true;
|
|
41
|
+
parentMessages: import("../../providers/types.js").Message[];
|
|
42
|
+
parentSystemPrompt: string;
|
|
43
|
+
} | undefined;
|
|
44
|
+
|
|
45
|
+
if (fork) {
|
|
46
|
+
const parentConversation = manager.resolveParentConversation?.(
|
|
47
|
+
context.conversationId,
|
|
48
|
+
);
|
|
49
|
+
if (!parentConversation) {
|
|
50
|
+
return {
|
|
51
|
+
content:
|
|
52
|
+
"Cannot fork: parent conversation could not be resolved. " +
|
|
53
|
+
"This may happen if the conversation was evicted or the resolveParentConversation callback is not wired.",
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const parentMessages = [...parentConversation.messages];
|
|
59
|
+
const parentSystemPrompt = parentConversation.getCurrentSystemPrompt();
|
|
60
|
+
|
|
61
|
+
forkFields = {
|
|
62
|
+
fork: true,
|
|
63
|
+
parentMessages,
|
|
64
|
+
parentSystemPrompt,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
32
68
|
try {
|
|
33
69
|
const subagentId = await manager.spawn(
|
|
34
70
|
{
|
|
@@ -37,7 +73,12 @@ export async function executeSubagentSpawn(
|
|
|
37
73
|
objective,
|
|
38
74
|
context: extraContext,
|
|
39
75
|
sendResultToUser,
|
|
40
|
-
|
|
76
|
+
// For fork mode, role is ignored by the manager (forced to general),
|
|
77
|
+
// but we still omit it from the config to signal intent.
|
|
78
|
+
...(!fork && role
|
|
79
|
+
? { role: role as import("../../subagent/types.js").SubagentRole }
|
|
80
|
+
: {}),
|
|
81
|
+
...forkFields,
|
|
41
82
|
},
|
|
42
83
|
sendToClient as (msg: unknown) => void,
|
|
43
84
|
);
|
|
@@ -47,7 +88,10 @@ export async function executeSubagentSpawn(
|
|
|
47
88
|
subagentId,
|
|
48
89
|
label,
|
|
49
90
|
status: "pending",
|
|
50
|
-
|
|
91
|
+
...(fork ? { isFork: true } : {}),
|
|
92
|
+
message: fork
|
|
93
|
+
? `Forked subagent "${label}" spawned with full parent context. You will be notified automatically when it completes or fails - do NOT poll subagent_status. Continue the conversation normally.`
|
|
94
|
+
: `Subagent "${label}" spawned. You will be notified automatically when it completes or fails - do NOT poll subagent_status. Continue the conversation normally.`,
|
|
51
95
|
}),
|
|
52
96
|
isError: false,
|
|
53
97
|
};
|