@vellumai/assistant 0.12.0 → 0.12.1-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -1
- package/ARCHITECTURE.md +11 -12
- package/docs/architecture/turn-actor.md +15 -8
- package/openapi.yaml +292 -2
- package/package.json +1 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +17 -5
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +13 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +19 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +94 -9
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/index.ts +6 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +58 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +37 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +53 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +117 -0
- package/src/config/__tests__/platform-rehydration.test.ts +93 -0
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +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 +8 -12
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +144 -0
- package/src/config/platform-rehydration.ts +75 -21
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +10 -8
- package/src/daemon/conversation-tool-setup.ts +37 -43
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +119 -0
- package/src/desktop/desktop-chrome-policy.test.ts +71 -0
- package/src/desktop/desktop-chrome-policy.ts +42 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +306 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +627 -0
- package/src/desktop/desktop-session-manager.ts +814 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +2 -0
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/graph/capability-seed.ts +4 -6
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +6 -1
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +89 -0
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +192 -78
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/credential-routes.ts +19 -10
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +55 -3
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +8 -21
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +4 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/safe-env.test.ts +6 -6
- package/src/tools/terminal/safe-env.ts +23 -3
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +5 -54
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -60,7 +60,7 @@ Apps live under `/workspace/data/apps/`:
|
|
|
60
60
|
<slug>.json # App metadata
|
|
61
61
|
<slug>/
|
|
62
62
|
src/ # Source files (TSX) — what you write
|
|
63
|
-
dist/ # Compiled output
|
|
63
|
+
dist/ # Compiled output, generated by app_refresh or assistant apps refresh
|
|
64
64
|
records/ # Data records (one JSON file per record)
|
|
65
65
|
<slug>.preview # Preview image (auto-generated)
|
|
66
66
|
```
|
|
@@ -156,9 +156,9 @@ Calculators, single-page tools, landing pages, and slide decks skip this — pas
|
|
|
156
156
|
|
|
157
157
|
### 3 — Create the app (scaffold, then expand)
|
|
158
158
|
|
|
159
|
-
⚠️ **`app_create` is ONE-SHOT per build.** Call it exactly once. After it returns an `app_id`, all further changes go through `file_write` / `file_edit`
|
|
159
|
+
⚠️ **`app_create` is ONE-SHOT per build.** Call it exactly once. After it returns an `app_id`, all further changes go through `file_write` / `file_edit` and a single compile (`assistant apps refresh` or `app_refresh`). To start over: `app_delete(app_id)` first, then a fresh `app_create`.
|
|
160
160
|
|
|
161
|
-
Apps are multi-file Preact + TSX projects; esbuild bundles
|
|
161
|
+
Apps are multi-file Preact + TSX projects; esbuild bundles when you compile. Structure:
|
|
162
162
|
|
|
163
163
|
```
|
|
164
164
|
src/
|
|
@@ -179,9 +179,9 @@ render(<App />, document.getElementById("app")!);
|
|
|
179
179
|
|
|
180
180
|
1. **`app_create`** with a **4-file scaffold**: `src/index.html`, `src/main.tsx`, a **placeholder** `src/components/App.tsx` (`<div>Loading...</div>`), and an **empty** `src/styles.css`. The placeholders make the first compile clean — a 2-file scaffold leaves broken imports.
|
|
181
181
|
2. **`file_write`** each real file, one per tool call, overwriting the placeholders and adding components.
|
|
182
|
-
3.
|
|
182
|
+
3. Compile ONCE at the end. File edits do not compile on their own. Use `assistant apps inspect <name>` to see whether source drifted, then `assistant apps refresh <name>` (or `app_refresh`).
|
|
183
183
|
|
|
184
|
-
**Actually write the files.** Source code in reasoning/thinking is invisible to the app sandbox and is not a file write. Put every real file body in an `app_create.source_files`, `file_write`, `file_edit`, or `app_update.source_files` tool call, then verify the files exist before
|
|
184
|
+
**Actually write the files.** Source code in reasoning/thinking is invisible to the app sandbox and is not a file write. Put every real file body in an `app_create.source_files`, `file_write`, `file_edit`, or `app_update.source_files` tool call, then verify the files exist before compiling when there is any doubt.
|
|
185
185
|
|
|
186
186
|
**Allowed packages** (esbuild-resolved, no CDN): `date-fns`, `chart.js`, `lodash-es`, `zod`, `clsx`. For icons, write inline `<svg>` markup directly — no icon package is bundled.
|
|
187
187
|
|
|
@@ -221,11 +221,14 @@ app_create({ app_create({
|
|
|
221
221
|
|
|
222
222
|
### 4 — Compile
|
|
223
223
|
|
|
224
|
+
File edits do not compile on their own. After ALL file writes, compile once:
|
|
225
|
+
|
|
224
226
|
```
|
|
225
|
-
|
|
227
|
+
assistant apps inspect <name>
|
|
228
|
+
assistant apps refresh <name>
|
|
226
229
|
```
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
`assistant apps inspect` reports whether source changed since the last compile. `assistant apps refresh` compiles and refreshes open surfaces. `app_refresh(app_id)` also compiles. If compile fails, the response has error details; fix with `file_edit`, then compile again.
|
|
229
232
|
|
|
230
233
|
### 5 — Show the preview card
|
|
231
234
|
|
|
@@ -244,7 +247,7 @@ Editing an existing app means reusing its `app_id` — never `app_create`. Resol
|
|
|
244
247
|
- **`app_update`** — rename or change description/schema, and/or rewrite files via `source_files`, in one call. It recompiles for you, so no separate `app_refresh` is needed. Use this instead of editing `<slug>.json` by hand.
|
|
245
248
|
- **Full rebrand** — still iteration, edit the existing files.
|
|
246
249
|
|
|
247
|
-
Then `app_refresh(app_id)`
|
|
250
|
+
Then compile ONCE (`assistant apps refresh <name>` or `app_refresh(app_id)`) unless you used `app_update`, which already recompiled. If the change is substantial, `app_open(app_id, open_mode: "preview")` for a fresh card; for small tweaks the existing card stays valid.
|
|
248
251
|
|
|
249
252
|
> ⚠️ **`skill_load("app-builder")` is required before every `app_*` call** (including the first `app_create`). The skill can auto-unload between turns; without the reload, `app_refresh` / `app_open` error with "not currently active." It's idempotent — call it every time.
|
|
250
253
|
|
|
@@ -350,7 +353,7 @@ Slide decks are a different domain — skip app patterns (contextual headers, se
|
|
|
350
353
|
## SKILL COMPLETE WHEN
|
|
351
354
|
|
|
352
355
|
- [ ] Request was scoped: personal build (sandbox) or complex/shippable (handed off to a project folder + coding agent)
|
|
353
|
-
- [ ] **Sandbox path:** `app_create` returned an `app_id`; all files written via `file_write`;
|
|
356
|
+
- [ ] **Sandbox path:** `app_create` returned an `app_id`; all files written via `file_write`; compile ran ONCE clean (`assistant apps refresh` or `app_refresh`); `app_open(open_mode: "preview")` rendered the card; user told what was built (3-6 bullets); iterations reflected live
|
|
354
357
|
- [ ] **Handoff path:** project folder established; coding agent spawned via `acp_spawn({ task, cwd })`; user told work continues in the folder
|
|
355
358
|
|
|
356
359
|
---
|
|
@@ -143,7 +143,7 @@ Older routes were written with a second `context` argument (`context.assistantEv
|
|
|
143
143
|
|
|
144
144
|
## Key rules
|
|
145
145
|
|
|
146
|
-
- Always create the route handler files via `file_write` before
|
|
146
|
+
- Always create the route handler files via `file_write` before compiling (`app_refresh` or `assistant apps refresh`)
|
|
147
147
|
- Export an optional `description` string for CLI discoverability (`assistant routes list`)
|
|
148
148
|
- Handlers have full Node.js API access — `fs`, `path`, `crypto`, etc.
|
|
149
149
|
- Handlers get a 30-second timeout per request
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
"name": "messaging_send",
|
|
128
|
-
"description": "Send a message, reply to a thread, or create a draft. On Gmail and Outlook, always creates a real mailbox draft for review (recipients optional on Outlook). Supports replies (via thread_id)
|
|
128
|
+
"description": "Send a message, reply to a thread, or create a draft. On Gmail and Outlook, always creates a real mailbox draft for review (recipients optional on Outlook). On a channel (Slack, Telegram, Discord, WhatsApp) the message goes out as the channel's own bot, threaded when thread_id names a thread. Supports replies (via thread_id) and attachments (via attachment_paths, Gmail and Outlook).",
|
|
129
129
|
"category": "messaging",
|
|
130
130
|
"risk": "high",
|
|
131
131
|
"input_schema": {
|
|
@@ -133,11 +133,11 @@
|
|
|
133
133
|
"properties": {
|
|
134
134
|
"platform": {
|
|
135
135
|
"type": "string",
|
|
136
|
-
"description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected.
|
|
136
|
+
"description": "Platform (e.g. \"gmail\", \"slack\", \"telegram\", \"discord\"). Auto-detected if only one is connected."
|
|
137
137
|
},
|
|
138
138
|
"account": {
|
|
139
139
|
"type": "string",
|
|
140
|
-
"description": "Email address of the account to use. Required when multiple accounts are connected for the same platform. If omitted, uses the most recently connected account."
|
|
140
|
+
"description": "Email address of the account to use. Required when multiple accounts are connected for the same email platform. If omitted, uses the most recently connected account. Does not apply to a channel: a channel send goes out as the channel's own bot."
|
|
141
141
|
},
|
|
142
142
|
"conversation_id": {
|
|
143
143
|
"type": "string",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
|
|
4
|
-
import { isChannelId } from "../../../../channels/types.js";
|
|
5
4
|
import type { OutboundAttachment } from "../../../../messaging/provider-types.js";
|
|
6
5
|
import {
|
|
7
6
|
createDraft,
|
|
@@ -16,11 +15,10 @@ import {
|
|
|
16
15
|
toOutlookFileAttachments,
|
|
17
16
|
} from "../../../../messaging/providers/outlook/client.js";
|
|
18
17
|
import type { OutlookDraftMessage } from "../../../../messaging/providers/outlook/types.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
import { normalizeExternalThreadId } from "../../../../persistence/external-conversation-store.js";
|
|
18
|
+
import {
|
|
19
|
+
isProactivelyAddressable,
|
|
20
|
+
sendChannelText,
|
|
21
|
+
} from "../../../../runtime/channel-send.js";
|
|
24
22
|
import {
|
|
25
23
|
isAbortLikeError,
|
|
26
24
|
throwIfCancelled,
|
|
@@ -29,7 +27,6 @@ import type {
|
|
|
29
27
|
ToolContext,
|
|
30
28
|
ToolExecutionResult,
|
|
31
29
|
} from "../../../../tools/types.js";
|
|
32
|
-
import { getLogger } from "../../../../util/logger.js";
|
|
33
30
|
import { guessMimeType } from "../../../../util/mime-type.js";
|
|
34
31
|
import {
|
|
35
32
|
err,
|
|
@@ -42,8 +39,6 @@ import {
|
|
|
42
39
|
resolveProvider,
|
|
43
40
|
} from "./shared.js";
|
|
44
41
|
|
|
45
|
-
const log = getLogger("messaging-send");
|
|
46
|
-
|
|
47
42
|
/** Read attachment files from disk into in-memory parts for outbound sending. */
|
|
48
43
|
async function readAttachments(paths: string[]): Promise<OutboundAttachment[]> {
|
|
49
44
|
return Promise.all(
|
|
@@ -58,123 +53,6 @@ async function readAttachments(paths: string[]): Promise<OutboundAttachment[]> {
|
|
|
58
53
|
/** Email providers that accept file attachments on outbound sends. */
|
|
59
54
|
const ATTACHMENT_CAPABLE_PLATFORMS = new Set(["gmail", "outlook"]);
|
|
60
55
|
|
|
61
|
-
/**
|
|
62
|
-
* Record a message the provider just accepted where the chat's proactive
|
|
63
|
-
* posts live, so the chat's own conversation and `recall` can see what was
|
|
64
|
-
* sent from elsewhere (a scheduled run, another conversation).
|
|
65
|
-
*
|
|
66
|
-
* Only channel providers have such a home; an email send is tool-mediated
|
|
67
|
-
* and has no chat conversation. The row is written after the provider
|
|
68
|
-
* returned the message id, never before, and carries that id on its
|
|
69
|
-
* envelope and in `channel_outbound_posts` like every other post the daemon
|
|
70
|
-
* makes.
|
|
71
|
-
*
|
|
72
|
-
* A send into the sending turn's own chat writes no row: its tool call and
|
|
73
|
-
* result already sit in that conversation's history, and a second assistant
|
|
74
|
-
* row beside the tool pair would break history repair. Whether the send is
|
|
75
|
-
* "its own" is decided by the delivered target against the turn, not by the
|
|
76
|
-
* home: the turn arrived on a channel, in a chat, in a thread (the turn-local
|
|
77
|
-
* snapshot on the tool context), and a post the provider delivered to exactly
|
|
78
|
-
* that place is a same-conversation send even when the chat's home resolves
|
|
79
|
-
* elsewhere. The delivered thread is the one the provider reports, not the
|
|
80
|
-
* one requested: a provider that ignores the request lands the post in the
|
|
81
|
-
* thread-less chat, and the record must say so; a post into a thread is
|
|
82
|
-
* recorded in the thread's own conversation, where its replies arrive. The home
|
|
83
|
-
* comparison stays as the second test, for a sender that arrived through no
|
|
84
|
-
* channel but is the home. The post is then in the outbound index only
|
|
85
|
-
* through no path, which is the same class as a raw API send and is deferred
|
|
86
|
-
* with it.
|
|
87
|
-
*
|
|
88
|
-
* Failures here never fail the send: the message is already out.
|
|
89
|
-
*/
|
|
90
|
-
async function recordSentChannelPost(params: {
|
|
91
|
-
providerId: string;
|
|
92
|
-
externalChatId: string;
|
|
93
|
-
/** The thread the provider delivered into, as it reported it. */
|
|
94
|
-
deliveredThreadId: string | undefined;
|
|
95
|
-
text: string;
|
|
96
|
-
providerMessageId: string;
|
|
97
|
-
sender: ToolContext;
|
|
98
|
-
}): Promise<void> {
|
|
99
|
-
const { providerId, externalChatId, sender } = params;
|
|
100
|
-
if (!isChannelId(providerId) || !params.providerMessageId) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
if (
|
|
105
|
-
isSendIntoOwnChat(
|
|
106
|
-
sender,
|
|
107
|
-
providerId,
|
|
108
|
-
externalChatId,
|
|
109
|
-
params.deliveredThreadId,
|
|
110
|
-
)
|
|
111
|
-
) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const home = await resolveProactiveHomeConversation({
|
|
115
|
-
sourceChannel: providerId,
|
|
116
|
-
externalChatId,
|
|
117
|
-
threadId: params.deliveredThreadId,
|
|
118
|
-
source: "notification",
|
|
119
|
-
conversationType: "background",
|
|
120
|
-
title: `Messages to ${externalChatId}`,
|
|
121
|
-
});
|
|
122
|
-
if (home.conversationId === sender.conversationId) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const recorded = await recordDeliveredChannelPost({
|
|
126
|
-
conversationId: home.conversationId,
|
|
127
|
-
channel: providerId,
|
|
128
|
-
externalChatId,
|
|
129
|
-
...(params.deliveredThreadId
|
|
130
|
-
? { threadId: params.deliveredThreadId }
|
|
131
|
-
: {}),
|
|
132
|
-
text: params.text,
|
|
133
|
-
providerMessageId: params.providerMessageId,
|
|
134
|
-
crossPostedFrom: sender.conversationId,
|
|
135
|
-
});
|
|
136
|
-
const homeConversation = getConversation(home.conversationId);
|
|
137
|
-
if (homeConversation) {
|
|
138
|
-
syncMessageToDisk(
|
|
139
|
-
home.conversationId,
|
|
140
|
-
recorded.messageId,
|
|
141
|
-
homeConversation.createdAt,
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
} catch (e) {
|
|
145
|
-
log.warn(
|
|
146
|
-
{ err: e, provider: providerId, externalChatId },
|
|
147
|
-
"Failed to record the sent message in the chat's conversation",
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* True when the delivered chat and thread are the ones the sending turn
|
|
154
|
-
* arrived in. Reads the turn-local snapshot the tool context carries
|
|
155
|
-
* (`executionChannel`, `requesterChatId`, `sourceThreadId`), never the live
|
|
156
|
-
* binding, which a concurrent inbound can rewrite while this call runs.
|
|
157
|
-
* Thread ids compare in the binding store's normalized form, so an absent
|
|
158
|
-
* thread on both sides matches and a thread-less delivery never matches a
|
|
159
|
-
* turn that arrived in a thread.
|
|
160
|
-
*/
|
|
161
|
-
function isSendIntoOwnChat(
|
|
162
|
-
sender: ToolContext,
|
|
163
|
-
channel: string,
|
|
164
|
-
externalChatId: string,
|
|
165
|
-
deliveredThreadId: string | undefined,
|
|
166
|
-
): boolean {
|
|
167
|
-
if (!sender.executionChannel || !sender.requesterChatId) {
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
return (
|
|
171
|
-
sender.executionChannel === channel &&
|
|
172
|
-
sender.requesterChatId === externalChatId &&
|
|
173
|
-
normalizeExternalThreadId(sender.sourceThreadId) ===
|
|
174
|
-
normalizeExternalThreadId(deliveredThreadId)
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
56
|
export async function run(
|
|
179
57
|
input: Record<string, unknown>,
|
|
180
58
|
context: ToolContext,
|
|
@@ -197,9 +75,62 @@ export async function run(
|
|
|
197
75
|
throwIfCancelled(context);
|
|
198
76
|
|
|
199
77
|
try {
|
|
200
|
-
|
|
78
|
+
// A channel whose transport can be addressed from a named chat is sent
|
|
79
|
+
// through the transport, which is the one send implementation for that
|
|
80
|
+
// channel, records what it sent, and reaches channels with no messaging
|
|
81
|
+
// provider at all. A platform named outright is checked first; one
|
|
82
|
+
// auto-detected from the connected providers is checked the same way.
|
|
83
|
+
const namedChannel =
|
|
84
|
+
platform && isProactivelyAddressable(platform) ? platform : undefined;
|
|
85
|
+
const provider = namedChannel ? undefined : await resolveProvider(platform);
|
|
86
|
+
const channel =
|
|
87
|
+
namedChannel ??
|
|
88
|
+
(provider && isProactivelyAddressable(provider.id)
|
|
89
|
+
? provider.id
|
|
90
|
+
: undefined);
|
|
91
|
+
|
|
92
|
+
if (channel) {
|
|
93
|
+
if (attachmentPaths?.length) {
|
|
94
|
+
return err("Attachments are only supported on Gmail and Outlook.");
|
|
95
|
+
}
|
|
96
|
+
// A channel's transport sends as the channel's one bot identity, so an
|
|
97
|
+
// account cannot select where the send goes out from. Refusing is
|
|
98
|
+
// safer than silently sending from the default account.
|
|
99
|
+
if (input.account) {
|
|
100
|
+
return err(
|
|
101
|
+
`account does not apply to ${channel}: a channel send goes out as the channel's own bot.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
// Recheck: provider resolution above is an await, and this posts to
|
|
105
|
+
// the channel.
|
|
106
|
+
throwIfCancelled(context);
|
|
107
|
+
const sent = await sendChannelText({
|
|
108
|
+
channel,
|
|
109
|
+
target: {
|
|
110
|
+
kind: "chat",
|
|
111
|
+
chatId: conversationId,
|
|
112
|
+
...(threadId ? { threadId } : {}),
|
|
113
|
+
},
|
|
114
|
+
text,
|
|
115
|
+
renderRichly: true,
|
|
116
|
+
assistantId: context.assistantId,
|
|
117
|
+
sender: {
|
|
118
|
+
conversationId: context.conversationId,
|
|
119
|
+
executionChannel: context.executionChannel,
|
|
120
|
+
requesterChatId: context.requesterChatId,
|
|
121
|
+
sourceThreadId: context.sourceThreadId,
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
const threadSuffix = sent.threadId
|
|
125
|
+
? `, "thread_id": "${sent.threadId}"`
|
|
126
|
+
: "";
|
|
127
|
+
return ok(`Message sent (ID: ${sent.lastMessageId}${threadSuffix}).`);
|
|
128
|
+
}
|
|
129
|
+
if (!provider) {
|
|
130
|
+
throw new Error(`Messaging provider "${platform}" not found.`);
|
|
131
|
+
}
|
|
201
132
|
|
|
202
|
-
// Reject attachments on platforms that can't carry them
|
|
133
|
+
// Reject attachments on platforms that can't carry them.
|
|
203
134
|
if (
|
|
204
135
|
attachmentPaths?.length &&
|
|
205
136
|
!ATTACHMENT_CAPABLE_PLATFORMS.has(provider.id)
|
|
@@ -426,7 +357,14 @@ export async function run(
|
|
|
426
357
|
);
|
|
427
358
|
}
|
|
428
359
|
|
|
429
|
-
//
|
|
360
|
+
// A provider with a send of its own and no direct transport (a plugin's,
|
|
361
|
+
// for instance). Its send is tool-mediated and has no chat conversation
|
|
362
|
+
// to record in.
|
|
363
|
+
if (!provider.sendMessage) {
|
|
364
|
+
return err(
|
|
365
|
+
`${provider.displayName} cannot send from here: it has no send of its own and no channel transport to address.`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
430
368
|
const attachments = attachmentPaths?.length
|
|
431
369
|
? await readAttachments(attachmentPaths)
|
|
432
370
|
: undefined;
|
|
@@ -442,16 +380,6 @@ export async function run(
|
|
|
442
380
|
const threadSuffix = result.threadId
|
|
443
381
|
? `, "thread_id": "${result.threadId}"`
|
|
444
382
|
: "";
|
|
445
|
-
|
|
446
|
-
await recordSentChannelPost({
|
|
447
|
-
providerId: provider.id,
|
|
448
|
-
externalChatId: conversationId,
|
|
449
|
-
deliveredThreadId: result.threadId,
|
|
450
|
-
text,
|
|
451
|
-
providerMessageId: result.id,
|
|
452
|
-
sender: context,
|
|
453
|
-
});
|
|
454
|
-
|
|
455
383
|
return ok(`Message sent (ID: ${result.id}${threadSuffix}).`);
|
|
456
384
|
} catch (e) {
|
|
457
385
|
// A cancelled turn is not a send failure: let it reach the executor's
|
|
@@ -23,7 +23,7 @@ sqlite3 "$VELLUM_WORKSPACE_DIR/data/db/assistant.db" \
|
|
|
23
23
|
|
|
24
24
|
This returns all messages in chronological order with:
|
|
25
25
|
|
|
26
|
-
- `role: "user"` - caller speech
|
|
26
|
+
- `role: "user"` - caller speech and system events
|
|
27
27
|
- `role: "assistant"` - assistant responses, including `text` content and any `tool_use`/`tool_result` blocks
|
|
28
28
|
|
|
29
29
|
## Quick one-liner for the most recent call
|
|
@@ -124,7 +124,14 @@ their ear. Silence is the cue that it is their turn.
|
|
|
124
124
|
|
|
125
125
|
**Advance on evidence, not on time.** Move to the next step when a fresh
|
|
126
126
|
picture shows this one done, or when they tell you it is. Do not move on
|
|
127
|
-
because a plausible amount of time has passed.
|
|
127
|
+
because a plausible amount of time has passed.
|
|
128
|
+
|
|
129
|
+
The most direct telling is automatic. When the user clicks the control an
|
|
130
|
+
arrow is pointing at, a message arrives as their turn saying they clicked it,
|
|
131
|
+
by name, and that mark comes down on its own. Treat it as the step done: say
|
|
132
|
+
what comes next and point at it. Nothing arrives for a click anywhere else,
|
|
133
|
+
and nothing arrives for a ring, so for those the picture and their words are
|
|
134
|
+
still the evidence. If the next picture shows the
|
|
128
135
|
step not done, or done to the wrong thing, point at the same place again with
|
|
129
136
|
a shorter caption and say what you saw. Pointing at the next step while the
|
|
130
137
|
previous one is still open is how someone ends up two steps behind a mark.
|
|
@@ -53,7 +53,10 @@ describe("screen_point_at", () => {
|
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
expect(result.isError).toBe(true);
|
|
56
|
-
expect(result.content).toContain(
|
|
56
|
+
expect(result.content).toContain(
|
|
57
|
+
"The Vellum desktop app is needed to view or control your screen",
|
|
58
|
+
);
|
|
59
|
+
expect(result.content).toContain("https://www.vellum.ai/downloads");
|
|
57
60
|
});
|
|
58
61
|
|
|
59
62
|
/** Drawing on the user's screen is a side effect a stopped turn must not have. */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"tools": [
|
|
4
4
|
{
|
|
5
5
|
"name": "scaffold_managed_skill",
|
|
6
|
-
"description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load when a valid top-level SKILL.md is written under the skill directory. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory.
|
|
6
|
+
"description": "Create or update a managed skill in {workspaceDir}/skills. The skill becomes available for skill_load when a valid top-level SKILL.md is written under the skill directory. Never persist a skill without explicit user consent. Before persisting, test the snippet: write to a temp file with bash and run with `bun run /tmp/vellum-eval/snippet.ts`. Iterate up to 3 attempts, then ask the user. Clean up temp files after. Do not use file_write for temp files outside the working directory.",
|
|
7
7
|
"category": "skills",
|
|
8
8
|
"risk": "high",
|
|
9
9
|
"input_schema": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"name": "delete_managed_skill",
|
|
95
|
-
"description": "Delete a managed skill directory from {workspaceDir}/skills. Never delete a skill without explicit user confirmation.
|
|
95
|
+
"description": "Delete a managed skill directory from {workspaceDir}/skills. Never delete a skill without explicit user confirmation.",
|
|
96
96
|
"category": "skills",
|
|
97
97
|
"risk": "high",
|
|
98
98
|
"input_schema": {
|
|
@@ -137,7 +137,7 @@ const VELLUM_PROFILE_IMPLS: ProfileImpls = {
|
|
|
137
137
|
// The managed latency class, also what the live-voice front model runs on.
|
|
138
138
|
// Its leading tokens are the turn-taking verdict, so what this profile
|
|
139
139
|
// optimizes is the tail of time-to-first-token rather than the median: a
|
|
140
|
-
// verdict slower than `
|
|
140
|
+
// verdict slower than `voice.frontModel.endpointDecisionTimeoutMs`
|
|
141
141
|
// trips the speculative fail-open commit in live-voice-session.ts, which
|
|
142
142
|
// is audible dead air.
|
|
143
143
|
//
|
package/src/config/env.ts
CHANGED
|
@@ -229,8 +229,8 @@ export function setPlatformAssistantId(value: string | undefined): void {
|
|
|
229
229
|
* Platform assistant ID — UUID of this assistant on the platform.
|
|
230
230
|
*
|
|
231
231
|
* Resolved from the in-memory override (populated by providers-setup
|
|
232
|
-
* rehydration
|
|
233
|
-
*
|
|
232
|
+
* rehydration via platform validate, or by secret-routes when the
|
|
233
|
+
* platform pushes the value).
|
|
234
234
|
*/
|
|
235
235
|
export function getPlatformAssistantId(): string {
|
|
236
236
|
return _platformAssistantIdOverride ?? "";
|
|
@@ -29,18 +29,6 @@
|
|
|
29
29
|
"description": "Enable user-hosted onboarding flow",
|
|
30
30
|
"defaultEnabled": false
|
|
31
31
|
},
|
|
32
|
-
{
|
|
33
|
-
"id": "proactive-tips",
|
|
34
|
-
"scope": "client",
|
|
35
|
-
"key": "proactive-tips",
|
|
36
|
-
"label": "Proactive Tips",
|
|
37
|
-
"description": "Gates the dismissible proactive tip card in the web sidebar. String-valued so future A/B arms can be added as new values.",
|
|
38
|
-
"defaultEnabled": "off",
|
|
39
|
-
"values": [
|
|
40
|
-
"off",
|
|
41
|
-
"on"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
32
|
{
|
|
45
33
|
"id": "vision-mode",
|
|
46
34
|
"scope": "client",
|
|
@@ -523,6 +511,14 @@
|
|
|
523
511
|
"label": "Send User Message Tool",
|
|
524
512
|
"description": "Route all user-facing assistant text through the send_user_message tool instead of streamed assistant text",
|
|
525
513
|
"defaultEnabled": false
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
"id": "assistant-desktop",
|
|
517
|
+
"scope": "assistant",
|
|
518
|
+
"key": "assistant-desktop",
|
|
519
|
+
"label": "Assistant Desktop",
|
|
520
|
+
"description": "Streams an interactive desktop from a containerized assistant pod to the web client over /v1/desktop/stream. Off: no desktop affordance is rendered. On: the chat header offers a desktop panel that views and controls the pod's display.",
|
|
521
|
+
"defaultEnabled": false
|
|
526
522
|
}
|
|
527
523
|
]
|
|
528
524
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isMemoryV1Active,
|
|
7
7
|
isMemoryV2ExplicitlyDisabled,
|
|
8
8
|
isMemoryV3Live,
|
|
9
|
+
isSkillImprovementActive,
|
|
9
10
|
isV2InjectionEngineActive,
|
|
10
11
|
isV3TierActive,
|
|
11
12
|
usesConceptPageMemory,
|
|
@@ -55,12 +56,16 @@ function makeConfig(
|
|
|
55
56
|
enabled: TriState,
|
|
56
57
|
v2Enabled: TriState,
|
|
57
58
|
v3Live: TriState,
|
|
59
|
+
skillImprovement?: boolean,
|
|
58
60
|
): AssistantConfig {
|
|
59
61
|
return {
|
|
60
62
|
memory: {
|
|
61
63
|
...(enabled === undefined ? {} : { enabled }),
|
|
62
64
|
...(v2Enabled === undefined ? {} : { v2: { enabled: v2Enabled } }),
|
|
63
65
|
...(v3Live === undefined ? {} : { v3: { live: v3Live } }),
|
|
66
|
+
...(skillImprovement === undefined
|
|
67
|
+
? {}
|
|
68
|
+
: { retrospective: { skillImprovement } }),
|
|
64
69
|
},
|
|
65
70
|
} as AssistantConfig;
|
|
66
71
|
}
|
|
@@ -296,6 +301,32 @@ describe("v1-staleness guard is one condition", () => {
|
|
|
296
301
|
* proc-to-skills alias of `isMemoryV3Live` (which did not). One predicate now
|
|
297
302
|
* answers for both features.
|
|
298
303
|
*/
|
|
304
|
+
describe("skill improvement gate", () => {
|
|
305
|
+
test("defaults on for a live v3 tier", () => {
|
|
306
|
+
expect(isSkillImprovementActive(makeConfig(true, undefined, true))).toBe(
|
|
307
|
+
true,
|
|
308
|
+
);
|
|
309
|
+
expect(isSkillImprovementActive(makeConfig(undefined, undefined, true))).toBe(
|
|
310
|
+
true,
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("explicit opt-out disables skill improvement without disabling v3", () => {
|
|
315
|
+
const config = makeConfig(true, undefined, true, false);
|
|
316
|
+
|
|
317
|
+
expect(isV3TierActive(config)).toBe(true);
|
|
318
|
+
expect(isSkillImprovementActive(config)).toBe(false);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("requires the live v3 tier even when explicitly enabled", () => {
|
|
322
|
+
const v2Config = makeConfig(true, true, false, true);
|
|
323
|
+
const memoryOffConfig = makeConfig(false, undefined, true, true);
|
|
324
|
+
|
|
325
|
+
expect(isSkillImprovementActive(v2Config)).toBe(false);
|
|
326
|
+
expect(isSkillImprovementActive(memoryOffConfig)).toBe(false);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
299
330
|
describe("v3 tier is one condition", () => {
|
|
300
331
|
test("memory off suppresses the v3 tier even with memory.v3.live set", () => {
|
|
301
332
|
const config = makeConfig(false, undefined, true);
|
|
@@ -81,28 +81,32 @@ export function isMemoryV3Live(config: AssistantConfig): boolean {
|
|
|
81
81
|
* FEATURE asks this predicate; only the injection-suppression checks that must
|
|
82
82
|
* mirror the raw `memory.v3.live` key ask {@link isMemoryV3Live} directly.
|
|
83
83
|
*
|
|
84
|
-
* The
|
|
84
|
+
* The memory concept graph is gated on it: both `GET /memory-graph`
|
|
85
|
+
* (`supported`) and the cheap `graph_supported` bit on `GET /memory/stats`
|
|
86
|
+
* must agree with whether the graph can build from the v3 concept-page
|
|
87
|
+
* substrate.
|
|
85
88
|
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* never drift. The graph builds off the v3 concept-page substrate.
|
|
90
|
-
* - Procedural-memory-as-skills — the retrospective's skill-authoring step and
|
|
91
|
-
* its permission grant. Scoped to the v3 tier because skill retrieval rides
|
|
92
|
-
* the v3 lanes and the usage-prune stage lives in the v3 maintain job. That
|
|
93
|
-
* prune ships observe-first: with `memory.maintenance.skillPruneDays` at its
|
|
94
|
-
* default (`null`) it reports stale assistant-authored skills
|
|
95
|
-
* (`prunableSkills`) but deletes none — so a v3-tier assistant authors skills
|
|
96
|
-
* without an automatic retirement bound until a positive `skillPruneDays` is
|
|
97
|
-
* configured.
|
|
98
|
-
*
|
|
99
|
-
* Both write into memory on the user's behalf, so both honor the Memory
|
|
100
|
-
* opt-out identically: one predicate, one answer.
|
|
89
|
+
* Retrospective skill improvement is a separate optional v3-tier feature.
|
|
90
|
+
* Ask {@link isSkillImprovementActive} for it so an operator can stop managed
|
|
91
|
+
* skill authoring without turning off ordinary retrospective memory capture.
|
|
101
92
|
*/
|
|
102
93
|
export function isV3TierActive(config: AssistantConfig): boolean {
|
|
103
94
|
return isMemoryEnabled(config) && isMemoryV3Live(config);
|
|
104
95
|
}
|
|
105
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Whether a retrospective may turn observed procedures into managed skills.
|
|
99
|
+
* This requires the v3 tier because skills are retrieved through its memory
|
|
100
|
+
* substrate, and it honors the retrospective-specific opt-out without
|
|
101
|
+
* disabling ordinary retrospective fact retention.
|
|
102
|
+
*/
|
|
103
|
+
export function isSkillImprovementActive(config: AssistantConfig): boolean {
|
|
104
|
+
return (
|
|
105
|
+
isV3TierActive(config) &&
|
|
106
|
+
config.memory?.retrospective?.skillImprovement !== false
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
106
110
|
/**
|
|
107
111
|
* Minimal structural view of the memory config the concept-page gate reads.
|
|
108
112
|
* Accepting the shape (rather than the full `MemoryConfig`) lets call sites
|