langchain_agentx_stream_ui 0.2.3 → 0.2.6

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/dist/default.css CHANGED
@@ -2050,6 +2050,24 @@
2050
2050
  --lax-workflow-indent: calc(var(--lax-workflow-depth, 0) * var(--lax-workflow-indent-unit, 1rem));
2051
2051
  }
2052
2052
 
2053
+ .lax-workflow-closeout-warning {
2054
+ margin: 0 0 0.75rem;
2055
+ padding: 0.65rem 0.85rem;
2056
+ border-radius: var(--lax-radius, 8px);
2057
+ border: 1px solid color-mix(in srgb, var(--lax-color-warning, #b8860b) 45%, transparent);
2058
+ background: var(--lax-bg-warning, #fdf8ec);
2059
+ color: color-mix(in srgb, var(--lax-color-warning, #b8860b) 85%, #000);
2060
+ font-size: 0.875rem;
2061
+ line-height: 1.45;
2062
+ }
2063
+
2064
+ .lax-workflow-closeout-warning__scopes {
2065
+ margin: 0.45rem 0 0 1.1rem;
2066
+ padding: 0;
2067
+ font-family: Consolas, monospace;
2068
+ font-size: 0.8125rem;
2069
+ }
2070
+
2053
2071
  .lax-workflow-chrome {
2054
2072
  display: flex;
2055
2073
  flex-direction: column;
@@ -2267,6 +2285,11 @@
2267
2285
  color: var(--lax-color-done, var(--lax-accent-primary));
2268
2286
  }
2269
2287
 
2288
+ .lax-workflow-container-line__status-dot[data-status='warning'],
2289
+ .lax-workflow-stage-container__status-dot[data-status='warning'] {
2290
+ color: var(--lax-color-warning, #b8860b);
2291
+ }
2292
+
2270
2293
  .lax-workflow-stage-container__status-dot[data-status='pending'] {
2271
2294
  color: var(--lax-muted);
2272
2295
  opacity: 0.5;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, PointerEventHandler } from 'react';
4
- import { q as Source, L as LangchainAgentEvent, N as NodeRegistry, v as TierOverrides, I as InteractionBus, M as MarkdownRenderer, p as SessionTree, l as SessionStatus, R as ReducerError, r as SourceEventContext, T as Task, t as TaskPhase, E as EventTier } from './multi-session-Dxyo3y7w.js';
5
- export { C as CommonFields, D as DEFAULT_EVENT_TIERS, a as EventType, b as InteractionBusHandlers, c as MarkdownRendererProps, d as MultiAgentSession, e as MultiAgentSessionProps, f as MultiSessionSourceConfig, g as MultiSessionStore, h as MultiSessionStoreOptions, i as MultiSessionStoreState, j as NodeWidget, k as NodeWidgetProps, P as PermissionResolvePayload, S as SessionSlice, m as SessionStore, n as SessionStoreOptions, o as SessionStoreState, s as SourceHandler, u as TaskStatus, w as createActiveSessionBridge, x as createEmptyTree, y as createInteractionBus, z as createMultiSessionStore, A as createSessionStore, B as getNoopInteractionBus, F as useActiveSession, G as useSessionIds } from './multi-session-Dxyo3y7w.js';
4
+ import { q as Source, L as LangchainAgentEvent, N as NodeRegistry, v as TierOverrides, I as InteractionBus, M as MarkdownRenderer, p as SessionTree, l as SessionStatus, R as ReducerError, r as SourceEventContext, T as Task, t as TaskPhase, E as EventTier } from './multi-session-xvSZstSN.js';
5
+ export { C as CommonFields, D as DEFAULT_EVENT_TIERS, a as EventType, b as InteractionBusHandlers, c as MarkdownRendererProps, d as MultiAgentSession, e as MultiAgentSessionProps, f as MultiSessionSourceConfig, g as MultiSessionStore, h as MultiSessionStoreOptions, i as MultiSessionStoreState, j as NodeWidget, k as NodeWidgetProps, P as PermissionResolvePayload, S as SessionSlice, m as SessionStore, n as SessionStoreOptions, o as SessionStoreState, s as SourceHandler, u as TaskStatus, w as createActiveSessionBridge, x as createEmptyTree, y as createInteractionBus, z as createMultiSessionStore, A as createSessionStore, B as getNoopInteractionBus, F as useActiveSession, G as useSessionIds } from './multi-session-xvSZstSN.js';
6
6
  import { T as ToolDisplayRegistry, P as PermissionUiMode, b as DisplayMode, c as ProjectionContext, R as RenderableEntry, C as CanonicalNodeRef } from './sessionViewOptions-WM00Fpcn.js';
7
7
  export { a as CollapsedExplorePayload, D as DEFAULT_SESSION_VIEW_OPTIONS, S as SessionViewOptions, d as ToolUseSnapshot, e as buildProjectionContext, f as buildProjectionViewContext, g as createDefaultToolRegistry, h as createProjectionContext, r as resolveEffectiveToolBodyMode, s as shouldApplyGrouping, i as shouldShowExploreDetail } from './sessionViewOptions-WM00Fpcn.js';
8
8
  import { a as ToolBodyMode } from './types-BEXSHQG7.js';
@@ -240,6 +240,8 @@ declare function partitionWorkflowStages(stages: WorkflowContainerRuntimeNode[],
240
240
  * 不含合并 TaskListFooter(P3);parallel 结构仅基础字段。
241
241
  */
242
242
 
243
+ /** SDK v1.9.0 根 workflow 终态语义 SSOT */
244
+ type WorkflowTerminalKind = 'healthy' | 'forced_closeout' | 'failed';
243
245
  /** P2:stage 完成摘要缓存(loop 树淘汰后仍显示 teaser) */
244
246
  interface CachedStageDoneSummary {
245
247
  label: string;
@@ -277,6 +279,10 @@ interface WorkflowScopeFrame {
277
279
  }
278
280
  interface WorkflowSessionState {
279
281
  status: SessionStatus;
282
+ /** SDK v1.9.0 根终态语义;stream 推断 done 时为 null */
283
+ terminalKind: WorkflowTerminalKind | null;
284
+ /** forced_closeout 时 SDK 写入的未关闭 child scope */
285
+ openChildIdentities: string[];
280
286
  meta: {
281
287
  workflowRunId: string | null;
282
288
  startedAt: number | null;
@@ -955,6 +961,67 @@ declare class MissingApplicationKindError extends Error {
955
961
  constructor();
956
962
  }
957
963
 
964
+ /**
965
+ * 职责:SSE transport profile / policy 解析与终态、重连预算语义(D31 P1)。
966
+ * 链路位置:createAgentxSseSource 入口 → resolveSseTransportPolicy → 连接状态机。
967
+ * 当前裁剪范围:三预设 profile + 显式 policy 覆写;不含 backend replayMode。
968
+ */
969
+
970
+ type SseTransportProfile = 'oneshot' | 'oneshot-workflow' | 'replayable-workflow';
971
+ type SseReplayMode = 'none' | 'resume' | 'history';
972
+ type SseReconnectMode = 'off' | 'bounded' | 'unbounded';
973
+ interface SseReconnectPolicy {
974
+ mode: SseReconnectMode;
975
+ maxReconnects: number;
976
+ reconnectTimeoutMs: number;
977
+ reconnectDelayMs: number;
978
+ }
979
+ interface SseTerminalPolicy {
980
+ primary: readonly string[];
981
+ allowFinishFallback: boolean;
982
+ }
983
+ interface SseTransportPolicy {
984
+ profile?: SseTransportProfile;
985
+ replay: SseReplayMode;
986
+ reconnect: SseReconnectPolicy;
987
+ terminal: SseTerminalPolicy;
988
+ }
989
+ interface SseTransportPolicyOverride {
990
+ replay?: SseReplayMode;
991
+ reconnect?: Partial<SseReconnectPolicy>;
992
+ terminal?: Partial<SseTerminalPolicy>;
993
+ }
994
+ interface ResolveSseTransportPolicyInput {
995
+ profile?: SseTransportProfile;
996
+ policy?: SseTransportPolicyOverride;
997
+ /** @deprecated 使用 profile 或 policy.reconnect */
998
+ autoReconnect?: boolean;
999
+ /** @deprecated 使用 policy.reconnect.reconnectDelayMs */
1000
+ reconnectDelayMs?: number;
1001
+ expectedApplicationKind?: ApplicationKind;
1002
+ }
1003
+ declare const BOUNDED_DEFAULTS: {
1004
+ readonly maxReconnects: 5;
1005
+ readonly reconnectTimeoutMs: 60000;
1006
+ readonly reconnectDelayMs: 1000;
1007
+ };
1008
+ declare const PROFILE_PRESETS: Record<SseTransportProfile, SseTransportPolicy>;
1009
+ /** 将 profile / policy / 旧 autoReconnect 选项解析为完整 transport 策略。 */
1010
+ declare function resolveSseTransportPolicy(input: ResolveSseTransportPolicyInput): SseTransportPolicy;
1011
+ /** 判断 agentx 帧是否为传输层终态(触发 benign close / 停止重连)。 */
1012
+ declare function isSseStreamTerminalEvent(agentEvent: LangchainAgentEvent, terminal: SseTerminalPolicy, expectedApplicationKind?: ApplicationKind): boolean;
1013
+ /** bounded reconnect 预算:次数 + 总时长双门禁。 */
1014
+ declare class ReconnectBudget {
1015
+ private readonly maxReconnects;
1016
+ private readonly timeoutMs;
1017
+ private readonly startedAtMs;
1018
+ private attemptCount;
1019
+ constructor(maxReconnects: number, timeoutMs: number, startedAtMs?: number);
1020
+ get attempts(): number;
1021
+ canRetry(nowMs?: number): boolean;
1022
+ recordAttempt(): void;
1023
+ }
1024
+
958
1025
  /**
959
1026
  * 职责:浏览器 EventSource 封装,解码 meta/agentx 命名帧并校验协议版本。
960
1027
  * 链路位置:业务页面 createAgentxSseSource({ url }) → AgentSession source prop。
@@ -967,14 +1034,24 @@ declare class MissingApplicationKindError extends Error {
967
1034
 
968
1035
  declare const EXPECTED_PROTOCOL_VERSION = "1";
969
1036
  /** 传输层 terminal 失败(须排查/fix);与 agentx error 帧(业务错误)分离。 */
970
- type SseTransportFailureKind = 'handshake_failed' | 'premature_close';
1037
+ type SseTransportFailureKind = 'handshake_failed' | 'premature_close' | 'reconnect_budget_exhausted';
971
1038
  declare class SseTransportTerminalError extends Error {
972
1039
  readonly kind: SseTransportFailureKind;
973
1040
  constructor(kind: SseTransportFailureKind, message: string);
974
1041
  }
975
1042
  interface AgentxSseSourceOptions {
976
1043
  url: string;
1044
+ /** D31:预设 transport 策略(ask / 短 workflow / 可回放 workflow) */
1045
+ profile?: SseTransportProfile;
1046
+ /** D31:在 profile 基础上显式覆写策略字段 */
1047
+ policy?: SseTransportPolicyOverride;
1048
+ /**
1049
+ * @deprecated 使用 profile 或 policy.reconnect;未指定 profile 时仍生效
1050
+ */
977
1051
  autoReconnect?: boolean;
1052
+ /**
1053
+ * @deprecated 使用 policy.reconnect.reconnectDelayMs
1054
+ */
978
1055
  reconnectDelayMs?: number;
979
1056
  /** 每收到 agentx 帧 SSE id 时回调(含续传重叠 id) */
980
1057
  onLastEventId?: (id: string) => void;
@@ -989,6 +1066,7 @@ interface AgentxMetaFrame {
989
1066
  started_at?: number;
990
1067
  application_kind?: ApplicationKind;
991
1068
  }
1069
+ /** 手动重连时追加 resume 查询参数(EventSource 无法设 Last-Event-ID 请求头)。 */
992
1070
  declare function buildResumeStreamUrl(baseUrl: string, lastEventId: string | null | undefined, queryParam?: string): string;
993
1071
  declare function createAgentxSseSource(options: AgentxSseSourceOptions): Source;
994
1072
  /** 测试/Story 用:内存事件源 */
@@ -1130,4 +1208,4 @@ interface ClassifyContext {
1130
1208
  declare function resolveEventTier(eventType: string, overrides?: TierOverrides): EventTier;
1131
1209
  declare function classifyEvent(event: LangchainAgentEvent, ctx: ClassifyContext, overrides?: TierOverrides, eventIndex?: number): ClassifiedEvent;
1132
1210
 
1133
- export { Agent, AgentLoopView, type AgentLoopViewProps, AgentSession, type AgentSessionProps, type AgentxMetaFrame, type AgentxSseSourceOptions, type ApplicationKind, ApplicationKindMismatchError, AskUserQuestion, Bash, CanonicalNodeRef, type ClassifiedEvent, type ClassifyContext, CollapseKind, CollapsedExploreNode, type CollapsedExploreNodeProps, DEFAULT_WORKFLOW_SCALE_OPTIONS, type DiagramFitLayout, DiagramZoomShell, type DiagramZoomShellProps, DiagramZoomView, type DiagramZoomViewProps, DisplayMode, EXPECTED_PROTOCOL_VERSION, Edit, ErrorCard, EventTier, ExploreCollapseAccumulator, GROUPABLE_TOOL_NAMES, Glob, Grep, GroupedToolUseAccumulator, type HydrateLoopSessionAsyncOptions, InteractionBus, LangchainAgentEvent, MIN_HINT_DISPLAY_MS, MarkdownRenderer, MermaidDiagram, type MermaidDiagramProps, type MermaidInitOptions, type MermaidThemeName, MissingApplicationKindError, NodeRegistry, PermissionCard, PermissionUiMode, PlantUMLBlock, type PlantUMLBlockProps, PlantUMLDiagram, type PlantUMLDiagramProps, type ProjectedTimelineItem, ProjectionContext, DisplayMode as ProjectionDisplayMode, Read, ReasoningBubble, type ReduceOptions, ReducerError, RenderableEntry, RenderableTimelineProjector, type ReplayOptions, type ReplaySourceOptions, RichMarkdown, type RichMarkdownProps, SHELL_PROGRESS_MIN_ELAPSED_SECONDS, SessionNode, SessionNodeKind, SessionStatus, SessionTree, Skill, Source, SourceEventContext, Spinner, type SpinnerProps, type SseTransportFailureKind, SseTransportTerminalError, StepCard, SubAgentBlock, SystemSummaryAccumulator, SystemSummaryNode, Task, TaskList, TaskListFooter, TextBubble, TierOverrides, Timeline, TimelineItem, ToolBodyMode, ToolCallCard, ToolDisplayRegistry, ToolProjectionClassifier, UnknownCard, WORKFLOW_STRUCTURE_EVENT_PREFIXES, WORKFLOW_STRUCTURE_EVENT_TYPES, WebFetch, WebSearch, WorkflowChrome, type WorkflowChromeProps, type WorkflowLoopReplayHandler, WorkflowLoopReplayProvider, type WorkflowProgressState, WorkflowScaleContext, type WorkflowScaleOptions, type WorkflowScopeFrame, WorkflowSession, type WorkflowSessionProps, type WorkflowSessionState, type WorkflowSessionStore, type WorkflowSessionStoreOptions, type WorkflowSessionStoreState, type WorkflowStageProgress, WorkflowTaskListFooter, Write, buildCanonicalFromTree, buildRelevantMemoriesCanonical, buildResumeStreamUrl, buildSystemEventCanonical, buildTimelineEntries, classifyEvent, commandAsHint, computeViewportFitTransform, configureMermaid, configurePlantumlServer, createAgentxSseSource, createDefaultRegistry, createEmptyWorkflowSessionState, createMockSource, createReplaySource, createWorkflowSessionStore, deriveLoopSessionIdFromAgentEvent, entryKey, filterAgentLoopReplayEvents, fixDiagramSvg, formatAgentTitle, formatExploreSummaryText, formatHookFinishedSummary, formatShellProgressSuffix, formatTaskFooterLabel, getChildIds, getMainStageIds, isAgentLoopReplayEventType, isGitOperationCommand, isGroupableToolName, isMcpToolName, isWorkflowStructureEventType, iterMountCandidates, partitionWorkflowStages, projectCanonical, projectTimeline, reduceAgentLoopEvent, reduceEvents, reduceTree, reduceWorkflowEvents, reduceWorkflowSession, replayEvents, replayTree, resolveEventTier, resolveResponseId, resolveShellProgressFromPayload, shouldShowTaskListFooter, streamChunk, useChildren, useInteractionBus, useInternalErrors, useMinDisplayTime, useNode, useNodeTyped, useProjectedTimeline, useReplayTree, useSessionMeta, useSessionStatus, useTimeline, useWorkflowSessionStatus, useWorkflowSessionStoreApi };
1211
+ export { Agent, AgentLoopView, type AgentLoopViewProps, AgentSession, type AgentSessionProps, type AgentxMetaFrame, type AgentxSseSourceOptions, type ApplicationKind, ApplicationKindMismatchError, AskUserQuestion, BOUNDED_DEFAULTS, Bash, CanonicalNodeRef, type ClassifiedEvent, type ClassifyContext, CollapseKind, CollapsedExploreNode, type CollapsedExploreNodeProps, DEFAULT_WORKFLOW_SCALE_OPTIONS, type DiagramFitLayout, DiagramZoomShell, type DiagramZoomShellProps, DiagramZoomView, type DiagramZoomViewProps, DisplayMode, EXPECTED_PROTOCOL_VERSION, Edit, ErrorCard, EventTier, ExploreCollapseAccumulator, GROUPABLE_TOOL_NAMES, Glob, Grep, GroupedToolUseAccumulator, type HydrateLoopSessionAsyncOptions, InteractionBus, LangchainAgentEvent, MIN_HINT_DISPLAY_MS, MarkdownRenderer, MermaidDiagram, type MermaidDiagramProps, type MermaidInitOptions, type MermaidThemeName, MissingApplicationKindError, NodeRegistry, PROFILE_PRESETS, PermissionCard, PermissionUiMode, PlantUMLBlock, type PlantUMLBlockProps, PlantUMLDiagram, type PlantUMLDiagramProps, type ProjectedTimelineItem, ProjectionContext, DisplayMode as ProjectionDisplayMode, Read, ReasoningBubble, ReconnectBudget, type ReduceOptions, ReducerError, RenderableEntry, RenderableTimelineProjector, type ReplayOptions, type ReplaySourceOptions, RichMarkdown, type RichMarkdownProps, SHELL_PROGRESS_MIN_ELAPSED_SECONDS, SessionNode, SessionNodeKind, SessionStatus, SessionTree, Skill, Source, SourceEventContext, Spinner, type SpinnerProps, type SseReconnectMode, type SseReconnectPolicy, type SseReplayMode, type SseTerminalPolicy, type SseTransportFailureKind, type SseTransportPolicy, type SseTransportPolicyOverride, type SseTransportProfile, SseTransportTerminalError, StepCard, SubAgentBlock, SystemSummaryAccumulator, SystemSummaryNode, Task, TaskList, TaskListFooter, TextBubble, TierOverrides, Timeline, TimelineItem, ToolBodyMode, ToolCallCard, ToolDisplayRegistry, ToolProjectionClassifier, UnknownCard, WORKFLOW_STRUCTURE_EVENT_PREFIXES, WORKFLOW_STRUCTURE_EVENT_TYPES, WebFetch, WebSearch, WorkflowChrome, type WorkflowChromeProps, type WorkflowLoopReplayHandler, WorkflowLoopReplayProvider, type WorkflowProgressState, WorkflowScaleContext, type WorkflowScaleOptions, type WorkflowScopeFrame, WorkflowSession, type WorkflowSessionProps, type WorkflowSessionState, type WorkflowSessionStore, type WorkflowSessionStoreOptions, type WorkflowSessionStoreState, type WorkflowStageProgress, WorkflowTaskListFooter, type WorkflowTerminalKind, Write, buildCanonicalFromTree, buildRelevantMemoriesCanonical, buildResumeStreamUrl, buildSystemEventCanonical, buildTimelineEntries, classifyEvent, commandAsHint, computeViewportFitTransform, configureMermaid, configurePlantumlServer, createAgentxSseSource, createDefaultRegistry, createEmptyWorkflowSessionState, createMockSource, createReplaySource, createWorkflowSessionStore, deriveLoopSessionIdFromAgentEvent, entryKey, filterAgentLoopReplayEvents, fixDiagramSvg, formatAgentTitle, formatExploreSummaryText, formatHookFinishedSummary, formatShellProgressSuffix, formatTaskFooterLabel, getChildIds, getMainStageIds, isAgentLoopReplayEventType, isGitOperationCommand, isGroupableToolName, isMcpToolName, isSseStreamTerminalEvent, isWorkflowStructureEventType, iterMountCandidates, partitionWorkflowStages, projectCanonical, projectTimeline, reduceAgentLoopEvent, reduceEvents, reduceTree, reduceWorkflowEvents, reduceWorkflowSession, replayEvents, replayTree, resolveEventTier, resolveResponseId, resolveShellProgressFromPayload, resolveSseTransportPolicy, shouldShowTaskListFooter, streamChunk, useChildren, useInteractionBus, useInternalErrors, useMinDisplayTime, useNode, useNodeTyped, useProjectedTimeline, useReplayTree, useSessionMeta, useSessionStatus, useTimeline, useWorkflowSessionStatus, useWorkflowSessionStoreApi };
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  fixDiagramSvg,
24
24
  markEventIdSeen,
25
25
  shouldSkipDuplicateEvent
26
- } from "./chunk-DGC43A5L.js";
26
+ } from "./chunk-CUKNUCGU.js";
27
27
  import {
28
28
  DefaultToolBody,
29
29
  InteractionBusContext,
@@ -32,8 +32,8 @@ import {
32
32
  getNoopInteractionBus,
33
33
  resolveToolBody,
34
34
  useInteractionBus
35
- } from "./chunk-T626YBJX.js";
36
- import "./chunk-6Z4ZF36Z.js";
35
+ } from "./chunk-6DYISADG.js";
36
+ import "./chunk-DP7V33X7.js";
37
37
  import {
38
38
  CollapseKind,
39
39
  CollapsedExploreNode,
@@ -111,7 +111,7 @@ import {
111
111
  useSessionStatus,
112
112
  useSessionViewOptions,
113
113
  useTimeline
114
- } from "./chunk-OFXC2GZI.js";
114
+ } from "./chunk-SUEMZC4U.js";
115
115
  import {
116
116
  Agent,
117
117
  AgentToolBody,
@@ -138,7 +138,7 @@ import {
138
138
  WriteToolBody,
139
139
  createDefaultToolRegistry,
140
140
  formatAgentTitle
141
- } from "./chunk-YUZXTGJJ.js";
141
+ } from "./chunk-7CLAU74Y.js";
142
142
  import {
143
143
  BodyBlockList,
144
144
  DiffView,
@@ -154,7 +154,7 @@ import {
154
154
  formatTimeoutFooter,
155
155
  parseDiffText,
156
156
  truncateCommand
157
- } from "./chunk-GBY5DJ7L.js";
157
+ } from "./chunk-4RIOBLGB.js";
158
158
 
159
159
  // src/view/AgentSession.tsx
160
160
  import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "react";
@@ -257,6 +257,163 @@ var MissingApplicationKindError = class extends Error {
257
257
  }
258
258
  };
259
259
 
260
+ // src/transport/sseTransportPolicy.ts
261
+ var BOUNDED_DEFAULTS = {
262
+ maxReconnects: 5,
263
+ reconnectTimeoutMs: 6e4,
264
+ reconnectDelayMs: 1e3
265
+ };
266
+ var AGENT_TERMINAL = {
267
+ primary: ["finish"],
268
+ allowFinishFallback: false
269
+ };
270
+ var WORKFLOW_TERMINAL_STRICT = {
271
+ primary: ["workflow-end", "workflow-failed"],
272
+ allowFinishFallback: false
273
+ };
274
+ var WORKFLOW_TERMINAL_WITH_FALLBACK = {
275
+ primary: ["workflow-end", "workflow-failed"],
276
+ allowFinishFallback: true
277
+ };
278
+ var PROFILE_PRESETS = {
279
+ oneshot: {
280
+ profile: "oneshot",
281
+ replay: "none",
282
+ reconnect: {
283
+ mode: "off",
284
+ maxReconnects: 0,
285
+ reconnectTimeoutMs: 0,
286
+ reconnectDelayMs: BOUNDED_DEFAULTS.reconnectDelayMs
287
+ },
288
+ terminal: AGENT_TERMINAL
289
+ },
290
+ "oneshot-workflow": {
291
+ profile: "oneshot-workflow",
292
+ replay: "none",
293
+ reconnect: {
294
+ mode: "off",
295
+ maxReconnects: 0,
296
+ reconnectTimeoutMs: 0,
297
+ reconnectDelayMs: BOUNDED_DEFAULTS.reconnectDelayMs
298
+ },
299
+ terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
300
+ },
301
+ "replayable-workflow": {
302
+ profile: "replayable-workflow",
303
+ replay: "history",
304
+ reconnect: {
305
+ mode: "bounded",
306
+ ...BOUNDED_DEFAULTS
307
+ },
308
+ terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
309
+ }
310
+ };
311
+ function legacyTerminal(expectedApplicationKind) {
312
+ if (expectedApplicationKind === "workflow") {
313
+ return WORKFLOW_TERMINAL_STRICT;
314
+ }
315
+ return AGENT_TERMINAL;
316
+ }
317
+ function legacyReconnect(autoReconnect, reconnectDelayMs) {
318
+ if (autoReconnect === false) {
319
+ return {
320
+ mode: "off",
321
+ maxReconnects: 0,
322
+ reconnectTimeoutMs: 0,
323
+ reconnectDelayMs: reconnectDelayMs ?? BOUNDED_DEFAULTS.reconnectDelayMs
324
+ };
325
+ }
326
+ return {
327
+ mode: "unbounded",
328
+ maxReconnects: Number.POSITIVE_INFINITY,
329
+ reconnectTimeoutMs: Number.POSITIVE_INFINITY,
330
+ reconnectDelayMs: reconnectDelayMs ?? BOUNDED_DEFAULTS.reconnectDelayMs
331
+ };
332
+ }
333
+ function mergePolicy(base, override) {
334
+ if (!override) return base;
335
+ return {
336
+ profile: base.profile,
337
+ replay: override.replay ?? base.replay,
338
+ reconnect: {
339
+ ...base.reconnect,
340
+ ...override.reconnect
341
+ },
342
+ terminal: {
343
+ primary: override.terminal?.primary ?? base.terminal.primary,
344
+ allowFinishFallback: override.terminal?.allowFinishFallback ?? base.terminal.allowFinishFallback
345
+ }
346
+ };
347
+ }
348
+ function resolveSseTransportPolicy(input) {
349
+ const {
350
+ profile,
351
+ policy: policyOverride,
352
+ autoReconnect,
353
+ reconnectDelayMs,
354
+ expectedApplicationKind
355
+ } = input;
356
+ let base;
357
+ if (profile) {
358
+ base = { ...PROFILE_PRESETS[profile] };
359
+ } else {
360
+ base = {
361
+ replay: "history",
362
+ reconnect: legacyReconnect(autoReconnect, reconnectDelayMs),
363
+ terminal: legacyTerminal(expectedApplicationKind)
364
+ };
365
+ }
366
+ return mergePolicy(base, policyOverride);
367
+ }
368
+ function isSseStreamTerminalEvent(agentEvent, terminal, expectedApplicationKind) {
369
+ const eventType = agentEvent.event_type;
370
+ if (eventType === "error") return true;
371
+ if (terminal.primary.includes(eventType)) return true;
372
+ if (terminal.allowFinishFallback && eventType === "finish" && expectedApplicationKind === "workflow") {
373
+ return true;
374
+ }
375
+ if (!profileUsesWorkflowTerminal(terminal) && expectedApplicationKind !== "workflow" && eventType === "finish") {
376
+ return true;
377
+ }
378
+ return false;
379
+ }
380
+ function profileUsesWorkflowTerminal(terminal) {
381
+ return terminal.primary.includes("workflow-end") || terminal.primary.includes("workflow-failed");
382
+ }
383
+ var ReconnectBudget = class {
384
+ constructor(maxReconnects, timeoutMs, startedAtMs = Date.now()) {
385
+ this.maxReconnects = maxReconnects;
386
+ this.timeoutMs = timeoutMs;
387
+ this.startedAtMs = startedAtMs;
388
+ }
389
+ maxReconnects;
390
+ timeoutMs;
391
+ startedAtMs;
392
+ attemptCount = 0;
393
+ get attempts() {
394
+ return this.attemptCount;
395
+ }
396
+ canRetry(nowMs = Date.now()) {
397
+ if (this.maxReconnects <= 0) return false;
398
+ if (!Number.isFinite(this.maxReconnects) && !Number.isFinite(this.timeoutMs)) {
399
+ return true;
400
+ }
401
+ if (Number.isFinite(this.maxReconnects) && this.attemptCount >= this.maxReconnects) {
402
+ return false;
403
+ }
404
+ if (Number.isFinite(this.timeoutMs) && nowMs - this.startedAtMs > this.timeoutMs) {
405
+ return false;
406
+ }
407
+ return true;
408
+ }
409
+ recordAttempt() {
410
+ this.attemptCount += 1;
411
+ }
412
+ };
413
+ function shouldAutoReconnect(reconnect) {
414
+ return reconnect.mode !== "off";
415
+ }
416
+
260
417
  // src/transport/createAgentxSseSource.ts
261
418
  var EXPECTED_PROTOCOL_VERSION = "1";
262
419
  var SseTransportTerminalError = class extends Error {
@@ -275,14 +432,6 @@ function transportLog(message, level = "warn") {
275
432
  console.warn(line);
276
433
  }
277
434
  }
278
- function isSseStreamTerminalEvent(agentEvent, expectedApplicationKind) {
279
- const eventType = agentEvent.event_type;
280
- if (eventType === "error") return true;
281
- if (expectedApplicationKind === "workflow") {
282
- return eventType === "workflow-end" || eventType === "workflow-failed";
283
- }
284
- return eventType === "finish";
285
- }
286
435
  function buildResumeStreamUrl(baseUrl, lastEventId, queryParam = "last_event_id") {
287
436
  if (!lastEventId) return baseUrl;
288
437
  const sep = baseUrl.includes("?") ? "&" : "?";
@@ -291,12 +440,26 @@ function buildResumeStreamUrl(baseUrl, lastEventId, queryParam = "last_event_id"
291
440
  function createAgentxSseSource(options) {
292
441
  const {
293
442
  url,
294
- autoReconnect = true,
295
- reconnectDelayMs = 1e3,
443
+ profile,
444
+ policy: policyOverride,
445
+ autoReconnect,
446
+ reconnectDelayMs,
296
447
  onLastEventId,
297
448
  EventSourceImpl = EventSource,
298
449
  expectedApplicationKind
299
450
  } = options;
451
+ const transportPolicy = resolveSseTransportPolicy({
452
+ profile,
453
+ policy: policyOverride,
454
+ autoReconnect,
455
+ reconnectDelayMs,
456
+ expectedApplicationKind
457
+ });
458
+ const reconnectDelay = transportPolicy.reconnect.reconnectDelayMs;
459
+ const reconnectBudget = transportPolicy.reconnect.mode === "bounded" ? new ReconnectBudget(
460
+ transportPolicy.reconnect.maxReconnects,
461
+ transportPolicy.reconnect.reconnectTimeoutMs
462
+ ) : null;
300
463
  return {
301
464
  start(handler, signal) {
302
465
  return new Promise((resolve, reject) => {
@@ -428,7 +591,11 @@ function createAgentxSseSource(options) {
428
591
  } else {
429
592
  flushBufferedDelta();
430
593
  handler(agentEvent, ctx);
431
- if (isSseStreamTerminalEvent(agentEvent, expectedApplicationKind)) {
594
+ if (isSseStreamTerminalEvent(
595
+ agentEvent,
596
+ transportPolicy.terminal,
597
+ expectedApplicationKind
598
+ )) {
432
599
  streamTerminal = true;
433
600
  }
434
601
  }
@@ -454,9 +621,22 @@ function createAgentxSseSource(options) {
454
621
  finish(error);
455
622
  return;
456
623
  }
457
- if (autoReconnect) {
624
+ if (shouldAutoReconnect(transportPolicy.reconnect)) {
625
+ if (transportPolicy.reconnect.mode === "bounded" && reconnectBudget && !reconnectBudget.canRetry()) {
626
+ const error = new SseTransportTerminalError(
627
+ "reconnect_budget_exhausted",
628
+ "SSE reconnect budget exhausted"
629
+ );
630
+ transportLog(error.message, "error");
631
+ cleanup();
632
+ finish(error);
633
+ return;
634
+ }
635
+ if (reconnectBudget) {
636
+ reconnectBudget.recordAttempt();
637
+ }
458
638
  cleanup();
459
- setTimeout(() => connect(lastEventId), reconnectDelayMs);
639
+ setTimeout(() => connect(lastEventId), reconnectDelay);
460
640
  return;
461
641
  }
462
642
  failPrematureClose();
@@ -762,6 +942,8 @@ function createEmptyWorkflowProgress() {
762
942
  function createEmptyWorkflowSessionState() {
763
943
  return {
764
944
  status: "connecting",
945
+ terminalKind: null,
946
+ openChildIdentities: [],
765
947
  meta: { workflowRunId: null, startedAt: null, lastEventId: null },
766
948
  containerTree: createEmptyWorkflowContainerTreeState(),
767
949
  workflowProgress: createEmptyWorkflowProgress(),
@@ -1926,6 +2108,38 @@ function readWorkflowId(data) {
1926
2108
  return readWorkflowIdFromData(data);
1927
2109
  }
1928
2110
 
2111
+ // src/core/workflow/workflowTerminalKind.ts
2112
+ function resolveWorkflowTerminalKind(data, eventType) {
2113
+ const raw = data.terminal_kind;
2114
+ if (raw === "healthy" || raw === "forced_closeout" || raw === "failed") {
2115
+ return raw;
2116
+ }
2117
+ if (data.closeout_forced === true) {
2118
+ return "forced_closeout";
2119
+ }
2120
+ if (eventType === "workflow-failed") {
2121
+ return "failed";
2122
+ }
2123
+ return "healthy";
2124
+ }
2125
+ function sessionStatusFromTerminalKind(kind, isWorkflowFailedEvent) {
2126
+ if (isWorkflowFailedEvent || kind === "failed") {
2127
+ return "error";
2128
+ }
2129
+ if (kind === "forced_closeout") {
2130
+ return "done_with_warning";
2131
+ }
2132
+ return "done";
2133
+ }
2134
+ function readOpenChildIdentities(data) {
2135
+ const raw = data.open_child_identities;
2136
+ if (!Array.isArray(raw)) return [];
2137
+ return raw.filter((item) => typeof item === "string");
2138
+ }
2139
+ function isWorkflowSessionTerminal(status) {
2140
+ return status === "done" || status === "done_with_warning" || status === "error";
2141
+ }
2142
+
1929
2143
  // src/core/workflowReducer.ts
1930
2144
  function readString5(data, key) {
1931
2145
  const value = data[key];
@@ -2299,9 +2513,25 @@ function applyStructureEvent(state, event) {
2299
2513
  if (workflowProgress.status === "done") status = "done";
2300
2514
  else if (workflowProgress.status === "error") status = "error";
2301
2515
  else if (status === "connecting") status = "running";
2516
+ let terminalKind = state.terminalKind;
2517
+ let openChildIdentities = state.openChildIdentities;
2518
+ if (projectedEvent.event_type === "workflow-end" || projectedEvent.event_type === "workflow-failed") {
2519
+ const resolved = resolveWorkflowTerminalKind(
2520
+ structureData,
2521
+ projectedEvent.event_type
2522
+ );
2523
+ status = sessionStatusFromTerminalKind(
2524
+ resolved,
2525
+ projectedEvent.event_type === "workflow-failed"
2526
+ );
2527
+ terminalKind = resolved;
2528
+ openChildIdentities = resolved === "forced_closeout" ? readOpenChildIdentities(structureData) : [];
2529
+ }
2302
2530
  const base = {
2303
2531
  ...nextState,
2304
2532
  status,
2533
+ terminalKind,
2534
+ openChildIdentities,
2305
2535
  containerTree,
2306
2536
  workflowProgress
2307
2537
  };
@@ -2386,7 +2616,7 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
2386
2616
  };
2387
2617
  }
2388
2618
  function finalizeWorkflowSessionOnStreamComplete(state) {
2389
- if (state.status === "done" || state.status === "error") {
2619
+ if (isWorkflowSessionTerminal(state.status)) {
2390
2620
  return { ...state, activeLoopSessionId: null };
2391
2621
  }
2392
2622
  const tree = state.containerTree;
@@ -2968,7 +3198,10 @@ function mapContainerStatusLabel(status) {
2968
3198
  return "pending";
2969
3199
  }
2970
3200
  }
2971
- function mapWorkflowRootStatusLabel(status) {
3201
+ function mapWorkflowRootStatusLabel(status, sessionStatus) {
3202
+ if (sessionStatus === "done_with_warning" && status === "completed") {
3203
+ return "done (warning)";
3204
+ }
2972
3205
  switch (status) {
2973
3206
  case "completed":
2974
3207
  return "done";
@@ -3681,9 +3914,12 @@ function WorkflowRootContainer({
3681
3914
  containersById
3682
3915
  }) {
3683
3916
  const { verbose } = useSessionViewOptions();
3917
+ const sessionStatus = useWorkflowSessionStatus();
3684
3918
  const visuallyActive = containersById ? isWorkflowRootVisuallyActive(node, containersById) : node.status === "running" || node.status === "retrying";
3685
- const uiStatus = mapContainerUiStatus(visuallyActive ? "running" : node.status);
3686
- const statusLabel = mapWorkflowRootStatusLabel(visuallyActive ? "running" : node.status);
3919
+ const nodeStatus = visuallyActive ? "running" : node.status;
3920
+ const forcedCloseout = sessionStatus === "done_with_warning" && nodeStatus === "completed";
3921
+ const uiStatus = forcedCloseout ? "warning" : mapContainerUiStatus(nodeStatus);
3922
+ const statusLabel = mapWorkflowRootStatusLabel(nodeStatus, sessionStatus);
3687
3923
  const meta = containersById ? buildEffectiveWorkflowRootMeta(node, containersById) : buildWorkflowRootMeta(node);
3688
3924
  return /* @__PURE__ */ jsxs7("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
3689
3925
  /* @__PURE__ */ jsx12(
@@ -4393,6 +4629,10 @@ function WorkflowSession({
4393
4629
  storeRef.current,
4394
4630
  (s) => s.state.loopTreesBySessionId
4395
4631
  );
4632
+ const openChildIdentities = useStore4(
4633
+ storeRef.current,
4634
+ (s) => s.state.openChildIdentities
4635
+ );
4396
4636
  const sessionStatus = useStore4(
4397
4637
  storeRef.current,
4398
4638
  (s) => s.state.status
@@ -4431,6 +4671,22 @@ function WorkflowSession({
4431
4671
  className: "lax-agent-session lax-workflow-session",
4432
4672
  "data-testid": "lax-workflow-session",
4433
4673
  children: [
4674
+ sessionStatus === "done_with_warning" ? /* @__PURE__ */ jsxs12(
4675
+ "div",
4676
+ {
4677
+ className: "lax-workflow-closeout-warning",
4678
+ "data-testid": "lax-workflow-closeout-warning",
4679
+ role: "status",
4680
+ children: [
4681
+ /* @__PURE__ */ jsx17("strong", { children: "Workflow forced closeout" }),
4682
+ /* @__PURE__ */ jsxs12("span", { children: [
4683
+ " ",
4684
+ "\u2014 stream ended with unresolved child scopes; not a healthy completion."
4685
+ ] }),
4686
+ openChildIdentities.length > 0 ? /* @__PURE__ */ jsx17("ul", { className: "lax-workflow-closeout-warning__scopes", children: openChildIdentities.map((identity) => /* @__PURE__ */ jsx17("li", { children: identity }, identity)) }) : null
4687
+ ]
4688
+ }
4689
+ ) : null,
4434
4690
  sessionStatus === "error" || internalErrors.length > 0 ? /* @__PURE__ */ jsx17(
4435
4691
  "div",
4436
4692
  {
@@ -4546,6 +4802,7 @@ export {
4546
4802
  ApplicationKindMismatchError,
4547
4803
  AskUserQuestion,
4548
4804
  AskUserQuestionToolBody,
4805
+ BOUNDED_DEFAULTS,
4549
4806
  Bash,
4550
4807
  BashToolBody,
4551
4808
  BodyBlockList,
@@ -4578,6 +4835,7 @@ export {
4578
4835
  MissingApplicationKindError,
4579
4836
  MultiAgentSession,
4580
4837
  NodeRegistry,
4838
+ PROFILE_PRESETS,
4581
4839
  PermissionCard,
4582
4840
  PlantUMLBlock,
4583
4841
  PlantUMLDiagram,
@@ -4585,6 +4843,7 @@ export {
4585
4843
  Read,
4586
4844
  ReadToolBody,
4587
4845
  ReasoningBubble,
4846
+ ReconnectBudget,
4588
4847
  RenderableTimelineProjector,
4589
4848
  RichMarkdown,
4590
4849
  SHELL_PROGRESS_MIN_ELAPSED_SECONDS,
@@ -4669,6 +4928,7 @@ export {
4669
4928
  isGitOperationCommand,
4670
4929
  isGroupableToolName,
4671
4930
  isMcpToolName,
4931
+ isSseStreamTerminalEvent,
4672
4932
  isWorkflowStructureEventType,
4673
4933
  iterMountCandidates,
4674
4934
  parseDiffText,
@@ -4686,6 +4946,7 @@ export {
4686
4946
  resolveEventTier,
4687
4947
  resolveResponseId,
4688
4948
  resolveShellProgressFromPayload,
4949
+ resolveSseTransportPolicy,
4689
4950
  resolveToolBody,
4690
4951
  shouldApplyGrouping,
4691
4952
  shouldShowExploreDetail,