@vellumai/assistant 0.12.1 → 0.12.2-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +539 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +24 -12
- package/src/config/loader.ts +1 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +186 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +54 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +36 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CES HTTP backend for non-secret credential records (identity + policy).
|
|
3
|
+
*
|
|
4
|
+
* Used when the CES RPC socket is down but the CES HTTP sidecar is
|
|
5
|
+
* reachable (containerized deployments with CES_CREDENTIAL_URL).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type CesHttpCredentialClient,
|
|
10
|
+
createCesHttpCredentialClient,
|
|
11
|
+
} from "@vellumai/ces-client/http-credentials";
|
|
12
|
+
import type { CredentialRecord } from "@vellumai/service-contracts/credential-rpc";
|
|
13
|
+
|
|
14
|
+
import { getIsContainerized } from "../config/env-registry.js";
|
|
15
|
+
import { getLogger } from "../util/logger.js";
|
|
16
|
+
import type { CredentialRecordBackend } from "./ces-rpc-record-backend.js";
|
|
17
|
+
|
|
18
|
+
const log = getLogger("ces-http-record-backend");
|
|
19
|
+
|
|
20
|
+
type RecordHttpClient = Pick<
|
|
21
|
+
CesHttpCredentialClient,
|
|
22
|
+
| "getRecord"
|
|
23
|
+
| "setRecord"
|
|
24
|
+
| "deleteRecord"
|
|
25
|
+
| "listRecords"
|
|
26
|
+
| "bulkSetRecords"
|
|
27
|
+
>;
|
|
28
|
+
|
|
29
|
+
function resolveEnvHttpClient(): RecordHttpClient | undefined {
|
|
30
|
+
const baseUrl = process.env.CES_CREDENTIAL_URL?.trim();
|
|
31
|
+
const serviceToken = process.env.CES_SERVICE_TOKEN?.trim();
|
|
32
|
+
if (!baseUrl || !serviceToken) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return createCesHttpCredentialClient({ baseUrl, serviceToken }, log);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class CesHttpRecordBackend implements CredentialRecordBackend {
|
|
39
|
+
readonly name = "ces-http";
|
|
40
|
+
|
|
41
|
+
constructor(private readonly client?: RecordHttpClient) {}
|
|
42
|
+
|
|
43
|
+
private resolveClient(): RecordHttpClient | undefined {
|
|
44
|
+
return this.client ?? resolveEnvHttpClient();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
isAvailable(): boolean {
|
|
48
|
+
return this.resolveClient() !== undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async get(account: string): Promise<CredentialRecord | undefined> {
|
|
52
|
+
const client = this.resolveClient();
|
|
53
|
+
if (!client) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const result = await client.getRecord(account);
|
|
57
|
+
return result.record;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async set(account: string, record: CredentialRecord): Promise<boolean> {
|
|
61
|
+
const client = this.resolveClient();
|
|
62
|
+
if (!client) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return client.setRecord(account, record);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async delete(
|
|
69
|
+
account: string,
|
|
70
|
+
): Promise<"deleted" | "not-found" | "error"> {
|
|
71
|
+
const client = this.resolveClient();
|
|
72
|
+
if (!client) {
|
|
73
|
+
return "error";
|
|
74
|
+
}
|
|
75
|
+
return client.deleteRecord(account);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async list(): Promise<Array<{
|
|
79
|
+
account: string;
|
|
80
|
+
record: CredentialRecord;
|
|
81
|
+
}> | null> {
|
|
82
|
+
const client = this.resolveClient();
|
|
83
|
+
if (!client) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const result = await client.listRecords();
|
|
87
|
+
if (result.unreachable) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return result.records ?? [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async bulkSet(
|
|
94
|
+
records: Array<{ account: string; record: CredentialRecord }>,
|
|
95
|
+
): Promise<Array<{ account: string; ok: boolean }>> {
|
|
96
|
+
const client = this.resolveClient();
|
|
97
|
+
if (!client) {
|
|
98
|
+
return records.map((entry) => ({ account: entry.account, ok: false }));
|
|
99
|
+
}
|
|
100
|
+
return client.bulkSetRecords(records);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* CES HTTP record backend when the assistant is containerized and the
|
|
106
|
+
* CES HTTP sidecar URL is configured. Returns null otherwise.
|
|
107
|
+
*/
|
|
108
|
+
export function createCesHttpRecordBackendIfConfigured(): CredentialRecordBackend | null {
|
|
109
|
+
if (!getIsContainerized()) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
if (!process.env.CES_CREDENTIAL_URL?.trim()) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const backend = new CesHttpRecordBackend();
|
|
116
|
+
if (!backend.isAvailable()) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return backend;
|
|
120
|
+
}
|
|
@@ -11,6 +11,11 @@ import { getLogger } from "../util/logger.js";
|
|
|
11
11
|
const log = getLogger("ces-rpc-record-backend");
|
|
12
12
|
|
|
13
13
|
export interface CredentialRecordBackend {
|
|
14
|
+
/**
|
|
15
|
+
* Transport id. `"ces-rpc"` is the attached CES socket backend; live
|
|
16
|
+
* catalog listing fails over to CES HTTP when that transport is down.
|
|
17
|
+
*/
|
|
18
|
+
readonly name?: string;
|
|
14
19
|
isAvailable(): boolean;
|
|
15
20
|
get(account: string): Promise<CredentialRecord | undefined>;
|
|
16
21
|
set(account: string, record: CredentialRecord): Promise<boolean>;
|
|
@@ -29,6 +34,8 @@ export interface CredentialRecordBackend {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export class CesRpcRecordBackend implements CredentialRecordBackend {
|
|
37
|
+
readonly name = "ces-rpc";
|
|
38
|
+
|
|
32
39
|
constructor(private readonly client: CesClient) {}
|
|
33
40
|
|
|
34
41
|
isAvailable(): boolean {
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* 3. Enforces per-source character budgets to prevent context flooding.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { safeStringSlice } from "../util/unicode.js";
|
|
15
|
+
|
|
14
16
|
// ---------------------------------------------------------------------------
|
|
15
17
|
// Types
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
@@ -156,7 +158,7 @@ export function escapeContentBoundaries(content: string): string {
|
|
|
156
158
|
|
|
157
159
|
/** Sanitize a value for use as an XML attribute (no quotes, angle brackets, newlines). */
|
|
158
160
|
function sanitizeAttr(value: string): string {
|
|
159
|
-
return value.replace(/[<>"&\r\n]/g, "")
|
|
161
|
+
return safeStringSlice(value.replace(/[<>"&\r\n]/g, ""), 0, 200);
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
function parseExternalContentAttributes(
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
import { readWorkspaceSkillMdSignature } from "../workspace-skill-md-signature.js";
|
|
7
|
+
|
|
8
|
+
const ROOT = mkdtempSync(join(tmpdir(), "skill-md-signature-"));
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
rmSync(ROOT, { recursive: true, force: true });
|
|
12
|
+
mkdirSync(ROOT, { recursive: true });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function writeSkill(id: string, body = "body"): string {
|
|
16
|
+
const dir = join(ROOT, id);
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
const skillMd = join(dir, "SKILL.md");
|
|
19
|
+
writeFileSync(skillMd, `---\nname: ${id}\ndescription: ${id}\n---\n${body}\n`);
|
|
20
|
+
return skillMd;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("readWorkspaceSkillMdSignature", () => {
|
|
24
|
+
test("empty or missing skills dir is the empty signature", () => {
|
|
25
|
+
expect(readWorkspaceSkillMdSignature(join(ROOT, "missing"))).toBe("");
|
|
26
|
+
mkdirSync(ROOT, { recursive: true });
|
|
27
|
+
expect(readWorkspaceSkillMdSignature(ROOT)).toBe("");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("includes SKILL.md mtimes and ignores TOOLS.json", () => {
|
|
31
|
+
const skillMd = writeSkill("alpha");
|
|
32
|
+
const first = readWorkspaceSkillMdSignature(ROOT);
|
|
33
|
+
expect(first).toMatch(/^alpha:\d+$/);
|
|
34
|
+
|
|
35
|
+
writeFileSync(join(ROOT, "alpha", "TOOLS.json"), '{"version":1,"tools":[]}');
|
|
36
|
+
expect(readWorkspaceSkillMdSignature(ROOT)).toBe(first);
|
|
37
|
+
|
|
38
|
+
const later = new Date(Date.now() + 5_000);
|
|
39
|
+
utimesSync(skillMd, later, later);
|
|
40
|
+
expect(readWorkspaceSkillMdSignature(ROOT)).not.toBe(first);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("add and remove skill directories change the signature", () => {
|
|
44
|
+
writeSkill("alpha");
|
|
45
|
+
const one = readWorkspaceSkillMdSignature(ROOT);
|
|
46
|
+
writeSkill("bravo");
|
|
47
|
+
const two = readWorkspaceSkillMdSignature(ROOT);
|
|
48
|
+
expect(two).not.toBe(one);
|
|
49
|
+
expect(two.split("\n")).toHaveLength(2);
|
|
50
|
+
|
|
51
|
+
rmSync(join(ROOT, "bravo"), { recursive: true, force: true });
|
|
52
|
+
expect(readWorkspaceSkillMdSignature(ROOT)).toBe(one);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("directories without SKILL.md are omitted", () => {
|
|
56
|
+
mkdirSync(join(ROOT, "not-a-skill"), { recursive: true });
|
|
57
|
+
writeFileSync(join(ROOT, "README.md"), "nope");
|
|
58
|
+
expect(readWorkspaceSkillMdSignature(ROOT)).toBe("");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { getWorkspaceSkillsDir } from "../util/platform.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Cheap change detector for workspace skill capability cards: the sorted set
|
|
8
|
+
* of immediate skill directories that contain a top-level `SKILL.md`, each
|
|
9
|
+
* paired with that file's mtime. Capability cards are frontmatter fields from
|
|
10
|
+
* `SKILL.md`. Body files, `TOOLS.json`, scripts, and `references/` are not part
|
|
11
|
+
* of this signature.
|
|
12
|
+
*
|
|
13
|
+
* Listing the skills directory (not only statting known paths) is what makes
|
|
14
|
+
* install and delete visible: a new or removed skill directory changes the
|
|
15
|
+
* signature even when no previously known `SKILL.md` mtime moved.
|
|
16
|
+
*/
|
|
17
|
+
export function readWorkspaceSkillMdSignature(
|
|
18
|
+
skillsDir: string = getWorkspaceSkillsDir(),
|
|
19
|
+
): string {
|
|
20
|
+
if (!existsSync(skillsDir)) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
27
|
+
} catch {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const parts: string[] = [];
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const skillMdPath = join(skillsDir, entry.name, "SKILL.md");
|
|
37
|
+
try {
|
|
38
|
+
const st = statSync(skillMdPath);
|
|
39
|
+
if (!st.isFile()) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
parts.push(`${entry.name}:${Math.trunc(st.mtimeMs)}`);
|
|
43
|
+
} catch {
|
|
44
|
+
// No readable SKILL.md: not a catalog skill.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
parts.sort();
|
|
49
|
+
return parts.join("\n");
|
|
50
|
+
}
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
import { readdir } from "node:fs/promises";
|
|
34
34
|
import { join } from "node:path";
|
|
35
35
|
|
|
36
|
-
import type { SkillSummary } from "../config/skills.js";
|
|
37
36
|
import type { TrustContext } from "../daemon/trust-context-types.js";
|
|
38
37
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
39
38
|
import { truncate as truncateText } from "../util/truncate.js";
|
|
@@ -56,15 +55,6 @@ export interface AdvisorContextSources {
|
|
|
56
55
|
* from the catalog section, mirroring the `skill_load` gate.
|
|
57
56
|
*/
|
|
58
57
|
enabledPluginSet?: ReadonlySet<string> | null;
|
|
59
|
-
/**
|
|
60
|
-
* Pre-resolved skill catalog, typically the parent conversation's warm
|
|
61
|
-
* `skillProjectionCache.catalog`. Passing it keeps the synchronous on-disk
|
|
62
|
-
* catalog scan out of the consult path (and matches the catalog view the
|
|
63
|
-
* parent turn's tool projection used). When absent, the section falls back
|
|
64
|
-
* to a fresh `loadSkillCatalog()` scan, the same call every agent turn's
|
|
65
|
-
* projection already makes.
|
|
66
|
-
*/
|
|
67
|
-
skillCatalog?: readonly SkillSummary[];
|
|
68
58
|
}
|
|
69
59
|
|
|
70
60
|
/** Cap a block so the assembled context never balloons the consult prompt. */
|
|
@@ -114,7 +104,6 @@ async function buildToolsSection(
|
|
|
114
104
|
*/
|
|
115
105
|
async function buildSkillsSection(
|
|
116
106
|
enabledPluginSet: ReadonlySet<string> | null | undefined,
|
|
117
|
-
preResolvedCatalog: readonly SkillSummary[] | undefined,
|
|
118
107
|
): Promise<string | null> {
|
|
119
108
|
try {
|
|
120
109
|
const [
|
|
@@ -130,19 +119,17 @@ async function buildSkillsSection(
|
|
|
130
119
|
]);
|
|
131
120
|
const config = getConfig();
|
|
132
121
|
const pluginScope = enabledPluginSet ?? null;
|
|
133
|
-
const catalog =
|
|
134
|
-
(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
},
|
|
145
|
-
);
|
|
122
|
+
const catalog = loadSkillCatalog().filter((skill) => {
|
|
123
|
+
if (
|
|
124
|
+
pluginScope !== null &&
|
|
125
|
+
skill.owner?.kind === "plugin" &&
|
|
126
|
+
!pluginScope.has(skill.owner.id)
|
|
127
|
+
) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const flagKey = skillFlagKey(skill);
|
|
131
|
+
return !flagKey || isAssistantFeatureFlagEnabled(flagKey, config);
|
|
132
|
+
});
|
|
146
133
|
if (catalog.length === 0) {
|
|
147
134
|
return null;
|
|
148
135
|
}
|
|
@@ -384,7 +371,7 @@ export async function buildAdvisorContext(
|
|
|
384
371
|
const sections = await Promise.all(
|
|
385
372
|
[
|
|
386
373
|
buildToolsSection(sources.allowedToolNames),
|
|
387
|
-
buildSkillsSection(sources.enabledPluginSet
|
|
374
|
+
buildSkillsSection(sources.enabledPluginSet),
|
|
388
375
|
buildWorkspaceSection(sources),
|
|
389
376
|
].map((section) => withSectionTimeout(section, sectionTimeoutMs)),
|
|
390
377
|
);
|
|
@@ -158,6 +158,7 @@ const SKILL_TOOLS: GuardedTool[] = [
|
|
|
158
158
|
proxied("computer_use_drag"),
|
|
159
159
|
proxied("computer_use_open_app"),
|
|
160
160
|
proxied("computer_use_run_applescript"),
|
|
161
|
+
proxied("computer_use_sequence"),
|
|
161
162
|
proxied("computer_use_respond"),
|
|
162
163
|
|
|
163
164
|
{ name: "contact_merge", input: { keep_id: "c-1", merge_id: "c-2" } },
|
|
@@ -90,7 +90,9 @@ describe("executeAcpListAgents", () => {
|
|
|
90
90
|
const codex = parsed.agents.find((a: { id: string }) => a.id === "codex");
|
|
91
91
|
expect(codex.available).toBe(false);
|
|
92
92
|
expect(codex.unavailableReason).toBe("'codex-acp' is not on PATH");
|
|
93
|
-
expect(codex.setupHint).toBe(
|
|
93
|
+
expect(codex.setupHint).toBe(
|
|
94
|
+
"bun add -g @agentclientprotocol/codex-acp@1.10.0",
|
|
95
|
+
);
|
|
94
96
|
|
|
95
97
|
const claude = parsed.agents.find((a: { id: string }) => a.id === "claude");
|
|
96
98
|
expect(claude.available).toBe(true);
|
|
@@ -124,11 +124,22 @@ const spawnMock = mock(
|
|
|
124
124
|
_cwd: string,
|
|
125
125
|
_parentConversationId: string,
|
|
126
126
|
_sendToVellum: (msg: unknown) => void,
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
acpSessionId:
|
|
130
|
-
protocolSessionId:
|
|
131
|
-
|
|
127
|
+
_options?: { parentToolUseId?: string; model?: string },
|
|
128
|
+
): Promise<{
|
|
129
|
+
acpSessionId: string;
|
|
130
|
+
protocolSessionId: string;
|
|
131
|
+
requestedModel?: string;
|
|
132
|
+
effectiveModel?: string;
|
|
133
|
+
modelWarning?: string;
|
|
134
|
+
}> => {
|
|
135
|
+
const requestedModel = _options?.model?.trim() || undefined;
|
|
136
|
+
return {
|
|
137
|
+
acpSessionId: "acp-session-test",
|
|
138
|
+
protocolSessionId: "proto-session-test",
|
|
139
|
+
requestedModel,
|
|
140
|
+
effectiveModel: "opus",
|
|
141
|
+
};
|
|
142
|
+
},
|
|
132
143
|
);
|
|
133
144
|
|
|
134
145
|
// Spread the real module's exports so transitive importers that pull other
|
|
@@ -201,6 +212,8 @@ describe("executeAcpSpawn - happy path", () => {
|
|
|
201
212
|
const payload = JSON.parse(result.content);
|
|
202
213
|
expect(payload.acpSessionId).toBe("acp-session-test");
|
|
203
214
|
expect(payload.status).toBe("running");
|
|
215
|
+
expect(payload.requestedModel).toBeNull();
|
|
216
|
+
expect(payload.effectiveModel).toBe("opus");
|
|
204
217
|
// The real adapter binary is spawned (no `bun x` wrapper).
|
|
205
218
|
const agentConfigArg = spawnMock.mock.calls[0][1] as { command: string };
|
|
206
219
|
expect(agentConfigArg.command).toBe("claude-agent-acp");
|
|
@@ -214,8 +227,10 @@ describe("executeAcpSpawn - happy path", () => {
|
|
|
214
227
|
|
|
215
228
|
expect(result.isError).toBe(false);
|
|
216
229
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
217
|
-
//
|
|
218
|
-
expect(spawnMock.mock.calls[0][6]).
|
|
230
|
+
// The options bag is the 7th positional arg to spawn().
|
|
231
|
+
expect(spawnMock.mock.calls[0][6]).toEqual({
|
|
232
|
+
parentToolUseId: "toolu_abc123",
|
|
233
|
+
});
|
|
219
234
|
});
|
|
220
235
|
|
|
221
236
|
test("default-profile fallback when user config is empty", async () => {
|
|
@@ -272,7 +287,7 @@ describe("executeAcpSpawn — input validation", () => {
|
|
|
272
287
|
expect(result.isError).toBe(true);
|
|
273
288
|
expect(result.content).toContain("claude-agent-acp is not on PATH");
|
|
274
289
|
expect(result.content).toContain(
|
|
275
|
-
"bun add -g @agentclientprotocol/claude-agent-acp",
|
|
290
|
+
"bun add -g @agentclientprotocol/claude-agent-acp@0.75.1",
|
|
276
291
|
);
|
|
277
292
|
expect(execFileMock).not.toHaveBeenCalled();
|
|
278
293
|
expect(spawnMock).not.toHaveBeenCalled();
|
|
@@ -309,7 +324,7 @@ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () =>
|
|
|
309
324
|
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
310
325
|
const payload = JSON.parse(result.content);
|
|
311
326
|
expect(payload.message).toContain(
|
|
312
|
-
"Installed @agentclientprotocol/claude-agent-acp automatically.",
|
|
327
|
+
"Installed @agentclientprotocol/claude-agent-acp@0.75.1 automatically.",
|
|
313
328
|
);
|
|
314
329
|
// The real binary was spawned with cwd = the project dir and token
|
|
315
330
|
// injected (trusted-binary config, no resolution at spawn).
|
|
@@ -330,7 +345,7 @@ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () =>
|
|
|
330
345
|
expect(args).toEqual([
|
|
331
346
|
"add",
|
|
332
347
|
"--global",
|
|
333
|
-
"@agentclientprotocol/claude-agent-acp",
|
|
348
|
+
"@agentclientprotocol/claude-agent-acp@0.75.1",
|
|
334
349
|
]);
|
|
335
350
|
});
|
|
336
351
|
|
|
@@ -419,7 +434,7 @@ describe("executeAcpSpawn: sandboxed bun auto-install on missing binary", () =>
|
|
|
419
434
|
expect(result.isError).toBe(true);
|
|
420
435
|
expect(result.content).toContain("claude-agent-acp is not on PATH");
|
|
421
436
|
expect(result.content).toContain(
|
|
422
|
-
"bun add -g @agentclientprotocol/claude-agent-acp",
|
|
437
|
+
"bun add -g @agentclientprotocol/claude-agent-acp@0.75.1",
|
|
423
438
|
);
|
|
424
439
|
expect(result.content).toContain("auto-install failed");
|
|
425
440
|
expect(result.content).toContain("EACCES");
|
|
@@ -458,6 +473,126 @@ describe("executeAcpSpawn — per-agent resume hint", () => {
|
|
|
458
473
|
});
|
|
459
474
|
});
|
|
460
475
|
|
|
476
|
+
describe("executeAcpSpawn - model selection", () => {
|
|
477
|
+
test("threads the requested model into manager.spawn's options", async () => {
|
|
478
|
+
const result = await executeAcpSpawn(
|
|
479
|
+
{ agent: "claude", task: "do something", model: "opus" },
|
|
480
|
+
{ ...makeContext(), toolUseId: "toolu_model" },
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
expect(result.isError).toBe(false);
|
|
484
|
+
expect(spawnMock.mock.calls[0][6]).toEqual({
|
|
485
|
+
parentToolUseId: "toolu_model",
|
|
486
|
+
model: "opus",
|
|
487
|
+
});
|
|
488
|
+
const payload = JSON.parse(result.content);
|
|
489
|
+
expect(payload.requestedModel).toBe("opus");
|
|
490
|
+
expect(payload.effectiveModel).toBe("opus");
|
|
491
|
+
expect(payload.message).toContain(
|
|
492
|
+
'The top-level ACP session reports "opus" as its effective model.',
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
test("reports the manager-normalized requested model", async () => {
|
|
497
|
+
const result = await executeAcpSpawn(
|
|
498
|
+
{ agent: "claude", task: "do something", model: " opus " },
|
|
499
|
+
makeContext(),
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
expect(spawnMock.mock.calls[0][6]).toEqual({
|
|
503
|
+
parentToolUseId: undefined,
|
|
504
|
+
model: " opus ",
|
|
505
|
+
});
|
|
506
|
+
expect(JSON.parse(result.content).requestedModel).toBe("opus");
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("a null model is treated as omitted", async () => {
|
|
510
|
+
const result = await executeAcpSpawn(
|
|
511
|
+
{ agent: "claude", task: "do something", model: null },
|
|
512
|
+
makeContext(),
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
expect(result.isError).toBe(false);
|
|
516
|
+
const options = spawnMock.mock.calls[0][6] as { model?: string };
|
|
517
|
+
expect(options.model).toBeUndefined();
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("a modelWarning from the spawn is relayed in the result message", async () => {
|
|
521
|
+
spawnMock.mockImplementationOnce(async () => ({
|
|
522
|
+
acpSessionId: "acp-session-test",
|
|
523
|
+
protocolSessionId: "proto-session-test",
|
|
524
|
+
requestedModel: "nope",
|
|
525
|
+
effectiveModel: "opus",
|
|
526
|
+
modelWarning: "Unknown model: nope",
|
|
527
|
+
}));
|
|
528
|
+
|
|
529
|
+
const result = await executeAcpSpawn(
|
|
530
|
+
{ agent: "claude", task: "do something", model: "nope" },
|
|
531
|
+
makeContext(),
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
expect(result.isError).toBe(false);
|
|
535
|
+
const payload = JSON.parse(result.content);
|
|
536
|
+
expect(payload.message).toContain(
|
|
537
|
+
"The requested model was not applied: Unknown model: nope",
|
|
538
|
+
);
|
|
539
|
+
expect(payload.requestedModel).toBe("nope");
|
|
540
|
+
expect(payload.effectiveModel).toBe("opus");
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test("the note relays a no-selector warning without calling it a refusal", async () => {
|
|
544
|
+
spawnMock.mockImplementationOnce(async () => ({
|
|
545
|
+
acpSessionId: "acp-session-test",
|
|
546
|
+
protocolSessionId: "proto-session-test",
|
|
547
|
+
requestedModel: "opus",
|
|
548
|
+
modelWarning:
|
|
549
|
+
'Agent "claude" does not support model selection, so the session is running on the agent\'s own model.',
|
|
550
|
+
}));
|
|
551
|
+
|
|
552
|
+
const result = await executeAcpSpawn(
|
|
553
|
+
{ agent: "claude", task: "do something", model: "opus" },
|
|
554
|
+
makeContext(),
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
const payload = JSON.parse(result.content);
|
|
558
|
+
expect(payload.message).toContain("does not support model selection");
|
|
559
|
+
expect(payload.message).not.toContain("refused");
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
test("no model note when the spawn reports no warning", async () => {
|
|
563
|
+
const result = await executeAcpSpawn(
|
|
564
|
+
{ agent: "claude", task: "do something", model: "opus" },
|
|
565
|
+
makeContext(),
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
expect(result.isError).toBe(false);
|
|
569
|
+
const payload = JSON.parse(result.content);
|
|
570
|
+
expect(payload.message).not.toContain(
|
|
571
|
+
"The requested model was not applied",
|
|
572
|
+
);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test("reports when the adapter does not identify the effective model", async () => {
|
|
576
|
+
spawnMock.mockImplementationOnce(async () => ({
|
|
577
|
+
acpSessionId: "acp-session-test",
|
|
578
|
+
protocolSessionId: "proto-session-test",
|
|
579
|
+
requestedModel: "sonnet",
|
|
580
|
+
}));
|
|
581
|
+
|
|
582
|
+
const result = await executeAcpSpawn(
|
|
583
|
+
{ agent: "claude", task: "do something", model: "sonnet" },
|
|
584
|
+
makeContext(),
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
const payload = JSON.parse(result.content);
|
|
588
|
+
expect(payload.requestedModel).toBe("sonnet");
|
|
589
|
+
expect(payload.effectiveModel).toBeNull();
|
|
590
|
+
expect(payload.message).toContain(
|
|
591
|
+
"The top-level ACP session did not report an effective model.",
|
|
592
|
+
);
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
461
596
|
// ---------------------------------------------------------------------------
|
|
462
597
|
// executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN env injection + preflight
|
|
463
598
|
//
|
package/src/tools/acp/spawn.ts
CHANGED
|
@@ -37,6 +37,7 @@ export const acpSpawnInputSchema = z.looseObject({
|
|
|
37
37
|
agent: nullAsOmitted(z.string()),
|
|
38
38
|
task: nullAsOmitted(z.string()),
|
|
39
39
|
cwd: nullAsOmitted(z.string()),
|
|
40
|
+
model: nullAsOmitted(z.string()),
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
/**
|
|
@@ -66,6 +67,7 @@ export async function executeAcpSpawn(
|
|
|
66
67
|
}
|
|
67
68
|
const agent = parsedInput.data.agent || "claude";
|
|
68
69
|
const task = parsedInput.data.task;
|
|
70
|
+
const model = parsedInput.data.model;
|
|
69
71
|
|
|
70
72
|
if (!task) {
|
|
71
73
|
return { content: '"task" is required.', isError: true };
|
|
@@ -124,14 +126,20 @@ export async function executeAcpSpawn(
|
|
|
124
126
|
// Recheck: the auto-install and the agent-env preparation above are both
|
|
125
127
|
// awaits, and this is the point a long-lived subprocess starts.
|
|
126
128
|
throwIfCancelled(context);
|
|
127
|
-
const {
|
|
129
|
+
const {
|
|
130
|
+
acpSessionId,
|
|
131
|
+
protocolSessionId,
|
|
132
|
+
requestedModel,
|
|
133
|
+
effectiveModel,
|
|
134
|
+
modelWarning,
|
|
135
|
+
} = await manager.spawn(
|
|
128
136
|
agent,
|
|
129
137
|
agentConfig,
|
|
130
138
|
task,
|
|
131
139
|
cwd,
|
|
132
140
|
context.conversationId,
|
|
133
141
|
sendToClient,
|
|
134
|
-
context.toolUseId,
|
|
142
|
+
{ parentToolUseId: context.toolUseId, model },
|
|
135
143
|
// The manager rechecks after its own protocol handshake and session
|
|
136
144
|
// creation, so a turn stopped in that window tears the child process
|
|
137
145
|
// down instead of handing it the task.
|
|
@@ -150,16 +158,26 @@ export async function executeAcpSpawn(
|
|
|
150
158
|
const installNote = autoInstalledPackage
|
|
151
159
|
? ` Installed ${autoInstalledPackage} automatically.`
|
|
152
160
|
: "";
|
|
161
|
+
// Relayed verbatim: the warning already distinguishes an adapter with no
|
|
162
|
+
// selector from one that refused the value.
|
|
163
|
+
const modelNote = modelWarning
|
|
164
|
+
? ` The requested model was not applied: ${modelWarning}`
|
|
165
|
+
: "";
|
|
166
|
+
const effectiveModelNote = effectiveModel
|
|
167
|
+
? ` The top-level ACP session reports "${effectiveModel}" as its effective model.`
|
|
168
|
+
: " The top-level ACP session did not report an effective model.";
|
|
153
169
|
const payload = JSON.stringify({
|
|
154
170
|
acpSessionId,
|
|
155
171
|
protocolSessionId,
|
|
156
172
|
agent,
|
|
157
173
|
cwd,
|
|
174
|
+
requestedModel: requestedModel ?? null,
|
|
175
|
+
effectiveModel: effectiveModel ?? null,
|
|
158
176
|
status: "running",
|
|
159
177
|
message:
|
|
160
178
|
`ACP agent "${agent}" spawned (session: ${protocolSessionId}). ` +
|
|
161
179
|
`Results stream back via SSE. You will be notified when it completes.` +
|
|
162
|
-
`${installNote}${resumeHint}`,
|
|
180
|
+
`${installNote}${modelNote}${effectiveModelNote}${resumeHint}`,
|
|
163
181
|
});
|
|
164
182
|
|
|
165
183
|
return { content: payload, isError: false };
|