langchain_agentx_stream_ui 0.1.0 → 0.1.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.
@@ -36,7 +36,8 @@ function createEmptyTree() {
36
36
  total: 0,
37
37
  completed: 0,
38
38
  customMessage: null
39
- }
39
+ },
40
+ modelRetry: null
40
41
  };
41
42
  }
42
43
 
@@ -344,6 +345,39 @@ function parseSnapshotTasks(rawTasks) {
344
345
  return result;
345
346
  }
346
347
 
348
+ // src/types/modelRetry.ts
349
+ function parseModelRetryHookData(data, timestampMs) {
350
+ if (data.name !== "model_retry") return null;
351
+ const attempt = typeof data.attempt === "number" ? data.attempt : Number(data.attempt);
352
+ const maxRetries = typeof data.max_retries === "number" ? data.max_retries : Number(data.max_retries);
353
+ const retryDelayMs = typeof data.retry_delay_ms === "number" ? data.retry_delay_ms : Number(data.retry_delay_ms ?? 0);
354
+ if (!Number.isFinite(attempt) || !Number.isFinite(maxRetries)) return null;
355
+ return {
356
+ attempt: Math.max(1, Math.floor(attempt)),
357
+ maxRetries: Math.max(1, Math.floor(maxRetries)),
358
+ retryDelayMs: Number.isFinite(retryDelayMs) ? Math.max(0, retryDelayMs) : 0,
359
+ errorCategory: typeof data.error_category === "string" ? data.error_category : "api_error",
360
+ errorMessage: typeof data.error_message === "string" ? data.error_message : "",
361
+ errorStatus: typeof data.error_status === "number" ? data.error_status : data.error_status != null ? Number(data.error_status) : null,
362
+ phase: typeof data.phase === "string" ? data.phase : "scheduled",
363
+ updatedAt: timestampMs
364
+ };
365
+ }
366
+ function formatModelRetryCategoryLabel(category) {
367
+ switch (category) {
368
+ case "server_overload":
369
+ return "Overloaded";
370
+ case "rate_limit":
371
+ return "Rate limited";
372
+ case "connection":
373
+ return "Connection error";
374
+ case "server_error":
375
+ return "Server error";
376
+ default:
377
+ return "API error";
378
+ }
379
+ }
380
+
347
381
  // src/core/reducer.ts
348
382
  function isInternalTool(toolName) {
349
383
  return INTERNAL_TOOL_NAMES.has(toolName);
@@ -370,9 +404,13 @@ function cloneTree(tree) {
370
404
  hasEverShownTaskList: tree.hasEverShownTaskList,
371
405
  waitingForFinalResponseAfterTasks: tree.waitingForFinalResponseAfterTasks,
372
406
  taskStartTime: tree.taskStartTime,
373
- hookProgress: { ...tree.hookProgress }
407
+ hookProgress: { ...tree.hookProgress },
408
+ modelRetry: tree.modelRetry ? { ...tree.modelRetry } : null
374
409
  };
375
410
  }
