@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-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 +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/docs/guardian-request-flow.md +0 -16
- package/openapi.yaml +49 -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 +2 -4
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/document-sync-tags.test.ts +0 -25
- 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__/notification-vellum-adapter.test.ts +2 -85
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +16 -8
- 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/events/notification-intent.ts +0 -2
- 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/bash.help.ts +13 -8
- package/src/cli/commands/bash.ts +25 -7
- 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 +9 -99
- 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/SKILL.md +3 -3
- 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/schedule/SKILL.md +4 -3
- 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 -24
- package/src/config/loader.ts +0 -1
- 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 +27 -11
- 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/services.ts +0 -8
- 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/orphan-reaper.ts +1 -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/home/__tests__/feed-writer.test.ts +0 -22
- package/src/home/feed-writer.ts +2 -11
- 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__/guardian-feed-projection.test.ts +2 -158
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
- package/src/notifications/adapters/macos.ts +0 -3
- 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/guardian-feed-projection.ts +5 -125
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/notifications/schedule-result-producer.ts +22 -66
- package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
- package/src/oauth/byo-connection.test.ts +0 -45
- package/src/oauth/byo-connection.ts +6 -36
- package/src/oauth/connection-resolver.test.ts +0 -73
- package/src/oauth/connection-resolver.ts +2 -95
- package/src/oauth/seed-providers.ts +0 -165
- 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 +2 -4
- 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/inference/auth.ts +3 -2
- package/src/providers/inference/connection-availability.ts +8 -27
- package/src/providers/inference/resolve-auth.ts +4 -22
- 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__/default-provider-routes.test.ts +2 -2
- 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/debug-bash-routes.ts +173 -0
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +2 -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 +20 -6
- 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__/service-account-token.test.ts +0 -247
- 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__/bash.test.ts +0 -53
- 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 -255
- 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 -201
- 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/inference/service-account-token.ts +0 -248
- 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/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
- package/src/tools/terminal/sanitized-bash.ts +0 -105
- 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,245 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for `assistant apps inspect` and `assistant apps refresh`.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
|
-
|
|
10
|
-
import { createApp, getAppDirPath } from "../../../apps/app-store.js";
|
|
11
|
-
import { writeAppSourceFingerprint } from "../../../apps/source-fingerprint.js";
|
|
12
|
-
import { getWorkspacePluginsDir } from "../../../util/platform.js";
|
|
13
|
-
import { runCliCommand } from "./cli-test-harness.js";
|
|
14
|
-
|
|
15
|
-
let lastIpcCall: {
|
|
16
|
-
method: string;
|
|
17
|
-
params?: Record<string, unknown>;
|
|
18
|
-
} | null = null;
|
|
19
|
-
|
|
20
|
-
let mockIpcResult: {
|
|
21
|
-
ok: boolean;
|
|
22
|
-
result?: unknown;
|
|
23
|
-
error?: string;
|
|
24
|
-
statusCode?: number;
|
|
25
|
-
} = { ok: true, result: { ok: true, compiled: true } };
|
|
26
|
-
|
|
27
|
-
mock.module("../../../ipc/cli-client.js", () => ({
|
|
28
|
-
cliIpcCall: async (method: string, params?: Record<string, unknown>) => {
|
|
29
|
-
lastIpcCall = { method, params };
|
|
30
|
-
return mockIpcResult;
|
|
31
|
-
},
|
|
32
|
-
exitCodeFromIpcResult: (r: { statusCode?: number }) => {
|
|
33
|
-
if (r.statusCode === undefined) {
|
|
34
|
-
return 10;
|
|
35
|
-
}
|
|
36
|
-
if (r.statusCode >= 500) {
|
|
37
|
-
return 3;
|
|
38
|
-
}
|
|
39
|
-
if (r.statusCode >= 400) {
|
|
40
|
-
return 2;
|
|
41
|
-
}
|
|
42
|
-
return 1;
|
|
43
|
-
},
|
|
44
|
-
}));
|
|
45
|
-
|
|
46
|
-
const { registerAppsCommand } = await import("../apps.js");
|
|
47
|
-
|
|
48
|
-
let workspaceDir: string;
|
|
49
|
-
let previousWorkspaceDir: string | undefined;
|
|
50
|
-
|
|
51
|
-
function freshWorkspace(): string {
|
|
52
|
-
return join(
|
|
53
|
-
tmpdir(),
|
|
54
|
-
`vellum-apps-cli-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function uniqueAppName(prefix: string): string {
|
|
59
|
-
return `${prefix} ${Math.random().toString(36).slice(2, 8)}`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
previousWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
|
|
64
|
-
workspaceDir = freshWorkspace();
|
|
65
|
-
process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
|
|
66
|
-
lastIpcCall = null;
|
|
67
|
-
mockIpcResult = {
|
|
68
|
-
ok: true,
|
|
69
|
-
result: {
|
|
70
|
-
ok: true,
|
|
71
|
-
appId: "app-1",
|
|
72
|
-
name: "Budget",
|
|
73
|
-
compiled: true,
|
|
74
|
-
compile_duration_ms: 12,
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
afterEach(() => {
|
|
80
|
-
rmSync(workspaceDir, { recursive: true, force: true });
|
|
81
|
-
if (previousWorkspaceDir === undefined) {
|
|
82
|
-
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
83
|
-
} else {
|
|
84
|
-
process.env.VELLUM_WORKSPACE_DIR = previousWorkspaceDir;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
function parseJson(stdout: string): Record<string, unknown> {
|
|
89
|
-
return JSON.parse(stdout.trim()) as Record<string, unknown>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
describe("assistant apps inspect", () => {
|
|
93
|
-
test("reports never_compiled for a new workspace app", async () => {
|
|
94
|
-
const name = uniqueAppName("Inspect Fresh");
|
|
95
|
-
const created = createApp({
|
|
96
|
-
name,
|
|
97
|
-
schemaJson: "{}",
|
|
98
|
-
htmlDefinition: "<h1>Budget</h1>",
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const result = await runCliCommand(registerAppsCommand, [
|
|
102
|
-
"apps",
|
|
103
|
-
"inspect",
|
|
104
|
-
created.id,
|
|
105
|
-
"--json",
|
|
106
|
-
]);
|
|
107
|
-
|
|
108
|
-
expect(result.exitCode).toBe(0);
|
|
109
|
-
const body = parseJson(result.stdout);
|
|
110
|
-
expect(body.ok).toBe(true);
|
|
111
|
-
const compile = body.compile as { status: string };
|
|
112
|
-
expect(compile.status).toBe("never_compiled");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("reports stale after source changes since the last fingerprint", async () => {
|
|
116
|
-
const name = uniqueAppName("Inspect Stale");
|
|
117
|
-
const created = createApp({
|
|
118
|
-
name,
|
|
119
|
-
schemaJson: "{}",
|
|
120
|
-
htmlDefinition: "<h1>Budget</h1>",
|
|
121
|
-
});
|
|
122
|
-
const appDir = getAppDirPath(created.id);
|
|
123
|
-
expect(appDir.length).toBeGreaterThan(0);
|
|
124
|
-
mkdirSync(join(appDir, "src"), { recursive: true });
|
|
125
|
-
writeFileSync(join(appDir, "src", "main.tsx"), "export const n = 1;\n");
|
|
126
|
-
mkdirSync(join(appDir, "dist"), { recursive: true });
|
|
127
|
-
writeFileSync(join(appDir, "dist", "index.html"), "<html></html>");
|
|
128
|
-
writeAppSourceFingerprint(appDir, join(appDir, "dist"));
|
|
129
|
-
writeFileSync(join(appDir, "src", "main.tsx"), "export const n = 2;\n");
|
|
130
|
-
|
|
131
|
-
const result = await runCliCommand(registerAppsCommand, [
|
|
132
|
-
"apps",
|
|
133
|
-
"inspect",
|
|
134
|
-
created.id,
|
|
135
|
-
"--json",
|
|
136
|
-
]);
|
|
137
|
-
|
|
138
|
-
expect(result.exitCode).toBe(0);
|
|
139
|
-
const body = parseJson(result.stdout);
|
|
140
|
-
const app = body.app as { id: string; source: string };
|
|
141
|
-
expect(app.id).toBe(created.id);
|
|
142
|
-
expect(app.source).toBe(appDir);
|
|
143
|
-
const compile = body.compile as {
|
|
144
|
-
status: string;
|
|
145
|
-
modified: string[];
|
|
146
|
-
};
|
|
147
|
-
expect(compile.status).toBe("stale");
|
|
148
|
-
expect(compile.modified).toEqual(["src/main.tsx"]);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test("exits 1 for an unknown app", async () => {
|
|
152
|
-
const result = await runCliCommand(registerAppsCommand, [
|
|
153
|
-
"apps",
|
|
154
|
-
"inspect",
|
|
155
|
-
"missing",
|
|
156
|
-
"--json",
|
|
157
|
-
]);
|
|
158
|
-
expect(result.exitCode).toBe(1);
|
|
159
|
-
const body = parseJson(result.stdout);
|
|
160
|
-
expect(body.ok).toBe(false);
|
|
161
|
-
expect(String(body.error)).toContain("assistant apps list");
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe("assistant apps refresh", () => {
|
|
166
|
-
test("sends apps_refresh IPC for a workspace app", async () => {
|
|
167
|
-
const name = uniqueAppName("Refresh");
|
|
168
|
-
const created = createApp({
|
|
169
|
-
name,
|
|
170
|
-
schemaJson: "{}",
|
|
171
|
-
htmlDefinition: "<h1>Budget</h1>",
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
const result = await runCliCommand(registerAppsCommand, [
|
|
175
|
-
"apps",
|
|
176
|
-
"refresh",
|
|
177
|
-
created.id,
|
|
178
|
-
"--json",
|
|
179
|
-
]);
|
|
180
|
-
|
|
181
|
-
expect(result.exitCode).toBe(0);
|
|
182
|
-
expect(lastIpcCall).toEqual({
|
|
183
|
-
method: "apps_refresh",
|
|
184
|
-
params: { pathParams: { id: created.id } },
|
|
185
|
-
});
|
|
186
|
-
const body = parseJson(result.stdout);
|
|
187
|
-
expect(body.compiled).toBe(true);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
test("sends apps_refresh IPC for a plugin app", async () => {
|
|
191
|
-
const pluginName = `charts-${Math.random().toString(36).slice(2, 8)}`;
|
|
192
|
-
const pluginDir = join(getWorkspacePluginsDir(), pluginName);
|
|
193
|
-
mkdirSync(pluginDir, { recursive: true });
|
|
194
|
-
writeFileSync(
|
|
195
|
-
join(pluginDir, "package.json"),
|
|
196
|
-
JSON.stringify({ name: pluginName, version: "1.0.0" }),
|
|
197
|
-
);
|
|
198
|
-
mkdirSync(join(pluginDir, "apps", "viewer"), { recursive: true });
|
|
199
|
-
writeFileSync(join(pluginDir, "apps", "viewer", "index.html"), "<h1></h1>");
|
|
200
|
-
|
|
201
|
-
const result = await runCliCommand(registerAppsCommand, [
|
|
202
|
-
"apps",
|
|
203
|
-
"refresh",
|
|
204
|
-
"viewer",
|
|
205
|
-
"--json",
|
|
206
|
-
]);
|
|
207
|
-
|
|
208
|
-
expect(result.exitCode).toBe(0);
|
|
209
|
-
expect(lastIpcCall).toEqual({
|
|
210
|
-
method: "apps_refresh",
|
|
211
|
-
params: { pathParams: { id: `plugins~${pluginName}~viewer` } },
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("exits 1 when compile fails", async () => {
|
|
216
|
-
const name = uniqueAppName("Refresh Fail");
|
|
217
|
-
const created = createApp({
|
|
218
|
-
name,
|
|
219
|
-
schemaJson: "{}",
|
|
220
|
-
htmlDefinition: "<h1>Budget</h1>",
|
|
221
|
-
});
|
|
222
|
-
mockIpcResult = {
|
|
223
|
-
ok: true,
|
|
224
|
-
result: {
|
|
225
|
-
ok: true,
|
|
226
|
-
appId: created.id,
|
|
227
|
-
name,
|
|
228
|
-
compiled: false,
|
|
229
|
-
compile_duration_ms: 8,
|
|
230
|
-
compile_errors: [{ text: "Could not resolve foo" }],
|
|
231
|
-
},
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const result = await runCliCommand(registerAppsCommand, [
|
|
235
|
-
"apps",
|
|
236
|
-
"refresh",
|
|
237
|
-
created.id,
|
|
238
|
-
"--json",
|
|
239
|
-
]);
|
|
240
|
-
|
|
241
|
-
expect(result.exitCode).toBe(1);
|
|
242
|
-
const body = parseJson(result.stdout);
|
|
243
|
-
expect(body.compiled).toBe(false);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for `assistant bash`: local sanitized spawn, no IPC round-trip.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
6
|
-
|
|
7
|
-
import { runCliCommand } from "./cli-test-harness.js";
|
|
8
|
-
|
|
9
|
-
const ipcCalls: Array<{ method: string }> = [];
|
|
10
|
-
|
|
11
|
-
mock.module("../../../ipc/cli-client.js", () => ({
|
|
12
|
-
cliIpcCall: async (method: string) => {
|
|
13
|
-
ipcCalls.push({ method });
|
|
14
|
-
return { ok: false, error: "IPC should not be used" };
|
|
15
|
-
},
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
mock.module("../../logger.js", () => ({
|
|
19
|
-
log: {
|
|
20
|
-
info: () => {},
|
|
21
|
-
warn: () => {},
|
|
22
|
-
error: (message: string) => {
|
|
23
|
-
process.stderr.write(`${message}\n`);
|
|
24
|
-
},
|
|
25
|
-
debug: () => {},
|
|
26
|
-
},
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
const { registerBashCommand } = await import("../bash.js");
|
|
30
|
-
|
|
31
|
-
describe("assistant bash", () => {
|
|
32
|
-
test("spawns locally and prints stdout without IPC", async () => {
|
|
33
|
-
ipcCalls.length = 0;
|
|
34
|
-
const result = await runCliCommand(registerBashCommand, [
|
|
35
|
-
"bash",
|
|
36
|
-
"echo hello",
|
|
37
|
-
]);
|
|
38
|
-
expect(ipcCalls).toEqual([]);
|
|
39
|
-
expect(result.stdout.trim()).toBe("hello");
|
|
40
|
-
expect(result.exitCode).toBe(0);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test("rejects a non-positive timeout", async () => {
|
|
44
|
-
const result = await runCliCommand(registerBashCommand, [
|
|
45
|
-
"bash",
|
|
46
|
-
"echo hello",
|
|
47
|
-
"--timeout",
|
|
48
|
-
"0",
|
|
49
|
-
]);
|
|
50
|
-
expect(result.stderr).toContain("Invalid timeout value");
|
|
51
|
-
expect(result.exitCode).toBe(1);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { roadmapHelp } from "../roadmap.help.js";
|
|
4
|
-
|
|
5
|
-
const DISCORD_URL = "https://vellum.ai/community";
|
|
6
|
-
|
|
7
|
-
describe("roadmap help is feature-proposal-only", () => {
|
|
8
|
-
test("top-level help describes feature proposals and routes bugs to Discord", () => {
|
|
9
|
-
const description = roadmapHelp.description;
|
|
10
|
-
const helpText = roadmapHelp.helpText ?? "";
|
|
11
|
-
const combined = `${description}\n${helpText}`.toLowerCase();
|
|
12
|
-
|
|
13
|
-
expect(combined).toMatch(/feature proposals?/);
|
|
14
|
-
expect(description.split(DISCORD_URL)).toHaveLength(2);
|
|
15
|
-
expect(helpText).toContain(DISCORD_URL);
|
|
16
|
-
expect(combined).toContain("instruct the user");
|
|
17
|
-
expect(combined).not.toMatch(/support@|github\.com|share feedback/);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
// The command is a door onto the daemon's one send implementation, so what
|
|
4
|
-
// matters is the request it hands the route and what it does with the
|
|
5
|
-
// answer. IPC is stubbed to capture that request; the exit-code helper
|
|
6
|
-
// stays real, so the tests assert the mapping that ships.
|
|
7
|
-
|
|
8
|
-
const ipcCalls: Array<{
|
|
9
|
-
method: string;
|
|
10
|
-
params: Record<string, unknown>;
|
|
11
|
-
opts?: Record<string, unknown>;
|
|
12
|
-
}> = [];
|
|
13
|
-
let ipcResult: Record<string, unknown> = {
|
|
14
|
-
ok: true,
|
|
15
|
-
result: {
|
|
16
|
-
channel: "slack",
|
|
17
|
-
chatId: "C0123456789",
|
|
18
|
-
messageIds: ["1700000000.000100"],
|
|
19
|
-
lastMessageId: "1700000000.000100",
|
|
20
|
-
recordedIn: "conv-home",
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const actualCliClient = await import("../../../../ipc/cli-client.js");
|
|
25
|
-
mock.module("../../../../ipc/cli-client.js", () => ({
|
|
26
|
-
...actualCliClient,
|
|
27
|
-
cliIpcCall: async (
|
|
28
|
-
method: string,
|
|
29
|
-
params: Record<string, unknown>,
|
|
30
|
-
opts?: Record<string, unknown>,
|
|
31
|
-
) => {
|
|
32
|
-
ipcCalls.push({ method, params, ...(opts ? { opts } : {}) });
|
|
33
|
-
return ipcResult;
|
|
34
|
-
},
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
// Imported after the mock is installed, so the command module under test
|
|
38
|
-
// binds the stubbed IPC call rather than the real one.
|
|
39
|
-
const { runCliCommand } = await import("../../__tests__/cli-test-harness.js");
|
|
40
|
-
const { registerChannelsCommand } = await import("../index.js");
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
ipcCalls.length = 0;
|
|
44
|
-
ipcResult = {
|
|
45
|
-
ok: true,
|
|
46
|
-
result: {
|
|
47
|
-
channel: "slack",
|
|
48
|
-
chatId: "C0123456789",
|
|
49
|
-
messageIds: ["1700000000.000100"],
|
|
50
|
-
lastMessageId: "1700000000.000100",
|
|
51
|
-
recordedIn: "conv-home",
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// The shipped registration as-is: `send` declares its own `--json`, which is
|
|
57
|
-
// why the program's global leaf-option pass skips it.
|
|
58
|
-
function runSend(args: string[]) {
|
|
59
|
-
return runCliCommand(registerChannelsCommand, ["channels", "send", ...args]);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** The body the command handed the route on its one call. */
|
|
63
|
-
function sentBody(): Record<string, unknown> {
|
|
64
|
-
expect(ipcCalls).toHaveLength(1);
|
|
65
|
-
expect(ipcCalls[0]!.method).toBe("channels_send_post");
|
|
66
|
-
return ipcCalls[0]!.params.body as Record<string, unknown>;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
describe("the request the command makes", () => {
|
|
70
|
-
test("names the chat and asks for the channel's rich rendering", async () => {
|
|
71
|
-
const r = await runSend([
|
|
72
|
-
"slack",
|
|
73
|
-
"C0123456789",
|
|
74
|
-
"--text",
|
|
75
|
-
"deploy is green",
|
|
76
|
-
]);
|
|
77
|
-
expect(r.exitCode).toBe(0);
|
|
78
|
-
expect(sentBody()).toEqual({
|
|
79
|
-
channel: "slack",
|
|
80
|
-
target: { kind: "chat", chatId: "C0123456789" },
|
|
81
|
-
text: "deploy is green",
|
|
82
|
-
renderRichly: true,
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("carries a named thread as the target's thread", async () => {
|
|
87
|
-
await runSend([
|
|
88
|
-
"slack",
|
|
89
|
-
"C0123456789",
|
|
90
|
-
"--thread",
|
|
91
|
-
"1700000000.000001",
|
|
92
|
-
"--text",
|
|
93
|
-
"and the smoke tests passed",
|
|
94
|
-
]);
|
|
95
|
-
expect(sentBody().target).toEqual({
|
|
96
|
-
kind: "chat",
|
|
97
|
-
chatId: "C0123456789",
|
|
98
|
-
threadId: "1700000000.000001",
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("--plain asks for the text verbatim", async () => {
|
|
103
|
-
await runSend(["telegram", "123456789", "--text", "morning", "--plain"]);
|
|
104
|
-
const body = sentBody();
|
|
105
|
-
expect(body.renderRichly).toBeUndefined();
|
|
106
|
-
expect(body.channel).toBe("telegram");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("sends the text as the caller wrote it, whitespace included", async () => {
|
|
110
|
-
await runSend([
|
|
111
|
-
"slack",
|
|
112
|
-
"C0123456789",
|
|
113
|
-
"--text",
|
|
114
|
-
" indented ",
|
|
115
|
-
"--plain",
|
|
116
|
-
]);
|
|
117
|
-
expect(sentBody().text).toBe(" indented ");
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test("waits longer than the IPC default, since the send itself retries", async () => {
|
|
121
|
-
await runSend(["slack", "C0123456789", "--text", "hi"]);
|
|
122
|
-
expect(ipcCalls[0]!.opts).toMatchObject({ timeoutMs: 120_000 });
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("refuses an empty text before it sends anything", async () => {
|
|
126
|
-
for (const args of [
|
|
127
|
-
["slack", "C0123456789"],
|
|
128
|
-
["slack", "C0123456789", "--text", " "],
|
|
129
|
-
]) {
|
|
130
|
-
ipcCalls.length = 0;
|
|
131
|
-
const r = await runSend(args);
|
|
132
|
-
expect(r.exitCode).toBe(1);
|
|
133
|
-
expect(r.stderr).toContain("--text is required");
|
|
134
|
-
expect(ipcCalls).toEqual([]);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe("what the command reports", () => {
|
|
140
|
-
test("names where the post landed and the conversation it was recorded in", async () => {
|
|
141
|
-
const r = await runSend(["slack", "C0123456789", "--text", "hello"]);
|
|
142
|
-
expect(r.stdout).toContain("C0123456789");
|
|
143
|
-
expect(r.stdout).toContain("1700000000.000100");
|
|
144
|
-
expect(r.stdout).toContain("conv-home");
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("says so when the channel split the text", async () => {
|
|
148
|
-
ipcResult = {
|
|
149
|
-
ok: true,
|
|
150
|
-
result: {
|
|
151
|
-
channel: "telegram",
|
|
152
|
-
chatId: "123456789",
|
|
153
|
-
messageIds: ["10", "11", "12"],
|
|
154
|
-
lastMessageId: "12",
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
const r = await runSend(["telegram", "123456789", "--text", "a long one"]);
|
|
158
|
-
expect(r.stdout).toContain("3 posts");
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test("--json answers with the route's own result", async () => {
|
|
162
|
-
const r = await runSend([
|
|
163
|
-
"slack",
|
|
164
|
-
"C0123456789",
|
|
165
|
-
"--text",
|
|
166
|
-
"hello",
|
|
167
|
-
"--json",
|
|
168
|
-
]);
|
|
169
|
-
expect(JSON.parse(r.stdout)).toEqual({
|
|
170
|
-
channel: "slack",
|
|
171
|
-
chatId: "C0123456789",
|
|
172
|
-
messageIds: ["1700000000.000100"],
|
|
173
|
-
lastMessageId: "1700000000.000100",
|
|
174
|
-
recordedIn: "conv-home",
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test("a refused send reports the route's reason and its exit code", async () => {
|
|
179
|
-
ipcResult = {
|
|
180
|
-
ok: false,
|
|
181
|
-
error: 'Channel "email" is not addressable',
|
|
182
|
-
statusCode: 400,
|
|
183
|
-
};
|
|
184
|
-
const r = await runSend(["email", "someone", "--text", "hello"]);
|
|
185
|
-
expect(r.exitCode).toBe(
|
|
186
|
-
actualCliClient.exitCodeFromIpcResult({ statusCode: 400 }),
|
|
187
|
-
);
|
|
188
|
-
expect(r.stderr).toContain("not addressable");
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
test("a timeout is reported as an unknown outcome, not a failure to retry", async () => {
|
|
192
|
-
ipcResult = { ok: false, error: "Request timed out", timedOut: true };
|
|
193
|
-
const r = await runSend(["slack", "C0123456789", "--text", "hello"]);
|
|
194
|
-
expect(r.stderr).toContain("may still be in flight");
|
|
195
|
-
expect(r.stderr).toContain("before sending again");
|
|
196
|
-
expect(r.stderr).not.toContain("The send failed");
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test("a refusal in --json mode is a JSON error envelope", async () => {
|
|
200
|
-
ipcResult = {
|
|
201
|
-
ok: false,
|
|
202
|
-
error: 'Channel "slack" did not acknowledge the send',
|
|
203
|
-
statusCode: 502,
|
|
204
|
-
};
|
|
205
|
-
const r = await runSend([
|
|
206
|
-
"slack",
|
|
207
|
-
"C0123456789",
|
|
208
|
-
"--text",
|
|
209
|
-
"hello",
|
|
210
|
-
"--json",
|
|
211
|
-
]);
|
|
212
|
-
expect(JSON.parse(r.stdout)).toEqual({
|
|
213
|
-
ok: false,
|
|
214
|
-
error: 'Channel "slack" did not acknowledge the send',
|
|
215
|
-
});
|
|
216
|
-
expect(r.exitCode).toBe(3);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `assistant channels send`: post text to a chat on a channel, as the
|
|
3
|
-
* assistant's own bot.
|
|
4
|
-
*
|
|
5
|
-
* The send door. It runs the daemon function the messaging tool runs
|
|
6
|
-
* (`sendChannelText`, over `POST /v1/channels/send`), so a send from the
|
|
7
|
-
* command line reaches the chat through the channel's transport and leaves
|
|
8
|
-
* the same record: threaded where a thread is named, rendered the way the
|
|
9
|
-
* channel renders a reply, and written to the chat's conversation only once
|
|
10
|
-
* the channel has acknowledged it.
|
|
11
|
-
*
|
|
12
|
-
* Posting the same text through `channels request` reaches the platform API
|
|
13
|
-
* directly and leaves no record of what was said, which is why this command
|
|
14
|
-
* exists beside it. Both act as the bot and both carry the same rating: the
|
|
15
|
-
* effect is a message either way.
|
|
16
|
-
*
|
|
17
|
-
* A channel whose transport cannot be addressed from a named chat is
|
|
18
|
-
* refused by the route before anything is sent.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import type { Command } from "commander";
|
|
22
|
-
|
|
23
|
-
import { cliIpcCall, exitCodeFromIpcResult } from "../../../ipc/cli-client.js";
|
|
24
|
-
import { subcommand } from "../../lib/cli-command-help.js";
|
|
25
|
-
import { log } from "../../logger.js";
|
|
26
|
-
import { shouldOutputJson, writeError, writeOutput } from "../../output.js";
|
|
27
|
-
|
|
28
|
-
/** What the channel acknowledged, and where the post was recorded. */
|
|
29
|
-
interface ChannelSendResponse {
|
|
30
|
-
channel: string;
|
|
31
|
-
chatId: string;
|
|
32
|
-
threadId?: string;
|
|
33
|
-
messageIds: string[];
|
|
34
|
-
lastMessageId: string;
|
|
35
|
-
recordedIn?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface ChannelSendOptions {
|
|
39
|
-
text?: string;
|
|
40
|
-
thread?: string;
|
|
41
|
-
plain?: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* How long to wait for the daemon before giving up on an answer.
|
|
46
|
-
*
|
|
47
|
-
* Longer than the IPC default because the work behind this call is a
|
|
48
|
-
* platform request that retries: a rate-limited Slack send sleeps for the
|
|
49
|
-
* `Retry-After` Slack names and tries again up to three times
|
|
50
|
-
* (`MAX_RATE_LIMIT_RETRIES`, providers/slack/web-api-transport.ts), and the
|
|
51
|
-
* other channels back off similarly. Waiting past that is better than
|
|
52
|
-
* abandoning a send that is still being made, because giving up tells the
|
|
53
|
-
* caller nothing about whether the message went out.
|
|
54
|
-
*/
|
|
55
|
-
const SEND_CALL_TIMEOUT_MS = 120_000;
|
|
56
|
-
|
|
57
|
-
export function registerChannelsSendCommand(channels: Command): void {
|
|
58
|
-
subcommand(channels, "send").action(
|
|
59
|
-
async (
|
|
60
|
-
channel: string,
|
|
61
|
-
chatId: string,
|
|
62
|
-
opts: ChannelSendOptions,
|
|
63
|
-
cmd: Command,
|
|
64
|
-
) => {
|
|
65
|
-
// Trimmed only to reject a blank message: the text goes out as the
|
|
66
|
-
// caller wrote it, which `--plain` in particular promises.
|
|
67
|
-
const text = opts.text;
|
|
68
|
-
if (!text?.trim()) {
|
|
69
|
-
writeError(cmd, "--text is required and cannot be empty.");
|
|
70
|
-
process.exitCode = 1;
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const threadId = opts.thread?.trim();
|
|
75
|
-
const r = await cliIpcCall<ChannelSendResponse>(
|
|
76
|
-
"channels_send_post",
|
|
77
|
-
{
|
|
78
|
-
body: {
|
|
79
|
-
channel,
|
|
80
|
-
target: {
|
|
81
|
-
kind: "chat",
|
|
82
|
-
chatId,
|
|
83
|
-
...(threadId ? { threadId } : {}),
|
|
84
|
-
},
|
|
85
|
-
text,
|
|
86
|
-
// Rich unless asked otherwise, so a send from here reads like
|
|
87
|
-
// the assistant's other posts in that chat rather than like a
|
|
88
|
-
// different sender.
|
|
89
|
-
...(opts.plain ? {} : { renderRichly: true }),
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
{ timeoutMs: SEND_CALL_TIMEOUT_MS },
|
|
93
|
-
);
|
|
94
|
-
if (!r.ok) {
|
|
95
|
-
// A timeout is not a failure: closing the client socket does not
|
|
96
|
-
// stop the daemon, so the message may still go out. Saying so is
|
|
97
|
-
// what keeps a retry from posting it twice.
|
|
98
|
-
const message = r.timedOut
|
|
99
|
-
? `The daemon did not answer within ${Math.round(SEND_CALL_TIMEOUT_MS / 1000)}s. The send may still be in flight, so it may or may not have gone out. Check the chat before sending again.`
|
|
100
|
-
: (r.error ?? "The send failed.");
|
|
101
|
-
// Reported rather than exited, so `--json` gets an error envelope
|
|
102
|
-
// like every other answer this command gives.
|
|
103
|
-
writeError(cmd, message);
|
|
104
|
-
process.exitCode = exitCodeFromIpcResult({
|
|
105
|
-
statusCode: r.statusCode,
|
|
106
|
-
});
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const sent = r.result!;
|
|
111
|
-
if (shouldOutputJson(cmd)) {
|
|
112
|
-
writeOutput(cmd, sent);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const where = sent.threadId
|
|
117
|
-
? `${sent.chatId}, thread ${sent.threadId}`
|
|
118
|
-
: sent.chatId;
|
|
119
|
-
log.info(`Sent to ${channel} ${where} (id: ${sent.lastMessageId})`);
|
|
120
|
-
if (sent.messageIds.length > 1) {
|
|
121
|
-
log.info(`The channel split it into ${sent.messageIds.length} posts`);
|
|
122
|
-
}
|
|
123
|
-
if (sent.recordedIn) {
|
|
124
|
-
log.info(`Recorded in conversation ${sent.recordedIn}`);
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
);
|
|
128
|
-
}
|