@vellumai/assistant 0.12.1-staging.1 → 0.12.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 +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/openapi.yaml +1 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- 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 +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +1 -2
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- 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 +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +0 -93
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -8
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +21 -75
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- 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-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +0 -2
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +21 -8
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -117
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -144
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The dictation cleanup pass rewrites what the user actually said, so a
|
|
3
|
-
* rewrite the provider cut off at the token cap must never become the
|
|
4
|
-
* payload: the raw transcript is sent instead.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, expect, test } from "bun:test";
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
computeMaxTokens,
|
|
11
|
-
resolveCleanedDictation,
|
|
12
|
-
} from "../diagnostics-routes.js";
|
|
13
|
-
|
|
14
|
-
const SPOKEN =
|
|
15
|
-
"Hello Quill can you generate a list of 20 open HR coordinator or HR " +
|
|
16
|
-
"related roles that are remote and pay about 50 to 60 K per year";
|
|
17
|
-
|
|
18
|
-
describe("resolveCleanedDictation", () => {
|
|
19
|
-
test("accepts an ordinary cleanup", () => {
|
|
20
|
-
const cleaned =
|
|
21
|
-
"Hello Quill, can you generate a list of 20 open HR coordinator or HR " +
|
|
22
|
-
"related roles that are remote and pay about 50 to 60K per year?";
|
|
23
|
-
expect(resolveCleanedDictation(SPOKEN, cleaned, "end_turn")).toEqual({
|
|
24
|
-
text: cleaned,
|
|
25
|
-
rejected: null,
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("trims surrounding whitespace off an accepted cleanup", () => {
|
|
30
|
-
const { text } = resolveCleanedDictation(
|
|
31
|
-
"so um I think we should ship the thing on Tuesday you know",
|
|
32
|
-
" I think we should ship the thing on Tuesday. ",
|
|
33
|
-
"end_turn",
|
|
34
|
-
);
|
|
35
|
-
expect(text).toBe("I think we should ship the thing on Tuesday.");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("rejects a rewrite cut off by max_tokens and keeps the raw words", () => {
|
|
39
|
-
expect(
|
|
40
|
-
resolveCleanedDictation(
|
|
41
|
-
SPOKEN,
|
|
42
|
-
"Hello Quill, can you create a list",
|
|
43
|
-
"max_tokens",
|
|
44
|
-
),
|
|
45
|
-
).toEqual({ text: SPOKEN, rejected: "truncated" });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("rejects a max_tokens rewrite even when it kept most of the words", () => {
|
|
49
|
-
const nearlyComplete = SPOKEN.slice(0, SPOKEN.length - 4);
|
|
50
|
-
const { rejected } = resolveCleanedDictation(
|
|
51
|
-
SPOKEN,
|
|
52
|
-
nearlyComplete,
|
|
53
|
-
"max_tokens",
|
|
54
|
-
);
|
|
55
|
-
expect(rejected).toBe("truncated");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("rejects the token-cap stop under every provider's name for it", () => {
|
|
59
|
-
for (const stopReason of ["length", "max_output_tokens", "MAX_TOKENS "]) {
|
|
60
|
-
expect(
|
|
61
|
-
resolveCleanedDictation(SPOKEN, "Hello Quill, can you", stopReason)
|
|
62
|
-
.rejected,
|
|
63
|
-
).toBe("truncated");
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("a missing stop reason is not treated as a truncation", () => {
|
|
68
|
-
const cleaned = "Hello Quill, can you generate a list?";
|
|
69
|
-
expect(resolveCleanedDictation(SPOKEN, cleaned, undefined)).toEqual({
|
|
70
|
-
text: cleaned,
|
|
71
|
-
rejected: null,
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("a much shorter rewrite is the model's call, not a rejection", () => {
|
|
76
|
-
// The prompt asks for fillers and vague phrasing to go, so a concise
|
|
77
|
-
// rewrite of a rambling utterance is a correct cleanup.
|
|
78
|
-
const spoken =
|
|
79
|
-
"um you know I just kind of wanted to say like thank you so much you know";
|
|
80
|
-
expect(
|
|
81
|
-
resolveCleanedDictation(spoken, "Thank you so much.", "end_turn"),
|
|
82
|
-
).toEqual({ text: "Thank you so much.", rejected: null });
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("an empty rewrite falls back to raw without flagging a rejection", () => {
|
|
86
|
-
// The model returned nothing usable; there is no rewrite to distrust.
|
|
87
|
-
expect(resolveCleanedDictation(SPOKEN, " ", "end_turn")).toEqual({
|
|
88
|
-
text: SPOKEN,
|
|
89
|
-
rejected: null,
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe("computeMaxTokens", () => {
|
|
95
|
-
test("budgets room for the transcript, reasoning, and JSON scaffolding", () => {
|
|
96
|
-
// The tool call carries the whole transcript back in `text`, so the
|
|
97
|
-
// budget has to clear the input's own token count with room to spare.
|
|
98
|
-
const estimatedInputTokens = Math.ceil(SPOKEN.length / 3);
|
|
99
|
-
expect(computeMaxTokens(SPOKEN.length)).toBeGreaterThan(
|
|
100
|
-
estimatedInputTokens * 2,
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("holds a floor for very short transcripts", () => {
|
|
105
|
-
expect(computeMaxTokens(5)).toBe(512);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route handler for a model-directed text send to a channel chat.
|
|
3
|
-
*
|
|
4
|
-
* POST /v1/channels/send: deliver text to a chat on a channel through the
|
|
5
|
-
* channel's transport, recorded after acknowledgement. The
|
|
6
|
-
* messaging tool's channel branch runs the same function in-process; this
|
|
7
|
-
* route is the door for the CLI and scripts, so the two doors share one
|
|
8
|
-
* seam and one record.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { z } from "zod";
|
|
12
|
-
|
|
13
|
-
import { CHANNEL_IDS } from "../../channels/types.js";
|
|
14
|
-
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
15
|
-
import {
|
|
16
|
-
ChannelNotAddressableError,
|
|
17
|
-
ChannelSendFailedError,
|
|
18
|
-
sendChannelText,
|
|
19
|
-
} from "../channel-send.js";
|
|
20
|
-
import { BadGatewayError, BadRequestError } from "./errors.js";
|
|
21
|
-
import { parseBody } from "./parse-body.js";
|
|
22
|
-
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
23
|
-
|
|
24
|
-
const ProactiveTargetSchema = z.object({
|
|
25
|
-
kind: z.literal("chat"),
|
|
26
|
-
chatId: z.string().min(1).describe("Chat id in the channel's own id space"),
|
|
27
|
-
threadId: z
|
|
28
|
-
.string()
|
|
29
|
-
.min(1)
|
|
30
|
-
.optional()
|
|
31
|
-
.describe("Thread within the chat, in the channel's own id space"),
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const ChannelSendRequestSchema = z.object({
|
|
35
|
-
channel: z.enum(CHANNEL_IDS).describe("Channel to send on"),
|
|
36
|
-
target: ProactiveTargetSchema,
|
|
37
|
-
text: z.string().min(1).describe("Text to send"),
|
|
38
|
-
renderRichly: z
|
|
39
|
-
.boolean()
|
|
40
|
-
.optional()
|
|
41
|
-
.describe("Ask the channel for its rich rendering, where it has one"),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const ChannelSendResponseSchema = z.object({
|
|
45
|
-
channel: z.enum(CHANNEL_IDS),
|
|
46
|
-
chatId: z.string(),
|
|
47
|
-
threadId: z.string().optional(),
|
|
48
|
-
messageIds: z.array(z.string()),
|
|
49
|
-
lastMessageId: z.string(),
|
|
50
|
-
recordedIn: z.string().optional(),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
async function handleChannelSend({ body }: RouteHandlerArgs) {
|
|
54
|
-
const request = parseBody(ChannelSendRequestSchema, body);
|
|
55
|
-
try {
|
|
56
|
-
return await sendChannelText(request);
|
|
57
|
-
} catch (e) {
|
|
58
|
-
if (e instanceof ChannelNotAddressableError) {
|
|
59
|
-
throw new BadRequestError(e.message);
|
|
60
|
-
}
|
|
61
|
-
if (e instanceof ChannelSendFailedError) {
|
|
62
|
-
throw new BadGatewayError(e.message);
|
|
63
|
-
}
|
|
64
|
-
throw e;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const ROUTES: RouteDefinition[] = [
|
|
69
|
-
{
|
|
70
|
-
operationId: "channels_send_post",
|
|
71
|
-
endpoint: "channels/send",
|
|
72
|
-
method: "POST",
|
|
73
|
-
policy: {
|
|
74
|
-
requiredScopes: ["chat.write"],
|
|
75
|
-
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
76
|
-
},
|
|
77
|
-
summary: "Send text to a channel chat",
|
|
78
|
-
description:
|
|
79
|
-
"Deliver text to a chat on a channel through the channel's transport, recorded after the channel acknowledges it.",
|
|
80
|
-
tags: ["channels"],
|
|
81
|
-
handler: handleChannelSend,
|
|
82
|
-
requestBody: ChannelSendRequestSchema,
|
|
83
|
-
responseBody: ChannelSendResponseSchema,
|
|
84
|
-
},
|
|
85
|
-
];
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
afterAll,
|
|
3
|
-
afterEach,
|
|
4
|
-
beforeEach,
|
|
5
|
-
describe,
|
|
6
|
-
expect,
|
|
7
|
-
spyOn,
|
|
8
|
-
test,
|
|
9
|
-
} from "bun:test";
|
|
10
|
-
|
|
11
|
-
import { setOverridesForTesting } from "../../__tests__/feature-flag-test-helpers.js";
|
|
12
|
-
import { desktopDependencyInstaller } from "../../desktop/desktop-dependencies.js";
|
|
13
|
-
import { ROUTES } from "./desktop-setup-routes.js";
|
|
14
|
-
import { NotFoundError } from "./errors.js";
|
|
15
|
-
|
|
16
|
-
const originalContainerized = process.env.IS_CONTAINERIZED;
|
|
17
|
-
const status = spyOn(desktopDependencyInstaller, "getStatus");
|
|
18
|
-
const start = spyOn(desktopDependencyInstaller, "start");
|
|
19
|
-
|
|
20
|
-
afterAll(() => {
|
|
21
|
-
status.mockRestore();
|
|
22
|
-
start.mockRestore();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
process.env.IS_CONTAINERIZED = "true";
|
|
27
|
-
status.mockReturnValue({ state: "required" });
|
|
28
|
-
start.mockReturnValue({ state: "installing", stage: "packages" });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
if (originalContainerized === undefined) {
|
|
33
|
-
delete process.env.IS_CONTAINERIZED;
|
|
34
|
-
} else {
|
|
35
|
-
process.env.IS_CONTAINERIZED = originalContainerized;
|
|
36
|
-
}
|
|
37
|
-
setOverridesForTesting({});
|
|
38
|
-
status.mockClear();
|
|
39
|
-
start.mockClear();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("desktop setup route feature gate", () => {
|
|
43
|
-
for (const route of ROUTES) {
|
|
44
|
-
for (const flag of [false, undefined]) {
|
|
45
|
-
test(`${route.method} refuses a disabled or missing flag (${flag}) without touching setup`, () => {
|
|
46
|
-
setOverridesForTesting(
|
|
47
|
-
flag === undefined ? {} : { "assistant-desktop": flag },
|
|
48
|
-
);
|
|
49
|
-
expect(() => route.handler({})).toThrow(NotFoundError);
|
|
50
|
-
expect(status).not.toHaveBeenCalled();
|
|
51
|
-
expect(start).not.toHaveBeenCalled();
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
test(`${route.method} refuses non-containerized assistants even with the flag enabled`, () => {
|
|
56
|
-
process.env.IS_CONTAINERIZED = "false";
|
|
57
|
-
setOverridesForTesting({ "assistant-desktop": true });
|
|
58
|
-
expect(() => route.handler({})).toThrow(NotFoundError);
|
|
59
|
-
expect(status).not.toHaveBeenCalled();
|
|
60
|
-
expect(start).not.toHaveBeenCalled();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test(`${route.method} serves setup only with the flag enabled in a container`, () => {
|
|
64
|
-
setOverridesForTesting({ "assistant-desktop": true });
|
|
65
|
-
expect(route.handler({})).toEqual(
|
|
66
|
-
route.method === "GET"
|
|
67
|
-
? { state: "required" }
|
|
68
|
-
: { state: "installing", stage: "packages" },
|
|
69
|
-
);
|
|
70
|
-
expect(route.method === "GET" ? status : start).toHaveBeenCalledTimes(1);
|
|
71
|
-
expect(route.method === "GET" ? start : status).not.toHaveBeenCalled();
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
import { getConfig } from "../../config/loader.js";
|
|
4
|
-
import { desktopDependencyInstaller } from "../../desktop/desktop-dependencies.js";
|
|
5
|
-
import { isAssistantDesktopEnabled } from "../../desktop/desktop-feature.js";
|
|
6
|
-
import { GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
|
|
7
|
-
import { NotFoundError } from "./errors.js";
|
|
8
|
-
import type { RouteDefinition } from "./types.js";
|
|
9
|
-
|
|
10
|
-
const statusSchema = z.object({
|
|
11
|
-
state: z.enum(["required", "installing", "ready", "failed", "unsupported"]),
|
|
12
|
-
stage: z.enum(["packages", "chrome", "checking"]).optional(),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export const ROUTES: RouteDefinition[] = ["GET", "POST"].map((method) => ({
|
|
16
|
-
operationId:
|
|
17
|
-
method === "GET" ? "desktop_setup_status" : "desktop_setup_install",
|
|
18
|
-
endpoint: "desktop/setup",
|
|
19
|
-
method,
|
|
20
|
-
policy: { requiredScopes: [], allowedPrincipalTypes: GATEWAY_PRINCIPALS },
|
|
21
|
-
handler: () => {
|
|
22
|
-
if (!isAssistantDesktopEnabled(getConfig())) {
|
|
23
|
-
throw new NotFoundError("Desktop is not available on this assistant");
|
|
24
|
-
}
|
|
25
|
-
return method === "GET"
|
|
26
|
-
? desktopDependencyInstaller.getStatus()
|
|
27
|
-
: desktopDependencyInstaller.start();
|
|
28
|
-
},
|
|
29
|
-
summary:
|
|
30
|
-
method === "GET"
|
|
31
|
-
? "Get desktop setup status"
|
|
32
|
-
: "Install desktop components",
|
|
33
|
-
tags: ["desktop"],
|
|
34
|
-
responseBody: statusSchema,
|
|
35
|
-
}));
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, spyOn, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import type { CredentialRecord } from "@vellumai/service-contracts/credential-rpc";
|
|
4
|
-
|
|
5
|
-
import * as envRegistry from "../../config/env-registry.js";
|
|
6
|
-
import {
|
|
7
|
-
CesHttpRecordBackend,
|
|
8
|
-
createCesHttpRecordBackendIfConfigured,
|
|
9
|
-
} from "../ces-http-record-backend.js";
|
|
10
|
-
|
|
11
|
-
const ORIGINAL_URL = process.env.CES_CREDENTIAL_URL;
|
|
12
|
-
const ORIGINAL_TOKEN = process.env.CES_SERVICE_TOKEN;
|
|
13
|
-
|
|
14
|
-
function restoreCesHttpEnv(): void {
|
|
15
|
-
if (ORIGINAL_URL === undefined) {
|
|
16
|
-
delete process.env.CES_CREDENTIAL_URL;
|
|
17
|
-
} else {
|
|
18
|
-
process.env.CES_CREDENTIAL_URL = ORIGINAL_URL;
|
|
19
|
-
}
|
|
20
|
-
if (ORIGINAL_TOKEN === undefined) {
|
|
21
|
-
delete process.env.CES_SERVICE_TOKEN;
|
|
22
|
-
} else {
|
|
23
|
-
process.env.CES_SERVICE_TOKEN = ORIGINAL_TOKEN;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function fakeHttpClient(options: {
|
|
28
|
-
listRecords: () => Promise<{
|
|
29
|
-
records: Array<{ account: string; record: CredentialRecord }>;
|
|
30
|
-
unreachable: boolean;
|
|
31
|
-
}>;
|
|
32
|
-
}) {
|
|
33
|
-
return {
|
|
34
|
-
getRecord: async () => ({ record: undefined, unreachable: false }),
|
|
35
|
-
setRecord: async () => false,
|
|
36
|
-
deleteRecord: async () => "error" as const,
|
|
37
|
-
listRecords: options.listRecords,
|
|
38
|
-
bulkSetRecords: async (records: Array<{ account: string }>) =>
|
|
39
|
-
records.map((entry) => ({ account: entry.account, ok: false })),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
describe("CES HTTP credential record backend", () => {
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
restoreCesHttpEnv();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("factory returns null when the assistant is not containerized", () => {
|
|
49
|
-
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(false);
|
|
50
|
-
process.env.CES_CREDENTIAL_URL = "http://ces-container:8090";
|
|
51
|
-
process.env.CES_SERVICE_TOKEN = "token-123";
|
|
52
|
-
try {
|
|
53
|
-
expect(createCesHttpRecordBackendIfConfigured()).toBeNull();
|
|
54
|
-
} finally {
|
|
55
|
-
spy.mockRestore();
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("factory returns null when CES_CREDENTIAL_URL is unset", () => {
|
|
60
|
-
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(true);
|
|
61
|
-
delete process.env.CES_CREDENTIAL_URL;
|
|
62
|
-
process.env.CES_SERVICE_TOKEN = "token-123";
|
|
63
|
-
try {
|
|
64
|
-
expect(createCesHttpRecordBackendIfConfigured()).toBeNull();
|
|
65
|
-
} finally {
|
|
66
|
-
spy.mockRestore();
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("factory returns null when CES_SERVICE_TOKEN is unset", () => {
|
|
71
|
-
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(true);
|
|
72
|
-
process.env.CES_CREDENTIAL_URL = "http://ces-container:8090";
|
|
73
|
-
delete process.env.CES_SERVICE_TOKEN;
|
|
74
|
-
try {
|
|
75
|
-
expect(createCesHttpRecordBackendIfConfigured()).toBeNull();
|
|
76
|
-
} finally {
|
|
77
|
-
spy.mockRestore();
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("factory returns a backend when containerized with CES HTTP env", () => {
|
|
82
|
-
const spy = spyOn(envRegistry, "getIsContainerized").mockReturnValue(true);
|
|
83
|
-
process.env.CES_CREDENTIAL_URL = "http://ces-container:8090";
|
|
84
|
-
process.env.CES_SERVICE_TOKEN = "token-123";
|
|
85
|
-
try {
|
|
86
|
-
const backend = createCesHttpRecordBackendIfConfigured();
|
|
87
|
-
expect(backend?.name).toBe("ces-http");
|
|
88
|
-
expect(backend?.isAvailable()).toBe(true);
|
|
89
|
-
} finally {
|
|
90
|
-
spy.mockRestore();
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("list returns records when HTTP reports a reachable catalog", async () => {
|
|
95
|
-
const record: CredentialRecord = {
|
|
96
|
-
credentialId: "cred-http",
|
|
97
|
-
service: "github",
|
|
98
|
-
field: "token",
|
|
99
|
-
allowedTools: ["bash"],
|
|
100
|
-
allowedDomains: [],
|
|
101
|
-
createdAt: 1,
|
|
102
|
-
updatedAt: 2,
|
|
103
|
-
};
|
|
104
|
-
const backend = new CesHttpRecordBackend(
|
|
105
|
-
fakeHttpClient({
|
|
106
|
-
listRecords: async () => ({
|
|
107
|
-
records: [{ account: "github:token", record }],
|
|
108
|
-
unreachable: false,
|
|
109
|
-
}),
|
|
110
|
-
}),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const listed = await backend.list();
|
|
114
|
-
expect(listed).toEqual([{ account: "github:token", record }]);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("list returns an empty catalog when HTTP is reachable and empty", async () => {
|
|
118
|
-
const backend = new CesHttpRecordBackend(
|
|
119
|
-
fakeHttpClient({
|
|
120
|
-
listRecords: async () => ({ records: [], unreachable: false }),
|
|
121
|
-
}),
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const listed = await backend.list();
|
|
125
|
-
expect(listed).toEqual([]);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("list returns null when HTTP reports the vault unreachable", async () => {
|
|
129
|
-
const backend = new CesHttpRecordBackend(
|
|
130
|
-
fakeHttpClient({
|
|
131
|
-
listRecords: async () => ({ records: [], unreachable: true }),
|
|
132
|
-
}),
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
expect(await backend.list()).toBeNull();
|
|
136
|
-
});
|
|
137
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
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
|
-
}
|