411
+ function clearModelRetry(tree) {
412
+ tree.modelRetry = null;
413
+ }
376
414
  function buildClassifyContext(tree) {
377
415
  let streamingToolNodeId;
378
416
  const childIds = getActiveChildIds(tree);
@@ -479,6 +517,7 @@ function handleStart(tree, event) {
479
517
  tree.status = "running";
480
518
  tree.meta.startedAt = eventTimeToMs(event.timestamp);
481
519
  if (event.session_id) tree.meta.sessionId = event.session_id;
520
+ clearModelRetry(tree);
482
521
  }
483
522
  function resolveErrorEventFields(data) {
484
523
  const message = typeof data.message === "string" && data.message || typeof data.error === "string" && data.error || "Unknown error";
@@ -532,6 +571,7 @@ function finalizeStreamingNodes(tree) {
532
571
  }
533
572
  }
534
573
  function handleFinish(tree, event, seq) {
574
+ clearModelRetry(tree);
535
575
  const data = event.data;
536
576
  const endedAt = eventTimeToMs(event.timestamp);
537
577
  if (data.is_error === true) {
@@ -601,6 +641,11 @@ function appendTaskDebugEvent(tree, event, eventIndex) {
601
641
  }
602
642
  function handleHookProgress(tree, event) {
603
643
  const data = event.data;
644
+ const modelRetry = parseModelRetryHookData(data, eventTimeToMs(event.timestamp));
645
+ if (modelRetry) {
646
+ tree.modelRetry = modelRetry;
647
+ return;
648
+ }
604
649
  const toolUseID = data.tool_use_id;
605
650
  const hookEvent = data.hook_event;
606
651
  if (!toolUseID || hookEvent !== "Stop" && hookEvent !== "SubagentStop") return;
@@ -609,7 +654,7 @@ function handleHookProgress(tree, event) {
609
654
  currentToolUseID: toolUseID,
610
655
  total: 1,
611
656
  completed: 0,
612
- customMessage: data.status_message ?? null
657
+ customMessage: typeof data.status_message === "string" ? data.status_message : null
613
658
  };
614
659
  } else {
615
660
  tree.hookProgress.total += 1;
@@ -641,6 +686,7 @@ function handleHookFinished(tree, event) {
641
686
  }
642
687
  }
643
688
  function handleTextStart(tree, event, seq) {
689
+ clearModelRetry(tree);
644
690
  const id = `text:${seq}`;
645
691
  const node = {
646
692
  kind: "text",
@@ -1325,6 +1371,7 @@ function reduceTree(tree, event, eventIndex, options) {
1325
1371
  }
1326
1372
  return next;
1327
1373
  }
1374
+ var reduceAgentLoopEvent = reduceTree;
1328
1375
  function reduceEvents(events, options) {
1329
1376
  return events.reduce(
1330
1377
  (tree, event, index) => reduceTree(tree, event, index, options),
@@ -1511,7 +1558,7 @@ var ToolDisplayOptionsContext = createContext(DEFAULT_TOOL_DISPLAY);
1511
1558
  function useSessionStoreApi() {
1512
1559
  const store = useContext(SessionStoreContext);
1513
1560
  if (!store) {
1514
- throw new Error("useSessionStoreApi must be used within AgentSession or MultiAgentSession");
1561
+ throw new Error("useSessionStoreApi must be used within AgentSession, MultiAgentSession, or AgentLoopView scoped store");
1515
1562
  }
1516
1563
  return store;
1517
1564
  }
@@ -3941,9 +3988,72 @@ function TaskItem({ task, allTasks }) {
3941
3988
  }
3942
3989
 
3943
3990
  // src/view/Spinner.tsx
3944
- import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef6, useState as useState6 } from "react";
3991
+ import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
3945
3992
  import { useSyncExternalStore } from "react";
3946
3993
 
3994
+ // src/view/ModelRetryBlock.tsx
3995
+ import { useEffect as useEffect4, useState as useState4 } from "react";
3996
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
3997
+ var MAX_ERROR_CHARS = 1e3;
3998
+ function formatRetryCountdownSeconds(retryDelayMs, updatedAt, nowMs) {
3999
+ const elapsed = Math.max(0, nowMs - updatedAt);
4000
+ return Math.max(0, Math.round((retryDelayMs - elapsed) / 1e3));
4001
+ }
4002
+ function ModelRetryBlock({ state, verbose = false }) {
4003
+ const [expanded, setExpanded] = useState4(false);
4004
+ const [nowMs, setNowMs] = useState4(() => Date.now());
4005
+ useEffect4(() => {
4006
+ const timer = window.setInterval(() => setNowMs(Date.now()), 1e3);
4007
+ return () => window.clearInterval(timer);
4008
+ }, []);
4009
+ const retryInSeconds = formatRetryCountdownSeconds(state.retryDelayMs, state.updatedAt, nowMs);
4010
+ const categoryLabel = formatModelRetryCategoryLabel(state.errorCategory);
4011
+ const summary = `Retrying\u2026 (attempt ${state.attempt}/${state.maxRetries})`;
4012
+ const errorText = state.errorMessage.trim();
4013
+ const truncated = !verbose && errorText.length > MAX_ERROR_CHARS;
4014
+ const visibleError = truncated ? `${errorText.slice(0, MAX_ERROR_CHARS)}\u2026` : errorText;
4015
+ return /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry", "data-testid": "lax-model-retry", children: [
4016
+ /* @__PURE__ */ jsx5(
4017
+ "button",
4018
+ {
4019
+ type: "button",
4020
+ className: "lax-model-retry__toggle lax-workflow-expand-trigger",
4021
+ onClick: () => setExpanded((prev) => !prev),
4022
+ "aria-expanded": expanded,
4023
+ "data-testid": "lax-model-retry-toggle",
4024
+ children: /* @__PURE__ */ jsxs3("span", { className: "lax-model-retry__summary", "data-testid": "lax-model-retry-summary", children: [
4025
+ "\u23BF ",
4026
+ summary,
4027
+ !expanded && retryInSeconds > 0 ? /* @__PURE__ */ jsxs3("span", { className: "lax-model-retry__inline-countdown", children: [
4028
+ " \xB7 ",
4029
+ retryInSeconds,
4030
+ "s"
4031
+ ] }) : null
4032
+ ] })
4033
+ }
4034
+ ),
4035
+ expanded ? /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry__body", "data-testid": "lax-model-retry-body", children: [
4036
+ errorText ? /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry__error", "data-testid": "lax-model-retry-error", children: [
4037
+ /* @__PURE__ */ jsx5("span", { className: "lax-model-retry__category", children: categoryLabel }),
4038
+ visibleError ? /* @__PURE__ */ jsx5("pre", { className: "lax-model-retry__error-text", children: visibleError }) : null,
4039
+ truncated ? /* @__PURE__ */ jsx5("span", { className: "lax-model-retry__hint", children: "\u2026 (ctrl+o to expand)" }) : null
4040
+ ] }) : /* @__PURE__ */ jsx5("div", { className: "lax-model-retry__category-only", children: categoryLabel }),
4041
+ /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry__countdown", "data-testid": "lax-model-retry-countdown", children: [
4042
+ "Retrying in ",
4043
+ retryInSeconds,
4044
+ " ",
4045
+ retryInSeconds === 1 ? "second" : "seconds",
4046
+ "\u2026 (attempt",
4047
+ " ",
4048
+ state.attempt,
4049
+ "/",
4050
+ state.maxRetries,
4051
+ ")"
4052
+ ] })
4053
+ ] }) : null
4054
+ ] });
4055
+ }
4056
+
3947
4057
  // src/view/spinner/constants.ts
