@vellumai/assistant 0.4.56 → 0.4.57
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/ARCHITECTURE.md +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +237 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +547 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +139 -274
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +25 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +1 -0
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/emit-signal.ts +4 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +166 -33
- package/src/prompts/templates/IDENTITY.md +8 -23
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +46 -2
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +1 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +63 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +9 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +8 -8
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +263 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +220 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/telemetry/usage-telemetry-reporter.ts +51 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Host shell tool
|
|
2
|
+
* Host shell tool - `host_bash`.
|
|
3
3
|
*
|
|
4
4
|
* Unlike the sandboxed `bash` tool, `host_bash` runs commands directly on the
|
|
5
5
|
* host machine without the OS-level sandbox. Under CES shell lockdown for
|
|
6
6
|
* untrusted actors, `host_bash` remains available as a user-approved escape
|
|
7
|
-
* hatch
|
|
7
|
+
* hatch - the guardian must explicitly approve each invocation. It is NOT part
|
|
8
8
|
* of the strong CES secrecy guarantee because it runs unsandboxed and could
|
|
9
9
|
* access protected paths or credential material on disk.
|
|
10
10
|
*
|
|
@@ -48,7 +48,8 @@ function buildHostShellEnv(): Record<string, string> {
|
|
|
48
48
|
|
|
49
49
|
class HostShellTool implements Tool {
|
|
50
50
|
name = "host_bash";
|
|
51
|
-
description =
|
|
51
|
+
description =
|
|
52
|
+
"LAST RESORT — Execute a shell command directly on the user's host machine. You MUST strongly prefer the regular `bash` tool for all commands. Only use `host_bash` when you are absolutely certain the command MUST run on the user's host machine and CANNOT run in the workspace (e.g., managing host-level system services, accessing host-only peripherals, or interacting with host paths outside the workspace). If in doubt, use `bash` instead. Approval-gated: your user must allow each invocation. Do not use for commands that require injected credentials or secrets.";
|
|
52
53
|
category = "host-terminal";
|
|
53
54
|
// host_bash is a weaker-tier escape hatch under CES lockdown. It remains
|
|
54
55
|
// Medium risk by default but persistent approvals are disabled for
|
|
@@ -64,9 +65,9 @@ class HostShellTool implements Tool {
|
|
|
64
65
|
properties: {
|
|
65
66
|
command: {
|
|
66
67
|
type: "string",
|
|
67
|
-
description: "The host shell command to execute",
|
|
68
|
+
description: "The host shell command to execute.",
|
|
68
69
|
},
|
|
69
|
-
|
|
70
|
+
activity: {
|
|
70
71
|
type: "string",
|
|
71
72
|
description:
|
|
72
73
|
'Brief non-technical explanation of what this command does and why, shown to a non-technical user in the permission prompt. Avoid jargon and technical terms. Good: "to check if a required program is installed on your computer". Bad: "to check if gcloud CLI is installed". Good: "to download a helper program". Bad: "to run npm install".',
|
|
@@ -82,7 +83,7 @@ class HostShellTool implements Tool {
|
|
|
82
83
|
"Optional timeout in seconds. Uses configured default and max limits.",
|
|
83
84
|
},
|
|
84
85
|
},
|
|
85
|
-
required: ["command", "
|
|
86
|
+
required: ["command", "activity"],
|
|
86
87
|
},
|
|
87
88
|
};
|
|
88
89
|
}
|
|
@@ -128,7 +129,7 @@ class HostShellTool implements Tool {
|
|
|
128
129
|
// lockdown is active for untrusted actors, persistent approvals are
|
|
129
130
|
// disabled (every invocation requires fresh guardian approval) and the
|
|
130
131
|
// VELLUM_UNTRUSTED_SHELL flag is injected to self-deny raw-secret CLI
|
|
131
|
-
// commands. This does NOT provide the strong CES secrecy guarantee
|
|
132
|
+
// commands. This does NOT provide the strong CES secrecy guarantee -
|
|
132
133
|
// the subprocess runs unsandboxed and could access protected paths.
|
|
133
134
|
//
|
|
134
135
|
// NOTE: forcePromptSideEffects is set in executor.ts BEFORE the
|
|
@@ -260,7 +261,7 @@ class HostShellTool implements Tool {
|
|
|
260
261
|
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
261
262
|
hint = !existsSync(workingDir)
|
|
262
263
|
? `. The working directory does not exist: ${workingDir}`
|
|
263
|
-
: ". The command was not found
|
|
264
|
+
: ". The command was not found - check that it is installed and in PATH.";
|
|
264
265
|
}
|
|
265
266
|
resolve({
|
|
266
267
|
content: `Error spawning command: ${err.message}${hint}`,
|
|
@@ -37,9 +37,10 @@ export function createMcpTool(
|
|
|
37
37
|
): Tool {
|
|
38
38
|
const namespacedName = mcpToolName(serverId, metadata.name);
|
|
39
39
|
const riskLevel = riskMap[serverConfig.defaultRiskLevel] ?? RiskLevel.High;
|
|
40
|
-
const
|
|
40
|
+
const serverDefinesActivity = schemaDefinesProperty(
|
|
41
41
|
metadata.inputSchema,
|
|
42
|
-
"
|
|
42
|
+
"activity",
|
|
43
|
+
{ refBehavior: "assume-defined" },
|
|
43
44
|
);
|
|
44
45
|
|
|
45
46
|
return {
|
|
@@ -64,14 +65,14 @@ export function createMcpTool(
|
|
|
64
65
|
_context: ToolContext,
|
|
65
66
|
): Promise<ToolExecutionResult> {
|
|
66
67
|
try {
|
|
67
|
-
// Strip injected
|
|
68
|
-
const {
|
|
68
|
+
// Strip injected activity before sending to MCP server
|
|
69
|
+
const { activity: _activity, ...mcpInput } = input as Record<
|
|
69
70
|
string,
|
|
70
71
|
unknown
|
|
71
72
|
> & {
|
|
72
|
-
|
|
73
|
+
activity?: unknown;
|
|
73
74
|
};
|
|
74
|
-
const forwardInput =
|
|
75
|
+
const forwardInput = serverDefinesActivity ? input : mcpInput;
|
|
75
76
|
const result = await manager.callTool(
|
|
76
77
|
serverId,
|
|
77
78
|
metadata.name,
|
|
@@ -3,19 +3,19 @@ import type { ToolDefinition } from "../../providers/types.js";
|
|
|
3
3
|
export const memoryRecallDefinition: ToolDefinition = {
|
|
4
4
|
name: "memory_recall",
|
|
5
5
|
description:
|
|
6
|
-
"Hybrid search across memory (semantic and recency) for specific information.
|
|
6
|
+
"Hybrid search across memory (semantic and recency) for specific information. Relevant memories are auto-injected each turn, so only call this when the auto-injected context doesn't contain what you need - e.g. the user references a past session, or you need deeper recall. Be specific in your query for best results. Returns formatted memory context with item IDs for use with memory_manage.",
|
|
7
7
|
input_schema: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
10
|
query: {
|
|
11
11
|
type: "string",
|
|
12
|
-
description: "The search query
|
|
12
|
+
description: "The search query - be specific and descriptive",
|
|
13
13
|
},
|
|
14
14
|
scope: {
|
|
15
15
|
type: "string",
|
|
16
16
|
enum: ["default", "conversation"],
|
|
17
17
|
description:
|
|
18
|
-
'Scope to search
|
|
18
|
+
'Scope to search - "default" searches all memory, "conversation" restricts to current conversation',
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
required: ["query"],
|
|
@@ -47,7 +47,8 @@ const memoryManageProperties = {
|
|
|
47
47
|
"constraint",
|
|
48
48
|
"event",
|
|
49
49
|
],
|
|
50
|
-
description:
|
|
50
|
+
description:
|
|
51
|
+
'Category of the memory item (required for save). Use "constraint" for mistakes, gotchas, discoveries, and working solutions - write as advice to your future self.',
|
|
51
52
|
},
|
|
52
53
|
subject: {
|
|
53
54
|
type: "string" as const,
|
|
@@ -58,7 +59,7 @@ const memoryManageProperties = {
|
|
|
58
59
|
export const memoryManageDefinition: ToolDefinition = {
|
|
59
60
|
name: "memory_manage",
|
|
60
61
|
description:
|
|
61
|
-
"Save, update, or delete memory items. Use 'save' for new information worth remembering, 'update' to correct existing items, 'delete' to remove outdated items.",
|
|
62
|
+
"Save, update, or delete memory items. Memory does not survive session restarts - if you want to remember something, save it now. Use 'save' for new information worth remembering (facts, preferences, mistakes, discoveries, gotchas), 'update' to correct existing items, 'delete' to remove outdated items. When a user says 'remember this', save immediately. For user profile or personality changes, update workspace files (USER.md, SOUL.md) instead.",
|
|
62
63
|
input_schema: {
|
|
63
64
|
type: "object",
|
|
64
65
|
properties: memoryManageProperties,
|
|
@@ -305,7 +305,7 @@ describe("handleMemoryRecall", () => {
|
|
|
305
305
|
// ── Empty results ─────────────────────────────────────────────────
|
|
306
306
|
|
|
307
307
|
test("returns empty result when no memories match", async () => {
|
|
308
|
-
// No items seeded
|
|
308
|
+
// No items seeded - tables cleared in beforeEach
|
|
309
309
|
const result = await handleMemoryRecall(
|
|
310
310
|
{ query: "quantum physics" },
|
|
311
311
|
TEST_CONFIG,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
// Mutable mock state
|
|
3
|
+
// Mutable mock state - set per test
|
|
4
4
|
let mockWebSearchProvider: string | undefined = "perplexity";
|
|
5
5
|
let mockBraveSecureKey: string | undefined;
|
|
6
6
|
let mockPerplexitySecureKey: string | undefined;
|
|
@@ -383,8 +383,8 @@ describe("web_search tool", () => {
|
|
|
383
383
|
expect(capturedUrl).toContain("perplexity");
|
|
384
384
|
});
|
|
385
385
|
|
|
386
|
-
test("maps
|
|
387
|
-
mockWebSearchProvider = "
|
|
386
|
+
test("maps inference-provider-native to perplexity", async () => {
|
|
387
|
+
mockWebSearchProvider = "inference-provider-native";
|
|
388
388
|
mockPerplexitySecureKey = "pplx-key";
|
|
389
389
|
let capturedUrl = "";
|
|
390
390
|
globalThis.fetch = (async (url: string) => {
|
|
@@ -55,13 +55,13 @@ export function normalizeDomain(input: string): DomainInfo | null {
|
|
|
55
55
|
|
|
56
56
|
if (!hostname) return null;
|
|
57
57
|
|
|
58
|
-
// Reject IP addresses and localhost
|
|
58
|
+
// Reject IP addresses and localhost - they don't have registrable domains
|
|
59
59
|
if (isIPAddress(hostname) || hostname === "localhost") {
|
|
60
60
|
return null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// Reject malformed hostnames. Each DNS label must start and end with an
|
|
64
|
-
// alphanumeric and contain only alphanumerics/hyphens
|
|
64
|
+
// alphanumeric and contain only alphanumerics/hyphens - no consecutive dots,
|
|
65
65
|
// no labels starting or ending with hyphens.
|
|
66
66
|
if (
|
|
67
67
|
!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/.test(
|
|
@@ -128,7 +128,7 @@ function resolveInjectionTemplates(
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
// ---------------------------------------------------------------------------
|
|
131
|
-
// Session start hooks
|
|
131
|
+
// Session start hooks - wires assistant credential resolution into core
|
|
132
132
|
// ---------------------------------------------------------------------------
|
|
133
133
|
|
|
134
134
|
function buildSessionStartHooks(): SessionStartHooks {
|
|
@@ -216,13 +216,13 @@ function buildSessionStartHooks(): SessionStartHooks {
|
|
|
216
216
|
if (bestCandidates.length === 1) {
|
|
217
217
|
perCredentialBest.push({ credId, tpl: bestCandidates[0] });
|
|
218
218
|
} else if (bestCandidates.length > 1) {
|
|
219
|
-
// Same credential, same-specificity tie
|
|
219
|
+
// Same credential, same-specificity tie - ambiguous, block
|
|
220
220
|
return null;
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
if (perCredentialBest.length === 0) return req.headers;
|
|
225
|
-
// Cross-credential ambiguity
|
|
225
|
+
// Cross-credential ambiguity - block
|
|
226
226
|
if (perCredentialBest.length > 1) return null;
|
|
227
227
|
|
|
228
228
|
const { credId, tpl } = perCredentialBest[0];
|
|
@@ -245,7 +245,7 @@ function buildSessionStartHooks(): SessionStartHooks {
|
|
|
245
245
|
if (!headerValue) {
|
|
246
246
|
log.warn(
|
|
247
247
|
{ host: req.hostname, credentialId: credId },
|
|
248
|
-
"MITM rewrite: blocking request
|
|
248
|
+
"MITM rewrite: blocking request - composeWith credential missing",
|
|
249
249
|
);
|
|
250
250
|
return null;
|
|
251
251
|
}
|
|
@@ -325,7 +325,7 @@ function buildSessionStartHooks(): SessionStartHooks {
|
|
|
325
325
|
if (!headerValue) {
|
|
326
326
|
log.warn(
|
|
327
327
|
{ hostname, credentialId },
|
|
328
|
-
"Policy: blocking matched request
|
|
328
|
+
"Policy: blocking matched request - composeWith credential missing",
|
|
329
329
|
);
|
|
330
330
|
return null;
|
|
331
331
|
}
|
|
@@ -334,7 +334,7 @@ function buildSessionStartHooks(): SessionStartHooks {
|
|
|
334
334
|
return {};
|
|
335
335
|
}
|
|
336
336
|
case "ambiguous":
|
|
337
|
-
return null; // block
|
|
337
|
+
return null; // block - can't auto-resolve
|
|
338
338
|
case "ask_missing_credential":
|
|
339
339
|
case "ask_unauthenticated":
|
|
340
340
|
if (managed.approvalCallback) {
|
|
@@ -364,7 +364,7 @@ function buildSessionStartHooks(): SessionStartHooks {
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
// ---------------------------------------------------------------------------
|
|
367
|
-
// Public API
|
|
367
|
+
// Public API - thin wrappers that delegate to session-core with the store
|
|
368
368
|
// ---------------------------------------------------------------------------
|
|
369
369
|
|
|
370
370
|
/**
|
|
@@ -389,7 +389,7 @@ export function createSession(
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
/**
|
|
392
|
-
* Start the proxy session
|
|
392
|
+
* Start the proxy session - opens an HTTP server on an ephemeral port.
|
|
393
393
|
*/
|
|
394
394
|
export async function startSession(
|
|
395
395
|
sessionId: ProxySessionId,
|
|
@@ -399,7 +399,7 @@ export async function startSession(
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
/**
|
|
402
|
-
* Gracefully stop a session
|
|
402
|
+
* Gracefully stop a session - closes the HTTP server and clears the idle timer.
|
|
403
403
|
*/
|
|
404
404
|
export async function stopSession(sessionId: ProxySessionId): Promise<void> {
|
|
405
405
|
return coreStopSession(sessionId, store);
|
|
@@ -414,7 +414,7 @@ export function getSessionEnv(sessionId: ProxySessionId): ProxyEnvVars {
|
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
/**
|
|
417
|
-
* Atomically acquire a proxy session for a conversation
|
|
417
|
+
* Atomically acquire a proxy session for a conversation - reuses an active
|
|
418
418
|
* session or creates + starts a new one.
|
|
419
419
|
*/
|
|
420
420
|
export async function getOrStartSession(
|
|
@@ -309,7 +309,7 @@ function normalizeText(text: string): string {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
// Lighter normalization for markdown that preserves indentation, multiple spaces,
|
|
312
|
-
// and trailing whitespace
|
|
312
|
+
// and trailing whitespace - all of which carry semantic meaning in markdown
|
|
313
313
|
// (code blocks, nested lists, table alignment, line breaks).
|
|
314
314
|
function normalizeMarkdown(text: string): string {
|
|
315
315
|
return text
|
|
@@ -44,9 +44,9 @@ interface PerplexityResponse {
|
|
|
44
44
|
function getWebSearchProvider(): WebSearchProvider {
|
|
45
45
|
const config = getConfig();
|
|
46
46
|
const configured = config.services["web-search"].provider ?? "perplexity";
|
|
47
|
-
// '
|
|
48
|
-
// fall back to perplexity for other providers.
|
|
49
|
-
if (configured === "
|
|
47
|
+
// 'inference-provider-native' is handled by the inference provider client
|
|
48
|
+
// directly; fall back to perplexity for other providers.
|
|
49
|
+
if (configured === "inference-provider-native") return "perplexity";
|
|
50
50
|
return configured as WebSearchProvider;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -90,7 +90,7 @@ export class PermissionChecker {
|
|
|
90
90
|
);
|
|
91
91
|
|
|
92
92
|
// Private conversations force prompting for side-effect tools even when a
|
|
93
|
-
// trust/allow rule would auto-allow. Deny decisions are preserved
|
|
93
|
+
// trust/allow rule would auto-allow. Deny decisions are preserved -
|
|
94
94
|
// only allow → prompt promotion happens here.
|
|
95
95
|
if (
|
|
96
96
|
context.forcePromptSideEffects &&
|
|
@@ -139,8 +139,8 @@ export class PermissionChecker {
|
|
|
139
139
|
if (result.decision === "prompt") {
|
|
140
140
|
// Guardian-trust sessions (e.g. scheduled jobs, reminders) should be
|
|
141
141
|
// able to use bundled tools without interactive approval. The guardian
|
|
142
|
-
// is the owner
|
|
143
|
-
// Exception: requireFreshApproval tools cannot be auto-approved
|
|
142
|
+
// is the owner - prompting makes no sense when there is no client.
|
|
143
|
+
// Exception: requireFreshApproval tools cannot be auto-approved -
|
|
144
144
|
// without a human present, bundle installation must be denied.
|
|
145
145
|
if (
|
|
146
146
|
context.isInteractive === false &&
|
|
@@ -158,7 +158,7 @@ export class PermissionChecker {
|
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
// Non-interactive sessions have no client to respond to prompts
|
|
161
|
+
// Non-interactive sessions have no client to respond to prompts -
|
|
162
162
|
// deny immediately instead of blocking for the full permission timeout.
|
|
163
163
|
if (context.isInteractive === false) {
|
|
164
164
|
const durationMs = Date.now() - startTime;
|
|
@@ -190,9 +190,9 @@ export class PermissionChecker {
|
|
|
190
190
|
// Temporary approval override: if the guardian has enabled a
|
|
191
191
|
// conversation-scoped "allow all" mode (allow_10m or allow_conversation),
|
|
192
192
|
// skip the interactive prompt and auto-approve. Only applies to
|
|
193
|
-
// guardian actors
|
|
193
|
+
// guardian actors - untrusted actors cannot leverage this to bypass
|
|
194
194
|
// guardian-required gates (those are enforced in pre-execution gates).
|
|
195
|
-
// Exception: requireFreshApproval tools must always show the prompt
|
|
195
|
+
// Exception: requireFreshApproval tools must always show the prompt -
|
|
196
196
|
// cached temporary overrides cannot substitute for per-invocation
|
|
197
197
|
// human review.
|
|
198
198
|
if (
|
|
@@ -206,7 +206,7 @@ export class PermissionChecker {
|
|
|
206
206
|
riskLevel,
|
|
207
207
|
conversationId: context.conversationId,
|
|
208
208
|
},
|
|
209
|
-
"Temporary approval override active
|
|
209
|
+
"Temporary approval override active - auto-approving without prompt",
|
|
210
210
|
);
|
|
211
211
|
return { allowed: true, decision: "temporary_override", riskLevel };
|
|
212
212
|
}
|
|
@@ -237,7 +237,7 @@ export class PermissionChecker {
|
|
|
237
237
|
const persistentDecisionsAllowed = !context.requireFreshApproval;
|
|
238
238
|
|
|
239
239
|
// Offer temporary approval options to guardians. Suppressed when
|
|
240
|
-
// requireFreshApproval is true
|
|
240
|
+
// requireFreshApproval is true - temporary overrides would be
|
|
241
241
|
// misleading since future invocations still require fresh approval.
|
|
242
242
|
const temporaryOptionsAvailable:
|
|
243
243
|
| Array<"allow_10m" | "allow_conversation">
|
package/src/tools/registry.ts
CHANGED
|
@@ -65,7 +65,7 @@ export function registerSkillTools(newTools: Tool[]): Tool[] {
|
|
|
65
65
|
);
|
|
66
66
|
continue;
|
|
67
67
|
}
|
|
68
|
-
// Existing is also a skill tool
|
|
68
|
+
// Existing is also a skill tool - only allow replacement from the same owner.
|
|
69
69
|
if (existing.ownerSkillId !== tool.ownerSkillId) {
|
|
70
70
|
throw new Error(
|
|
71
71
|
`Skill tool "${tool.name}" is already registered by skill "${existing.ownerSkillId}"`,
|
|
@@ -108,7 +108,7 @@ export function unregisterSkillTools(skillId: string): void {
|
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// Last reference
|
|
111
|
+
// Last reference - actually remove the tools
|
|
112
112
|
skillRefCount.delete(skillId);
|
|
113
113
|
for (const [name, tool] of tools) {
|
|
114
114
|
if (tool.origin === "skill" && tool.ownerSkillId === skillId) {
|
|
@@ -210,9 +210,9 @@ export function getSkillRefCount(skillId: string): number {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
export function getAllToolDefinitions(): ToolDefinition[] {
|
|
213
|
-
// Exclude proxy tools (e.g. computer_use_* tools)
|
|
213
|
+
// Exclude proxy tools (e.g. computer_use_* tools) - they are projected
|
|
214
214
|
// into sessions by the skill system, not via the global tool list.
|
|
215
|
-
// Exclude skill-origin tools
|
|
215
|
+
// Exclude skill-origin tools - they are managed by the session-level
|
|
216
216
|
// skill projection system (projectSkillTools) and must not leak into
|
|
217
217
|
// the base tool list, which is shared across sessions via the global
|
|
218
218
|
// registry. Including them here causes "Tool names must be unique"
|
|
@@ -239,7 +239,7 @@ export async function initializeTools(): Promise<void> {
|
|
|
239
239
|
// Import tool modules to trigger registration side effects.
|
|
240
240
|
await loadEagerModules();
|
|
241
241
|
|
|
242
|
-
// Explicit tool instances
|
|
242
|
+
// Explicit tool instances - no side-effect import required.
|
|
243
243
|
for (const tool of explicitTools) {
|
|
244
244
|
registerTool(tool);
|
|
245
245
|
}
|
|
@@ -256,7 +256,7 @@ export async function initializeTools(): Promise<void> {
|
|
|
256
256
|
registerTool(tool);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
// CES tools
|
|
259
|
+
// CES tools - registered only when the CES feature flag is enabled.
|
|
260
260
|
const activeCesTools = getCesToolsIfEnabled();
|
|
261
261
|
for (const tool of activeCesTools) {
|
|
262
262
|
registerTool(tool);
|
|
@@ -298,7 +298,7 @@ export async function initializeTools(): Promise<void> {
|
|
|
298
298
|
|
|
299
299
|
/**
|
|
300
300
|
* Reset registry to its post-initializeTools() baseline. Exposed
|
|
301
|
-
* exclusively for test isolation
|
|
301
|
+
* exclusively for test isolation - prevents cross-file contamination
|
|
302
302
|
* when multiple test suites share a single Bun process.
|
|
303
303
|
*
|
|
304
304
|
* Restores core tools from a snapshot taken after the first
|
|
@@ -116,14 +116,14 @@ export async function executeScheduleList(
|
|
|
116
116
|
if (oneShot) {
|
|
117
117
|
const fireTime = formatLocalDate(job.nextRunAt);
|
|
118
118
|
lines.push(
|
|
119
|
-
` - [${status}] ${job.name} (id: ${job.id}) (one-shot, ${job.mode})
|
|
119
|
+
` - [${status}] ${job.name} (id: ${job.id}) (one-shot, ${job.mode}) - fire at: ${fireTime} [${job.status}]`,
|
|
120
120
|
);
|
|
121
121
|
} else {
|
|
122
122
|
const next = job.enabled ? formatLocalDate(job.nextRunAt) : "n/a";
|
|
123
123
|
lines.push(
|
|
124
124
|
` - [${status}] ${job.name} (id: ${job.id}) ([${
|
|
125
125
|
job.syntax
|
|
126
|
-
}] ${describeSchedule(job)}, ${job.mode})
|
|
126
|
+
}] ${describeSchedule(job)}, ${job.mode}) - next: ${next}`,
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import type { ToolDefinition } from "../providers/types.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Tools that should never have
|
|
4
|
+
* Tools that should never have an `activity` field injected into their schema.
|
|
5
|
+
* Now empty — all tools define their own `activity` property or get it injected.
|
|
5
6
|
*/
|
|
6
|
-
export const
|
|
7
|
-
"skill_execute",
|
|
8
|
-
"bash",
|
|
9
|
-
"host_bash",
|
|
10
|
-
"request_system_permission",
|
|
11
|
-
]);
|
|
7
|
+
export const ACTIVITY_SKIP_SET = new Set<string>();
|
|
12
8
|
|
|
13
9
|
/**
|
|
14
|
-
* Injects
|
|
15
|
-
* unless the tool is in the skip set, already has
|
|
16
|
-
* non-object schema.
|
|
10
|
+
* Injects an `activity` string property into each tool definition's input
|
|
11
|
+
* schema, unless the tool is in the skip set, already has an activity field,
|
|
12
|
+
* or has a non-object schema.
|
|
17
13
|
*
|
|
18
|
-
* CRITICAL: Never mutates the input definitions
|
|
14
|
+
* CRITICAL: Never mutates the input definitions - always returns deep clones
|
|
19
15
|
* for any modified definition, since `getDefinition()` returns shared refs.
|
|
20
16
|
*/
|
|
21
|
-
export function
|
|
17
|
+
export function injectActivityField(
|
|
22
18
|
definitions: ToolDefinition[],
|
|
23
|
-
skip: Set<string> =
|
|
19
|
+
skip: Set<string> = ACTIVITY_SKIP_SET,
|
|
24
20
|
): ToolDefinition[] {
|
|
25
21
|
return definitions.map((def) => {
|
|
26
22
|
if (skip.has(def.name)) {
|
|
@@ -33,15 +29,22 @@ export function injectReasonField(
|
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
const properties = schema.properties as Record<string, unknown>;
|
|
36
|
-
if (schemaDefinesProperty(schema, "
|
|
32
|
+
if (schemaDefinesProperty(schema, "activity")) {
|
|
37
33
|
return def;
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
// Deep clone to avoid mutating shared refs
|
|
41
|
-
const newProperties = {
|
|
37
|
+
const newProperties = {
|
|
38
|
+
...properties,
|
|
39
|
+
activity: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description:
|
|
42
|
+
"Brief, natural description of what you're doing, shown as a live status update (e.g. 'Checking your project settings')",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
42
45
|
const existingRequired = Array.isArray(schema.required)
|
|
43
|
-
? [...schema.required, "
|
|
44
|
-
: ["
|
|
46
|
+
? [...schema.required, "activity"]
|
|
47
|
+
: ["activity"];
|
|
45
48
|
|
|
46
49
|
return {
|
|
47
50
|
...def,
|
|
@@ -57,21 +60,28 @@ export function injectReasonField(
|
|
|
57
60
|
/**
|
|
58
61
|
* Checks whether a JSON Schema defines a given property name.
|
|
59
62
|
* Walks `allOf`, `oneOf`, `anyOf` recursively.
|
|
60
|
-
*
|
|
63
|
+
*
|
|
64
|
+
* `$ref` handling is configurable via `refBehavior`:
|
|
65
|
+
* - `'assume-undefined'` (default): fail-closed, treat `$ref` as not defining
|
|
66
|
+
* the property. Good for injection (safe to double-inject).
|
|
67
|
+
* - `'assume-defined'`: fail-open, treat `$ref` as possibly defining the
|
|
68
|
+
* property. Good for stripping decisions (don't strip what the server may need).
|
|
61
69
|
*/
|
|
62
70
|
export function schemaDefinesProperty(
|
|
63
71
|
schema: unknown,
|
|
64
72
|
propertyName: string,
|
|
73
|
+
options?: { refBehavior?: "assume-defined" | "assume-undefined" },
|
|
65
74
|
): boolean {
|
|
66
75
|
if (schema == null || typeof schema !== "object") {
|
|
67
76
|
return false;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
const s = schema as Record<string, unknown>;
|
|
80
|
+
const refBehavior = options?.refBehavior ?? "assume-undefined";
|
|
71
81
|
|
|
72
|
-
//
|
|
82
|
+
// $ref: we can't resolve it, so use the configured behavior
|
|
73
83
|
if ("$ref" in s) {
|
|
74
|
-
return
|
|
84
|
+
return refBehavior === "assume-defined";
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
// Check direct properties
|
|
@@ -88,7 +98,7 @@ export function schemaDefinesProperty(
|
|
|
88
98
|
const arr = s[keyword];
|
|
89
99
|
if (Array.isArray(arr)) {
|
|
90
100
|
for (const member of arr) {
|
|
91
|
-
if (schemaDefinesProperty(member, propertyName)) {
|
|
101
|
+
if (schemaDefinesProperty(member, propertyName, options)) {
|
|
92
102
|
return true;
|
|
93
103
|
}
|
|
94
104
|
}
|
|
@@ -43,7 +43,7 @@ const VALID_KINDS = new Set<string>(Object.keys(KIND_PREFIX));
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Generate an 8-char uppercase base-36 short ID.
|
|
46
|
-
* Provides ~41 bits of entropy
|
|
46
|
+
* Provides ~41 bits of entropy - sufficient for intra-request uniqueness.
|
|
47
47
|
*/
|
|
48
48
|
function generateShortId(): string {
|
|
49
49
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
@@ -29,7 +29,7 @@ export type EditEngineResult =
|
|
|
29
29
|
* Core match/replace logic shared by both sandbox and host edit tools,
|
|
30
30
|
* and by the executor's preview-diff computation.
|
|
31
31
|
*
|
|
32
|
-
* This function is pure
|
|
32
|
+
* This function is pure - it takes file content and edit parameters and
|
|
33
33
|
* returns the result without performing any I/O.
|
|
34
34
|
*/
|
|
35
35
|
export function applyEdit(
|
|
@@ -132,7 +132,7 @@ export class FileSystemOps {
|
|
|
132
132
|
try {
|
|
133
133
|
oldContent = readFileSync(filePath, "utf-8");
|
|
134
134
|
} catch {
|
|
135
|
-
// Unreadable existing file
|
|
135
|
+
// Unreadable existing file - keep oldContent as empty string.
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
@@ -167,14 +167,14 @@ export class FileSystemOps {
|
|
|
167
167
|
}
|
|
168
168
|
const filePath = pathCheck.resolved;
|
|
169
169
|
|
|
170
|
-
// Size-check the file on disk (swallow ENOENT
|
|
170
|
+
// Size-check the file on disk (swallow ENOENT - readFileSync gives a clearer error)
|
|
171
171
|
try {
|
|
172
172
|
const sizeErr = checkFileSizeOnDisk(filePath, this.sizeLimit);
|
|
173
173
|
if (sizeErr) {
|
|
174
174
|
return { ok: false, error: Err.sizeLimitExceeded(filePath, sizeErr) };
|
|
175
175
|
}
|
|
176
176
|
} catch {
|
|
177
|
-
// Fall through
|
|
177
|
+
// Fall through - the readFileSync below will surface NOT_FOUND.
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
let content: string;
|
|
@@ -14,7 +14,8 @@ export const IMAGE_EXTENSIONS = new Set([
|
|
|
14
14
|
".webp",
|
|
15
15
|
]);
|
|
16
16
|
|
|
17
|
-
const MAX_SIZE_BYTES = 20 * 1024 * 1024; // 20 MB
|
|
17
|
+
const MAX_SIZE_BYTES = 20 * 1024 * 1024; // 20 MB — LLM API transport limit (post-optimization)
|
|
18
|
+
const MAX_SOURCE_SIZE_BYTES = 100 * 1024 * 1024; // 100 MB — pre-optimization guard
|
|
18
19
|
|
|
19
20
|
// Images above this threshold get auto-optimized via sips (macOS) to avoid
|
|
20
21
|
// sending multi-MB base64 payloads to the LLM API.
|
|
@@ -102,7 +103,7 @@ function optimizeWithSips(srcPath: string): string | null {
|
|
|
102
103
|
* Read an image file from disk, optionally optimize it, and return a
|
|
103
104
|
* ToolExecutionResult with base64-encoded image content blocks.
|
|
104
105
|
*
|
|
105
|
-
* The caller is responsible for path resolution and sandbox enforcement
|
|
106
|
+
* The caller is responsible for path resolution and sandbox enforcement -
|
|
106
107
|
* `resolvedPath` must be an already-validated absolute path.
|
|
107
108
|
*/
|
|
108
109
|
export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
@@ -120,10 +121,10 @@ export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
|
120
121
|
return { content: `Error: ${resolvedPath} is not a file`, isError: true };
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
if (stat.size >
|
|
124
|
+
if (stat.size > MAX_SOURCE_SIZE_BYTES) {
|
|
124
125
|
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
125
126
|
return {
|
|
126
|
-
content: `Error: image too large (${sizeMB} MB). Maximum is
|
|
127
|
+
content: `Error: image too large (${sizeMB} MB). Maximum source file size is 100 MB.`,
|
|
127
128
|
isError: true,
|
|
128
129
|
};
|
|
129
130
|
}
|
|
@@ -139,6 +140,14 @@ export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
|
139
140
|
buffer = readFileSync(tmpPath) as Buffer;
|
|
140
141
|
optimized = true;
|
|
141
142
|
} else {
|
|
143
|
+
// sips unavailable — fast-fail if original file exceeds the transport limit
|
|
144
|
+
if (stat.size > MAX_SIZE_BYTES) {
|
|
145
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
146
|
+
return {
|
|
147
|
+
content: `Error: image too large (${sizeMB} MB). Maximum is 20 MB. Image optimization (sips) is unavailable on this platform.`,
|
|
148
|
+
isError: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
142
151
|
buffer = readFileSync(resolvedPath) as Buffer;
|
|
143
152
|
}
|
|
144
153
|
} else {
|
|
@@ -157,7 +166,18 @@ export function readImageFile(resolvedPath: string): ToolExecutionResult {
|
|
|
157
166
|
}
|
|
158
167
|
}
|
|
159
168
|
|
|
160
|
-
|
|
169
|
+
if (buffer.length > MAX_SIZE_BYTES) {
|
|
170
|
+
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
171
|
+
const msg = optimized
|
|
172
|
+
? `Error: image too large after optimization (${sizeMB} MB). Maximum is 20 MB.`
|
|
173
|
+
: `Error: image too large (${sizeMB} MB). Maximum is 20 MB.`;
|
|
174
|
+
return {
|
|
175
|
+
content: msg,
|
|
176
|
+
isError: true,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Detect actual format from magic bytes - never trust the file extension
|
|
161
181
|
// alone, since sips converts to JPEG and files can be misnamed.
|
|
162
182
|
const detectedType = detectMediaType(buffer);
|
|
163
183
|
if (!detectedType) {
|