mixdog 1.4.0 → 1.4.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/package.json +1 -1
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/gate-local.mjs +12 -3
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test-data-dir.mjs +10 -7
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/browser-use/SKILL.md +6 -4
- package/src/defaults/skills/computer-use/SKILL.md +4 -2
- package/src/lib/rules-builder.cjs +29 -12
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +5 -4
- package/src/rules/lead/LEAD.md +10 -5
- package/src/rules/shared/10-tool-workflow.md +3 -1
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +1 -1
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/registry.mjs +12 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +8 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +61 -6
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- package/src/runtime/computer-bridge/action-schema.mjs +2 -2
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/defaults-separation.mjs +1 -4
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/session-activity-signal.mjs +14 -0
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +6 -10
- package/src/runtime/visualize-bridge/client.mjs +2 -0
- package/src/session-runtime/agent-home-delivery.mjs +41 -0
- package/src/session-runtime/agent-task-sessions.mjs +152 -63
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/tools.mjs +5 -1
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- package/src/standalone/daemon-session-control.mjs +18 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +75 -28
- package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
- package/src/ui/statusline-agents.mjs +1 -3
|
@@ -184,6 +184,7 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
|
|
|
184
184
|
signal: attemptSignal,
|
|
185
185
|
label: provider.name,
|
|
186
186
|
providerName: provider.name,
|
|
187
|
+
sessionId: opts.sessionId,
|
|
187
188
|
onStreamDelta: opts.onStreamDelta,
|
|
188
189
|
onToolCall: opts.onToolCall,
|
|
189
190
|
onTextDelta: opts.onTextDelta,
|
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { mergeToolCallDelta } from './tool-call-acc.mjs';
|
|
7
7
|
import { reportStreamItemStart } from '../lib/send-callbacks.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
appendText,
|
|
10
|
+
drainLeakedCalls,
|
|
11
|
+
relayText,
|
|
12
|
+
reportLifecycle,
|
|
13
|
+
reportProgress,
|
|
14
|
+
reportTransport,
|
|
15
|
+
} from './stream-state.mjs';
|
|
9
16
|
import { chatContentParts } from '../openai-compat-reasoning-field.mjs';
|
|
10
17
|
|
|
11
18
|
function appendReasoning(state, text, source) {
|
|
@@ -50,7 +57,7 @@ export function applyCompatChunk(state, chunk) {
|
|
|
50
57
|
const choice = chunk?.choices?.[0];
|
|
51
58
|
const delta = choice?.delta;
|
|
52
59
|
if (typeof delta?.role === 'string' && delta.role) {
|
|
53
|
-
|
|
60
|
+
reportLifecycle(state, 'semantic');
|
|
54
61
|
}
|
|
55
62
|
const contentParts = chatContentParts(delta?.content);
|
|
56
63
|
if (contentParts) applyContentParts(state, contentParts);
|
|
@@ -70,6 +70,13 @@ export function reportProgress(state, kind) {
|
|
|
70
70
|
} catch {}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** Chunk metadata without output (a role-only delta): progress once the
|
|
74
|
+
* model has produced output; before that it neither ends the first-output
|
|
75
|
+
* phase nor starts the semantic idle (the chunk already reported transport). */
|
|
76
|
+
export function reportLifecycle(state, kind) {
|
|
77
|
+
if (state.semanticIdleDeadlineAt > 0) reportProgress(state, kind);
|
|
78
|
+
}
|
|
79
|
+
|
|
73
80
|
/** Text the model exposed: accumulate, refresh the idle deadline, relay live. */
|
|
74
81
|
export function appendText(state, text) {
|
|
75
82
|
state.content += text;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
|
|
11
11
|
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
12
12
|
createTimeoutSignal,
|
|
13
|
+
providerFirstOutputTimeoutMs,
|
|
13
14
|
} from '../stall-policy.mjs';
|
|
14
15
|
import { TIMEOUT_PHASE } from '../../../shared/provider-timeout-phase.mjs';
|
|
15
16
|
import {
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
toolWorkStarted,
|
|
29
30
|
} from './openai-compat-chat-stream/stream-state.mjs';
|
|
30
31
|
import { applyCompatChunk } from './openai-compat-chat-stream/chunk-events.mjs';
|
|
32
|
+
import { streamFirstOutputWindow } from './lib/first-output-window.mjs';
|
|
31
33
|
import { incompleteStreamError, settleCompatStream } from './openai-compat-chat-stream/stream-result.mjs';
|
|
32
34
|
|
|
33
35
|
export async function consumeCompatChatCompletionStream(
|
|
@@ -45,11 +47,20 @@ export async function consumeCompatChatCompletionStream(
|
|
|
45
47
|
semanticIdleTimeoutMs,
|
|
46
48
|
firstByteTimeoutMs,
|
|
47
49
|
leakedToolCallRecovery = true,
|
|
50
|
+
sessionId = null,
|
|
48
51
|
} = {}
|
|
49
52
|
) {
|
|
50
53
|
const idleOverrideEnabled = Number.isFinite(Number(semanticIdleTimeoutMs)) && Number(semanticIdleTimeoutMs) > 0;
|
|
51
54
|
const idleEnabled = idleOverrideEnabled || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
52
55
|
const idleMs = idleOverrideEnabled ? Number(semanticIdleTimeoutMs) : PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
56
|
+
// Until the first semantic output the logical request's first-output window
|
|
57
|
+
// bounds the stream; a request that already had output runs under the
|
|
58
|
+
// semantic idle from the stream's opening.
|
|
59
|
+
const firstOutput = streamFirstOutputWindow({
|
|
60
|
+
sessionId,
|
|
61
|
+
onStreamDelta,
|
|
62
|
+
timeoutMs: providerFirstOutputTimeoutMs(idleMs),
|
|
63
|
+
});
|
|
53
64
|
const state = createCompatStreamState({
|
|
54
65
|
providerName,
|
|
55
66
|
knownToolNames,
|
|
@@ -60,6 +71,7 @@ export async function consumeCompatChatCompletionStream(
|
|
|
60
71
|
onStreamItemStart,
|
|
61
72
|
leakedToolCallRecovery,
|
|
62
73
|
});
|
|
74
|
+
if (firstOutput.outputSeen) state.semanticIdleDeadlineAt = Date.now() + idleMs;
|
|
63
75
|
reportTransport(state);
|
|
64
76
|
const iterator = stream[Symbol.asyncIterator]();
|
|
65
77
|
let iteratorDone = false;
|
|
@@ -72,9 +84,8 @@ export async function consumeCompatChatCompletionStream(
|
|
|
72
84
|
while (true) {
|
|
73
85
|
const { value: chunk, done } = await nextAsyncWithWatchdog(iterator, {
|
|
74
86
|
signal: state.sawFirstEvent ? signal : firstByteTimeout.signal,
|
|
75
|
-
idleMs,
|
|
76
|
-
|
|
77
|
-
idleEnabled: state.sawFirstEvent && idleEnabled && state.semanticIdleDeadlineAt > 0,
|
|
87
|
+
...firstOutput.idleWatch(state.semanticIdleDeadlineAt, idleMs),
|
|
88
|
+
idleEnabled: state.sawFirstEvent && idleEnabled,
|
|
78
89
|
idleLabel: `${label} SSE idle`,
|
|
79
90
|
streamLabel: label,
|
|
80
91
|
emittedToolCall: () => toolWorkStarted(state),
|
|
@@ -147,7 +147,8 @@ function settleMaxOutputIncomplete(event, state, ctx, reason) {
|
|
|
147
147
|
function onCreated(event, state, ctx) {
|
|
148
148
|
if (event.response?.model) state.model = event.response.model;
|
|
149
149
|
if (event.response?.id) state.responseId = event.response.id;
|
|
150
|
-
|
|
150
|
+
// Metadata, not output: progress only once the stream has seen output.
|
|
151
|
+
ctx.onLifecycle?.('semantic');
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
function onOutputTextDelta(event, state, ctx) {
|
|
@@ -196,7 +197,8 @@ function onOutputItemAdded(event, state, ctx) {
|
|
|
196
197
|
state.toolInFlight = true;
|
|
197
198
|
}
|
|
198
199
|
reportStreamItemStart(ctx.onStreamItemStart, responsesItemStart(event.item));
|
|
199
|
-
|
|
200
|
+
if (state.toolInFlight) signal(ctx, 'tool');
|
|
201
|
+
else ctx.onLifecycle?.('semantic');
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
// A tool call's args (or custom-tool input) are streaming — mark tool work
|
|
@@ -241,6 +241,7 @@ async function streamCompatResponses({ provider, params, label, signal, opts, he
|
|
|
241
241
|
return await consumeCompatResponsesStream(stream, {
|
|
242
242
|
signal: attemptSignal,
|
|
243
243
|
label,
|
|
244
|
+
sessionId: opts.sessionId,
|
|
244
245
|
onStreamDelta: opts.onStreamDelta,
|
|
245
246
|
onToolCall: opts.onToolCall,
|
|
246
247
|
onTextDelta: opts.onTextDelta,
|
|
@@ -168,9 +168,11 @@ export async function nextAsyncWithWatchdog(
|
|
|
168
168
|
}
|
|
169
169
|
// The SDK JSON-decodes each frame inside next(): a SyntaxError here is a
|
|
170
170
|
// malformed wire frame, typed at this boundary and nowhere else.
|
|
171
|
-
iterator
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
iterator
|
|
172
|
+
.next()
|
|
173
|
+
.then(resolve, (error) =>
|
|
174
|
+
reject(error instanceof SyntaxError ? malformedCompatStreamError(streamLabel || idleLabel, error) : error)
|
|
175
|
+
);
|
|
174
176
|
});
|
|
175
177
|
} catch (err) {
|
|
176
178
|
if (idleTimedOut)
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
|
|
8
8
|
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
9
9
|
createTimeoutSignal,
|
|
10
|
+
providerFirstOutputTimeoutMs,
|
|
10
11
|
} from '../stall-policy.mjs';
|
|
11
12
|
import {
|
|
12
13
|
closeCompatStream,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
parseCompletedToolCallArgumentsJson,
|
|
24
25
|
} from './lib/openai-tool-args.mjs';
|
|
25
26
|
import { handleCompatResponsesStreamEvent } from './openai-compat-responses-events.mjs';
|
|
27
|
+
import { streamFirstOutputWindow } from './lib/first-output-window.mjs';
|
|
26
28
|
import {
|
|
27
29
|
attachPartialState,
|
|
28
30
|
createResponsesLeakRelay,
|
|
@@ -56,6 +58,7 @@ export async function consumeCompatResponsesStream(
|
|
|
56
58
|
responseOutputText,
|
|
57
59
|
knownToolNames,
|
|
58
60
|
semanticIdleTimeoutMs,
|
|
61
|
+
sessionId = null,
|
|
59
62
|
} = {}
|
|
60
63
|
) {
|
|
61
64
|
try {
|
|
@@ -68,13 +71,23 @@ export async function consumeCompatResponsesStream(
|
|
|
68
71
|
const idleEnabled = idleOverrideEnabled || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
69
72
|
// Per-event (last-event-relative) SEMANTIC idle — see the Chat path note.
|
|
70
73
|
const idleMs = idleOverrideEnabled ? Number(semanticIdleTimeoutMs) : PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
74
|
+
// Until the first semantic output the logical request's first-output window
|
|
75
|
+
// bounds the stream; a request that already had output runs under the
|
|
76
|
+
// semantic idle from the stream's opening.
|
|
77
|
+
const firstOutput = streamFirstOutputWindow({
|
|
78
|
+
sessionId,
|
|
79
|
+
onStreamDelta,
|
|
80
|
+
timeoutMs: providerFirstOutputTimeoutMs(idleMs),
|
|
81
|
+
});
|
|
71
82
|
const state = createResponsesStreamState();
|
|
83
|
+
if (firstOutput.outputSeen) state.semanticIdleDeadlineAt = Date.now() + idleMs;
|
|
72
84
|
const reportProgress = (kind) => {
|
|
73
85
|
if (kind !== 'transport') state.semanticIdleDeadlineAt = Date.now() + idleMs;
|
|
74
86
|
try {
|
|
75
87
|
onStreamDelta?.(kind);
|
|
76
88
|
} catch {}
|
|
77
89
|
};
|
|
90
|
+
const reportLifecycle = (kind) => reportProgress(state.semanticIdleDeadlineAt > 0 ? kind : 'transport');
|
|
78
91
|
let sawFirstEvent = false;
|
|
79
92
|
const { relayLeakText, flushLeak, leakedCalls } = createResponsesLeakRelay({
|
|
80
93
|
state,
|
|
@@ -88,6 +101,7 @@ export async function consumeCompatResponsesStream(
|
|
|
88
101
|
parseResponsesToolCalls,
|
|
89
102
|
responseOutputText,
|
|
90
103
|
onStreamDelta: reportProgress,
|
|
104
|
+
onLifecycle: reportLifecycle,
|
|
91
105
|
onToolCall,
|
|
92
106
|
onTextDelta,
|
|
93
107
|
onStreamItemStart,
|
|
@@ -98,9 +112,8 @@ export async function consumeCompatResponsesStream(
|
|
|
98
112
|
while (true) {
|
|
99
113
|
const { value: event, done } = await nextAsyncWithWatchdog(iterator, {
|
|
100
114
|
signal: sawFirstEvent ? signal : firstByteTimeout.signal,
|
|
101
|
-
idleMs,
|
|
102
|
-
|
|
103
|
-
idleEnabled: sawFirstEvent && idleEnabled && state.semanticIdleDeadlineAt > 0,
|
|
115
|
+
...firstOutput.idleWatch(state.semanticIdleDeadlineAt, idleMs),
|
|
116
|
+
idleEnabled: sawFirstEvent && idleEnabled,
|
|
104
117
|
idleLabel: `${label} SSE idle`,
|
|
105
118
|
streamLabel: label,
|
|
106
119
|
// Unsafe-to-retry once any tool call (native or recovered-leaked)
|
|
@@ -131,6 +131,7 @@ export async function sendXaiResponses(provider, messages, useModel, tools, opts
|
|
|
131
131
|
return await consumeCompatResponsesStream(stream, {
|
|
132
132
|
signal: attemptSignal,
|
|
133
133
|
label: 'xai:responses',
|
|
134
|
+
sessionId: opts.sessionId,
|
|
134
135
|
onStreamDelta: opts.onStreamDelta,
|
|
135
136
|
onToolCall: opts.onToolCall,
|
|
136
137
|
onTextDelta: opts.onTextDelta,
|
|
@@ -15,7 +15,7 @@ function outputItemKind(item) {
|
|
|
15
15
|
return 'semantic';
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function createOutputItemEvents({ state, items, meaningful }) {
|
|
18
|
+
export function createOutputItemEvents({ state, items, meaningful, lifecycle }) {
|
|
19
19
|
const { toolTracker } = state;
|
|
20
20
|
|
|
21
21
|
const onOutputItemAdded = (item) => {
|
|
@@ -52,7 +52,10 @@ export function createOutputItemEvents({ state, items, meaningful }) {
|
|
|
52
52
|
toolTracker.mark(item);
|
|
53
53
|
state.toolInFlight = true;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
// A tool call starting is model output; an empty reasoning or message
|
|
56
|
+
// item is lifecycle (the silent thinking follows it).
|
|
57
|
+
if (state.toolInFlight) meaningful('tool');
|
|
58
|
+
else lifecycle('semantic');
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
const onToolInputDelta = (itemId) => {
|
package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs
CHANGED
|
@@ -7,18 +7,28 @@ import { createOutputItemEvents } from './response-events/output-items.mjs';
|
|
|
7
7
|
import { createTerminalFrameEvents } from './response-events/terminal-frames.mjs';
|
|
8
8
|
import { reportStreamItemStart, responsesItemStart } from '../lib/send-callbacks.mjs';
|
|
9
9
|
|
|
10
|
-
export function createResponseEvents({
|
|
11
|
-
|
|
10
|
+
export function createResponseEvents({
|
|
11
|
+
state,
|
|
12
|
+
items,
|
|
13
|
+
text,
|
|
14
|
+
outcome,
|
|
15
|
+
meaningful,
|
|
16
|
+
lifecycle,
|
|
17
|
+
onServerEvent,
|
|
18
|
+
onStreamItemStart,
|
|
19
|
+
}) {
|
|
20
|
+
const output = createOutputItemEvents({ state, items, meaningful, lifecycle });
|
|
12
21
|
const terminal = createTerminalFrameEvents({ state, items, text, outcome, meaningful });
|
|
13
22
|
|
|
14
23
|
const handleEvent = (event) => {
|
|
15
24
|
if (!event || typeof event.type !== 'string') return;
|
|
16
25
|
onServerEvent();
|
|
17
26
|
switch (event.type) {
|
|
27
|
+
// The server's ack is metadata, not model output: it satisfies only the
|
|
28
|
+
// first-server-event deadline (onServerEvent above).
|
|
18
29
|
case 'response.created':
|
|
19
30
|
if (event.response?.model) state.model = event.response.model;
|
|
20
31
|
if (event.response?.id) state.responseId = event.response.id;
|
|
21
|
-
meaningful('semantic');
|
|
22
32
|
break;
|
|
23
33
|
case 'response.output_text.delta':
|
|
24
34
|
text.relayLeakText(event.delta || '');
|
|
@@ -24,7 +24,8 @@ import { createResponseEvents } from './openai-http-sse-response-state/response-
|
|
|
24
24
|
* @param {(call: object) => void} [deps.onToolCall]
|
|
25
25
|
* @param {(text: string) => void} [deps.onTextDelta]
|
|
26
26
|
* @param {(kind: string) => void} deps.meaningful semantic progress (re-arms the idle watchdog)
|
|
27
|
-
* @param {() => void} deps.
|
|
27
|
+
* @param {(kind: string) => void} [deps.lifecycle] item lifecycle without output: progress only after semantic output
|
|
28
|
+
* @param {() => void} deps.onServerEvent any parsed server event (satisfies the first-server-event deadline)
|
|
28
29
|
* @param {(detail: object) => void} [deps.onStreamItemStart] a tool call or phased message started
|
|
29
30
|
*/
|
|
30
31
|
export function createHttpSseResponseState({
|
|
@@ -33,6 +34,7 @@ export function createHttpSseResponseState({
|
|
|
33
34
|
onTextDelta,
|
|
34
35
|
onStreamItemStart = null,
|
|
35
36
|
meaningful,
|
|
37
|
+
lifecycle = () => {},
|
|
36
38
|
onServerEvent,
|
|
37
39
|
label,
|
|
38
40
|
}) {
|
|
@@ -40,7 +42,16 @@ export function createHttpSseResponseState({
|
|
|
40
42
|
const outcome = createResponseOutcome({ state });
|
|
41
43
|
const items = createResponseItems({ state, onToolCall });
|
|
42
44
|
const text = createTextRelay({ state, body, onTextDelta, meaningful, emitToolCall: items.emitToolCall });
|
|
43
|
-
const events = createResponseEvents({
|
|
45
|
+
const events = createResponseEvents({
|
|
46
|
+
state,
|
|
47
|
+
items,
|
|
48
|
+
text,
|
|
49
|
+
outcome,
|
|
50
|
+
meaningful,
|
|
51
|
+
lifecycle,
|
|
52
|
+
onServerEvent,
|
|
53
|
+
onStreamItemStart,
|
|
54
|
+
});
|
|
44
55
|
|
|
45
56
|
return {
|
|
46
57
|
handleEvent: events.handleEvent,
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* openai-http-sse-watchdogs.mjs — liveness gates of one HTTP/SSE fallback
|
|
3
3
|
* stream: the total/external abort relay, the fixed first-server-event
|
|
4
|
-
* deadline, the SEMANTIC idle watchdog, and the
|
|
4
|
+
* deadline, the first-OUTPUT window, the SEMANTIC idle watchdog, and the
|
|
5
|
+
* cancellable reader.read().
|
|
6
|
+
*
|
|
7
|
+
* Until the model's first semantic output (reasoning, text, a tool call) only
|
|
8
|
+
* the first-server-event deadline and the first-output window run: server
|
|
9
|
+
* metadata (response.created / in_progress, an empty reasoning or message
|
|
10
|
+
* item) and keepalives neither end that phase nor start the semantic-idle
|
|
11
|
+
* clock, so a model may think silently longer than the idle window. From the
|
|
12
|
+
* first semantic output on, the semantic idle owns the stream.
|
|
5
13
|
*
|
|
6
14
|
* After headerTimeout.cleanup() the in-flight fetch no longer carries a live
|
|
7
15
|
* signal, so a totalTimeout / external abort that fires during a pending
|
|
@@ -17,8 +25,10 @@ import {
|
|
|
17
25
|
PROVIDER_FIRST_BYTE_TIMEOUT_MS,
|
|
18
26
|
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
19
27
|
PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
|
|
28
|
+
providerFirstOutputTimeoutMs,
|
|
20
29
|
streamStalledError,
|
|
21
30
|
} from '../stall-policy.mjs';
|
|
31
|
+
import { streamFirstOutputWindow } from './lib/first-output-window.mjs';
|
|
22
32
|
|
|
23
33
|
function positiveOverride(value) {
|
|
24
34
|
const ms = Number(value);
|
|
@@ -31,8 +41,18 @@ function positiveOverride(value) {
|
|
|
31
41
|
* @param {ReadableStreamDefaultReader} deps.reader
|
|
32
42
|
* @param {{ signal?: AbortSignal, cleanup(): void }} deps.totalTimeout
|
|
33
43
|
* @param {() => object} deps.stallPartial streamed partial state attached to a semantic-idle stall
|
|
44
|
+
* @param {string|null} [deps.sessionId] the session whose logical request this stream serves
|
|
45
|
+
* @param {Function} [deps.onStreamDelta] the stream's progress callback; without it the stream is a fork of the request
|
|
34
46
|
*/
|
|
35
|
-
export function createHttpSseWatchdogs({
|
|
47
|
+
export function createHttpSseWatchdogs({
|
|
48
|
+
opts,
|
|
49
|
+
reader,
|
|
50
|
+
totalTimeout,
|
|
51
|
+
stallPartial,
|
|
52
|
+
label: LABEL,
|
|
53
|
+
sessionId = null,
|
|
54
|
+
onStreamDelta,
|
|
55
|
+
}) {
|
|
36
56
|
let abortReason = null;
|
|
37
57
|
let pendingReadReject = null;
|
|
38
58
|
const rejectPendingRead = (err) => {
|
|
@@ -58,9 +78,46 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
58
78
|
else totalTimeout.signal.addEventListener('abort', onTotalAbort, { once: true });
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
81
|
+
// SEMANTIC idle watchdog: reset ONLY on meaningful() (text/reasoning/tool
|
|
82
|
+
// deltas), never on raw bytes/keepalive frames, so a stream that emits some
|
|
83
|
+
// deltas then goes silent trips a short, named terminal failure instead of
|
|
84
|
+
// hanging until the 30-min agent watchdog. Disablable via the shared env.
|
|
85
|
+
let semanticIdleTimer = null;
|
|
86
|
+
const semanticIdleOverrideMs = positiveOverride(opts?._semanticIdleTimeoutMs);
|
|
87
|
+
const semanticIdleMs = semanticIdleOverrideMs || PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
88
|
+
const semanticIdleEnabled = semanticIdleOverrideMs > 0 || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
89
|
+
// FIRST-OUTPUT window of the logical request this stream serves, retired by
|
|
90
|
+
// the first semantic output. Metadata-only streams (repeated
|
|
91
|
+
// response.in_progress, keepalive/ping frames, unknown event types) cannot
|
|
92
|
+
// refresh it, so a wedge that never produces a token still ends here.
|
|
93
|
+
const firstOutputOverrideMs = positiveOverride(opts?._firstOutputTimeoutMs);
|
|
94
|
+
const firstOutput = streamFirstOutputWindow({
|
|
95
|
+
sessionId,
|
|
96
|
+
onStreamDelta,
|
|
97
|
+
timeoutMs: firstOutputOverrideMs || providerFirstOutputTimeoutMs(semanticIdleMs),
|
|
98
|
+
});
|
|
99
|
+
const firstOutputEnabled = firstOutputOverrideMs > 0 || semanticIdleEnabled;
|
|
100
|
+
|
|
101
|
+
// A stalled stream fails with the named stall error, carrying the streamed
|
|
102
|
+
// partial state so the agent loop can accept a wedged FINAL no-tool summary
|
|
103
|
+
// as a successful partial-final instead of dropping the result.
|
|
104
|
+
// pendingToolUse gates out any mid-flight tool call.
|
|
105
|
+
const abortStalled = (timeoutMs) => {
|
|
106
|
+
const partial = stallPartial();
|
|
107
|
+
const err = streamStalledError(LABEL, timeoutMs, { emittedToolCall: partial.emittedToolCall });
|
|
108
|
+
try {
|
|
109
|
+
err.partialContent = partial.content;
|
|
110
|
+
err.partialToolCalls = partial.toolCalls;
|
|
111
|
+
err.pendingToolUse = partial.pendingToolUse;
|
|
112
|
+
err.partialModel = partial.model;
|
|
113
|
+
} catch {
|
|
114
|
+
/* best-effort enrichment */
|
|
115
|
+
}
|
|
116
|
+
abortStream(err);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Initial wait for any server event is governed by the fixed
|
|
120
|
+
// first-server-event policy; a lower semantic-idle override cannot shorten it.
|
|
64
121
|
let firstServerEventTimer = null;
|
|
65
122
|
const firstServerEventMs = positiveOverride(opts?._firstServerEventTimeoutMs) || PROVIDER_FIRST_BYTE_TIMEOUT_MS;
|
|
66
123
|
const clearFirstServerEvent = () => {
|
|
@@ -82,15 +139,15 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
82
139
|
firstServerEventTimer.unref?.();
|
|
83
140
|
} catch {}
|
|
84
141
|
};
|
|
142
|
+
// Until the stream's first output the request's first-output window bounds
|
|
143
|
+
// it. A request that already had output has none: its stream runs under the
|
|
144
|
+
// semantic idle from its first server event (noteServerEvent).
|
|
145
|
+
const armFirstOutput = () => {
|
|
146
|
+
if (!firstOutput.outputSeen && firstOutputEnabled && firstOutput.timeoutMs > 0) {
|
|
147
|
+
firstOutput.arm(() => abortStalled(firstOutput.timeoutMs));
|
|
148
|
+
}
|
|
149
|
+
};
|
|
85
150
|
|
|
86
|
-
// SEMANTIC idle watchdog: reset ONLY on meaningful() (text/reasoning/tool
|
|
87
|
-
// deltas), never on raw bytes/keepalive frames, so a stream that emits some
|
|
88
|
-
// deltas then goes silent trips a short, named terminal failure instead of
|
|
89
|
-
// hanging until the 30-min agent watchdog. Disablable via the shared env.
|
|
90
|
-
let semanticIdleTimer = null;
|
|
91
|
-
const semanticIdleOverrideMs = positiveOverride(opts?._semanticIdleTimeoutMs);
|
|
92
|
-
const semanticIdleMs = semanticIdleOverrideMs || PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS;
|
|
93
|
-
const semanticIdleEnabled = semanticIdleOverrideMs > 0 || PROVIDER_SSE_IDLE_WATCHDOG_ENABLED;
|
|
94
151
|
const clearSemanticIdle = () => {
|
|
95
152
|
if (semanticIdleTimer) {
|
|
96
153
|
clearTimeout(semanticIdleTimer);
|
|
@@ -100,43 +157,22 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
100
157
|
const armSemanticIdle = () => {
|
|
101
158
|
if (!semanticIdleEnabled || !(semanticIdleMs > 0)) return;
|
|
102
159
|
clearSemanticIdle();
|
|
103
|
-
semanticIdleTimer = setTimeout(() =>
|
|
104
|
-
const partial = stallPartial();
|
|
105
|
-
const err = streamStalledError(LABEL, semanticIdleMs, { emittedToolCall: partial.emittedToolCall });
|
|
106
|
-
// Partial-final recovery: attach the streamed partial state so the
|
|
107
|
-
// agent loop can accept a wedged FINAL no-tool summary as a successful
|
|
108
|
-
// partial-final instead of dropping the result. pendingToolUse gates
|
|
109
|
-
// out any mid-flight tool call.
|
|
110
|
-
try {
|
|
111
|
-
err.partialContent = partial.content;
|
|
112
|
-
err.partialToolCalls = partial.toolCalls;
|
|
113
|
-
err.pendingToolUse = partial.pendingToolUse;
|
|
114
|
-
err.partialModel = partial.model;
|
|
115
|
-
} catch {
|
|
116
|
-
/* best-effort enrichment */
|
|
117
|
-
}
|
|
118
|
-
abortStream(err);
|
|
119
|
-
}, semanticIdleMs);
|
|
160
|
+
semanticIdleTimer = setTimeout(() => abortStalled(semanticIdleMs), semanticIdleMs);
|
|
120
161
|
try {
|
|
121
162
|
semanticIdleTimer.unref?.();
|
|
122
163
|
} catch {}
|
|
123
164
|
};
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
// let a metadata-only stream (repeated response.in_progress, keepalive/ping
|
|
128
|
-
// frames, unknown event types) refresh the watchdog forever without
|
|
129
|
-
// producing a single token. Only meaningful progress re-arms it from then on.
|
|
130
|
-
let semanticIdleArmed = false;
|
|
165
|
+
// A parsed server event satisfies the first-server-event deadline and, for a
|
|
166
|
+
// request that already had output, starts the semantic idle (once: only
|
|
167
|
+
// output re-arms it).
|
|
131
168
|
const noteServerEvent = () => {
|
|
132
169
|
clearFirstServerEvent();
|
|
133
|
-
if (!
|
|
134
|
-
semanticIdleArmed = true;
|
|
135
|
-
armSemanticIdle();
|
|
136
|
-
}
|
|
170
|
+
if (firstOutput.outputSeen && !semanticIdleTimer) armSemanticIdle();
|
|
137
171
|
};
|
|
172
|
+
// Semantic output: the first one ends the first-output phase; every one
|
|
173
|
+
// re-arms the semantic idle.
|
|
138
174
|
const noteMeaningful = () => {
|
|
139
|
-
|
|
175
|
+
firstOutput.noteOutput();
|
|
140
176
|
armSemanticIdle();
|
|
141
177
|
};
|
|
142
178
|
|
|
@@ -151,6 +187,7 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
151
187
|
const dispose = () => {
|
|
152
188
|
pendingReadReject = null;
|
|
153
189
|
clearFirstServerEvent();
|
|
190
|
+
firstOutput.clear();
|
|
154
191
|
clearSemanticIdle();
|
|
155
192
|
try {
|
|
156
193
|
reader.releaseLock?.();
|
|
@@ -165,9 +202,11 @@ export function createHttpSseWatchdogs({ opts, reader, totalTimeout, stallPartia
|
|
|
165
202
|
return {
|
|
166
203
|
abortReason: () => abortReason,
|
|
167
204
|
armFirstServerEvent,
|
|
205
|
+
armFirstOutput,
|
|
168
206
|
clearSemanticIdle,
|
|
169
207
|
noteServerEvent,
|
|
170
208
|
noteMeaningful,
|
|
209
|
+
semanticSeen: () => firstOutput.outputSeen,
|
|
171
210
|
read,
|
|
172
211
|
dispose,
|
|
173
212
|
};
|
|
@@ -183,6 +183,8 @@ export async function sendViaHttpSse({
|
|
|
183
183
|
totalTimeout,
|
|
184
184
|
stallPartial: () => state.stallPartial(),
|
|
185
185
|
label,
|
|
186
|
+
sessionId: opts?.sessionId,
|
|
187
|
+
onStreamDelta,
|
|
186
188
|
});
|
|
187
189
|
const meaningful = (kind = 'semantic') => {
|
|
188
190
|
if (ttftMs == null) ttftMs = Date.now() - sseStartedAt;
|
|
@@ -191,12 +193,17 @@ export async function sendViaHttpSse({
|
|
|
191
193
|
onStreamDelta?.(kind);
|
|
192
194
|
} catch {}
|
|
193
195
|
};
|
|
196
|
+
// An empty reasoning or message item added: progress only after output.
|
|
197
|
+
const lifecycle = (kind = 'semantic') => {
|
|
198
|
+
if (watchdogs.semanticSeen()) meaningful(kind);
|
|
199
|
+
};
|
|
194
200
|
const state = createHttpSseResponseState({
|
|
195
201
|
body,
|
|
196
202
|
onToolCall,
|
|
197
203
|
onTextDelta,
|
|
198
204
|
onStreamItemStart: typeof opts?.onStreamItemStart === 'function' ? opts.onStreamItemStart : null,
|
|
199
205
|
meaningful,
|
|
206
|
+
lifecycle,
|
|
200
207
|
onServerEvent: watchdogs.noteServerEvent,
|
|
201
208
|
label,
|
|
202
209
|
});
|
|
@@ -210,6 +217,7 @@ export async function sendViaHttpSse({
|
|
|
210
217
|
let buffer = '';
|
|
211
218
|
try {
|
|
212
219
|
watchdogs.armFirstServerEvent();
|
|
220
|
+
watchdogs.armFirstOutput();
|
|
213
221
|
while (true) {
|
|
214
222
|
if (totalTimeout.signal?.aborted) {
|
|
215
223
|
watchdogs.clearSemanticIdle();
|