@vellumai/assistant 0.11.1 → 0.11.2-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +1 -3
- package/README.md +1 -1
- package/eslint.config.mjs +28 -8
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +48 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +25 -6
- package/openapi.yaml +292 -5
- package/package.json +1 -1
- package/src/__tests__/agent-loop-resume-interrupted.test.ts +223 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +43 -0
- package/src/__tests__/cli-logger-boundary-guard.test.ts +87 -0
- package/src/__tests__/compaction-events.test.ts +139 -2
- package/src/__tests__/config-loader-backfill.test.ts +35 -3
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +30 -0
- package/src/__tests__/conversation-agent-loop.test.ts +180 -7
- package/src/__tests__/conversation-error.test.ts +211 -4
- package/src/__tests__/conversation-options-turn-scoped-transport.test.ts +64 -0
- package/src/__tests__/conversation-queue.test.ts +126 -0
- package/src/__tests__/conversation-retry-route.test.ts +46 -7
- package/src/__tests__/conversation-slash.test.ts +4 -2
- package/src/__tests__/conversation-summarize-route.test.ts +7 -1
- package/src/__tests__/conversation-summarize-up-to.test.ts +71 -5
- package/src/__tests__/document-create-dedupe.test.ts +2 -1
- package/src/__tests__/document-find-replace.test.ts +2 -1
- package/src/__tests__/document-tool-security.test.ts +2 -1
- package/src/__tests__/document-update-default-surface.test.ts +2 -1
- package/src/__tests__/document-workspace-file.test.ts +467 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +290 -49
- package/src/__tests__/guardian-card-withdrawal.test.ts +91 -3
- package/src/__tests__/http-user-message-parity.test.ts +66 -0
- package/src/__tests__/list-messages-attachments.test.ts +87 -0
- package/src/__tests__/list-messages-provider-error.test.ts +143 -0
- package/src/__tests__/list-messages-system-card.test.ts +100 -0
- package/src/__tests__/llm-resolver.test.ts +19 -9
- package/src/__tests__/managed-profile-guard.test.ts +23 -0
- package/src/__tests__/notification-platform-adapter.test.ts +130 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +6 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +45 -0
- package/src/__tests__/plugin-api-model-profiles.test.ts +10 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
- package/src/__tests__/provider-error-scenarios.test.ts +140 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +95 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +66 -3
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +209 -0
- package/src/__tests__/skills.test.ts +27 -0
- package/src/__tests__/slack-channels-routes.test.ts +0 -2
- package/src/__tests__/slack-share-routes.test.ts +0 -3
- package/src/__tests__/slack-users-routes.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +11 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +58 -0
- package/src/__tests__/tool-result-spool.test.ts +5 -4
- package/src/__tests__/turn-boundary-resolution.test.ts +53 -0
- package/src/__tests__/turn-events-store.test.ts +26 -0
- package/src/__tests__/ui-visual-surface.test.ts +695 -0
- package/src/__tests__/unified-turn-context-visible-app.test.ts +99 -0
- package/src/__tests__/visible-app-context.test.ts +189 -0
- package/src/__tests__/workspace-git-service.test.ts +173 -33
- package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +157 -0
- package/src/__tests__/workspace-migration-138-backfill-home-feed-titles.test.ts +373 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +137 -0
- package/src/agent/loop.ts +36 -7
- package/src/api/events/context-window-usage.ts +31 -0
- package/src/api/events/notification-intent.ts +8 -0
- package/src/api/events/ui-surface-pending.ts +35 -0
- package/src/api/index.ts +14 -0
- package/src/api/responses/conversation-message.ts +25 -4
- package/src/api/surfaces.ts +90 -1
- package/src/approvals/guardian-card-withdrawal.ts +66 -31
- package/src/approvals/guardian-decision-primitive.ts +4 -0
- package/src/calls/__tests__/call-setup-router.test.ts +156 -26
- package/src/calls/__tests__/voice-session-bridge.test.ts +201 -12
- package/src/calls/call-setup-router.ts +80 -36
- package/src/calls/voice-session-bridge.ts +173 -26
- package/src/cli/commands/inference.help.ts +3 -3
- package/src/cli/commands/notifications.help.ts +3 -2
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +42 -128
- package/src/cli/commands/platform/__tests__/credits.test.ts +9 -78
- package/src/cli/commands/platform/__tests__/helpers.ts +90 -0
- package/src/cli/commands/platform/__tests__/invoices.test.ts +238 -0
- package/src/cli/commands/platform/__tests__/plans.test.ts +9 -88
- package/src/cli/commands/platform/__tests__/status.test.ts +12 -87
- package/src/cli/commands/platform/__tests__/subscription.test.ts +9 -86
- package/src/cli/commands/platform/index.help.ts +92 -0
- package/src/cli/commands/platform/index.ts +7 -0
- package/src/cli/commands/platform/invoices.ts +132 -0
- package/src/cli/commands/usage.help.ts +1 -1
- package/src/cli/lib/list-installed-plugins.ts +2 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +7 -10
- package/src/config/__tests__/deployment-context-defaults.test.ts +27 -5
- package/src/config/assistant-feature-flags.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -2
- package/src/config/bundled-skills/subagent/SKILL.md +3 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +3 -3
- package/src/config/bundled-skills/visualize/SKILL.md +163 -0
- package/src/config/call-site-defaults.ts +3 -2
- package/src/config/default-profile-catalog.ts +75 -70
- package/src/config/default-profile-names.ts +13 -28
- package/src/config/env-registry.ts +1 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/llm-resolver.ts +21 -5
- package/src/config/loader.ts +18 -11
- package/src/config/schemas/llm.ts +1 -9
- package/src/config/schemas/memory-retrospective.ts +9 -0
- package/src/config/schemas/monitoring.ts +28 -2
- package/src/config/schemas/workspace-git.ts +15 -0
- package/src/config/seed-inference-profiles.ts +19 -5
- package/src/context/post-turn-tool-result-truncation.ts +2 -2
- package/src/conversations/__tests__/message-consolidation.test.ts +54 -0
- package/src/conversations/message-consolidation.ts +17 -15
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +182 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +181 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +90 -7
- package/src/daemon/conversation-agent-loop.ts +107 -22
- package/src/daemon/conversation-error.ts +169 -48
- package/src/daemon/conversation-messaging.ts +87 -4
- package/src/daemon/conversation-process.ts +57 -45
- package/src/daemon/conversation-runtime-assembly.ts +57 -0
- package/src/daemon/conversation-store.ts +36 -1
- package/src/daemon/conversation-surfaces.ts +68 -6
- package/src/daemon/conversation-turn-finalize.ts +76 -18
- package/src/daemon/conversation.ts +122 -27
- package/src/daemon/lifecycle.ts +9 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/surfaces.ts +3 -0
- package/src/documents/document-store.ts +247 -10
- package/src/home/__tests__/feed-types.test.ts +43 -8
- package/src/home/__tests__/feed-writer.test.ts +59 -0
- package/src/home/feed-types.ts +34 -7
- package/src/home/feed-writer.ts +15 -6
- package/src/live-voice/__tests__/activity-label.test.ts +95 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +86 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +310 -25
- package/src/live-voice/__tests__/live-voice-events.test.ts +4 -4
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +8 -4
- package/src/live-voice/__tests__/live-voice-vad.test.ts +9 -1
- package/src/live-voice/activity-label.ts +169 -0
- package/src/live-voice/live-activity-reporter.ts +35 -6
- package/src/live-voice/live-voice-session.ts +282 -41
- package/src/live-voice/protocol.ts +35 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +84 -2
- package/src/messaging/providers/slack/adapter.ts +86 -19
- package/src/messaging/providers/slack/api.test.ts +85 -1
- package/src/messaging/providers/slack/api.ts +121 -288
- package/src/messaging/providers/slack/client.ts +20 -251
- package/src/messaging/providers/slack/send.test.ts +4 -9
- package/src/messaging/providers/slack/send.ts +1 -1
- package/src/messaging/providers/slack/types.ts +5 -0
- package/src/messaging/providers/slack/web-api-transport.test.ts +181 -0
- package/src/messaging/providers/slack/web-api-transport.ts +367 -0
- package/src/messaging/providers/slack/withdraw.ts +6 -14
- package/src/messaging/providers/telegram-bot/send.test.ts +31 -1
- package/src/messaging/providers/telegram-bot/send.ts +23 -2
- package/src/messaging/providers/telegram-bot/withdraw.test.ts +152 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +166 -0
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +18 -4
- package/src/monitoring/__tests__/file-descriptors.test.ts +144 -0
- package/src/monitoring/file-descriptors.ts +262 -0
- package/src/monitoring/process-memory.ts +4 -11
- package/src/monitoring/resource-sampler.ts +5 -0
- package/src/monitoring/worker.ts +12 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +740 -0
- package/src/notifications/__tests__/broadcaster.test.ts +347 -7
- package/src/notifications/__tests__/copy-composer.test.ts +53 -3
- package/src/notifications/__tests__/decision-engine.test.ts +349 -29
- package/src/notifications/__tests__/deterministic-checks.test.ts +21 -0
- package/src/notifications/__tests__/edit-notification.test.ts +330 -0
- package/src/notifications/__tests__/guardian-delivery-recorder.test.ts +71 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +170 -12
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/adapters/platform.ts +90 -14
- package/src/notifications/adapters/telegram.ts +6 -4
- package/src/notifications/assistant-reply-producer.ts +219 -0
- package/src/notifications/broadcaster.ts +523 -317
- package/src/notifications/copy-composer.ts +27 -6
- package/src/notifications/decision-engine.ts +154 -77
- package/src/notifications/deterministic-checks.ts +6 -7
- package/src/notifications/edit-notification.ts +8 -4
- package/src/notifications/emit-signal.ts +49 -16
- package/src/notifications/guardian-delivery-recorder.ts +8 -2
- package/src/notifications/home-feed-side-effect.ts +43 -6
- package/src/notifications/notification-utils.ts +39 -4
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +17 -0
- package/src/persistence/bookmark-crud.ts +18 -9
- package/src/persistence/conversation-attention-store.ts +26 -0
- package/src/persistence/conversation-crud.ts +79 -32
- package/src/persistence/conversation-queries.ts +30 -15
- package/src/persistence/conversation-title-service.ts +5 -170
- package/src/persistence/conversation-types.ts +192 -1
- package/src/persistence/db-init.ts +14 -1
- package/src/persistence/db-maintenance.ts +5 -4
- package/src/persistence/embeddings/__tests__/plugin-index-qdrant-init.test.ts +219 -0
- package/src/persistence/embeddings/__tests__/plugin-index.test.ts +22 -0
- package/src/persistence/embeddings/__tests__/worker-script-version.test.ts +76 -0
- package/src/persistence/embeddings/embedding-local.ts +2 -1
- package/src/persistence/embeddings/embedding-runtime-manager.ts +29 -5
- package/src/persistence/embeddings/plugin-index.ts +84 -12
- package/src/persistence/migrations/360-add-document-workspace-path.test.ts +110 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +39 -0
- package/src/persistence/planner-statistics.ts +14 -0
- package/src/persistence/schema/documents.ts +26 -11
- package/src/persistence/steps.ts +2 -0
- package/src/platform/client.test.ts +172 -2
- package/src/platform/client.ts +178 -55
- package/src/plugin-api/conversation-turn.ts +13 -0
- package/src/plugin-api/model-profiles.test.ts +6 -2
- package/src/plugins/defaults/memory/__tests__/bookmark-crud.test.ts +34 -0
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +22 -0
- package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +11 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +199 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +98 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +105 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +24 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +524 -0
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1 -1
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +9 -4
- package/src/plugins/defaults/memory/host-utils.ts +5 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +105 -1
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +64 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +36 -1
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +12 -3
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +1 -0
- package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +195 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +1 -1
- package/src/plugins/defaults/memory/substrate/skill-store.ts +167 -29
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +131 -0
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +1 -0
- package/src/plugins/defaults/memory/v2/activation-log-store.ts +4 -0
- package/src/plugins/defaults/memory/v2/injection.ts +48 -3
- package/src/plugins/defaults/memory/v2/rerank-local.ts +6 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -1
- package/src/plugins/defaults/platform-hosted/routes/reengage.ts +1 -1
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +26 -0
- package/src/plugins/types.ts +17 -0
- package/src/prompts/templates/system-sections.ts +2 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +43 -2
- package/src/providers/__tests__/vellum-mismatch-routing.test.ts +8 -0
- package/src/providers/call-site-routing.ts +77 -14
- package/src/providers/connection-resolution.ts +42 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +127 -1
- package/src/providers/inference/adapter-factory.ts +70 -12
- package/src/providers/model-catalog.ts +0 -11
- package/src/providers/model-intents.ts +11 -0
- package/src/providers/registry.ts +8 -0
- package/src/providers/retry.ts +85 -17
- package/src/providers/types.ts +8 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/agent-wake.ts +132 -9
- package/src/runtime/channel-approval-types.ts +25 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +2 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +11 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +121 -9
- package/src/runtime/routes/__tests__/platform-invoice-routes.test.ts +392 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/workspace-commit-routes.test.ts +63 -0
- package/src/runtime/routes/consolidation-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +18 -6
- package/src/runtime/routes/conversation-query-routes.ts +14 -23
- package/src/runtime/routes/conversation-routes.ts +113 -8
- package/src/runtime/routes/credential-routes.ts +1 -5
- package/src/runtime/routes/documents-routes.ts +207 -12
- package/src/runtime/routes/identity-routes.ts +3 -93
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +31 -1
- package/src/runtime/routes/inbound-stages/admission-policy.ts +18 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +52 -3
- package/src/runtime/routes/notification-routes.ts +4 -1
- package/src/runtime/routes/platform-routes.ts +238 -3
- package/src/runtime/routes/playground/guard.ts +1 -2
- package/src/runtime/routes/slack-channel-routes.ts +2 -4
- package/src/runtime/routes/workspace-commit-routes.ts +49 -2
- package/src/runtime/routes/workspace-routes.ts +11 -12
- package/src/runtime/routes/workspace-utils.ts +49 -2
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/subagent/__tests__/consult-context-gating.test.ts +98 -0
- package/src/subagent/__tests__/consult-context-skills.test.ts +82 -0
- package/src/subagent/__tests__/consult-context.test.ts +84 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +36 -0
- package/src/subagent/consult-context.ts +410 -0
- package/src/subagent/consult-prompt.ts +38 -5
- package/src/subagent/manager.ts +9 -4
- package/src/subagent/types.ts +8 -0
- package/src/telemetry/telemetry-event-sources.ts +7 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +1 -0
- package/src/telemetry/turn-events-store.ts +30 -1
- package/src/telemetry/types.ts +24 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/tools/browser/pinned-tabs.ts +3 -1
- package/src/tools/subagent/spawn.ts +23 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/ui-surface/definitions.ts +39 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +38 -4
- package/src/tools/ui-surface/visual-validation.ts +787 -0
- package/src/tools/workflows/run-workflow.test.ts +1 -0
- package/src/util/__tests__/short-title.test.ts +229 -0
- package/src/util/__tests__/worker-compute.test.ts +65 -0
- package/src/util/cgroup-cpu.ts +93 -0
- package/src/util/errors.ts +27 -0
- package/src/util/process-tree.ts +19 -0
- package/src/util/short-title.ts +189 -0
- package/src/util/worker-compute.ts +83 -0
- package/src/workspace/byok-default-profile-ensure.ts +25 -15
- package/src/workspace/git-service.ts +79 -30
- package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +131 -0
- package/src/workspace/migrations/138-backfill-home-feed-titles.ts +179 -0
- package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +92 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/plugins/defaults/memory/substrate/constants.ts +0 -8
|
@@ -254,13 +254,13 @@ describe("LiveVoiceSession archive and metrics events", () => {
|
|
|
254
254
|
userMessageInterface: "macos",
|
|
255
255
|
assistantMessageInterface: "macos",
|
|
256
256
|
// Pins the full production control prompt for the FRONT-DOOR leg (the
|
|
257
|
-
// first leg of every routed turn): the
|
|
258
|
-
//
|
|
259
|
-
// no-setup-flows rule (no OAuth/browser flows mid-call). The [-1]
|
|
257
|
+
// first leg of every routed turn): the speech-first rule (which now
|
|
258
|
+
// permits a surface, since the room reveals one by itself) and the
|
|
259
|
+
// shared no-setup-flows rule (no OAuth/browser flows mid-call). The [-1]
|
|
260
260
|
// room-minimize teaching is deliberately absent — only the escalated
|
|
261
261
|
// leg learns it (see live-voice-triage-escalate.test.ts).
|
|
262
262
|
voiceControlPrompt:
|
|
263
|
-
"You are speaking in a local live voice session. Keep replies brief and conversational.
|
|
263
|
+
"You are speaking in a local live voice session. Keep replies brief and conversational. Speech is the main channel: say the answer, and do not narrate a surface instead of answering. You can also put something on screen when it genuinely helps (a form, a list to pick from, a progress card for long work); the call overlay minimizes by itself once you finish speaking, so the user sees it without doing anything. Never tell the user you cannot show them something. " +
|
|
264
264
|
VOICE_NO_SETUP_FLOWS_RULE,
|
|
265
265
|
});
|
|
266
266
|
callbacks?.assistant_text_delta?.(makeTextDelta("Hello there."));
|
|
@@ -190,7 +190,7 @@ describe("live-voice triage-and-escalate routing", () => {
|
|
|
190
190
|
expect(escalated?.content).toBe(ESCALATION_CONTINUATION_CONTENT);
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
-
test("the
|
|
193
|
+
test("the screen-reveal teaching reaches the escalated leg's prompt but never the front-door leg's", async () => {
|
|
194
194
|
const { starter } = scriptedStartVoiceTurn({
|
|
195
195
|
frontDoor: ["[1] ", "Let me think about that."],
|
|
196
196
|
escalated: ["The detailed answer is 42."],
|
|
@@ -205,10 +205,14 @@ describe("live-voice triage-and-escalate routing", () => {
|
|
|
205
205
|
starter.mock.calls[0]?.[0]?.voiceControlPrompt ?? "";
|
|
206
206
|
const escalatedPrompt =
|
|
207
207
|
starter.mock.calls[1]?.[0]?.voiceControlPrompt ?? "";
|
|
208
|
-
// The toolless fast leg has nothing to show
|
|
209
|
-
//
|
|
208
|
+
// The toolless fast leg has nothing to show, so it is never told the
|
|
209
|
+
// screen will be revealed.
|
|
210
|
+
expect(frontDoorPrompt).not.toContain("the overlay minimizes");
|
|
211
|
+
expect(escalatedPrompt).toContain("the overlay minimizes");
|
|
212
|
+
// No leg is taught a marker any more: the reveal is decided by whether a
|
|
213
|
+
// ui tool ran, never by anything the model emits.
|
|
210
214
|
expect(frontDoorPrompt).not.toContain("[-1]");
|
|
211
|
-
expect(escalatedPrompt).toContain("[-1]");
|
|
215
|
+
expect(escalatedPrompt).not.toContain("[-1]");
|
|
212
216
|
});
|
|
213
217
|
|
|
214
218
|
test("the verdict token and any text past the bridge cap never reach the transcript", async () => {
|
|
@@ -1207,8 +1207,16 @@ describe("LiveVoiceSession server VAD", () => {
|
|
|
1207
1207
|
});
|
|
1208
1208
|
// The barge-in utterance transcribes to nothing, so no follow-up turn
|
|
1209
1209
|
// consumes the stash before the interrupt lands.
|
|
1210
|
+
//
|
|
1211
|
+
// "third question" is repeated because finals are handed out by transcriber
|
|
1212
|
+
// creation order, and the session pre-arms a transcriber for the next
|
|
1213
|
+
// utterance. The interrupt below can discard that pre-armed one and re-arm,
|
|
1214
|
+
// which shifts the final utterance onto the following index — with a
|
|
1215
|
+
// 3-entry list it would fall off the end and transcribe to the "utterance
|
|
1216
|
+
// N" placeholder, so the wait for "third question" would time out rather
|
|
1217
|
+
// than fail on the assertion. Both landing spots carry the same text.
|
|
1210
1218
|
const { frames, session } = createHarness({
|
|
1211
|
-
finals: ["first question", "", "third question"],
|
|
1219
|
+
finals: ["first question", "", "third question", "third question"],
|
|
1212
1220
|
startVoiceTurn,
|
|
1213
1221
|
streamTtsAudio,
|
|
1214
1222
|
spawnBackgroundContinuation,
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a tool call into one short user-facing line for the surfaces that show
|
|
3
|
+
* a running voice session.
|
|
4
|
+
*
|
|
5
|
+
* ## Why the copy lives here
|
|
6
|
+
*
|
|
7
|
+
* The iOS Live Activity has two drivers that must carry identical content: the
|
|
8
|
+
* live-voice socket, and an APNs push the daemon dispatches when the app is
|
|
9
|
+
* backgrounded and the web layer is suspended. Only one of them runs web code,
|
|
10
|
+
* so a label composed client-side could not be reproduced by the other. Both
|
|
11
|
+
* are therefore handed the same string, and this is where it is made.
|
|
12
|
+
*
|
|
13
|
+
* That does not contradict the rule that phase wording belongs to the web
|
|
14
|
+
* layer. The reason phase copy lives there is cadence: the iOS shell ships on
|
|
15
|
+
* App Store review while the web bundle deploys continuously, so a native
|
|
16
|
+
* `switch` would fossilize. This module deploys with the daemon, i.e.
|
|
17
|
+
* continuously, and it is the only layer that knows a tool ran at all.
|
|
18
|
+
*
|
|
19
|
+
* ## What makes a good label
|
|
20
|
+
*
|
|
21
|
+
* It is read on a Lock Screen, in a glance, by someone who is mid-conversation
|
|
22
|
+
* and not looking for detail. So: a present-participle phrase, no tool names,
|
|
23
|
+
* no arguments, no punctuation, and short enough to survive one line. Arguments
|
|
24
|
+
* are deliberately absent even though {@link summarizeToolInput} could supply
|
|
25
|
+
* them: a path or a URL on a Lock Screen is unreadable at a glance and can
|
|
26
|
+
* carry content the user would not choose to show a passer-by.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The tools that put something on screen worth looking at.
|
|
31
|
+
*
|
|
32
|
+
* `app_open` belongs here with the ui-surface tools: it proxies to a connected
|
|
33
|
+
* client to bring an app up, which is the same thing from the user's side, and
|
|
34
|
+
* a list keyed on "ui_" would have missed it.
|
|
35
|
+
*
|
|
36
|
+
* `ui_dismiss` is deliberately absent, and is handled as its own case: it
|
|
37
|
+
* retires a surface, and revealing the screen to show the user something that
|
|
38
|
+
* is no longer there is the opposite of the point.
|
|
39
|
+
*/
|
|
40
|
+
const UI_REVEAL_TOOLS: ReadonlySet<string> = new Set([
|
|
41
|
+
"ui_show",
|
|
42
|
+
"ui_update",
|
|
43
|
+
"app_open",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
/** The tool that takes a surface away again. */
|
|
47
|
+
const UI_DISMISS_TOOL = "ui_dismiss";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether running `toolName` leaves something on screen the user should be
|
|
51
|
+
* shown, which is what makes a live-voice turn reveal the screen behind the
|
|
52
|
+
* call overlay.
|
|
53
|
+
*/
|
|
54
|
+
export function revealsUiSurface(toolName: string): boolean {
|
|
55
|
+
return UI_REVEAL_TOOLS.has(toolName);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Whether `toolName` takes a surface off the screen. */
|
|
59
|
+
export function dismissesUiSurface(toolName: string): boolean {
|
|
60
|
+
return toolName === UI_DISMISS_TOOL;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Present-participle phrases for the built-in tools, by exact name.
|
|
65
|
+
*
|
|
66
|
+
* Grouped by what the user would say is happening, not by how the tool is
|
|
67
|
+
* implemented: `bash` and `host_bash` differ in where they run, which is not a
|
|
68
|
+
* distinction anyone glancing at a Lock Screen is making.
|
|
69
|
+
*/
|
|
70
|
+
const TOOL_ACTIVITY_LABELS: Readonly<Record<string, string>> = {
|
|
71
|
+
bash: "Running a command",
|
|
72
|
+
terminal: "Running a command",
|
|
73
|
+
host_bash: "Running a command",
|
|
74
|
+
file_read: "Reading a file",
|
|
75
|
+
host_file_read: "Reading a file",
|
|
76
|
+
file_write: "Writing a file",
|
|
77
|
+
host_file_write: "Writing a file",
|
|
78
|
+
file_edit: "Editing a file",
|
|
79
|
+
host_file_edit: "Editing a file",
|
|
80
|
+
host_file_transfer: "Moving a file",
|
|
81
|
+
file_search: "Searching files",
|
|
82
|
+
web_search: "Searching the web",
|
|
83
|
+
web_fetch: "Reading a page",
|
|
84
|
+
network_request: "Making a request",
|
|
85
|
+
browser: "Using the browser",
|
|
86
|
+
computer_use: "Using the computer",
|
|
87
|
+
memory: "Checking memory",
|
|
88
|
+
schedule: "Checking the schedule",
|
|
89
|
+
ui_show: "Putting something on screen",
|
|
90
|
+
ui_update: "Putting something on screen",
|
|
91
|
+
app_open: "Opening an app",
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Prefix-matched fallbacks, tried in order when a tool has no exact entry.
|
|
96
|
+
*
|
|
97
|
+
* The tool vocabulary is open: plugins, MCP servers, and skills all contribute
|
|
98
|
+
* names this module has never seen, and a new built-in should not have to be
|
|
99
|
+
* registered here to avoid a blank island. Prefixes cover the families whose
|
|
100
|
+
* names are structured; {@link GENERIC_ACTIVITY_LABEL} covers the rest.
|
|
101
|
+
*/
|
|
102
|
+
const TOOL_ACTIVITY_LABEL_PREFIXES: ReadonlyArray<readonly [string, string]> = [
|
|
103
|
+
["browser_", "Using the browser"],
|
|
104
|
+
["computer_", "Using the computer"],
|
|
105
|
+
["file_", "Working with files"],
|
|
106
|
+
["host_file_", "Working with files"],
|
|
107
|
+
["web_", "Looking something up"],
|
|
108
|
+
["mcp_", "Using a connected app"],
|
|
109
|
+
["skill_", "Running a skill"],
|
|
110
|
+
["subagent", "Working on it"],
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* What an unrecognized tool reads as.
|
|
115
|
+
*
|
|
116
|
+
* Deliberately vague rather than derived from the tool's name. A humanized
|
|
117
|
+
* `slack_conversations_history` is worse than "Working on it": it is longer
|
|
118
|
+
* than the slot, it leaks an internal vocabulary, and it reads like an error
|
|
119
|
+
* message. The label's job is to show the turn is alive and doing something,
|
|
120
|
+
* and this does that job for every tool that will ever exist.
|
|
121
|
+
*/
|
|
122
|
+
export const GENERIC_ACTIVITY_LABEL = "Working on it";
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The line to show while `toolName` runs.
|
|
126
|
+
*
|
|
127
|
+
* Total, by construction: every tool gets a label, because the alternative is
|
|
128
|
+
* a surface that goes blank precisely when the assistant is busiest.
|
|
129
|
+
*/
|
|
130
|
+
export function activityLabelForTool(toolName: string): string {
|
|
131
|
+
const exact = TOOL_ACTIVITY_LABELS[toolName];
|
|
132
|
+
if (exact !== undefined) {
|
|
133
|
+
return exact;
|
|
134
|
+
}
|
|
135
|
+
for (const [prefix, label] of TOOL_ACTIVITY_LABEL_PREFIXES) {
|
|
136
|
+
if (toolName.startsWith(prefix)) {
|
|
137
|
+
return label;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return GENERIC_ACTIVITY_LABEL;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The line to show while the turn is *waiting* on the user for `toolName`,
|
|
145
|
+
* rather than running it.
|
|
146
|
+
*
|
|
147
|
+
* The distinction matters more here than anywhere else this module is read.
|
|
148
|
+
* `tool_use_start` fires before the approval gate blocks, so a surface that
|
|
149
|
+
* showed only {@link activityLabelForTool} would say "Running a command" for
|
|
150
|
+
* the whole time the turn was in fact doing nothing but waiting — the same
|
|
151
|
+
* misstatement the spoken narration was fixed to stop making. This is the
|
|
152
|
+
* island's version of that fix.
|
|
153
|
+
*
|
|
154
|
+
* It keeps the tool's own phrase and appends who is being waited on, because
|
|
155
|
+
* the alternative — a bare "Waiting for your approval" — asks the user to
|
|
156
|
+
* approve something the surface will not name. That phrase is as specific as
|
|
157
|
+
* the island's vocabulary gets: no tool names, no arguments, nothing a
|
|
158
|
+
* passer-by reading a Lock Screen should not see (see the module header).
|
|
159
|
+
* Anyone wanting the detail has the card itself, one tap away.
|
|
160
|
+
*/
|
|
161
|
+
export function approvalActivityLabel(toolName: string): string {
|
|
162
|
+
// A confirmation raised outside the tool pipeline (a proxy or network
|
|
163
|
+
// prompter) has no tool to name, and "Working on it — needs your okay" would
|
|
164
|
+
// be a worse sentence than the bare one: the turn is not working.
|
|
165
|
+
if (toolName.length === 0) {
|
|
166
|
+
return "Needs your okay";
|
|
167
|
+
}
|
|
168
|
+
return `${activityLabelForTool(toolName)} — needs your okay`;
|
|
169
|
+
}
|
|
@@ -109,21 +109,45 @@ export function phaseForFrame(
|
|
|
109
109
|
*/
|
|
110
110
|
export class LiveActivityReporter {
|
|
111
111
|
private lastPhase: LiveActivityPhase | null = null;
|
|
112
|
+
private lastDetail = "";
|
|
112
113
|
private ended = false;
|
|
113
114
|
|
|
114
115
|
constructor(private readonly conversationId: string) {}
|
|
115
116
|
|
|
116
117
|
/**
|
|
117
|
-
* Report
|
|
118
|
-
* are on the session's send path
|
|
118
|
+
* Report what a frame implies about the phase or the activity line, if
|
|
119
|
+
* either changed. Fire-and-forget: callers are on the session's send path
|
|
120
|
+
* and must not await this.
|
|
121
|
+
*
|
|
122
|
+
* Both travel in one dispatch because they are two fields of one content
|
|
123
|
+
* state: ActivityKit replaces the whole thing per push, so a dispatch that
|
|
124
|
+
* carried only the field that moved would blank the other.
|
|
119
125
|
*/
|
|
120
126
|
report(frame: LiveVoiceServerFramePayload): void {
|
|
127
|
+
if (this.ended) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
121
130
|
const phase = phaseForFrame(frame, this.lastPhase);
|
|
122
|
-
|
|
131
|
+
// The session sends this frame only on a change it wants surfaced, and
|
|
132
|
+
// sends an empty label when the turn stops working, so it is taken
|
|
133
|
+
// verbatim rather than derived.
|
|
134
|
+
const detail = frame.type === "activity" ? frame.label : this.lastDetail;
|
|
135
|
+
const phaseMoved = phase !== null && phase !== this.lastPhase;
|
|
136
|
+
if (!phaseMoved && detail === this.lastDetail) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (phase !== null) {
|
|
140
|
+
this.lastPhase = phase;
|
|
141
|
+
}
|
|
142
|
+
this.lastDetail = detail;
|
|
143
|
+
// An activity line can arrive before any phase has been established (a
|
|
144
|
+
// turn whose first tool starts before the phase-bearing frame lands).
|
|
145
|
+
// There is no content state to attach it to yet, and the phase the next
|
|
146
|
+
// frame sets will carry it.
|
|
147
|
+
if (this.lastPhase === null) {
|
|
123
148
|
return;
|
|
124
149
|
}
|
|
125
|
-
this.lastPhase
|
|
126
|
-
void this.dispatch(phase, "update");
|
|
150
|
+
void this.dispatch(this.lastPhase, "update", this.lastDetail);
|
|
127
151
|
}
|
|
128
152
|
|
|
129
153
|
/**
|
|
@@ -139,13 +163,17 @@ export class LiveActivityReporter {
|
|
|
139
163
|
return;
|
|
140
164
|
}
|
|
141
165
|
this.ended = true;
|
|
142
|
-
|
|
166
|
+
// No detail on the way out: whatever the turn was doing, it is not doing
|
|
167
|
+
// it any more, and this state is the one that lingers on the Lock Screen
|
|
168
|
+
// through the dismissal window.
|
|
169
|
+
void this.dispatch("ending", "end", "");
|
|
143
170
|
}
|
|
144
171
|
|
|
145
172
|
/** `protected` so a test can observe what would be sent without sending it. */
|
|
146
173
|
protected async dispatch(
|
|
147
174
|
phase: LiveActivityPhase,
|
|
148
175
|
event: "update" | "end",
|
|
176
|
+
detail: string,
|
|
149
177
|
): Promise<void> {
|
|
150
178
|
try {
|
|
151
179
|
const client = await VellumPlatformClient.create();
|
|
@@ -162,6 +190,7 @@ export class LiveActivityReporter {
|
|
|
162
190
|
conversation_id: this.conversationId,
|
|
163
191
|
phase,
|
|
164
192
|
event,
|
|
193
|
+
detail,
|
|
165
194
|
}),
|
|
166
195
|
});
|
|
167
196
|
if (!response.ok) {
|