@vellumai/assistant 0.12.1 → 0.12.2-staging.2
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/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- 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-runtime-assembly.test.ts +28 -0
- 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-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- 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 +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- 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 +596 -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 +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -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__/secret-routes-acp-guard.test.ts +59 -1
- 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 +70 -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__/ui-channel-variants.test.ts +1 -56
- 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__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -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__/claude-token-refresh.test.ts +257 -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__/prepare-agent-env.test.ts +60 -1
- 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/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- 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/claude-token-refresh.ts +150 -0
- 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/prepare-agent-env.ts +21 -6
- 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 +120 -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 +56 -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 +155 -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 +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- 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/oauth/status.ts +5 -0
- 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 +29 -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 +33 -13
- package/src/config/loader.ts +2 -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 +18 -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 +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- 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 +224 -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-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -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__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- 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 +149 -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 +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- 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-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -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-claude-auth-routes.ts +11 -3
- 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 +90 -2
- 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 +20 -14
- 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 +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -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/ui-surface/channel-variants.ts +10 -59
- 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-retro.ts +6 -8
- 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
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* concept pages or seeded skill entries change.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
22
23
|
import { getLogger } from "../logging.js";
|
|
23
24
|
import {
|
|
24
25
|
CLI_COMMAND_SLUG_PREFIX,
|
|
@@ -51,7 +52,11 @@ const SUMMARY_MAX_LENGTH = 200;
|
|
|
51
52
|
* and corrupt the format the router parses.
|
|
52
53
|
*/
|
|
53
54
|
function normalizeSummary(raw: string): string {
|
|
54
|
-
return
|
|
55
|
+
return safeStringSlice(
|
|
56
|
+
raw.replace(/\s+/g, " ").trim(),
|
|
57
|
+
0,
|
|
58
|
+
SUMMARY_MAX_LENGTH,
|
|
59
|
+
);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadWorkspaceMcpConfig } from "../../../../mcp/workspace-mcp-config.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Generic input for building capability statements: the capability fields of
|
|
@@ -92,7 +92,7 @@ export function augmentMcpSetupDescription(
|
|
|
92
92
|
if (input.id !== "mcp-setup") {
|
|
93
93
|
return input;
|
|
94
94
|
}
|
|
95
|
-
const servers =
|
|
95
|
+
const servers = loadWorkspaceMcpConfig().servers;
|
|
96
96
|
if (!servers) {
|
|
97
97
|
return input;
|
|
98
98
|
}
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "../../graph/store.js";
|
|
27
27
|
import type { MemoryNode } from "../../graph/types.js";
|
|
28
28
|
import { isCapabilityNode } from "../../graph/types.js";
|
|
29
|
-
import { BackendUnavailableError } from "../../host-utils.js";
|
|
29
|
+
import { BackendUnavailableError, safeStringSlice } from "../../host-utils.js";
|
|
30
30
|
import { extractToolUse, userMessage } from "../../llm-helpers.js";
|
|
31
31
|
import { getLogger } from "../../logging.js";
|
|
32
32
|
import {
|
|
@@ -268,7 +268,9 @@ async function identifyDuplicateGroups(
|
|
|
268
268
|
const listing = nodes
|
|
269
269
|
.map((n) => {
|
|
270
270
|
const preview =
|
|
271
|
-
n.content.length > 100
|
|
271
|
+
n.content.length > 100
|
|
272
|
+
? safeStringSlice(n.content, 0, 100) + "…"
|
|
273
|
+
: n.content;
|
|
272
274
|
return `[${n.id}] ${preview}`;
|
|
273
275
|
})
|
|
274
276
|
.join("\n");
|
|
@@ -46,7 +46,7 @@ import type {
|
|
|
46
46
|
SourceType,
|
|
47
47
|
TriggerType,
|
|
48
48
|
} from "../../graph/types.js";
|
|
49
|
-
import { BackendUnavailableError } from "../../host-utils.js";
|
|
49
|
+
import { BackendUnavailableError, safeStringSlice } from "../../host-utils.js";
|
|
50
50
|
import { extractToolUse, userMessage } from "../../llm-helpers.js";
|
|
51
51
|
import { getLogger } from "../../logging.js";
|
|
52
52
|
import { buildIdentityContext } from "../identity-context.js";
|
|
@@ -1617,7 +1617,9 @@ async function findCandidateNodes(
|
|
|
1617
1617
|
await import("../../../../../persistence/embeddings/embed.js");
|
|
1618
1618
|
const searchText =
|
|
1619
1619
|
transcript.length > 3000
|
|
1620
|
-
? transcript
|
|
1620
|
+
? safeStringSlice(transcript, 0, 1500) +
|
|
1621
|
+
"\n...\n" +
|
|
1622
|
+
safeStringSlice(transcript, transcript.length - 1500)
|
|
1621
1623
|
: transcript;
|
|
1622
1624
|
|
|
1623
1625
|
try {
|
|
@@ -33,6 +33,7 @@ import type {
|
|
|
33
33
|
ScoredNode,
|
|
34
34
|
} from "../../graph/types.js";
|
|
35
35
|
import { isCapabilityNode } from "../../graph/types.js";
|
|
36
|
+
import { safeStringSlice } from "../../host-utils.js";
|
|
36
37
|
import { extractToolUse, userMessage } from "../../llm-helpers.js";
|
|
37
38
|
import { getLogger } from "../../logging.js";
|
|
38
39
|
import { searchGraphNodes } from "./graph-search.js";
|
|
@@ -491,7 +492,9 @@ export async function loadContextMemory(
|
|
|
491
492
|
try {
|
|
492
493
|
const queryText = opts.recentSummaries.join("\n\n");
|
|
493
494
|
const truncated =
|
|
494
|
-
queryText.length > 3000
|
|
495
|
+
queryText.length > 3000
|
|
496
|
+
? safeStringSlice(queryText, 0, 3000)
|
|
497
|
+
: queryText;
|
|
495
498
|
contextQueryText = truncated;
|
|
496
499
|
const result = await embedWithRetry(opts.config, [truncated], {
|
|
497
500
|
signal: opts.signal,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
|
|
5
5
|
import type { AssistantConfig } from "../../../../config/types.js";
|
|
6
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
6
7
|
import { getLogger } from "../logging.js";
|
|
7
8
|
import { getWorkspaceDir } from "../paths.js";
|
|
8
9
|
import { readPage } from "../substrate/page-store.js";
|
|
@@ -57,7 +58,11 @@ function buildPassage(slug: string, body: string): string {
|
|
|
57
58
|
const blank = trimmed.search(/\n\s*\n/);
|
|
58
59
|
const para = blank === -1 ? trimmed : trimmed.slice(0, blank);
|
|
59
60
|
const stripped = para.replace(/^#+\s.*\n/, "").trim();
|
|
60
|
-
const compact =
|
|
61
|
+
const compact = safeStringSlice(
|
|
62
|
+
stripped.replace(/\s+/g, " "),
|
|
63
|
+
0,
|
|
64
|
+
PASSAGE_CHAR_CAP,
|
|
65
|
+
);
|
|
61
66
|
return `${slug}\n${compact}`;
|
|
62
67
|
}
|
|
63
68
|
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import { describe, expect, test } from "bun:test";
|
|
8
8
|
|
|
9
|
+
import { stripOrphanedSurrogates } from "../../../../../util/unicode.js";
|
|
10
|
+
import { LINK_SEPARATOR } from "../../substrate/page-links.js";
|
|
9
11
|
import { renderCard } from "../card.js";
|
|
10
12
|
|
|
11
13
|
const PAGE = `---
|
|
@@ -73,6 +75,28 @@ Lead paragraph for page a.
|
|
|
73
75
|
expect(line.length).toBeLessThan(300);
|
|
74
76
|
});
|
|
75
77
|
|
|
78
|
+
test("capping a `current:` value never splits an emoji at the cap", () => {
|
|
79
|
+
// 279 BMP characters put the 280-character cap inside the emoji's pair.
|
|
80
|
+
const current = `${"x".repeat(279)}🎉 trailing`;
|
|
81
|
+
const card = renderCard(
|
|
82
|
+
"page-a",
|
|
83
|
+
`---\ncurrent: "${current}"\n---\n\nLead.\n`,
|
|
84
|
+
);
|
|
85
|
+
const line = card.split("\n")[1]!;
|
|
86
|
+
expect(stripOrphanedSurrogates(line)).toBe(line);
|
|
87
|
+
expect(line).toBe(`[current: ${"x".repeat(279)}…]`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("capping a link note never splits an emoji at the cap", () => {
|
|
91
|
+
const note = `${"n".repeat(79)}🎉 trailing`;
|
|
92
|
+
const card = renderCard(
|
|
93
|
+
"page-a",
|
|
94
|
+
`---\nkind: index\nlinks:\n - "other${LINK_SEPARATOR}${note}"\n---\n\nLead.\n`,
|
|
95
|
+
);
|
|
96
|
+
expect(stripOrphanedSurrogates(card)).toBe(card);
|
|
97
|
+
expect(card).toContain(`other${LINK_SEPARATOR}${"n".repeat(79)}…`);
|
|
98
|
+
});
|
|
99
|
+
|
|
76
100
|
test("the `status:` draft marker does NOT render as a card line", () => {
|
|
77
101
|
const card = renderCard("page-a", `---\nstatus: cc-draft\n---\n\nLead.\n`);
|
|
78
102
|
expect(card).not.toContain("cc-draft");
|
|
@@ -39,7 +39,9 @@ import type {
|
|
|
39
39
|
SendMessageOptions,
|
|
40
40
|
} from "@vellumai/plugin-api";
|
|
41
41
|
|
|
42
|
+
import { OpenRouterProvider } from "../../../../../providers/openrouter/client.js";
|
|
42
43
|
import { ProviderError } from "../../../../../util/errors.js";
|
|
44
|
+
import { stripOrphanedSurrogates } from "../../../../../util/unicode.js";
|
|
43
45
|
import { sectionHeadLine } from "../sections.js";
|
|
44
46
|
import type { MemoryRoutingTurn, Section } from "../types.js";
|
|
45
47
|
|
|
@@ -842,4 +844,127 @@ describe("selectPool: sections and keyword-in-context snippets", () => {
|
|
|
842
844
|
true,
|
|
843
845
|
);
|
|
844
846
|
});
|
|
847
|
+
test("a window whose edges land inside an emoji keeps both pairs whole and the request well-formed", async () => {
|
|
848
|
+
providerStub = makeProvider(toolUseResponse({ ids: [] }));
|
|
849
|
+
// 200 emoji (400 code units) on each side of the term, offset so that a
|
|
850
|
+
// code-unit window centered on the term starts and ends mid-pair. A
|
|
851
|
+
// window cut there carries half a pair on each edge, which strict
|
|
852
|
+
// provider parsers reject once JSON.stringify escapes each orphan.
|
|
853
|
+
const emoji = "😀".repeat(200);
|
|
854
|
+
const body = `${emoji}y turnip ${emoji}`;
|
|
855
|
+
const term = body.indexOf("turnip");
|
|
856
|
+
const naiveStart = term + 3 - 150;
|
|
857
|
+
const naive = body.slice(naiveStart, naiveStart + 300);
|
|
858
|
+
expect(stripOrphanedSurrogates(naive)).not.toBe(naive);
|
|
859
|
+
const section = sectionOf("page-a", "Rollout", body);
|
|
860
|
+
await selectPool(
|
|
861
|
+
finderOnly({
|
|
862
|
+
slug: "page-a",
|
|
863
|
+
descriptor: section.text,
|
|
864
|
+
section,
|
|
865
|
+
terms: ["turnip"],
|
|
866
|
+
}),
|
|
867
|
+
makeTurn("turnip?"),
|
|
868
|
+
);
|
|
869
|
+
const [block] = sentBlocks();
|
|
870
|
+
expect(stripOrphanedSurrogates(block.text)).toBe(block.text);
|
|
871
|
+
const line = block.text
|
|
872
|
+
.split("\n")
|
|
873
|
+
.find((l) => l.startsWith("[1] page-a "))!;
|
|
874
|
+
expect(line).toContain("§Rollout: … ");
|
|
875
|
+
expect(line).toContain("turnip");
|
|
876
|
+
expect(line.endsWith(" …")).toBe(true);
|
|
877
|
+
// The window stays bounded: at most one code unit shorter on each edge.
|
|
878
|
+
const windowStart = line.indexOf("… ") + "… ".length;
|
|
879
|
+
expect(line.length - windowStart).toBeLessThanOrEqual(300 + " …".length);
|
|
880
|
+
expect(line.length - windowStart).toBeGreaterThanOrEqual(298 + " …".length);
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
// ---------------------------------------------------------------------------
|
|
885
|
+
// selectPool: cataloged thinking and forced-tool compatibility.
|
|
886
|
+
// ---------------------------------------------------------------------------
|
|
887
|
+
|
|
888
|
+
describe("selectPool: cataloged thinking and forced-tool compatibility", () => {
|
|
889
|
+
test("OpenRouter Kimi K2.6 omits the forced choice and yields a structured selection", async () => {
|
|
890
|
+
// A real OpenAI chat-completions provider stands in for the cataloged
|
|
891
|
+
// OpenRouter Kimi profile. The request succeeds without a reactive retry
|
|
892
|
+
// because the forced select_pages choice is omitted before dispatch.
|
|
893
|
+
const wireRequests: unknown[] = [];
|
|
894
|
+
const kimi = new OpenRouterProvider(
|
|
895
|
+
"test-key",
|
|
896
|
+
"moonshotai/kimi-k2.6-20260420",
|
|
897
|
+
);
|
|
898
|
+
(kimi as unknown as { client: unknown }).client = {
|
|
899
|
+
chat: {
|
|
900
|
+
completions: {
|
|
901
|
+
create: async (params: unknown) => {
|
|
902
|
+
wireRequests.push(JSON.parse(JSON.stringify(params)));
|
|
903
|
+
return {
|
|
904
|
+
async *[Symbol.asyncIterator]() {
|
|
905
|
+
yield {
|
|
906
|
+
choices: [
|
|
907
|
+
{
|
|
908
|
+
delta: {
|
|
909
|
+
tool_calls: [
|
|
910
|
+
{
|
|
911
|
+
index: 0,
|
|
912
|
+
id: "call-1",
|
|
913
|
+
function: {
|
|
914
|
+
name: "select_pages",
|
|
915
|
+
arguments: '{"ids":[3]}',
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
],
|
|
919
|
+
},
|
|
920
|
+
finish_reason: "tool_calls",
|
|
921
|
+
},
|
|
922
|
+
],
|
|
923
|
+
usage: { prompt_tokens: 10, completion_tokens: 2 },
|
|
924
|
+
};
|
|
925
|
+
},
|
|
926
|
+
};
|
|
927
|
+
},
|
|
928
|
+
},
|
|
929
|
+
},
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
// The profile layer injects the thinking effort onto the call config in
|
|
933
|
+
// production; emulate that here so the forced tool_choice rides alongside
|
|
934
|
+
// reasoning on the wire.
|
|
935
|
+
providerStub = {
|
|
936
|
+
name: "kimi-openai-compat",
|
|
937
|
+
sendMessage: (messages: Message[], options?: SendMessageOptions) =>
|
|
938
|
+
kimi.sendMessage(messages, {
|
|
939
|
+
...options,
|
|
940
|
+
config: {
|
|
941
|
+
...options?.config,
|
|
942
|
+
effort: "high",
|
|
943
|
+
thinking: { enabled: true },
|
|
944
|
+
},
|
|
945
|
+
}),
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
const selection = await selectPool(makePool(), makeTurn("rollout?"));
|
|
949
|
+
|
|
950
|
+
// The preflight path avoids an incompatible first request, and the
|
|
951
|
+
// pool-level re-prompt loop never engages.
|
|
952
|
+
expect(wireRequests).toHaveLength(1);
|
|
953
|
+
const first = wireRequests[0] as {
|
|
954
|
+
tool_choice?: unknown;
|
|
955
|
+
reasoning?: { effort?: string; summary?: string };
|
|
956
|
+
};
|
|
957
|
+
expect(first.tool_choice).toBeUndefined();
|
|
958
|
+
expect(first.reasoning).toMatchObject({
|
|
959
|
+
effort: "high",
|
|
960
|
+
summary: "detailed",
|
|
961
|
+
});
|
|
962
|
+
expect(warnPayloads().filter((p) => p.reason === "provider_error")).toEqual(
|
|
963
|
+
[],
|
|
964
|
+
);
|
|
965
|
+
|
|
966
|
+
// Structured selection survived: ids [3] is the topic-x finder line.
|
|
967
|
+
expect(selection.keptAll).toBe(false);
|
|
968
|
+
expect(selection.pages).toEqual([{ slug: "topic-x", sections: [] }]);
|
|
969
|
+
});
|
|
845
970
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { stripOrphanedSurrogates } from "../../../../../util/unicode.js";
|
|
3
4
|
import {
|
|
4
5
|
buildSectionIndex,
|
|
5
6
|
leadSectionOfBody,
|
|
@@ -108,6 +109,31 @@ describe("buildSectionIndex", () => {
|
|
|
108
109
|
}
|
|
109
110
|
});
|
|
110
111
|
|
|
112
|
+
test("a hard split never lands inside a surrogate pair and loses no text", async () => {
|
|
113
|
+
// One unbroken line of emoji longer than the window, offset by one BMP
|
|
114
|
+
// character so the window boundary falls between a pair's halves.
|
|
115
|
+
const unbroken = `y${"😀".repeat(SECTION_CHUNK_CHARS)}`;
|
|
116
|
+
const index = await buildSectionIndex(
|
|
117
|
+
["page-a"],
|
|
118
|
+
reader({ "page-a": `## Big\n${unbroken}` }),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const big = index.sections.filter((s) => s.title === "Big");
|
|
122
|
+
expect(big.length).toBeGreaterThan(1);
|
|
123
|
+
for (const chunk of big) {
|
|
124
|
+
expect(chunk.text.length).toBeLessThanOrEqual(SECTION_CHUNK_CHARS);
|
|
125
|
+
expect(stripOrphanedSurrogates(chunk.text)).toBe(chunk.text);
|
|
126
|
+
}
|
|
127
|
+
expect(big.map(sectionBody).join("")).toBe(unbroken);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("a capped head-line title never ends in half an emoji", () => {
|
|
131
|
+
const title = `${"t".repeat(199)}🎉 more`;
|
|
132
|
+
const head = sectionHeadLine("page-a", title);
|
|
133
|
+
expect(stripOrphanedSurrogates(head)).toBe(head);
|
|
134
|
+
expect(head).toBe(`page-a - ${"t".repeat(199)}`);
|
|
135
|
+
});
|
|
136
|
+
|
|
111
137
|
test("a heading longer than the window still chunks by near-window bodies; the key keeps the full title", async () => {
|
|
112
138
|
const title = "H".repeat(SECTION_CHUNK_CHARS + 100);
|
|
113
139
|
const body = "b".repeat(SECTION_CHUNK_CHARS * 2);
|
|
@@ -38,6 +38,7 @@ import { ESCALATION_CONTINUATION_CONTENT } from "../../../../../calls/voice-tria
|
|
|
38
38
|
import { MemoryV3GateSchema } from "../../../../../config/schemas/memory-v3.js";
|
|
39
39
|
import { ensureMemoryV3SelectionsSchema } from "../../../../../persistence/migrations/338-move-memory-v3-selections-to-memory-db.js";
|
|
40
40
|
import * as schema from "../../../../../persistence/schema/index.js";
|
|
41
|
+
import { stripOrphanedSurrogates } from "../../../../../util/unicode.js";
|
|
41
42
|
import type { HotSetEntry, HotSetOptions } from "../hot-set.js";
|
|
42
43
|
import type { OrchestrateResult } from "../orchestrate.js";
|
|
43
44
|
import {
|
|
@@ -1083,6 +1084,35 @@ describe("memory-v3 engine", () => {
|
|
|
1083
1084
|
expect(turn.previousAssistantMessage).toBeUndefined();
|
|
1084
1085
|
});
|
|
1085
1086
|
|
|
1087
|
+
test("the reply tail never starts inside a surrogate pair", async () => {
|
|
1088
|
+
// 1300 emoji then one BMP character: the 2500-code-unit tail would start
|
|
1089
|
+
// on a low surrogate, an orphan the embedding request's parser rejects.
|
|
1090
|
+
const reply = `${"😀".repeat(1300)}a`;
|
|
1091
|
+
messages = [
|
|
1092
|
+
{
|
|
1093
|
+
role: "user",
|
|
1094
|
+
content: JSON.stringify([{ type: "text", text: "first question" }]),
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
role: "assistant",
|
|
1098
|
+
content: JSON.stringify([{ type: "text", text: reply }]),
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
role: "user",
|
|
1102
|
+
content: JSON.stringify([{ type: "text", text: "and now this" }]),
|
|
1103
|
+
},
|
|
1104
|
+
];
|
|
1105
|
+
await observeTurn("conv-1", 1);
|
|
1106
|
+
|
|
1107
|
+
const turn = (
|
|
1108
|
+
orchestrateSpy.mock.calls as unknown as unknown[][]
|
|
1109
|
+
)[0]![0] as MemoryRoutingTurn;
|
|
1110
|
+
const tail = turn.previousAssistantMessage!;
|
|
1111
|
+
expect(stripOrphanedSurrogates(tail)).toBe(tail);
|
|
1112
|
+
expect(tail.length).toBeLessThanOrEqual(2500);
|
|
1113
|
+
expect(reply.endsWith(tail)).toBe(true);
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1086
1116
|
test("a page with lines from two lanes is attributed the lane of the line whose section was selected", () => {
|
|
1087
1117
|
const bulk: Section = {
|
|
1088
1118
|
article: "page-two",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FRONTMATTER_REGEX, parseFrontmatterFields } from "../frontmatter.js";
|
|
2
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
2
3
|
import { injectedConceptHeader } from "../substrate/injected-block-slugs.js";
|
|
3
4
|
import { LINK_SEPARATOR, parseLinkEntry } from "../substrate/page-links.js";
|
|
4
5
|
import type { Slug } from "./types.js";
|
|
@@ -38,7 +39,7 @@ function renderLinkEntry(entry: string): string {
|
|
|
38
39
|
}
|
|
39
40
|
const note =
|
|
40
41
|
description.length > LINK_NOTE_MAX_CHARS
|
|
41
|
-
? `${description
|
|
42
|
+
? `${safeStringSlice(description, 0, LINK_NOTE_MAX_CHARS).trimEnd()}…`
|
|
42
43
|
: description;
|
|
43
44
|
return `${target}${LINK_SEPARATOR}${note}`;
|
|
44
45
|
}
|
|
@@ -103,7 +104,7 @@ export function renderCurrentLine(
|
|
|
103
104
|
}
|
|
104
105
|
const capped =
|
|
105
106
|
collapsed.length > CURRENT_MAX_CHARS
|
|
106
|
-
? `${collapsed
|
|
107
|
+
? `${safeStringSlice(collapsed, 0, CURRENT_MAX_CHARS).trimEnd()}…`
|
|
107
108
|
: collapsed;
|
|
108
109
|
return `[current: ${capped}]`;
|
|
109
110
|
}
|
|
@@ -57,7 +57,7 @@ import { z } from "zod";
|
|
|
57
57
|
|
|
58
58
|
import { classifyConversationError } from "../../../../daemon/conversation-error.js";
|
|
59
59
|
import type { PendingConversationNotice } from "../../../../daemon/conversation-notices.js";
|
|
60
|
-
import { redactLogString, truncate } from "../host-utils.js";
|
|
60
|
+
import { redactLogString, safeStringSlice, truncate } from "../host-utils.js";
|
|
61
61
|
import {
|
|
62
62
|
cachedTextBlock,
|
|
63
63
|
extractToolUse,
|
|
@@ -276,7 +276,9 @@ function oneLine(text: string): string {
|
|
|
276
276
|
* `§<title>: … <window> …`, the window being up to {@link SNIPPET_MAX_CHARS}
|
|
277
277
|
* of the one-line section body centered on the first occurrence of the
|
|
278
278
|
* best-contributing query term that occurs in it (the lead, titled `""`,
|
|
279
|
-
* renders the window alone).
|
|
279
|
+
* renders the window alone). The window edges never split a surrogate pair:
|
|
280
|
+
* an orphaned half is invalid UTF-16 and strict provider parsers reject the
|
|
281
|
+
* whole request. `undefined` when no term occurs in the body.
|
|
280
282
|
*/
|
|
281
283
|
function renderKeywordInContext(
|
|
282
284
|
section: Section,
|
|
@@ -299,7 +301,7 @@ function renderKeywordInContext(
|
|
|
299
301
|
const end = Math.min(body.length, start + SNIPPET_MAX_CHARS);
|
|
300
302
|
const window = [
|
|
301
303
|
start > 0 ? "… " : "",
|
|
302
|
-
body
|
|
304
|
+
safeStringSlice(body, start, end).trim(),
|
|
303
305
|
end < body.length ? " …" : "",
|
|
304
306
|
].join("");
|
|
305
307
|
const title = section.title.trim();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
1
2
|
import type { Section, SectionIndex, Slug } from "./types.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -44,7 +45,7 @@ function lastSlugSegment(slug: Slug): string {
|
|
|
44
45
|
* {@link sectionBody}.
|
|
45
46
|
*/
|
|
46
47
|
export function sectionHeadLine(article: Slug, title: string): string {
|
|
47
|
-
return `${lastSlugSegment(article)} - ${title
|
|
48
|
+
return `${lastSlugSegment(article)} - ${safeStringSlice(title, 0, SECTION_HEAD_TITLE_CHARS)}`;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
/**
|
|
@@ -95,7 +96,9 @@ export function splitIntoRawSections(body: string): RawSection[] {
|
|
|
95
96
|
/**
|
|
96
97
|
* Split `text` into chunks no longer than `limit`, preferring to break on
|
|
97
98
|
* newlines so chunks stay readable. A single line longer than the limit is
|
|
98
|
-
* hard-split at the limit
|
|
99
|
+
* hard-split at the limit, backed off one code unit when the limit would cut
|
|
100
|
+
* a surrogate pair (an orphaned half is invalid UTF-16 that strict provider
|
|
101
|
+
* parsers reject). Order is preserved.
|
|
99
102
|
*/
|
|
100
103
|
function chunkText(text: string, limit: number): string[] {
|
|
101
104
|
if (text.length <= limit) {
|
|
@@ -109,7 +112,10 @@ function chunkText(text: string, limit: number): string[] {
|
|
|
109
112
|
const newlineBreak = window.lastIndexOf("\n");
|
|
110
113
|
// Only break on a newline if it leaves a non-trivial chunk; otherwise hard
|
|
111
114
|
// split at the window boundary to guarantee forward progress.
|
|
112
|
-
const breakAt =
|
|
115
|
+
const breakAt =
|
|
116
|
+
newlineBreak > 0
|
|
117
|
+
? newlineBreak
|
|
118
|
+
: Math.max(1, safeStringSlice(remaining, 0, limit).length);
|
|
113
119
|
chunks.push(remaining.slice(0, breakAt));
|
|
114
120
|
remaining = remaining.slice(breakAt).replace(/^\n/, "");
|
|
115
121
|
}
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
timeLatencySubSpan,
|
|
45
45
|
} from "../../../../daemon/turn-latency-sub-spans.js";
|
|
46
46
|
import { enqueueMemoryJob } from "../../../../persistence/jobs-store.js";
|
|
47
|
-
import { stripCommentLines } from "../host-utils.js";
|
|
47
|
+
import { safeStringSlice, stripCommentLines } from "../host-utils.js";
|
|
48
48
|
import { getLogger } from "../logging.js";
|
|
49
49
|
import { type MemorySqlite, memorySqliteOrNull } from "../memory-db.js";
|
|
50
50
|
import { getWorkspaceDir, getWorkspacePromptPath } from "../paths.js";
|
|
@@ -578,7 +578,10 @@ async function buildShadowTurn(
|
|
|
578
578
|
}
|
|
579
579
|
const text = stringifyMessageContent(rows[i]!.content);
|
|
580
580
|
if (text.length > 0) {
|
|
581
|
-
previousAssistantMessage =
|
|
581
|
+
previousAssistantMessage = safeStringSlice(
|
|
582
|
+
text,
|
|
583
|
+
Math.max(0, text.length - REPLY_QUERY_TAIL_CHARS),
|
|
584
|
+
);
|
|
582
585
|
break;
|
|
583
586
|
}
|
|
584
587
|
}
|
|
@@ -16,6 +16,10 @@ import { getConfig } from "../../../config/loader.js";
|
|
|
16
16
|
import { isMemoryEnabled } from "../../../config/memory-v3-gate.js";
|
|
17
17
|
import { rehydratePlatformCredentials } from "../../../config/platform-rehydration.js";
|
|
18
18
|
import { startConversationEvictor } from "../../../daemon/conversation-evictor.js";
|
|
19
|
+
import {
|
|
20
|
+
startWorkspaceSkillMdMtimePoll,
|
|
21
|
+
stopWorkspaceSkillMdMtimePoll,
|
|
22
|
+
} from "../../../daemon/skill-memory-refresh.js";
|
|
19
23
|
import { resetDb } from "../../../persistence/db-connection.js";
|
|
20
24
|
import {
|
|
21
25
|
EMBEDDING_SHUTDOWN_BUDGET_MS,
|
|
@@ -108,6 +112,7 @@ async function main(): Promise<void> {
|
|
|
108
112
|
shuttingDown = true;
|
|
109
113
|
log.info({ signal }, "Memory worker process shutting down");
|
|
110
114
|
worker?.stop();
|
|
115
|
+
stopWorkspaceSkillMdMtimePoll();
|
|
111
116
|
if (keepAlive != null) {
|
|
112
117
|
clearInterval(keepAlive);
|
|
113
118
|
}
|
|
@@ -161,6 +166,7 @@ async function main(): Promise<void> {
|
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
worker = startMemoryJobsWorkerLoop();
|
|
169
|
+
startWorkspaceSkillMdMtimePoll();
|
|
164
170
|
|
|
165
171
|
// Keep-alive: the worker's setTimeout timers are unref'd, so without
|
|
166
172
|
// this interval the process would exit immediately.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* This module reads those files and projects each entry onto the
|
|
15
15
|
* assistant's own `McpServerConfig` shape, so a plugin-declared server can
|
|
16
16
|
* flow through the same surfaces as one configured in the workspace
|
|
17
|
-
* `
|
|
17
|
+
* `mcp.json`.
|
|
18
18
|
*
|
|
19
19
|
* Failure isolation follows the spec: an invalid top-level `mcp.json`
|
|
20
20
|
* disables MCP for that plugin only, and an invalid individual server
|
|
@@ -39,17 +39,17 @@
|
|
|
39
39
|
import { existsSync, readFileSync } from "node:fs";
|
|
40
40
|
import { join } from "node:path";
|
|
41
41
|
|
|
42
|
-
import { z } from "zod";
|
|
43
|
-
|
|
44
42
|
import {
|
|
45
43
|
type AllPluginInfo,
|
|
46
44
|
listAllPlugins,
|
|
47
45
|
type ListInstalledPluginsOptions,
|
|
48
46
|
} from "../cli/lib/list-installed-plugins.js";
|
|
49
|
-
import type {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
import type { ResolvedMcpServerConfig } from "../config/schemas/mcp.js";
|
|
48
|
+
import {
|
|
49
|
+
projectSpecServerToTransport,
|
|
50
|
+
SpecMcpDocumentSchema,
|
|
51
|
+
SpecMcpServerSchema,
|
|
52
|
+
} from "../mcp/spec-schema.js";
|
|
53
53
|
import { getLogger } from "../util/logger.js";
|
|
54
54
|
|
|
55
55
|
const log = getLogger("plugin-mcp-servers");
|
|
@@ -77,45 +77,6 @@ export const PLUGIN_MCP_MANIFEST = "mcp.json";
|
|
|
77
77
|
* origin risk.
|
|
78
78
|
*/
|
|
79
79
|
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
// Wire schema (Agent Plugins 1.0.0)
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
|
|
84
|
-
const PluginStdioServerSchema = z.object({
|
|
85
|
-
type: z.literal("stdio"),
|
|
86
|
-
command: z.string().min(1),
|
|
87
|
-
args: z.array(z.string()).optional(),
|
|
88
|
-
env: z.record(z.string(), z.string()).optional(),
|
|
89
|
-
cwd: z.string().optional(),
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Remote MCP entry. Agent Plugins 1.0.0 requires `type` and names no
|
|
94
|
-
* default. MCP's current remote transport is Streamable HTTP, so this
|
|
95
|
-
* host fills omitted `type` and the Claude-style `http` alias as
|
|
96
|
-
* `streamable-http`. `sse` stays explicit.
|
|
97
|
-
*/
|
|
98
|
-
const PluginHttpServerSchema = z.object({
|
|
99
|
-
type: z
|
|
100
|
-
.union([
|
|
101
|
-
z.literal("streamable-http"),
|
|
102
|
-
z.literal("sse"),
|
|
103
|
-
z.literal("http").transform(() => "streamable-http" as const),
|
|
104
|
-
])
|
|
105
|
-
.default("streamable-http"),
|
|
106
|
-
url: z.string().min(1),
|
|
107
|
-
headers: z.record(z.string(), z.string()).optional(),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const PluginMcpServerSchema = z.union([
|
|
111
|
-
PluginStdioServerSchema,
|
|
112
|
-
PluginHttpServerSchema,
|
|
113
|
-
]);
|
|
114
|
-
|
|
115
|
-
const PluginMcpManifestSchema = z.object({
|
|
116
|
-
mcpServers: z.record(z.string(), z.unknown()),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
80
|
// ---------------------------------------------------------------------------
|
|
120
81
|
// Public shape
|
|
121
82
|
// ---------------------------------------------------------------------------
|
|
@@ -251,7 +212,7 @@ export function readPluginMcpServers(
|
|
|
251
212
|
continue;
|
|
252
213
|
}
|
|
253
214
|
|
|
254
|
-
const entry =
|
|
215
|
+
const entry = SpecMcpServerSchema.safeParse(raw);
|
|
255
216
|
if (!entry.success) {
|
|
256
217
|
issues.push({
|
|
257
218
|
pluginName: plugin.name,
|
|
@@ -325,7 +286,7 @@ function parseManifest(
|
|
|
325
286
|
};
|
|
326
287
|
}
|
|
327
288
|
|
|
328
|
-
const manifest =
|
|
289
|
+
const manifest = SpecMcpDocumentSchema.safeParse(json);
|
|
329
290
|
if (!manifest.success) {
|
|
330
291
|
return {
|
|
331
292
|
error: `${PLUGIN_MCP_MANIFEST} is missing a valid "mcpServers" object`,
|
|
@@ -334,33 +295,12 @@ function parseManifest(
|
|
|
334
295
|
return { mcpServers: manifest.data.mcpServers };
|
|
335
296
|
}
|
|
336
297
|
|
|
337
|
-
/**
|
|
338
|
-
* Project one spec-shaped entry onto the assistant's transport union. The
|
|
339
|
-
* two vocabularies already agree on type names and required fields, so this
|
|
340
|
-
* is a field copy plus path interpolation for the stdio case.
|
|
341
|
-
*/
|
|
342
298
|
function projectTransport(
|
|
343
|
-
entry:
|
|
299
|
+
entry: Parameters<typeof projectSpecServerToTransport>[0],
|
|
344
300
|
pluginRoot: string,
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
type: "stdio",
|
|
352
|
-
command: entry.command,
|
|
353
|
-
args: (entry.args ?? []).map(expand),
|
|
354
|
-
...(entry.env && {
|
|
355
|
-
env: Object.fromEntries(
|
|
356
|
-
Object.entries(entry.env).map(([k, v]) => [k, expand(v)]),
|
|
357
|
-
),
|
|
358
|
-
}),
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
return {
|
|
362
|
-
type: entry.type,
|
|
363
|
-
url: entry.url,
|
|
364
|
-
...(entry.headers && { headers: entry.headers }),
|
|
365
|
-
};
|
|
301
|
+
) {
|
|
302
|
+
const pluginData = join(pluginRoot, "data");
|
|
303
|
+
return projectSpecServerToTransport(entry, (value) =>
|
|
304
|
+
interpolatePluginPaths(value, pluginRoot, pluginData),
|
|
305
|
+
);
|
|
366
306
|
}
|