@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
|
@@ -81,6 +81,8 @@ export interface HistorySurface {
|
|
|
81
81
|
data: Record<string, unknown>;
|
|
82
82
|
actions?: Array<{ id: string; label: string; style?: string }>;
|
|
83
83
|
display?: string;
|
|
84
|
+
completed?: boolean;
|
|
85
|
+
completionSummary?: string;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
export interface RenderedHistoryContent {
|
|
@@ -108,6 +110,11 @@ export interface ConversationCreateOptions {
|
|
|
108
110
|
transport?: ConversationTransportMetadata;
|
|
109
111
|
assistantId?: string;
|
|
110
112
|
trustContext?: TrustContext;
|
|
113
|
+
/**
|
|
114
|
+
* Active task-run scope for this turn. Cleared when omitted so background
|
|
115
|
+
* task permissions do not leak into later turns on a reused conversation.
|
|
116
|
+
*/
|
|
117
|
+
taskRunId?: string;
|
|
111
118
|
/** Normalized auth context for the conversation. */
|
|
112
119
|
authContext?: AuthContext;
|
|
113
120
|
/** Whether this turn can block on interactive approval prompts. */
|
|
@@ -281,6 +288,11 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
281
288
|
: {},
|
|
282
289
|
actions: Array.isArray(block.actions) ? block.actions : undefined,
|
|
283
290
|
display: typeof block.display === "string" ? block.display : undefined,
|
|
291
|
+
completed: block.completed === true ? true : undefined,
|
|
292
|
+
completionSummary:
|
|
293
|
+
typeof block.completionSummary === "string"
|
|
294
|
+
? block.completionSummary
|
|
295
|
+
: undefined,
|
|
284
296
|
};
|
|
285
297
|
surfaces.push(surface);
|
|
286
298
|
contentOrder.push(`surface:${surfaces.length - 1}`);
|
|
@@ -337,6 +349,59 @@ export function renderHistoryContent(content: unknown): RenderedHistoryContent {
|
|
|
337
349
|
}
|
|
338
350
|
continue;
|
|
339
351
|
}
|
|
352
|
+
if (block.type === "server_tool_use") {
|
|
353
|
+
finalizeSegment();
|
|
354
|
+
const name = typeof block.name === "string" ? block.name : "unknown";
|
|
355
|
+
const input = isRecord(block.input)
|
|
356
|
+
? (block.input as Record<string, unknown>)
|
|
357
|
+
: {};
|
|
358
|
+
const id = typeof block.id === "string" ? block.id : "";
|
|
359
|
+
const entry: HistoryToolCall = { name, input };
|
|
360
|
+
toolCalls.push(entry);
|
|
361
|
+
if (id) pendingToolUses.set(id, entry);
|
|
362
|
+
contentOrder.push(`tool:${toolCalls.length - 1}`);
|
|
363
|
+
if (!seenToolUse) {
|
|
364
|
+
seenToolUse = true;
|
|
365
|
+
if (!seenText) toolCallsBeforeText = true;
|
|
366
|
+
}
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (block.type === "web_search_tool_result") {
|
|
370
|
+
const toolUseId =
|
|
371
|
+
typeof block.tool_use_id === "string" ? block.tool_use_id : "";
|
|
372
|
+
const isError =
|
|
373
|
+
isRecord(block.content) &&
|
|
374
|
+
(block.content as { type?: string }).type ===
|
|
375
|
+
"web_search_tool_result_error";
|
|
376
|
+
|
|
377
|
+
// Format search results into readable text.
|
|
378
|
+
let resultContent = "";
|
|
379
|
+
if (Array.isArray(block.content)) {
|
|
380
|
+
resultContent = (block.content as unknown[])
|
|
381
|
+
.filter(
|
|
382
|
+
(r): r is { type: string; title: string; url: string } =>
|
|
383
|
+
typeof r === "object" &&
|
|
384
|
+
r != null &&
|
|
385
|
+
(r as { type?: string }).type === "web_search_result",
|
|
386
|
+
)
|
|
387
|
+
.map((r) => `${r.title}\n${r.url}`)
|
|
388
|
+
.join("\n\n");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const matched = toolUseId ? pendingToolUses.get(toolUseId) : null;
|
|
392
|
+
if (matched) {
|
|
393
|
+
matched.result = resultContent;
|
|
394
|
+
matched.isError = isError;
|
|
395
|
+
} else {
|
|
396
|
+
toolCalls.push({
|
|
397
|
+
name: "web_search",
|
|
398
|
+
input: {},
|
|
399
|
+
result: resultContent,
|
|
400
|
+
isError,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
340
405
|
if (block.type === "tool_result") {
|
|
341
406
|
const toolUseId =
|
|
342
407
|
typeof block.tool_use_id === "string" ? block.tool_use_id : "";
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
statSync,
|
|
10
10
|
} from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
|
-
import { join, relative } from "node:path";
|
|
12
|
+
import { basename, join, relative, sep } from "node:path";
|
|
13
13
|
|
|
14
14
|
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
15
15
|
import {
|
|
@@ -31,9 +31,21 @@ import {
|
|
|
31
31
|
getConfiguredProvider,
|
|
32
32
|
userMessage,
|
|
33
33
|
} from "../../providers/provider-send-message.js";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
isTextMimeType as isTextMime,
|
|
36
|
+
MAX_INLINE_TEXT_SIZE,
|
|
37
|
+
} from "../../runtime/routes/workspace-utils.js";
|
|
35
38
|
import { getCatalog } from "../../skills/catalog-cache.js";
|
|
36
39
|
import {
|
|
40
|
+
hasHiddenOrSkippedSegment,
|
|
41
|
+
readCatalogSkillFileContent,
|
|
42
|
+
readCatalogSkillFiles,
|
|
43
|
+
sanitizeRelativePath,
|
|
44
|
+
type SkillFileEntry,
|
|
45
|
+
SKIP_DIRS,
|
|
46
|
+
} from "../../skills/catalog-files.js";
|
|
47
|
+
import {
|
|
48
|
+
type CatalogSkill,
|
|
37
49
|
installSkillLocally,
|
|
38
50
|
upsertSkillsIndex,
|
|
39
51
|
} from "../../skills/catalog-install.js";
|
|
@@ -64,6 +76,7 @@ import {
|
|
|
64
76
|
import { getWorkspaceSkillsDir } from "../../util/platform.js";
|
|
65
77
|
import type {
|
|
66
78
|
SkillDetailResponse,
|
|
79
|
+
SkillFileContentResponse,
|
|
67
80
|
SlimSkillResponse,
|
|
68
81
|
} from "../message-types/skills.js";
|
|
69
82
|
import {
|
|
@@ -73,10 +86,6 @@ import {
|
|
|
73
86
|
log,
|
|
74
87
|
} from "./shared.js";
|
|
75
88
|
|
|
76
|
-
// ─── MIME detection helpers ───────────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
const MAX_INLINE_SIZE = 2 * 1024 * 1024; // 2 MB
|
|
79
|
-
|
|
80
89
|
// ─── Shared context for standalone functions ─────────────────────────────────
|
|
81
90
|
|
|
82
91
|
/**
|
|
@@ -364,7 +373,7 @@ export async function listSkillsWithCatalog(
|
|
|
364
373
|
const installed = listSkills(ctx);
|
|
365
374
|
const installedIds = new Set(installed.map((s) => s.id));
|
|
366
375
|
|
|
367
|
-
let catalogSkills:
|
|
376
|
+
let catalogSkills: CatalogSkill[];
|
|
368
377
|
try {
|
|
369
378
|
catalogSkills = await getCatalog();
|
|
370
379
|
} catch {
|
|
@@ -376,15 +385,7 @@ export async function listSkillsWithCatalog(
|
|
|
376
385
|
// Create SlimSkillResponses for catalog skills not already installed.
|
|
377
386
|
const available: SlimSkillResponse[] = catalogSkills
|
|
378
387
|
.filter((cs) => !installedIds.has(cs.id))
|
|
379
|
-
.map((cs) => (
|
|
380
|
-
id: cs.id,
|
|
381
|
-
name: cs.metadata?.vellum?.["display-name"] ?? cs.name,
|
|
382
|
-
description: cs.description,
|
|
383
|
-
emoji: cs.emoji,
|
|
384
|
-
kind: "catalog" as const,
|
|
385
|
-
origin: "vellum" as const,
|
|
386
|
-
status: "available" as const,
|
|
387
|
-
}));
|
|
388
|
+
.map((cs) => catalogSkillToSlim(cs));
|
|
388
389
|
|
|
389
390
|
const merged = [...installed, ...available];
|
|
390
391
|
|
|
@@ -494,16 +495,12 @@ export async function getSkill(
|
|
|
494
495
|
|
|
495
496
|
// ─── Skill file listing ──────────────────────────────────────────────────────
|
|
496
497
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
content: string | null; // inline text if ≤ 2 MB and text MIME, else null
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const SKIP_DIRS = new Set(["node_modules", "__pycache__", ".git"]);
|
|
498
|
+
// `SkillFileEntry` lives in `../../skills/catalog-files.ts` to keep a single
|
|
499
|
+
// source of truth for the shape and avoid a circular import (catalog-files
|
|
500
|
+
// depends on `catalog-cache.ts`, which would otherwise be reachable via this
|
|
501
|
+
// handler module). Re-exported here so handlers can import it alongside
|
|
502
|
+
// the other skill handler exports.
|
|
503
|
+
export type { SkillFileEntry } from "../../skills/catalog-files.js";
|
|
507
504
|
|
|
508
505
|
/**
|
|
509
506
|
* Returns true if `filePath` is a symlink whose resolved real path escapes
|
|
@@ -550,7 +547,7 @@ function readDirRecursive(dir: string, rootDir: string): SkillFileEntry[] {
|
|
|
550
547
|
const mimeType = Bun.file(fullPath).type;
|
|
551
548
|
const isText = isTextMime(mimeType, dirent.name);
|
|
552
549
|
let content: string | null = null;
|
|
553
|
-
if (isText && stat.size <=
|
|
550
|
+
if (isText && stat.size <= MAX_INLINE_TEXT_SIZE) {
|
|
554
551
|
content = readFileSync(fullPath, "utf-8");
|
|
555
552
|
}
|
|
556
553
|
entries.push({
|
|
@@ -568,26 +565,224 @@ function readDirRecursive(dir: string, rootDir: string): SkillFileEntry[] {
|
|
|
568
565
|
return entries;
|
|
569
566
|
}
|
|
570
567
|
|
|
571
|
-
|
|
568
|
+
/**
|
|
569
|
+
* Map a `CatalogSkill` (from the Vellum platform API) to a `SlimSkillResponse`
|
|
570
|
+
* shaped for the "available catalog skill" case. Shared between
|
|
571
|
+
* `listSkillsWithCatalog` (merging catalog entries into the installed list)
|
|
572
|
+
* and `getSkillFiles` (catalog fallback for preview listings). Keeping the
|
|
573
|
+
* mapping in one place avoids divergence between the list and detail paths.
|
|
574
|
+
*/
|
|
575
|
+
function catalogSkillToSlim(cs: CatalogSkill): SlimSkillResponse {
|
|
576
|
+
return {
|
|
577
|
+
id: cs.id,
|
|
578
|
+
name: cs.metadata?.vellum?.["display-name"] ?? cs.name,
|
|
579
|
+
description: cs.description,
|
|
580
|
+
emoji: cs.emoji,
|
|
581
|
+
kind: "catalog",
|
|
582
|
+
origin: "vellum",
|
|
583
|
+
status: "available",
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Read a single file's content from an installed or catalog skill.
|
|
589
|
+
*
|
|
590
|
+
* Installed-skill path (eager): reads the file directly from the skill's
|
|
591
|
+
* on-disk directory. Applies lexical containment, symlink rejection, and
|
|
592
|
+
* realpath containment checks for defense in depth.
|
|
593
|
+
*
|
|
594
|
+
* Catalog fallback: when the skill id is not backed by a local directory
|
|
595
|
+
* (e.g. an uninstalled Vellum catalog skill), delegates to
|
|
596
|
+
* `readCatalogSkillFileContent`, which handles both the dev-mode repo
|
|
597
|
+
* checkout path and the platform preview API path internally.
|
|
598
|
+
*/
|
|
599
|
+
export async function getSkillFileContent(
|
|
572
600
|
skillId: string,
|
|
601
|
+
relativePath: string,
|
|
573
602
|
_ctx: SkillOperationContext,
|
|
574
|
-
):
|
|
603
|
+
): Promise<SkillFileContentResponse | { error: string; status: number }> {
|
|
604
|
+
const sanitized = sanitizeRelativePath(relativePath);
|
|
605
|
+
if (!sanitized) {
|
|
606
|
+
return { error: "Invalid path", status: 400 };
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Reject any sanitized path that references a hidden segment (dotfiles
|
|
610
|
+
// like `.env`, dot-dirs like `.git`) or a SKIP_DIRS segment (e.g.
|
|
611
|
+
// `node_modules`, `__pycache__`). Both file-listing endpoints (installed
|
|
612
|
+
// and catalog) intentionally omit these entries, so allowing the content
|
|
613
|
+
// endpoint to read them would create a data-exposure path and break
|
|
614
|
+
// parity with the visible file list. This check runs BEFORE both the
|
|
615
|
+
// installed-skill disk read and the catalog fallback so the rejection
|
|
616
|
+
// is uniform regardless of source.
|
|
617
|
+
if (hasHiddenOrSkippedSegment(sanitized)) {
|
|
618
|
+
return { error: "Invalid path", status: 400 };
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const found = findSkillById(skillId);
|
|
622
|
+
if (found) {
|
|
623
|
+
if (!existsSync(found.summary.directoryPath)) {
|
|
624
|
+
// Resolver lists the skill as installed but the directory is missing
|
|
625
|
+
// on disk (corrupted install, mid-delete race, external unmount, etc.).
|
|
626
|
+
// Return a distinct 404 instead of falling through to the catalog path
|
|
627
|
+
// so the content response stays consistent with `listSkillsWithCatalog`
|
|
628
|
+
// and `getSkillFiles`, which classify the same id as `kind: "installed"`.
|
|
629
|
+
return {
|
|
630
|
+
error: `Skill directory missing for "${skillId}"`,
|
|
631
|
+
status: 404,
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
const dir = found.summary.directoryPath;
|
|
635
|
+
const abs = join(dir, sanitized);
|
|
636
|
+
|
|
637
|
+
// Lexical containment: the resolved absolute path must stay inside the
|
|
638
|
+
// skill directory even after `join` normalization. Cheap short-circuit
|
|
639
|
+
// before any fs calls.
|
|
640
|
+
if (!(abs === dir || abs.startsWith(dir + sep))) {
|
|
641
|
+
return { error: "Invalid path", status: 400 };
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Defense-in-depth symlink rejection: refuse to follow a symlinked file
|
|
645
|
+
// inside the skill dir that could point outside the root. Also catches
|
|
646
|
+
// symlinked parent directories via a realpath containment check.
|
|
647
|
+
let lstat;
|
|
648
|
+
try {
|
|
649
|
+
lstat = lstatSync(abs);
|
|
650
|
+
} catch {
|
|
651
|
+
return { error: "File not found", status: 404 };
|
|
652
|
+
}
|
|
653
|
+
if (lstat.isSymbolicLink()) {
|
|
654
|
+
return { error: "File not found", status: 404 };
|
|
655
|
+
}
|
|
656
|
+
if (!lstat.isFile()) {
|
|
657
|
+
return { error: "File not found", status: 404 };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
let realAbs: string;
|
|
661
|
+
let realDir: string;
|
|
662
|
+
try {
|
|
663
|
+
realAbs = realpathSync(abs);
|
|
664
|
+
realDir = realpathSync(dir);
|
|
665
|
+
} catch {
|
|
666
|
+
return { error: "File not found", status: 404 };
|
|
667
|
+
}
|
|
668
|
+
if (!(realAbs === realDir || realAbs.startsWith(realDir + sep))) {
|
|
669
|
+
return { error: "File not found", status: 404 };
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
let stat;
|
|
673
|
+
try {
|
|
674
|
+
stat = statSync(abs);
|
|
675
|
+
} catch {
|
|
676
|
+
return { error: "File not found", status: 404 };
|
|
677
|
+
}
|
|
678
|
+
if (!stat.isFile()) {
|
|
679
|
+
return { error: "File not found", status: 404 };
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const name = basename(sanitized);
|
|
683
|
+
const mimeType = Bun.file(abs).type;
|
|
684
|
+
const isText = isTextMime(mimeType, name);
|
|
685
|
+
const isBinary = !isText;
|
|
686
|
+
let content: string | null = null;
|
|
687
|
+
if (isText && stat.size <= MAX_INLINE_TEXT_SIZE) {
|
|
688
|
+
try {
|
|
689
|
+
content = readFileSync(abs, "utf-8");
|
|
690
|
+
} catch {
|
|
691
|
+
content = null;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
path: sanitized,
|
|
696
|
+
name,
|
|
697
|
+
size: stat.size,
|
|
698
|
+
mimeType,
|
|
699
|
+
isBinary,
|
|
700
|
+
content,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Catalog fallback: skill is not installed locally. Try the catalog
|
|
705
|
+
// preview helper, which handles both dev-mode repo checkouts and the
|
|
706
|
+
// platform preview API.
|
|
707
|
+
let catalog: Awaited<ReturnType<typeof getCatalog>> = [];
|
|
708
|
+
try {
|
|
709
|
+
catalog = await getCatalog();
|
|
710
|
+
} catch {
|
|
711
|
+
catalog = [];
|
|
712
|
+
}
|
|
713
|
+
const inCatalog = catalog.some((s) => s.id === skillId);
|
|
714
|
+
if (!inCatalog) {
|
|
715
|
+
return { error: "Skill not found", status: 404 };
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const result = await readCatalogSkillFileContent(skillId, sanitized);
|
|
719
|
+
if (!result) {
|
|
720
|
+
return { error: "File not found", status: 404 };
|
|
721
|
+
}
|
|
722
|
+
return {
|
|
723
|
+
path: result.path,
|
|
724
|
+
name: result.name,
|
|
725
|
+
size: result.size,
|
|
726
|
+
mimeType: result.mimeType,
|
|
727
|
+
isBinary: result.isBinary,
|
|
728
|
+
content: result.content,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export async function getSkillFiles(
|
|
733
|
+
skillId: string,
|
|
734
|
+
_ctx: SkillOperationContext,
|
|
735
|
+
): Promise<
|
|
575
736
|
| { skill: SlimSkillResponse; files: SkillFileEntry[] }
|
|
576
|
-
| { error: string; status: number }
|
|
737
|
+
| { error: string; status: number }
|
|
738
|
+
> {
|
|
739
|
+
// Preferred path: the skill is resolved locally (bundled, managed,
|
|
740
|
+
// workspace, or extra) AND its directory exists on disk. Read files
|
|
741
|
+
// eagerly with inline content.
|
|
577
742
|
const found = findSkillById(skillId);
|
|
578
|
-
if (
|
|
743
|
+
if (found) {
|
|
744
|
+
if (existsSync(found.summary.directoryPath)) {
|
|
745
|
+
const dirPath = found.summary.directoryPath;
|
|
746
|
+
const files = readDirRecursive(dirPath, dirPath);
|
|
747
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
748
|
+
return { skill: found.item, files };
|
|
749
|
+
}
|
|
750
|
+
// Resolver lists the skill as installed but the directory is missing
|
|
751
|
+
// on disk (corrupted install, mid-delete race, external unmount, etc.).
|
|
752
|
+
// Return a distinct 404 instead of falling through to the catalog path
|
|
753
|
+
// so the detail response stays consistent with `listSkillsWithCatalog`,
|
|
754
|
+
// which classifies the same id as `kind: "installed"`.
|
|
755
|
+
return {
|
|
756
|
+
error: `Skill directory missing for "${skillId}"`,
|
|
757
|
+
status: 404,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Fallback: skill is not installed. Try the Vellum catalog — this covers
|
|
762
|
+
// previewing files for an uninstalled catalog skill without touching the
|
|
763
|
+
// install flow.
|
|
764
|
+
let catalog: CatalogSkill[];
|
|
765
|
+
try {
|
|
766
|
+
catalog = await getCatalog();
|
|
767
|
+
} catch {
|
|
768
|
+
return { error: `Skill "${skillId}" not found`, status: 404 };
|
|
769
|
+
}
|
|
770
|
+
const cs = catalog.find((c) => c.id === skillId);
|
|
771
|
+
if (!cs) {
|
|
579
772
|
return { error: `Skill "${skillId}" not found`, status: 404 };
|
|
580
773
|
}
|
|
581
774
|
|
|
582
|
-
const
|
|
583
|
-
if (
|
|
584
|
-
return {
|
|
775
|
+
const files = await readCatalogSkillFiles(skillId);
|
|
776
|
+
if (files === null) {
|
|
777
|
+
return {
|
|
778
|
+
error: `Skill files unavailable for "${skillId}"`,
|
|
779
|
+
status: 404,
|
|
780
|
+
};
|
|
585
781
|
}
|
|
586
782
|
|
|
587
|
-
const
|
|
783
|
+
const skill = catalogSkillToSlim(cs);
|
|
588
784
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
589
|
-
|
|
590
|
-
return { skill: found.item, files };
|
|
785
|
+
return { skill, files };
|
|
591
786
|
}
|
|
592
787
|
|
|
593
788
|
export function enableSkill(
|
|
@@ -14,6 +14,8 @@ interface PendingRequest {
|
|
|
14
14
|
reject: (err: Error) => void;
|
|
15
15
|
timer: ReturnType<typeof setTimeout>;
|
|
16
16
|
timeoutSec: number;
|
|
17
|
+
/** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
|
|
18
|
+
detachAbort: () => void;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export class HostBashProxy {
|
|
@@ -60,8 +62,14 @@ export class HostBashProxy {
|
|
|
60
62
|
const timeoutSec = input.timeout_seconds ?? shellMaxTimeoutSec;
|
|
61
63
|
// Proxy timeout: slightly after client-side timeout, but before executor's outer timeout
|
|
62
64
|
const proxyTimeoutSec = timeoutSec + 3;
|
|
65
|
+
|
|
66
|
+
// Declared up-front so onAbort (defined before detachAbort is assigned)
|
|
67
|
+
// can close over a stable reference once it's wired below.
|
|
68
|
+
let detachAbort: () => void = () => {};
|
|
69
|
+
|
|
63
70
|
const timer = setTimeout(() => {
|
|
64
71
|
this.pending.delete(requestId);
|
|
72
|
+
detachAbort();
|
|
65
73
|
this.onInternalResolve?.(requestId);
|
|
66
74
|
log.warn(
|
|
67
75
|
{ requestId, command: input.command },
|
|
@@ -78,13 +86,14 @@ export class HostBashProxy {
|
|
|
78
86
|
);
|
|
79
87
|
}, proxyTimeoutSec * 1000);
|
|
80
88
|
|
|
81
|
-
this.pending.set(requestId, { resolve, reject, timer, timeoutSec });
|
|
82
|
-
|
|
83
89
|
if (signal) {
|
|
84
90
|
const onAbort = () => {
|
|
85
91
|
if (this.pending.has(requestId)) {
|
|
86
92
|
clearTimeout(timer);
|
|
87
93
|
this.pending.delete(requestId);
|
|
94
|
+
// Abort fired — nothing to detach, but call the no-op for symmetry
|
|
95
|
+
// so callers can rely on detachAbort being idempotent.
|
|
96
|
+
detachAbort();
|
|
88
97
|
this.onInternalResolve?.(requestId);
|
|
89
98
|
try {
|
|
90
99
|
this.sendToClient({
|
|
@@ -98,19 +107,43 @@ export class HostBashProxy {
|
|
|
98
107
|
}
|
|
99
108
|
};
|
|
100
109
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
110
|
+
detachAbort = () => signal.removeEventListener("abort", onAbort);
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
this.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
this.pending.set(requestId, {
|
|
114
|
+
resolve,
|
|
115
|
+
reject,
|
|
116
|
+
timer,
|
|
117
|
+
timeoutSec,
|
|
118
|
+
detachAbort,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
this.sendToClient({
|
|
123
|
+
type: "host_bash_request",
|
|
124
|
+
requestId,
|
|
125
|
+
conversationId,
|
|
126
|
+
command: input.command,
|
|
127
|
+
working_dir: input.working_dir,
|
|
128
|
+
timeout_seconds: input.timeout_seconds,
|
|
129
|
+
...(input.env && Object.keys(input.env).length > 0
|
|
130
|
+
? { env: input.env }
|
|
131
|
+
: {}),
|
|
132
|
+
} as ServerMessage);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
// Sender threw synchronously (e.g. client transport error during
|
|
135
|
+
// event emission). Clean up pending state and timer so we don't
|
|
136
|
+
// leak an in-flight entry that nothing will ever resolve.
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
this.pending.delete(requestId);
|
|
139
|
+
detachAbort();
|
|
140
|
+
this.onInternalResolve?.(requestId);
|
|
141
|
+
log.warn(
|
|
142
|
+
{ requestId, command: input.command, err },
|
|
143
|
+
"Host bash proxy send failed",
|
|
144
|
+
);
|
|
145
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
146
|
+
}
|
|
114
147
|
});
|
|
115
148
|
}
|
|
116
149
|
|
|
@@ -129,6 +162,7 @@ export class HostBashProxy {
|
|
|
129
162
|
return;
|
|
130
163
|
}
|
|
131
164
|
clearTimeout(entry.timer);
|
|
165
|
+
entry.detachAbort();
|
|
132
166
|
this.pending.delete(requestId);
|
|
133
167
|
const result = formatShellOutput(
|
|
134
168
|
response.stdout,
|
|
@@ -151,6 +185,7 @@ export class HostBashProxy {
|
|
|
151
185
|
dispose(): void {
|
|
152
186
|
for (const [requestId, entry] of this.pending) {
|
|
153
187
|
clearTimeout(entry.timer);
|
|
188
|
+
entry.detachAbort();
|
|
154
189
|
this.onInternalResolve?.(requestId);
|
|
155
190
|
try {
|
|
156
191
|
this.sendToClient({
|