@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
|
@@ -120,6 +120,7 @@ import type { AssistantSurface } from "./conversation-agent-loop.js";
|
|
|
120
120
|
import {
|
|
121
121
|
buildConversationErrorMessage,
|
|
122
122
|
classifyConversationError,
|
|
123
|
+
type ConversationErrorAttribution,
|
|
123
124
|
maxTokensReachedClassification,
|
|
124
125
|
} from "./conversation-error.js";
|
|
125
126
|
import { buildDeferredFinalizeEffect } from "./conversation-turn-finalize.js";
|
|
@@ -224,6 +225,13 @@ export interface EventHandlerState {
|
|
|
224
225
|
* path.
|
|
225
226
|
*/
|
|
226
227
|
providerErrorCode: string | null;
|
|
228
|
+
/**
|
|
229
|
+
* Classified category of the most recent provider error
|
|
230
|
+
* (`classifyConversationError(...).errorCategory`). Stamped onto the
|
|
231
|
+
* synthetic error row's metadata alongside {@link providerErrorCode} when
|
|
232
|
+
* the loop persists the failure as an assistant message.
|
|
233
|
+
*/
|
|
234
|
+
providerErrorCategory: string | null;
|
|
227
235
|
persistProviderErrorAsAssistantMessage: boolean;
|
|
228
236
|
lastAssistantMessageId: string | undefined;
|
|
229
237
|
/**
|
|
@@ -487,6 +495,14 @@ export interface EventHandlerState {
|
|
|
487
495
|
* subprocess env override can never rewrite.
|
|
488
496
|
*/
|
|
489
497
|
readonly stagedRevealIdentities: Set<string>;
|
|
498
|
+
/**
|
|
499
|
+
* `ui_show` tool calls whose streaming input has already been classified
|
|
500
|
+
* for a surface placeholder (see {@link handleInputJsonDelta}). Holds both
|
|
501
|
+
* outcomes, the ones that emitted `ui_surface_pending` and the ones whose
|
|
502
|
+
* prefix proved they are not a `visual`, so the scan runs once per call
|
|
503
|
+
* rather than on every delta.
|
|
504
|
+
*/
|
|
505
|
+
readonly surfacePendingScannedToolUseIds: Set<string>;
|
|
490
506
|
/**
|
|
491
507
|
* In-flight priming of {@link liveRevealGuardEntries}. The dispatcher
|
|
492
508
|
* awaits this before processing a `text_delta` (and before the
|
|
@@ -540,6 +556,8 @@ export interface EventHandlerDeps {
|
|
|
540
556
|
* degrades gracefully when it's absent.
|
|
541
557
|
*/
|
|
542
558
|
readonly latencyTracker?: TurnLatencyTracker;
|
|
559
|
+
/** Best-effort resolved route metadata for provider error classification. */
|
|
560
|
+
readonly errorAttribution?: () => ConversationErrorAttribution;
|
|
543
561
|
}
|
|
544
562
|
|
|
545
563
|
// ── Factory ──────────────────────────────────────────────────────────
|
|
@@ -560,6 +578,7 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
560
578
|
model: "",
|
|
561
579
|
providerErrorUserMessage: null,
|
|
562
580
|
providerErrorCode: null,
|
|
581
|
+
providerErrorCategory: null,
|
|
563
582
|
persistProviderErrorAsAssistantMessage: false,
|
|
564
583
|
lastAssistantMessageId: undefined,
|
|
565
584
|
assistantRowAwaitingFinalization: false,
|
|
@@ -606,6 +625,7 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
606
625
|
candidateRemintAuthorities: [],
|
|
607
626
|
forChatMintWatermark: currentForChatMintWatermark(),
|
|
608
627
|
stagedRevealIdentities: new Set(),
|
|
628
|
+
surfacePendingScannedToolUseIds: new Set(),
|
|
609
629
|
liveRevealGuardPriming: undefined,
|
|
610
630
|
};
|
|
611
631
|
}
|
|
@@ -628,7 +648,7 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
628
648
|
async function chatRevealCandidates(
|
|
629
649
|
state: EventHandlerState,
|
|
630
650
|
): Promise<readonly ResolvedRevealCandidate[] | undefined> {
|
|
631
|
-
if (!isAssistantFeatureFlagEnabled("chat-credential-reveal"
|
|
651
|
+
if (!isAssistantFeatureFlagEnabled("chat-credential-reveal")) {
|
|
632
652
|
return undefined;
|
|
633
653
|
}
|
|
634
654
|
return resolvedRevealCandidatesForState(state);
|
|
@@ -1169,6 +1189,25 @@ function schedulePartialFlush(
|
|
|
1169
1189
|
// tools the client discards it (extractCodePreview only handles app tools),
|
|
1170
1190
|
// so we skip forwarding entirely to avoid transport/decode overhead.
|
|
1171
1191
|
const APP_TOOL_NAMES = new Set(["app_create"]);
|
|
1192
|
+
|
|
1193
|
+
// ── Surface Placeholder Detection ────────────────────────────────────
|
|
1194
|
+
// A `visual` ui_show streams the longest tool input the model produces, and
|
|
1195
|
+
// nothing is on screen while it does: the ui_show chip is suppressed because
|
|
1196
|
+
// the surface renders in its place, and the surface only exists once the call
|
|
1197
|
+
// closes. Sniffing `surface_type` off the accumulated prefix lets the client
|
|
1198
|
+
// hold a placeholder for that gap. The input itself is never forwarded: the
|
|
1199
|
+
// fragment is large and the client has no use for a partial one.
|
|
1200
|
+
const UI_SHOW_TOOL_NAME = "ui_show";
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* How much of the accumulated input the surface-type sniff reads. `ui_show`
|
|
1204
|
+
* declares `surface_type` first, so a real one lands in the first few dozen
|
|
1205
|
+
* characters; past this window the call is classified as "not a visual" and
|
|
1206
|
+
* never re-scanned.
|
|
1207
|
+
*/
|
|
1208
|
+
const SURFACE_TYPE_SCAN_CHARS = 400;
|
|
1209
|
+
|
|
1210
|
+
const VISUAL_SURFACE_TYPE_PATTERN = /"surface_type"\s*:\s*"visual"/;
|
|
1172
1211
|
const MAX_TOKENS_CONTINUE_PROMPT =
|
|
1173
1212
|
"Continue from where you stopped. Do not repeat content you've already sent.";
|
|
1174
1213
|
const MAX_TOKENS_SURFACE_COMPLETION_SUMMARY = "Continue";
|
|
@@ -1734,11 +1773,48 @@ function handleToolOutputChunk(
|
|
|
1734
1773
|
});
|
|
1735
1774
|
}
|
|
1736
1775
|
|
|
1776
|
+
/**
|
|
1777
|
+
* Announce a still-streaming `ui_show` that is going to produce a `visual`
|
|
1778
|
+
* surface, so the client can hold a placeholder for it.
|
|
1779
|
+
*
|
|
1780
|
+
* Runs at most once per tool call: the first delta whose prefix matches emits,
|
|
1781
|
+
* and a call whose prefix grows past the scan window without matching is
|
|
1782
|
+
* recorded as classified so later deltas cost nothing.
|
|
1783
|
+
*/
|
|
1784
|
+
function announcePendingVisualSurface(
|
|
1785
|
+
state: EventHandlerState,
|
|
1786
|
+
deps: EventHandlerDeps,
|
|
1787
|
+
event: Extract<AgentEvent, { type: "input_json_delta" }>,
|
|
1788
|
+
): void {
|
|
1789
|
+
if (state.surfacePendingScannedToolUseIds.has(event.toolUseId)) {
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
const prefix = event.accumulatedJson.slice(0, SURFACE_TYPE_SCAN_CHARS);
|
|
1793
|
+
if (!VISUAL_SURFACE_TYPE_PATTERN.test(prefix)) {
|
|
1794
|
+
if (event.accumulatedJson.length >= SURFACE_TYPE_SCAN_CHARS) {
|
|
1795
|
+
state.surfacePendingScannedToolUseIds.add(event.toolUseId);
|
|
1796
|
+
}
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
state.surfacePendingScannedToolUseIds.add(event.toolUseId);
|
|
1800
|
+
deps.onEvent({
|
|
1801
|
+
type: "ui_surface_pending",
|
|
1802
|
+
surfaceType: "visual",
|
|
1803
|
+
conversationId: deps.ctx.conversationId,
|
|
1804
|
+
toolUseId: event.toolUseId,
|
|
1805
|
+
messageId: state.lastAssistantMessageId,
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1737
1809
|
export function handleInputJsonDelta(
|
|
1738
1810
|
state: EventHandlerState,
|
|
1739
1811
|
deps: EventHandlerDeps,
|
|
1740
1812
|
event: Extract<AgentEvent, { type: "input_json_delta" }>,
|
|
1741
1813
|
): void {
|
|
1814
|
+
if (event.toolName === UI_SHOW_TOOL_NAME) {
|
|
1815
|
+
announcePendingVisualSurface(state, deps, event);
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1742
1818
|
// Only forward input deltas for app tools — the client only uses this
|
|
1743
1819
|
// stream for app_create code previews. Non-app tools would send large
|
|
1744
1820
|
// cumulative JSON on every delta with no benefit.
|
|
@@ -2555,9 +2631,7 @@ function handleError(
|
|
|
2555
2631
|
deps: EventHandlerDeps,
|
|
2556
2632
|
event: Extract<AgentEvent, { type: "error" }>,
|
|
2557
2633
|
): void {
|
|
2558
|
-
const classified =
|
|
2559
|
-
phase: "agent_loop",
|
|
2560
|
-
});
|
|
2634
|
+
const classified = classifyAgentLoopError(event.error, deps);
|
|
2561
2635
|
if (classified.errorCategory === "provider_api_error") {
|
|
2562
2636
|
log.error(
|
|
2563
2637
|
{
|
|
@@ -2582,6 +2656,7 @@ function handleError(
|
|
|
2582
2656
|
);
|
|
2583
2657
|
state.providerErrorUserMessage = classified.userMessage;
|
|
2584
2658
|
state.providerErrorCode = classified.code;
|
|
2659
|
+
state.providerErrorCategory = classified.errorCategory;
|
|
2585
2660
|
state.persistProviderErrorAsAssistantMessage =
|
|
2586
2661
|
shouldPersistProviderErrorAsAssistantMessage(classified);
|
|
2587
2662
|
}
|
|
@@ -3066,9 +3141,7 @@ function handleProviderError(
|
|
|
3066
3141
|
deps: EventHandlerDeps,
|
|
3067
3142
|
event: Extract<AgentEvent, { type: "provider_error" }>,
|
|
3068
3143
|
): void {
|
|
3069
|
-
const classified =
|
|
3070
|
-
phase: "agent_loop",
|
|
3071
|
-
});
|
|
3144
|
+
const classified = classifyAgentLoopError(event.error, deps);
|
|
3072
3145
|
if (!shouldPersistProviderErrorAsAssistantMessage(classified)) {
|
|
3073
3146
|
return;
|
|
3074
3147
|
}
|
|
@@ -3090,6 +3163,16 @@ function handleProviderError(
|
|
|
3090
3163
|
}
|
|
3091
3164
|
}
|
|
3092
3165
|
|
|
3166
|
+
function classifyAgentLoopError(
|
|
3167
|
+
error: Error,
|
|
3168
|
+
deps: EventHandlerDeps,
|
|
3169
|
+
): ReturnType<typeof classifyConversationError> {
|
|
3170
|
+
return classifyConversationError(error, {
|
|
3171
|
+
phase: "agent_loop",
|
|
3172
|
+
...deps.errorAttribution?.(),
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
3175
|
+
|
|
3093
3176
|
// ── Dispatcher ───────────────────────────────────────────────────────
|
|
3094
3177
|
|
|
3095
3178
|
/** Routes an AgentEvent to the appropriate handler. */
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "../config/llm-context-resolution.js";
|
|
31
31
|
import {
|
|
32
32
|
resolveCallSiteConfig,
|
|
33
|
-
|
|
33
|
+
resolveCallSiteConfigWithProfile,
|
|
34
34
|
resolveProfilelessModelKey,
|
|
35
35
|
selectWinningProfile,
|
|
36
36
|
} from "../config/llm-resolver.js";
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
getLastUserTimestampBefore,
|
|
48
48
|
getMessageById,
|
|
49
49
|
provenanceFromTrustContext,
|
|
50
|
+
PROVIDER_ERROR_MESSAGE_KIND,
|
|
50
51
|
resolveOverrideProfile,
|
|
51
52
|
updateConversationContextWindow,
|
|
52
53
|
updateConversationSlackContextWatermark,
|
|
@@ -60,6 +61,11 @@ import { HOOKS } from "../plugin-api/constants.js";
|
|
|
60
61
|
import type { ConversationGraphMemory } from "../plugins/defaults/memory/graph/conversation-graph-memory.js";
|
|
61
62
|
import { enqueueMemoryRetrospectiveOnCompaction } from "../plugins/defaults/memory/memory-retrospective-enqueue.js";
|
|
62
63
|
import { runHook } from "../plugins/pipeline.js";
|
|
64
|
+
import { isManagedConnectionRoute } from "../providers/connection-resolution.js";
|
|
65
|
+
import {
|
|
66
|
+
ConnectionResolutionError,
|
|
67
|
+
resolveRoutingIdentity,
|
|
68
|
+
} from "../providers/routing-identity.js";
|
|
63
69
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
64
70
|
import type { Provider } from "../providers/types.js";
|
|
65
71
|
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
@@ -94,6 +100,7 @@ import {
|
|
|
94
100
|
budgetYieldUnrecoveredClassification,
|
|
95
101
|
buildConversationErrorMessage,
|
|
96
102
|
classifyConversationError,
|
|
103
|
+
type ConversationErrorAttribution,
|
|
97
104
|
isUserCancellation,
|
|
98
105
|
} from "./conversation-error.js";
|
|
99
106
|
import { raceWithTimeout } from "./conversation-media-retry.js";
|
|
@@ -132,6 +139,19 @@ const log = getLogger("conversation-agent-loop");
|
|
|
132
139
|
const DISK_PRESSURE_ERROR_CODE = "DISK_SPACE_CRITICAL" as const;
|
|
133
140
|
const DISK_PRESSURE_ERROR_CATEGORY = "disk_pressure";
|
|
134
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Assistant-voice wording persisted as the synthetic assistant row when a
|
|
144
|
+
* turn terminates because managed credits ran out. The shared classification
|
|
145
|
+
* (`managedBalanceClassification` in conversation-error.ts) keeps its
|
|
146
|
+
* `userMessage` context-neutral because it also feeds the non-terminal
|
|
147
|
+
* memory-v3 degraded notice, where a normal reply still follows; here the
|
|
148
|
+
* turn truly ends with no reply, so first-person copy is accurate. Phrased
|
|
149
|
+
* about the past failure, not the current balance, because the row stays in
|
|
150
|
+
* the transcript (and renders as a plain bubble) after the balance recovers.
|
|
151
|
+
*/
|
|
152
|
+
const OUT_OF_CREDITS_ASSISTANT_REPLY =
|
|
153
|
+
"I couldn't reply because you ran out of credits. Add credits in Settings → Billing and we can pick up where we left off.";
|
|
154
|
+
|
|
135
155
|
/** Title-cased friendly labels for tool names, used in confirmation chips. */
|
|
136
156
|
const TOOL_FRIENDLY_LABEL: Record<string, string> = {
|
|
137
157
|
bash: "Run Command",
|
|
@@ -254,6 +274,22 @@ export async function runAgentLoopImpl(
|
|
|
254
274
|
* the turn.
|
|
255
275
|
*/
|
|
256
276
|
isHiddenPrompt?: boolean;
|
|
277
|
+
/**
|
|
278
|
+
* Row the end-of-turn reply notification should treat as the prompt this
|
|
279
|
+
* turn answers. Defaults to `userMessageId`; a coalesced batch overrides it
|
|
280
|
+
* with its last push-eligible member (see
|
|
281
|
+
* `isReplyPushIneligibleUserMessage`), whose reply the user is actually
|
|
282
|
+
* waiting on, rather than a suppressed row that merely landed last.
|
|
283
|
+
*/
|
|
284
|
+
notifyUserMessageId?: string;
|
|
285
|
+
/**
|
|
286
|
+
* True when this run's reply streams to the app and nowhere else, so the
|
|
287
|
+
* end-of-turn reply notification ignores the initiating row's
|
|
288
|
+
* channel/voice delivery markers (see
|
|
289
|
+
* `isReplyPushIneligibleUserMessage`). Set by the retry route, which
|
|
290
|
+
* re-runs a stored anchor row without its original delivery orchestration.
|
|
291
|
+
*/
|
|
292
|
+
replyDeliveredInAppOnly?: boolean;
|
|
257
293
|
/**
|
|
258
294
|
* LLM call-site identifier threaded into the per-call provider config.
|
|
259
295
|
* Adapter callers (heartbeat, filing, scheduler, etc.) pass their own
|
|
@@ -392,10 +428,7 @@ export async function runAgentLoopImpl(
|
|
|
392
428
|
// connection and profile so credential/connection errors point at the
|
|
393
429
|
// exact slot to fix instead of a generic banner. Resolution can itself
|
|
394
430
|
// throw on a broken config — attribution must never mask the real error.
|
|
395
|
-
const turnErrorAttribution = (): {
|
|
396
|
-
connectionName?: string;
|
|
397
|
-
profileName?: string;
|
|
398
|
-
} => {
|
|
431
|
+
const turnErrorAttribution = (): ConversationErrorAttribution => {
|
|
399
432
|
try {
|
|
400
433
|
const overrideProfile = readCurrentOverrideProfile();
|
|
401
434
|
const resolveOpts = {
|
|
@@ -403,21 +436,30 @@ export async function runAgentLoopImpl(
|
|
|
403
436
|
forceOverrideProfile,
|
|
404
437
|
selectionSeed: ctx.conversationId,
|
|
405
438
|
};
|
|
406
|
-
const resolved =
|
|
407
|
-
turnCallSite,
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
439
|
+
const { config: resolved, profileName } =
|
|
440
|
+
resolveCallSiteConfigWithProfile(turnCallSite, config.llm, resolveOpts);
|
|
441
|
+
let connectionName = resolved.provider_connection;
|
|
442
|
+
try {
|
|
443
|
+
connectionName =
|
|
444
|
+
resolveRoutingIdentity(resolved.provider, resolved.model)
|
|
445
|
+
?.connectionName ?? connectionName;
|
|
446
|
+
} catch (error) {
|
|
447
|
+
if (!(error instanceof ConnectionResolutionError)) {
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
connectionName = error.connectionName;
|
|
451
|
+
}
|
|
452
|
+
// Managed-ness comes from the connection row, matching what dispatch
|
|
453
|
+
// decides. The profile's own provider can't stand in: a concrete
|
|
454
|
+
// provider tweak over a managed winner keeps the managed connection
|
|
455
|
+
// while replacing the provider (`llm-resolver.ts`).
|
|
456
|
+
const isManagedRoute = connectionName
|
|
457
|
+
? isManagedConnectionRoute(connectionName)
|
|
458
|
+
: undefined;
|
|
416
459
|
return {
|
|
417
|
-
...(
|
|
418
|
-
? { connectionName: resolved.provider_connection }
|
|
419
|
-
: {}),
|
|
460
|
+
...(connectionName ? { connectionName } : {}),
|
|
420
461
|
...(profileName ? { profileName } : {}),
|
|
462
|
+
...(isManagedRoute !== undefined ? { isManagedRoute } : {}),
|
|
421
463
|
};
|
|
422
464
|
} catch {
|
|
423
465
|
return {};
|
|
@@ -620,6 +662,11 @@ export async function runAgentLoopImpl(
|
|
|
620
662
|
// has been emitted. Guards the catch block: an error thrown afterwards
|
|
621
663
|
// (deferred turn-tail bookkeeping) must not relabel a visibly-replied turn.
|
|
622
664
|
let turnReplied = false;
|
|
665
|
+
// Narrower than `turnReplied`: true only for a `message_complete` branch that
|
|
666
|
+
// produced a genuine reply. A handed-off generation continues the run, and a
|
|
667
|
+
// provider-error turn's only assistant row is the synthetic error text, so
|
|
668
|
+
// the deferred tail must not treat either as a final reply.
|
|
669
|
+
let turnCompleted = false;
|
|
623
670
|
|
|
624
671
|
const publishLoopMessagesChanged = (): void => {
|
|
625
672
|
if (
|
|
@@ -914,6 +961,10 @@ export async function runAgentLoopImpl(
|
|
|
914
961
|
// `client_os` into the in-flight turn.
|
|
915
962
|
ctx.currentTurnClientOs = ctx.clientOs ?? undefined;
|
|
916
963
|
|
|
964
|
+
// Freeze the app the client had in view at turn start, for the same
|
|
965
|
+
// anti-race reason as `client_os` above.
|
|
966
|
+
ctx.currentTurnVisibleAppId = ctx.visibleAppId ?? undefined;
|
|
967
|
+
|
|
917
968
|
// Resolve the effective profile key for this turn and detect changes.
|
|
918
969
|
// `modelProfileKey` is the actual profile used for this turn. The
|
|
919
970
|
// notice key is narrower: it only marks turns where runtime context should
|
|
@@ -1014,6 +1065,7 @@ export async function runAgentLoopImpl(
|
|
|
1014
1065
|
turnInterfaceContext: capturedTurnInterfaceContext,
|
|
1015
1066
|
applyCompaction: applySuccessfulCompaction,
|
|
1016
1067
|
latencyTracker,
|
|
1068
|
+
errorAttribution: turnErrorAttribution,
|
|
1017
1069
|
};
|
|
1018
1070
|
const eventHandler = (event: AgentEvent): Promise<void> => {
|
|
1019
1071
|
if (
|
|
@@ -1343,10 +1395,23 @@ export async function runAgentLoopImpl(
|
|
|
1343
1395
|
capturedTurnInterfaceContext.userMessageInterface,
|
|
1344
1396
|
assistantMessageInterface:
|
|
1345
1397
|
capturedTurnInterfaceContext.assistantMessageInterface,
|
|
1398
|
+
// Mark the synthetic row as a provider-failure notice so clients
|
|
1399
|
+
// can render it as a themed card instead of persona speech
|
|
1400
|
+
// (`isProviderErrorMetadata` -> the wire `providerError` field).
|
|
1401
|
+
messageKind: PROVIDER_ERROR_MESSAGE_KIND,
|
|
1402
|
+
providerErrorCode: state.providerErrorCode ?? undefined,
|
|
1403
|
+
providerErrorCategory: state.providerErrorCategory ?? undefined,
|
|
1346
1404
|
};
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1405
|
+
// The persisted row re-enters LLM history and is displayed as
|
|
1406
|
+
// assistant speech, so managed-credits exhaustion swaps the
|
|
1407
|
+
// context-neutral classification copy for assistant-voice wording.
|
|
1408
|
+
const persistedErrorText =
|
|
1409
|
+
state.providerErrorCode === "PROVIDER_BILLING" &&
|
|
1410
|
+
state.providerErrorCategory === "credits_exhausted"
|
|
1411
|
+
? OUT_OF_CREDITS_ASSISTANT_REPLY
|
|
1412
|
+
: state.providerErrorUserMessage;
|
|
1413
|
+
const errorAssistantMessage =
|
|
1414
|
+
createAssistantMessage(persistedErrorText);
|
|
1350
1415
|
const errorRow = await addMessage(
|
|
1351
1416
|
ctx.conversationId,
|
|
1352
1417
|
"assistant",
|
|
@@ -1506,6 +1571,7 @@ export async function runAgentLoopImpl(
|
|
|
1506
1571
|
publishLoopMessagesChanged();
|
|
1507
1572
|
} else {
|
|
1508
1573
|
turnReplied = true;
|
|
1574
|
+
turnCompleted = !persistedErrorAssistantMessage;
|
|
1509
1575
|
ctx.emitActivityState("idle", "message_complete", {
|
|
1510
1576
|
anchor: "global",
|
|
1511
1577
|
requestId: reqId,
|
|
@@ -1541,7 +1607,17 @@ export async function runAgentLoopImpl(
|
|
|
1541
1607
|
// drain the deferred bookkeeping — after the SSE, before the `finally`
|
|
1542
1608
|
// commits and drains the queue for the next turn.
|
|
1543
1609
|
await settlePendingPartialFlush(state, deps);
|
|
1544
|
-
await runDeferredTurnTail({
|
|
1610
|
+
await runDeferredTurnTail({
|
|
1611
|
+
ctx,
|
|
1612
|
+
state,
|
|
1613
|
+
rlog,
|
|
1614
|
+
generationCompletedAt,
|
|
1615
|
+
turnCompleted,
|
|
1616
|
+
userMessageId: options?.notifyUserMessageId ?? userMessageId,
|
|
1617
|
+
...(options?.replyDeliveredInAppOnly
|
|
1618
|
+
? { replyDeliveredInAppOnly: true }
|
|
1619
|
+
: {}),
|
|
1620
|
+
});
|
|
1545
1621
|
} catch (err) {
|
|
1546
1622
|
clearConversationNotices(ctx.conversationId);
|
|
1547
1623
|
const errorCtx = {
|
|
@@ -1669,6 +1745,15 @@ export async function runAgentLoopImpl(
|
|
|
1669
1745
|
// Channel command intents (e.g. Telegram /start) are single-turn metadata.
|
|
1670
1746
|
// Clear at turn end so they never leak into subsequent unrelated messages.
|
|
1671
1747
|
ctx.commandIntent = undefined;
|
|
1748
|
+
// The app on screen is view state owned by the message that reported it,
|
|
1749
|
+
// not durable conversation state: nothing tells the daemon when the user
|
|
1750
|
+
// closes an app. Clear both copies at turn end so a later turn that brings
|
|
1751
|
+
// no client transport (a scheduled wake, a background follow-up) cannot
|
|
1752
|
+
// claim the user is looking at an app they may have closed. Every inbound
|
|
1753
|
+
// send re-applies it from transport before its own turn, including the
|
|
1754
|
+
// queue drain, so live turns are unaffected.
|
|
1755
|
+
ctx.visibleAppId = undefined;
|
|
1756
|
+
ctx.currentTurnVisibleAppId = undefined;
|
|
1672
1757
|
// taskRunId scopes ephemeral task-run permissions to a single turn. Clear
|
|
1673
1758
|
// before drainQueue so queued/drained turns on a reused conversation can't
|
|
1674
1759
|
// inherit stale in-task-run scope from the turn that just finished.
|