3948
4058
  var _BASE_FRAMES = ["\xB7", "\u2722", "*", "\u2736", "\u273B", "\u273D"];
3949
4059
  var SPINNER_FRAMES = [..._BASE_FRAMES, ..._BASE_FRAMES.slice().reverse()];
@@ -4174,7 +4284,7 @@ function getSpinnerFrameIndex(timeMs, reducedMotion = false) {
4174
4284
  import { useRef as useRef5 } from "react";
4175
4285
 
4176
4286
  // src/view/spinner/SpinnerGlimmerVerb.tsx
4177
- import { jsxs as jsxs3 } from "react/jsx-runtime";
4287
+ import { jsxs as jsxs4 } from "react/jsx-runtime";
4178
4288
  function SpinnerGlimmerVerb({
4179
4289
  verb,
4180
4290
  mode,
@@ -4204,7 +4314,7 @@ function SpinnerGlimmerVerb({
4204
4314
  ["--lax-spinner-flash-opacity"]: String(flashOpacity)
4205
4315
  }
4206
4316
  };
4207
- return /* @__PURE__ */ jsxs3("span", { className: classes.join(" "), style, children: [
4317
+ return /* @__PURE__ */ jsxs4("span", { className: classes.join(" "), style, children: [
4208
4318
  verb,
4209
4319
  "\u2026"
4210
4320
  ] });
@@ -4254,13 +4364,13 @@ function stepSmoothedTokenCount(current, target, reducedMotion) {
4254
4364
  }
4255
4365
 
4256
4366
  // src/view/spinner/useAnimationFrame.ts
4257
- import { useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
4367
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
4258
4368
  function useAnimationFrame(intervalMs) {
4259
- const [time, setTime] = useState4(0);
4369
+ const [time, setTime] = useState5(0);
4260
4370
  const startRef = useRef3(null);
4261
4371
  const lastTickRef = useRef3(0);
4262
4372
  const rafRef = useRef3(null);
4263
- useEffect4(() => {
4373
+ useEffect5(() => {
4264
4374
  if (intervalMs === null) return;
4265
4375
  startRef.current = null;
4266
4376
  lastTickRef.current = 0;
@@ -4286,14 +4396,14 @@ function useAnimationFrame(intervalMs) {
4286
4396
  }
4287
4397
 
4288
4398
  // src/view/spinner/useReducedMotion.ts
4289
- import { useEffect as useEffect5, useState as useState5 } from "react";
4399
+ import { useEffect as useEffect6, useState as useState6 } from "react";
4290
4400
  var QUERY = "(prefers-reduced-motion: reduce)";
4291
4401
  function useReducedMotion() {
4292
- const [reducedMotion, setReducedMotion] = useState5(() => {
4402
+ const [reducedMotion, setReducedMotion] = useState6(() => {
4293
4403
  if (typeof window === "undefined" || !window.matchMedia) return false;
4294
4404
  return window.matchMedia(QUERY).matches;
4295
4405
  });
4296
- useEffect5(() => {
4406
+ useEffect6(() => {
4297
4407
  if (typeof window === "undefined" || !window.matchMedia) return;
4298
4408
  const media = window.matchMedia(QUERY);
4299
4409
  const onChange = () => setReducedMotion(media.matches);
@@ -4353,7 +4463,7 @@ function useStalledSpinnerIntensity(time, currentResponseLength, hasActiveTools
4353
4463
  }
4354
4464
 
4355
4465
  // src/view/spinner/SpinnerAnimationRow.tsx
4356
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
4466
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4357
4467
  function SpinnerAnimationRow({
4358
4468
  mode,
4359
4469
  verb,
@@ -4412,9 +4522,9 @@ function SpinnerAnimationRow({
4412
4522
  className: thinkingStatus === "thinking" ? "lax-spinner-status-part--thinking" : void 0
4413
4523
  });
4414
4524
  }
4415
- return /* @__PURE__ */ jsxs4("div", { className: "lax-spinner", "data-testid": "lax-spinner", children: [
4416
- /* @__PURE__ */ jsx5("span", { className: "lax-spinner-frame", "data-testid": "lax-spinner-frame", children: frame }),
4417
- /* @__PURE__ */ jsx5(
4525
+ return /* @__PURE__ */ jsxs5("div", { className: "lax-spinner", "data-testid": "lax-spinner", children: [
4526
+ /* @__PURE__ */ jsx6("span", { className: "lax-spinner-frame", "data-testid": "lax-spinner-frame", children: frame }),
4527
+ /* @__PURE__ */ jsx6(
4418
4528
  SpinnerGlimmerVerb,
4419
4529
  {
4420
4530
  verb,
@@ -4425,9 +4535,9 @@ function SpinnerAnimationRow({
4425
4535
  shimmerActive
4426
4536
  }
4427
4537
  ),
4428
- statusParts.length > 0 && /* @__PURE__ */ jsxs4("span", { className: "lax-spinner-status", children: [
4538
+ statusParts.length > 0 && /* @__PURE__ */ jsxs5("span", { className: "lax-spinner-status", children: [
4429
4539
  " (",
4430
- statusParts.map((part, i) => /* @__PURE__ */ jsxs4(
4540
+ statusParts.map((part, i) => /* @__PURE__ */ jsxs5(
4431
4541
  "span",
4432
4542
  {
4433
4543
  className: `lax-spinner-status-part${part.className ? ` ${part.className}` : ""}`,
@@ -4468,11 +4578,11 @@ function shouldShowNextTaskLine(showTaskList, tasks, sessionStatus) {
4468
4578
  }
4469
4579
 
4470
4580
  // src/view/Spinner.tsx
4471
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4581
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4472
4582
  function MessageResponse({ children }) {
4473
- return /* @__PURE__ */ jsxs5("div", { className: "lax-message-response", children: [
4474
- /* @__PURE__ */ jsx6("span", { className: "lax-message-response__marker", children: "\u23BF " }),
4475
- /* @__PURE__ */ jsx6("span", { className: "lax-message-response__content", children })
4583
+ return /* @__PURE__ */ jsxs6("div", { className: "lax-message-response", children: [
4584
+ /* @__PURE__ */ jsx7("span", { className: "lax-message-response__marker", children: "\u23BF " }),
4585
+ /* @__PURE__ */ jsx7("span", { className: "lax-message-response__content", children })
4476
4586
  ] });
4477
4587
  }
4478
4588
  function shouldShowSpinnerTaskList(taskPhase, tasks, hasEverShownTaskList, contentCharCount = 0) {
@@ -4505,6 +4615,10 @@ function Spinner({
4505
4615
  (callback) => store.subscribe(callback),
4506
4616
  () => store.getState().tree.hookProgress
4507
4617
  );
4618
+ const modelRetry = useSyncExternalStore(
4619
+ (callback) => store.subscribe(callback),
4620
+ () => store.getState().tree.modelRetry
4621
+ );
4508
4622
  const taskListRevision = useSyncExternalStore(
4509
4623
  (callback) => store.subscribe(callback),
4510
4624
  () => {
@@ -4535,10 +4649,10 @@ function Spinner({
4535
4649
  const derivedMode = mode !== "idle" ? mode : activeRoundId ? "thinking" : "requesting";
4536
4650
  const effectiveMode = derivedMode === "tool-use" ? "tool-use" : derivedMode === "thinking" ? "thinking" : "requesting";
4537
4651
  const hasActiveTools = effectiveMode === "tool-use";
4538
- const [fallbackVerb] = useState6(() => pickRandomVerb());
4539
- const [thinkingStatus, setThinkingStatus] = useState6(null);
4652
+ const [fallbackVerb] = useState7(() => pickRandomVerb());
4653
+ const [thinkingStatus, setThinkingStatus] = useState7(null);
4540
4654
  const thinkingStartRef = useRef6(null);
4541
- useEffect6(() => {
4655
+ useEffect7(() => {
4542
4656
  let showDurationTimer = null;
4543
4657
  let clearStatusTimer = null;
4544
4658
  if (derivedMode === "thinking") {
@@ -4587,8 +4701,9 @@ function Spinner({
4587
4701
  } else if (typeof thinkingStatus === "number") {
4588
4702
  thinkingText = `thought for ${Math.max(1, Math.round(thinkingStatus / 1e3))}s`;
4589
4703
  }
4590
- return /* @__PURE__ */ jsxs5("div", { className: "lax-spinner-container", "data-testid": "lax-spinner-container", children: [
4591
- /* @__PURE__ */ jsx6(
4704
+ return /* @__PURE__ */ jsxs6("div", { className: "lax-spinner-container", "data-testid": "lax-spinner-container", children: [
4705
+ modelRetry ? /* @__PURE__ */ jsx7(ModelRetryBlock, { state: modelRetry, verbose }) : null,
4706
+ /* @__PURE__ */ jsx7(
4592
4707
  SpinnerAnimationRow,
4593
4708
  {
4594
4709
  mode: effectiveMode,
@@ -4603,19 +4718,19 @@ function Spinner({
4603
4718
  timing
4604
4719
  }
4605
4720
  ),
4606
- showTaskList && /* @__PURE__ */ jsx6(MessageResponse, { children: /* @__PURE__ */ jsx6(TaskList, { tasks, isStandalone: false }) }),
4607
- nextTask && /* @__PURE__ */ jsx6(MessageResponse, { children: /* @__PURE__ */ jsx6("span", { className: "lax-spinner-next-line", children: formatNextTaskLineText(nextTask) }) })
4721
+ showTaskList && /* @__PURE__ */ jsx7(MessageResponse, { children: /* @__PURE__ */ jsx7(TaskList, { tasks, isStandalone: false }) }),
4722
+ nextTask && /* @__PURE__ */ jsx7(MessageResponse, { children: /* @__PURE__ */ jsx7("span", { className: "lax-spinner-next-line", children: formatNextTaskLineText(nextTask) }) })
4608
4723
  ] });
4609
4724
  }
4610
4725
 
4611
4726
  // src/view/TaskListFooter.tsx
4612
- import { useMemo as useMemo6, useState as useState7 } from "react";
4727
+ import { useMemo as useMemo6, useState as useState8 } from "react";
4613
4728
  import { useSyncExternalStore as useSyncExternalStore2 } from "react";
4614
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4729
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4615
4730
  function MessageResponse2({ children }) {
4616
- return /* @__PURE__ */ jsxs6("div", { className: "lax-message-response", children: [
4617
- /* @__PURE__ */ jsx7("span", { className: "lax-message-response__marker", children: "\u23BF " }),
4618
- /* @__PURE__ */ jsx7("span", { className: "lax-message-response__content", children })
4731
+ return /* @__PURE__ */ jsxs7("div", { className: "lax-message-response", children: [
4732
+ /* @__PURE__ */ jsx8("span", { className: "lax-message-response__marker", children: "\u23BF " }),
4733
+ /* @__PURE__ */ jsx8("span", { className: "lax-message-response__content", children })
4619
4734
  ] });
4620
4735
  }
4621
4736
  function formatTaskFooterLabel(tasks) {
@@ -4651,7 +4766,7 @@ function readTaskListRevision(store) {
4651
4766
  function TaskListFooter() {
4652
4767
  const store = useSessionStoreApi();
4653
4768
  const sessionStatus = useSessionStatus();
4654
- const [expanded, setExpanded] = useState7(false);
4769
+ const [expanded, setExpanded] = useState8(false);
4655
4770
  const taskListRevision = useSyncExternalStore2(
4656
4771
  (callback) => store.subscribe(callback),
4657
4772
  () => readTaskListRevision(store)
@@ -4674,8 +4789,8 @@ function TaskListFooter() {
4674
4789
  return null;
4675
4790
  }
4676
4791
  const label = formatTaskFooterLabel(tasks);
4677
- return /* @__PURE__ */ jsxs6("div", { className: "lax-task-list-footer", "data-testid": "lax-task-list-footer", children: [
4678
- /* @__PURE__ */ jsxs6(
4792
+ return /* @__PURE__ */ jsxs7("div", { className: "lax-task-list-footer", "data-testid": "lax-task-list-footer", children: [
4793
+ /* @__PURE__ */ jsxs7(
4679
4794
  "button",
4680
4795
  {
4681
4796
  type: "button",
@@ -4685,16 +4800,16 @@ function TaskListFooter() {
4685
4800
  onClick: () => setExpanded((v) => !v),
4686
4801
  children: [
4687
4802
  label,
4688
- /* @__PURE__ */ jsx7("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
4803
+ /* @__PURE__ */ jsx8("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
4689
4804
  ]
4690
4805
  }
4691
4806
  ),
4692
- expanded && /* @__PURE__ */ jsx7(MessageResponse2, { children: /* @__PURE__ */ jsx7(TaskList, { tasks }) })
4807
+ expanded && /* @__PURE__ */ jsx8(MessageResponse2, { children: /* @__PURE__ */ jsx8(TaskList, { tasks }) })
4693
4808
  ] });
4694
4809
  }
4695
4810
 
4696
4811
  // src/view/GroupedToolCallRow.tsx
4697
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4812
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4698
4813
  var INITIALIZING = "Initializing\u2026";
4699
4814
  function readGroupMeta(payload) {
4700
4815
  const raw = payload.groupMeta;
@@ -4748,8 +4863,8 @@ function GroupedToolCallRow({ payload }) {
4748
4863
  const title = groupMeta?.title ?? `${toolName} \xD7 ${toolUses.length}`;
4749
4864
  const anyPending = Array.isArray(payload.pendingCallIds) && payload.pendingCallIds.length > 0;
4750
4865
  const anyError = Array.isArray(payload.erroredCallIds) && payload.erroredCallIds.length > 0;
4751
- return /* @__PURE__ */ jsxs7("div", { className: "lax-tool-group", "data-tool-name": toolName, "data-kind": "grouped_tool_use", children: [
4752
- /* @__PURE__ */ jsxs7(
4866
+ return /* @__PURE__ */ jsxs8("div", { className: "lax-tool-group", "data-tool-name": toolName, "data-kind": "grouped_tool_use", children: [
4867
+ /* @__PURE__ */ jsxs8(
4753
4868
  "div",
4754
4869
  {
4755
4870
  className: [
@@ -4758,27 +4873,27 @@ function GroupedToolCallRow({ payload }) {
4758
4873
  anyError ? "lax-tool-group__header--error" : ""
4759
4874
  ].filter(Boolean).join(" "),
4760
4875
  children: [
4761
- anyPending && /* @__PURE__ */ jsx8("span", { className: "lax-tool-group__dot", "aria-hidden": true, children: "\u25CF" }),
4762
- /* @__PURE__ */ jsx8("span", { className: "lax-tool-group__label", children: title })
4876
+ anyPending && /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__dot", "aria-hidden": true, children: "\u25CF" }),
4877
+ /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__label", children: title })
4763
4878
  ]
4764
4879
  }
4765
4880
  ),
4766
- /* @__PURE__ */ jsx8("div", { className: "lax-tool-group__items", children: toolUses.map((entry, index) => {
4881
+ /* @__PURE__ */ jsx9("div", { className: "lax-tool-group__items", children: toolUses.map((entry, index) => {
4767
4882
  const callId = String(entry.tool_call_id ?? "");
4768
4883
  const subTitle = formatAgentSublineTitle(entry, hideType);
4769
4884
  const status = sublineStatus(callId, payload);
4770
4885
  const isLast = index === toolUses.length - 1;
4771
4886
  const treeChar = isLast ? "\u2514\u2500" : "\u251C\u2500";
4772
- return /* @__PURE__ */ jsxs7(
4887
+ return /* @__PURE__ */ jsxs8(
4773
4888
  "div",
4774
4889
  {
4775
4890
  className: "lax-tool-group__item lax-tool-group__subline",
4776
4891
  "data-group-index": index + 1,
4777
4892
  "data-tool-call-id": callId,
4778
4893
  children: [
4779
- /* @__PURE__ */ jsx8("span", { className: "lax-tool-group__tree", "aria-hidden": true, children: treeChar }),
4780
- /* @__PURE__ */ jsx8("span", { className: "lax-tool-group__subline-title", children: subTitle }),
4781
- status && /* @__PURE__ */ jsxs7("span", { className: "lax-tool-group__subline-status", children: [
4894
+ /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__tree", "aria-hidden": true, children: treeChar }),
4895
+ /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__subline-title", children: subTitle }),
4896
+ status && /* @__PURE__ */ jsxs8("span", { className: "lax-tool-group__subline-status", children: [
4782
4897
  " \xB7 ",
4783
4898
  status
4784
4899
  ] })
@@ -4791,7 +4906,7 @@ function GroupedToolCallRow({ payload }) {
4791
4906
  }
4792
4907
 
4793
4908
  // src/view/nodes/ThinkingSummaryNode.tsx
4794
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4909
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4795
4910
  function formatThoughtDuration(seconds) {
4796
4911
  const rounded = Math.max(1, Math.round(seconds));
4797
4912
  return `${rounded}s`;
@@ -4816,29 +4931,29 @@ function ThinkingSummary({ node }) {
4816
4931
  const viewOptions = useSessionViewOptions();
4817
4932
  const showToolDetail = shouldShowReasoningInTimeline(viewOptions);
4818
4933
  const toolText = showToolDetail ? formatToolCounts(node) : null;
4819
- return /* @__PURE__ */ jsxs8(
4934
+ return /* @__PURE__ */ jsxs9(
4820
4935
  "div",
4821
4936
  {
4822
4937
  className: "lax-thought-summary",
4823
4938
  "data-kind": "thinking_summary",
4824
4939
  "data-round-id": node.roundId,
4825
4940
  children: [
4826
- /* @__PURE__ */ jsxs8("span", { className: "lax-thought-summary__text", children: [
4941
+ /* @__PURE__ */ jsxs9("span", { className: "lax-thought-summary__text", children: [
4827
4942
  "Thought for ",
4828
4943
  formatThoughtDuration(node.elapsedSeconds)
4829
4944
  ] }),
4830
- toolText ? /* @__PURE__ */ jsxs8("span", { className: "lax-thought-summary__tools", children: [
4945
+ toolText ? /* @__PURE__ */ jsxs9("span", { className: "lax-thought-summary__tools", children: [
4831
4946
  " \xB7 ",
4832
4947
  toolText
4833
4948
  ] }) : null,
4834
- !showToolDetail ? /* @__PURE__ */ jsx9("span", { className: "lax-thought-summary__hint", children: " (ctrl+o to expand)" }) : null
4949
+ !showToolDetail ? /* @__PURE__ */ jsx10("span", { className: "lax-thought-summary__hint", children: " (ctrl+o to expand)" }) : null
4835
4950
  ]
4836
4951
  }
4837
4952
  );
4838
4953
  }
4839
4954
 
4840
4955
  // src/view/projectedTimelineRender.tsx
4841
- import { jsx as jsx10 } from "react/jsx-runtime";
4956
+ import { jsx as jsx11 } from "react/jsx-runtime";
4842
4957
  function zeroToolCounts2() {
4843
4958
  return {
4844
4959
  searchCount: 0,
@@ -4866,39 +4981,39 @@ function ThinkingSummaryWrapper({ entry }) {
4866
4981
  isExpanded: Boolean(payload.isExpanded ?? false),
4867
4982
  startedAt: Number(payload.startedAt ?? Date.now())
4868
4983
  };
4869
- return /* @__PURE__ */ jsx10("div", { className: "lax-timeline__item", "data-kind": "thinking_summary", children: /* @__PURE__ */ jsx10(ThinkingSummary, { node }) });
4984
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "thinking_summary", children: /* @__PURE__ */ jsx11(ThinkingSummary, { node }) });
4870
4985
  }
4871
4986
  function renderProjectedTimelineItem(item, registry) {
4872
4987
  const { key, entry } = item;
4873
4988
  if (entry.kind === "thinking_summary") {
4874
- return /* @__PURE__ */ jsx10(ThinkingSummaryWrapper, { entry }, key);
4989
+ return /* @__PURE__ */ jsx11(ThinkingSummaryWrapper, { entry }, key);
4875
4990
  }
4876
4991
  if (entry.kind === "collapsed_explore") {
4877
- return /* @__PURE__ */ jsx10("div", { className: "lax-timeline__item", "data-kind": "collapsed_explore", children: /* @__PURE__ */ jsx10(CollapsedExploreNode, { entry, registry }) }, key);
4992
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "collapsed_explore", children: /* @__PURE__ */ jsx11(CollapsedExploreNode, { entry, registry }) }, key);
4878
4993
  }
4879
4994
  if (entry.kind === "grouped_tool_use") {
4880
- return /* @__PURE__ */ jsx10("div", { className: "lax-timeline__item", "data-kind": "grouped_tool_use", children: /* @__PURE__ */ jsx10(GroupedToolCallRow, { payload: entry.payload }) }, key);
4995
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "grouped_tool_use", children: /* @__PURE__ */ jsx11(GroupedToolCallRow, { payload: entry.payload }) }, key);
4881
4996
  }
4882
4997
  if (entry.kind === "system_summary") {
4883
4998
  const summary = String(entry.payload.summaryText ?? entry.payload.summary_text ?? "");
4884
- return /* @__PURE__ */ jsx10("div", { className: "lax-timeline__item", "data-kind": "system_summary", children: /* @__PURE__ */ jsx10(SystemSummaryNode, { summaryText: summary }) }, key);
4999
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "system_summary", children: /* @__PURE__ */ jsx11(SystemSummaryNode, { summaryText: summary }) }, key);
4885
5000
  }
4886
5001
  const nodeId = entry.sourceNodeIds[0];
4887
5002
  if (!nodeId) return null;
4888
- return /* @__PURE__ */ jsx10(TimelineItem, { nodeId, registry }, key);
5003
+ return /* @__PURE__ */ jsx11(TimelineItem, { nodeId, registry }, key);
4889
5004
  }
4890
5005
 
4891
5006
  // src/view/TimelineEntries.tsx
4892
- import { Fragment as Fragment2, jsx as jsx11 } from "react/jsx-runtime";
5007
+ import { Fragment as Fragment2, jsx as jsx12 } from "react/jsx-runtime";
4893
5008
  function TimelineEntries({ registry }) {
4894
5009
  const projectedItems = useProjectedTimeline();
4895
- return /* @__PURE__ */ jsx11(Fragment2, { children: projectedItems.map((item) => renderProjectedTimelineItem(item, registry)) });
5010
+ return /* @__PURE__ */ jsx12(Fragment2, { children: projectedItems.map((item) => renderProjectedTimelineItem(item, registry)) });
4896
5011
  }
4897
5012
 
4898
5013
  // src/view/Timeline.tsx
4899
5014
  import { useStore as useStore6 } from "zustand";
4900
5015
  import { useShallow as useShallow5 } from "zustand/shallow";
4901
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
5016
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
4902
5017
  function useContentCharCount() {
4903
5018
  const store = useSessionStoreApi();
4904
5019
  return useStore6(
@@ -4914,15 +5029,15 @@ function useContentCharCount() {
4914
5029
  })
4915
5030
  );
4916
5031
  }
4917
- function Timeline() {
5032
+ function Timeline({ showTaskListFooter = true }) {
4918
5033
  const registry = useNodeRegistry();
4919
5034
  const store = useSessionStoreApi();
4920
5035
  const { verbose } = useSessionViewOptions();
4921
5036
  const { startedAt } = useStore6(store, (s) => s.tree.meta);
4922
5037
  const contentCharCount = useContentCharCount();
4923
- return /* @__PURE__ */ jsxs9("div", { className: "lax-timeline", "data-testid": "lax-timeline", children: [
4924
- /* @__PURE__ */ jsx12(TimelineEntries, { registry }),
4925
- /* @__PURE__ */ jsx12(
5038
+ return /* @__PURE__ */ jsxs10("div", { className: "lax-timeline", "data-testid": "lax-timeline", children: [
5039
+ /* @__PURE__ */ jsx13(TimelineEntries, { registry }),
5040
+ /* @__PURE__ */ jsx13(
4926
5041
  Spinner,
4927
5042
  {
4928
5043
  startedAt,
@@ -4930,7 +5045,7 @@ function Timeline() {
4930
5045
  verbose
4931
5046
  }
4932
5047
  ),
4933
- /* @__PURE__ */ jsx12(TaskListFooter, {})
5048
+ showTaskListFooter ? /* @__PURE__ */ jsx13(TaskListFooter, {}) : null
4934
5049
  ] });
4935
5050
  }
4936
5051
 
@@ -4982,7 +5097,7 @@ function estimateProjectedEntrySize(entry, fallback = DEFAULT_ESTIMATE_SIZE) {
4982
5097
  import { useRef as useRef7 } from "react";
4983
5098
  import { useVirtualizer } from "@tanstack/react-virtual";
4984
5099
  import { useStore as useStore7 } from "zustand";
4985
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
5100
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4986
5101
  var DEFAULT_VIRTUALIZE_THRESHOLD = 500;
4987
5102
  function useContentCharCount2() {
4988
5103
  const store = useSessionStoreApi();
@@ -4997,7 +5112,8 @@ function useContentCharCount2() {
4997
5112
  });
4998
5113
  }
4999
5114
  function VirtualTimeline({
5000
- estimateSize = DEFAULT_ESTIMATE_SIZE
5115
+ estimateSize = DEFAULT_ESTIMATE_SIZE,
5116
+ showTaskListFooter = true
5001
5117
  }) {
5002
5118
  const items = useProjectedTimeline();
5003
5119
  const registry = useNodeRegistry();
@@ -5015,14 +5131,14 @@ function VirtualTimeline({
5015
5131
  },
5016
5132
  overscan: 5
5017
5133
  });
5018
- return /* @__PURE__ */ jsxs10(
5134
+ return /* @__PURE__ */ jsxs11(
5019
5135
  "div",
5020
5136
  {
5021
5137
  ref: parentRef,
5022
5138
  className: "lax-virtual-timeline",
5023
5139
  "data-testid": "lax-virtual-timeline",
5024
5140
  children: [
5025
- /* @__PURE__ */ jsx13(
5141
+ /* @__PURE__ */ jsx14(
5026
5142
  "div",
5027
5143
  {
5028
5144
  className: "lax-virtual-timeline__inner",
@@ -5034,7 +5150,7 @@ function VirtualTimeline({
5034
5150
  children: virtualizer.getVirtualItems().map((virtualRow) => {
5035
5151
  const item = items[virtualRow.index];
5036
5152
  if (!item) return null;
5037
- return /* @__PURE__ */ jsx13(
5153
+ return /* @__PURE__ */ jsx14(
5038
5154
  "div",
5039
5155
  {
5040
5156
  className: "lax-virtual-timeline__item lax-timeline__item",
@@ -5055,7 +5171,7 @@ function VirtualTimeline({
5055
5171
  })
5056
5172
  }
5057
5173
  ),
5058
- /* @__PURE__ */ jsx13(
5174
+ /* @__PURE__ */ jsx14(
5059
5175
  Spinner,
5060
5176
  {
5061
5177
  startedAt,
@@ -5063,27 +5179,28 @@ function VirtualTimeline({
5063
5179
  verbose
5064
5180
  }
5065
5181
  ),
5066
- /* @__PURE__ */ jsx13(TaskListFooter, {})
5182
+ showTaskListFooter ? /* @__PURE__ */ jsx14(TaskListFooter, {}) : null
5067
5183
  ]
5068
5184
  }
5069
5185
  );
5070
5186
  }
5071
5187
 
5072
5188
  // src/view/SessionTimeline.tsx
5073
- import { jsx as jsx14 } from "react/jsx-runtime";
5189
+ import { jsx as jsx15 } from "react/jsx-runtime";
5074
5190
  function SessionTimeline({
5075
5191
  virtualized = false,
5076
5192
  virtualizeThreshold = DEFAULT_VIRTUALIZE_THRESHOLD,
5077
- groupParallelTools = false
5193
+ groupParallelTools = false,
5194
+ showTaskListFooter = true
5078
5195
  }) {
5079
5196
  const ids = useTimeline();
5080
5197
  if (groupParallelTools) {
5081
- return /* @__PURE__ */ jsx14(Timeline, {});
5198
+ return /* @__PURE__ */ jsx15(Timeline, { showTaskListFooter });
5082
5199
  }
5083
5200
  if (virtualized && ids.length > virtualizeThreshold) {
5084
- return /* @__PURE__ */ jsx14(VirtualTimeline, {});
5201
+ return /* @__PURE__ */ jsx15(VirtualTimeline, { showTaskListFooter });
5085
5202
  }
5086
- return /* @__PURE__ */ jsx14(Timeline, {});
5203
+ return /* @__PURE__ */ jsx15(Timeline, { showTaskListFooter });
5087
5204
  }
5088
5205
 
5089
5206
  export {
@@ -5093,6 +5210,7 @@ export {
5093
5210
  classifyEvent,
5094
5211
  streamChunk,
5095
5212
  reduceTree,
5213
+ reduceAgentLoopEvent,
5096
5214
  reduceEvents,
5097
5215
  getMainStageIds,
5098
5216
  getChildIds,
@@ -5168,4 +5286,4 @@ export {
5168
5286
  VirtualTimeline,
5169
5287
  SessionTimeline
5170
5288
  };
5171
- //# sourceMappingURL=chunk-G2KYJCMM.js.map
5289
+ //# sourceMappingURL=chunk-Q4HRD7EM.js.map