langchain_agentx_stream_ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }
@@ -2277,6 +2324,14 @@ var ExploreCollapseAccumulator = class {
2277
2324
  }
2278
2325
  continue;
2279
2326
  }
2327
+ if (entry.payload.canonicalType === "memory_saved") {
2328
+ group = group ?? this.newGroup();
2329
+ const count = Number(entry.payload.count ?? 1);
2330
+ group.memoryWriteCount += Math.max(1, count);
2331
+ group.sourceNodeIds.push(entry.sourceNodeIds[0] ?? "mem");
2332
+ group.absorbed.push(entry);
2333
+ continue;
2334
+ }
2280
2335
  if (!isToolUseEntry2(entry)) {
2281
2336
  flush();
2282
2337
  result.push(entry);
@@ -3941,9 +3996,72 @@ function TaskItem({ task, allTasks }) {
3941
3996
  }
3942
3997
 
3943
3998
  // src/view/Spinner.tsx
3944
- import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef6, useState as useState6 } from "react";
3999
+ import { useEffect as useEffect7, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
3945
4000
  import { useSyncExternalStore } from "react";
3946
4001
 
4002
+ // src/view/ModelRetryBlock.tsx
4003
+ import { useEffect as useEffect4, useState as useState4 } from "react";
4004
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4005
+ var MAX_ERROR_CHARS = 1e3;
4006
+ function formatRetryCountdownSeconds(retryDelayMs, updatedAt, nowMs) {
4007
+ const elapsed = Math.max(0, nowMs - updatedAt);
4008
+ return Math.max(0, Math.round((retryDelayMs - elapsed) / 1e3));
4009
+ }
4010
+ function ModelRetryBlock({ state, verbose = false }) {
4011
+ const [expanded, setExpanded] = useState4(false);
4012
+ const [nowMs, setNowMs] = useState4(() => Date.now());
4013
+ useEffect4(() => {
4014
+ const timer = window.setInterval(() => setNowMs(Date.now()), 1e3);
4015
+ return () => window.clearInterval(timer);
4016
+ }, []);
4017
+ const retryInSeconds = formatRetryCountdownSeconds(state.retryDelayMs, state.updatedAt, nowMs);
4018
+ const categoryLabel = formatModelRetryCategoryLabel(state.errorCategory);
4019
+ const summary = `Retrying\u2026 (attempt ${state.attempt}/${state.maxRetries})`;
4020
+ const errorText = state.errorMessage.trim();
4021
+ const truncated = !verbose && errorText.length > MAX_ERROR_CHARS;
4022
+ const visibleError = truncated ? `${errorText.slice(0, MAX_ERROR_CHARS)}\u2026` : errorText;
4023
+ return /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry", "data-testid": "lax-model-retry", children: [
4024
+ /* @__PURE__ */ jsx5(
4025
+ "button",
4026
+ {
4027
+ type: "button",
4028
+ className: "lax-model-retry__toggle lax-workflow-expand-trigger",
4029
+ onClick: () => setExpanded((prev) => !prev),
4030
+ "aria-expanded": expanded,
4031
+ "data-testid": "lax-model-retry-toggle",
4032
+ children: /* @__PURE__ */ jsxs3("span", { className: "lax-model-retry__summary", "data-testid": "lax-model-retry-summary", children: [
4033
+ "\u23BF ",
4034
+ summary,
4035
+ !expanded && retryInSeconds > 0 ? /* @__PURE__ */ jsxs3("span", { className: "lax-model-retry__inline-countdown", children: [
4036
+ " \xB7 ",
4037
+ retryInSeconds,
4038
+ "s"
4039
+ ] }) : null
4040
+ ] })
4041
+ }
4042
+ ),
4043
+ expanded ? /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry__body", "data-testid": "lax-model-retry-body", children: [
4044
+ errorText ? /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry__error", "data-testid": "lax-model-retry-error", children: [
4045
+ /* @__PURE__ */ jsx5("span", { className: "lax-model-retry__category", children: categoryLabel }),
4046
+ visibleError ? /* @__PURE__ */ jsx5("pre", { className: "lax-model-retry__error-text", children: visibleError }) : null,
4047
+ truncated ? /* @__PURE__ */ jsx5("span", { className: "lax-model-retry__hint", children: "\u2026 (ctrl+o to expand)" }) : null
4048
+ ] }) : /* @__PURE__ */ jsx5("div", { className: "lax-model-retry__category-only", children: categoryLabel }),
4049
+ /* @__PURE__ */ jsxs3("div", { className: "lax-model-retry__countdown", "data-testid": "lax-model-retry-countdown", children: [
4050
+ "Retrying in ",
4051
+ retryInSeconds,
4052
+ " ",
4053
+ retryInSeconds === 1 ? "second" : "seconds",
4054
+ "\u2026 (attempt",
4055
+ " ",
4056
+ state.attempt,
4057
+ "/",
4058
+ state.maxRetries,
4059
+ ")"
4060
+ ] })
4061
+ ] }) : null
4062
+ ] });
4063
+ }
4064
+
3947
4065
  // src/view/spinner/constants.ts
3948
4066
  var _BASE_FRAMES = ["\xB7", "\u2722", "*", "\u2736", "\u273B", "\u273D"];
3949
4067
  var SPINNER_FRAMES = [..._BASE_FRAMES, ..._BASE_FRAMES.slice().reverse()];
@@ -4174,7 +4292,7 @@ function getSpinnerFrameIndex(timeMs, reducedMotion = false) {
4174
4292
  import { useRef as useRef5 } from "react";
4175
4293
 
4176
4294
  // src/view/spinner/SpinnerGlimmerVerb.tsx
4177
- import { jsxs as jsxs3 } from "react/jsx-runtime";
4295
+ import { jsxs as jsxs4 } from "react/jsx-runtime";
4178
4296
  function SpinnerGlimmerVerb({
4179
4297
  verb,
4180
4298
  mode,
@@ -4204,7 +4322,7 @@ function SpinnerGlimmerVerb({
4204
4322
  ["--lax-spinner-flash-opacity"]: String(flashOpacity)
4205
4323
  }
4206
4324
  };
4207
- return /* @__PURE__ */ jsxs3("span", { className: classes.join(" "), style, children: [
4325
+ return /* @__PURE__ */ jsxs4("span", { className: classes.join(" "), style, children: [
4208
4326
  verb,
4209
4327
  "\u2026"
4210
4328
  ] });
@@ -4254,13 +4372,13 @@ function stepSmoothedTokenCount(current, target, reducedMotion) {
4254
4372
  }
4255
4373
 
4256
4374
  // src/view/spinner/useAnimationFrame.ts
4257
- import { useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
4375
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
4258
4376
  function useAnimationFrame(intervalMs) {
4259
- const [time, setTime] = useState4(0);
4377
+ const [time, setTime] = useState5(0);
4260
4378
  const startRef = useRef3(null);
4261
4379
  const lastTickRef = useRef3(0);
4262
4380
  const rafRef = useRef3(null);
4263
- useEffect4(() => {
4381
+ useEffect5(() => {
4264
4382
  if (intervalMs === null) return;
4265
4383
  startRef.current = null;
4266
4384
  lastTickRef.current = 0;
@@ -4286,14 +4404,14 @@ function useAnimationFrame(intervalMs) {
4286
4404
  }
4287
4405
 
4288
4406
  // src/view/spinner/useReducedMotion.ts
4289
- import { useEffect as useEffect5, useState as useState5 } from "react";
4407
+ import { useEffect as useEffect6, useState as useState6 } from "react";
4290
4408
  var QUERY = "(prefers-reduced-motion: reduce)";
4291
4409
  function useReducedMotion() {
4292
- const [reducedMotion, setReducedMotion] = useState5(() => {
4410
+ const [reducedMotion, setReducedMotion] = useState6(() => {
4293
4411
  if (typeof window === "undefined" || !window.matchMedia) return false;
4294
4412
  return window.matchMedia(QUERY).matches;
4295
4413
  });
4296
- useEffect5(() => {
4414
+ useEffect6(() => {
4297
4415
  if (typeof window === "undefined" || !window.matchMedia) return;
4298
4416
  const media = window.matchMedia(QUERY);
4299
4417
  const onChange = () => setReducedMotion(media.matches);
@@ -4353,7 +4471,7 @@ function useStalledSpinnerIntensity(time, currentResponseLength, hasActiveTools
4353
4471
  }
4354
4472
 
4355
4473
  // src/view/spinner/SpinnerAnimationRow.tsx
4356
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
4474
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4357
4475
  function SpinnerAnimationRow({
4358
4476
  mode,
4359
4477
  verb,
@@ -4412,9 +4530,9 @@ function SpinnerAnimationRow({
4412
4530
  className: thinkingStatus === "thinking" ? "lax-spinner-status-part--thinking" : void 0
4413
4531
  });
4414
4532
  }
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(
4533
+ return /* @__PURE__ */ jsxs5("div", { className: "lax-spinner", "data-testid": "lax-spinner", children: [
4534
+ /* @__PURE__ */ jsx6("span", { className: "lax-spinner-frame", "data-testid": "lax-spinner-frame", children: frame }),
4535
+ /* @__PURE__ */ jsx6(
4418
4536
  SpinnerGlimmerVerb,
4419
4537
  {
4420
4538
  verb,
@@ -4425,9 +4543,9 @@ function SpinnerAnimationRow({
4425
4543
  shimmerActive
4426
4544
  }
4427
4545
  ),
4428
- statusParts.length > 0 && /* @__PURE__ */ jsxs4("span", { className: "lax-spinner-status", children: [
4546
+ statusParts.length > 0 && /* @__PURE__ */ jsxs5("span", { className: "lax-spinner-status", children: [
4429
4547
  " (",
4430
- statusParts.map((part, i) => /* @__PURE__ */ jsxs4(
4548
+ statusParts.map((part, i) => /* @__PURE__ */ jsxs5(
4431
4549
  "span",
4432
4550
  {
4433
4551
  className: `lax-spinner-status-part${part.className ? ` ${part.className}` : ""}`,
@@ -4468,11 +4586,11 @@ function shouldShowNextTaskLine(showTaskList, tasks, sessionStatus) {
4468
4586
  }
4469
4587
 
4470
4588
  // src/view/Spinner.tsx
4471
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4589
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4472
4590
  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 })
4591
+ return /* @__PURE__ */ jsxs6("div", { className: "lax-message-response", children: [
4592
+ /* @__PURE__ */ jsx7("span", { className: "lax-message-response__marker", children: "\u23BF " }),
4593
+ /* @__PURE__ */ jsx7("span", { className: "lax-message-response__content", children })
4476
4594
  ] });
4477
4595
  }
4478
4596
  function shouldShowSpinnerTaskList(taskPhase, tasks, hasEverShownTaskList, contentCharCount = 0) {
@@ -4505,6 +4623,10 @@ function Spinner({
4505
4623
  (callback) => store.subscribe(callback),
4506
4624
  () => store.getState().tree.hookProgress
4507
4625
  );
4626
+ const modelRetry = useSyncExternalStore(
4627
+ (callback) => store.subscribe(callback),
4628
+ () => store.getState().tree.modelRetry
4629
+ );
4508
4630
  const taskListRevision = useSyncExternalStore(
4509
4631
  (callback) => store.subscribe(callback),
4510
4632
  () => {
@@ -4535,10 +4657,10 @@ function Spinner({
4535
4657
  const derivedMode = mode !== "idle" ? mode : activeRoundId ? "thinking" : "requesting";
4536
4658
  const effectiveMode = derivedMode === "tool-use" ? "tool-use" : derivedMode === "thinking" ? "thinking" : "requesting";
4537
4659
  const hasActiveTools = effectiveMode === "tool-use";
4538
- const [fallbackVerb] = useState6(() => pickRandomVerb());
4539
- const [thinkingStatus, setThinkingStatus] = useState6(null);
4660
+ const [fallbackVerb] = useState7(() => pickRandomVerb());
4661
+ const [thinkingStatus, setThinkingStatus] = useState7(null);
4540
4662
  const thinkingStartRef = useRef6(null);
4541
- useEffect6(() => {
4663
+ useEffect7(() => {
4542
4664
  let showDurationTimer = null;
4543
4665
  let clearStatusTimer = null;
4544
4666
  if (derivedMode === "thinking") {
@@ -4587,8 +4709,9 @@ function Spinner({
4587
4709
  } else if (typeof thinkingStatus === "number") {
4588
4710
  thinkingText = `thought for ${Math.max(1, Math.round(thinkingStatus / 1e3))}s`;
4589
4711
  }
4590
- return /* @__PURE__ */ jsxs5("div", { className: "lax-spinner-container", "data-testid": "lax-spinner-container", children: [
4591
- /* @__PURE__ */ jsx6(
4712
+ return /* @__PURE__ */ jsxs6("div", { className: "lax-spinner-container", "data-testid": "lax-spinner-container", children: [
4713
+ modelRetry ? /* @__PURE__ */ jsx7(ModelRetryBlock, { state: modelRetry, verbose }) : null,
4714
+ /* @__PURE__ */ jsx7(
4592
4715
  SpinnerAnimationRow,
4593
4716
  {
4594
4717
  mode: effectiveMode,
@@ -4603,19 +4726,19 @@ function Spinner({
4603
4726
  timing
4604
4727
  }
4605
4728
  ),
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) }) })
4729
+ showTaskList && /* @__PURE__ */ jsx7(MessageResponse, { children: /* @__PURE__ */ jsx7(TaskList, { tasks, isStandalone: false }) }),
4730
+ nextTask && /* @__PURE__ */ jsx7(MessageResponse, { children: /* @__PURE__ */ jsx7("span", { className: "lax-spinner-next-line", children: formatNextTaskLineText(nextTask) }) })
4608
4731
  ] });
4609
4732
  }
4610
4733
 
4611
4734
  // src/view/TaskListFooter.tsx
4612
- import { useMemo as useMemo6, useState as useState7 } from "react";
4735
+ import { useMemo as useMemo6, useState as useState8 } from "react";
4613
4736
  import { useSyncExternalStore as useSyncExternalStore2 } from "react";
4614
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
4737
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4615
4738
  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 })
4739
+ return /* @__PURE__ */ jsxs7("div", { className: "lax-message-response", children: [
4740
+ /* @__PURE__ */ jsx8("span", { className: "lax-message-response__marker", children: "\u23BF " }),
4741
+ /* @__PURE__ */ jsx8("span", { className: "lax-message-response__content", children })
4619
4742
  ] });
4620
4743
  }
4621
4744
  function formatTaskFooterLabel(tasks) {
@@ -4651,7 +4774,7 @@ function readTaskListRevision(store) {
4651
4774
  function TaskListFooter() {
4652
4775
  const store = useSessionStoreApi();
4653
4776
  const sessionStatus = useSessionStatus();
4654
- const [expanded, setExpanded] = useState7(false);
4777
+ const [expanded, setExpanded] = useState8(false);
4655
4778
  const taskListRevision = useSyncExternalStore2(
4656
4779
  (callback) => store.subscribe(callback),
4657
4780
  () => readTaskListRevision(store)
@@ -4674,8 +4797,8 @@ function TaskListFooter() {
4674
4797
  return null;
4675
4798
  }
4676
4799
  const label = formatTaskFooterLabel(tasks);
4677
- return /* @__PURE__ */ jsxs6("div", { className: "lax-task-list-footer", "data-testid": "lax-task-list-footer", children: [
4678
- /* @__PURE__ */ jsxs6(
4800
+ return /* @__PURE__ */ jsxs7("div", { className: "lax-task-list-footer", "data-testid": "lax-task-list-footer", children: [
4801
+ /* @__PURE__ */ jsxs7(
4679
4802
  "button",
4680
4803
  {
4681
4804
  type: "button",
@@ -4685,16 +4808,16 @@ function TaskListFooter() {
4685
4808
  onClick: () => setExpanded((v) => !v),
4686
4809
  children: [
4687
4810
  label,
4688
- /* @__PURE__ */ jsx7("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
4811
+ /* @__PURE__ */ jsx8("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
4689
4812
  ]
4690
4813
  }
4691
4814
  ),
4692
- expanded && /* @__PURE__ */ jsx7(MessageResponse2, { children: /* @__PURE__ */ jsx7(TaskList, { tasks }) })
4815
+ expanded && /* @__PURE__ */ jsx8(MessageResponse2, { children: /* @__PURE__ */ jsx8(TaskList, { tasks }) })
4693
4816
  ] });
4694
4817
  }
4695
4818
 
4696
4819
  // src/view/GroupedToolCallRow.tsx
4697
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4820
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4698
4821
  var INITIALIZING = "Initializing\u2026";
4699
4822
  function readGroupMeta(payload) {
4700
4823
  const raw = payload.groupMeta;
@@ -4748,8 +4871,8 @@ function GroupedToolCallRow({ payload }) {
4748
4871
  const title = groupMeta?.title ?? `${toolName} \xD7 ${toolUses.length}`;
4749
4872
  const anyPending = Array.isArray(payload.pendingCallIds) && payload.pendingCallIds.length > 0;
4750
4873
  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(
4874
+ return /* @__PURE__ */ jsxs8("div", { className: "lax-tool-group", "data-tool-name": toolName, "data-kind": "grouped_tool_use", children: [
4875
+ /* @__PURE__ */ jsxs8(
4753
4876
  "div",
4754
4877
  {
4755
4878
  className: [
@@ -4758,27 +4881,27 @@ function GroupedToolCallRow({ payload }) {
4758
4881
  anyError ? "lax-tool-group__header--error" : ""
4759
4882
  ].filter(Boolean).join(" "),
4760
4883
  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 })
4884
+ anyPending && /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__dot", "aria-hidden": true, children: "\u25CF" }),
4885
+ /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__label", children: title })
4763
4886
  ]
4764
4887
  }
4765
4888
  ),
4766
- /* @__PURE__ */ jsx8("div", { className: "lax-tool-group__items", children: toolUses.map((entry, index) => {
4889
+ /* @__PURE__ */ jsx9("div", { className: "lax-tool-group__items", children: toolUses.map((entry, index) => {
4767
4890
  const callId = String(entry.tool_call_id ?? "");
4768
4891
  const subTitle = formatAgentSublineTitle(entry, hideType);
4769
4892
  const status = sublineStatus(callId, payload);
4770
4893
  const isLast = index === toolUses.length - 1;
4771
4894
  const treeChar = isLast ? "\u2514\u2500" : "\u251C\u2500";
4772
- return /* @__PURE__ */ jsxs7(
4895
+ return /* @__PURE__ */ jsxs8(
4773
4896
  "div",
4774
4897
  {
4775
4898
  className: "lax-tool-group__item lax-tool-group__subline",
4776
4899
  "data-group-index": index + 1,
4777
4900
  "data-tool-call-id": callId,
4778
4901
  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: [
4902
+ /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__tree", "aria-hidden": true, children: treeChar }),
4903
+ /* @__PURE__ */ jsx9("span", { className: "lax-tool-group__subline-title", children: subTitle }),
4904
+ status && /* @__PURE__ */ jsxs8("span", { className: "lax-tool-group__subline-status", children: [
4782
4905
  " \xB7 ",
4783
4906
  status
4784
4907
  ] })
@@ -4791,7 +4914,7 @@ function GroupedToolCallRow({ payload }) {
4791
4914
  }
4792
4915
 
4793
4916
  // src/view/nodes/ThinkingSummaryNode.tsx
4794
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4917
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4795
4918
  function formatThoughtDuration(seconds) {
4796
4919
  const rounded = Math.max(1, Math.round(seconds));
4797
4920
  return `${rounded}s`;
@@ -4816,29 +4939,29 @@ function ThinkingSummary({ node }) {
4816
4939
  const viewOptions = useSessionViewOptions();
4817
4940
  const showToolDetail = shouldShowReasoningInTimeline(viewOptions);
4818
4941
  const toolText = showToolDetail ? formatToolCounts(node) : null;
4819
- return /* @__PURE__ */ jsxs8(
4942
+ return /* @__PURE__ */ jsxs9(
4820
4943
  "div",
4821
4944
  {
4822
4945
  className: "lax-thought-summary",
4823
4946
  "data-kind": "thinking_summary",
4824
4947
  "data-round-id": node.roundId,
4825
4948
  children: [
4826
- /* @__PURE__ */ jsxs8("span", { className: "lax-thought-summary__text", children: [
4949
+ /* @__PURE__ */ jsxs9("span", { className: "lax-thought-summary__text", children: [
4827
4950
  "Thought for ",
4828
4951
  formatThoughtDuration(node.elapsedSeconds)
4829
4952
  ] }),
4830
- toolText ? /* @__PURE__ */ jsxs8("span", { className: "lax-thought-summary__tools", children: [
4953
+ toolText ? /* @__PURE__ */ jsxs9("span", { className: "lax-thought-summary__tools", children: [
4831
4954
  " \xB7 ",
4832
4955
  toolText
4833
4956
  ] }) : null,
4834
- !showToolDetail ? /* @__PURE__ */ jsx9("span", { className: "lax-thought-summary__hint", children: " (ctrl+o to expand)" }) : null
4957
+ !showToolDetail ? /* @__PURE__ */ jsx10("span", { className: "lax-thought-summary__hint", children: " (ctrl+o to expand)" }) : null
4835
4958
  ]
4836
4959
  }
4837
4960
  );
4838
4961
  }
4839
4962
 
4840
4963
  // src/view/projectedTimelineRender.tsx
4841
- import { jsx as jsx10 } from "react/jsx-runtime";
4964
+ import { jsx as jsx11 } from "react/jsx-runtime";
4842
4965
  function zeroToolCounts2() {
4843
4966
  return {
4844
4967
  searchCount: 0,
@@ -4866,39 +4989,39 @@ function ThinkingSummaryWrapper({ entry }) {
4866
4989
  isExpanded: Boolean(payload.isExpanded ?? false),
4867
4990
  startedAt: Number(payload.startedAt ?? Date.now())
4868
4991
  };
4869
- return /* @__PURE__ */ jsx10("div", { className: "lax-timeline__item", "data-kind": "thinking_summary", children: /* @__PURE__ */ jsx10(ThinkingSummary, { node }) });
4992
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "thinking_summary", children: /* @__PURE__ */ jsx11(ThinkingSummary, { node }) });
4870
4993
  }
4871
4994
  function renderProjectedTimelineItem(item, registry) {
4872
4995
  const { key, entry } = item;
4873
4996
  if (entry.kind === "thinking_summary") {
4874
- return /* @__PURE__ */ jsx10(ThinkingSummaryWrapper, { entry }, key);
4997
+ return /* @__PURE__ */ jsx11(ThinkingSummaryWrapper, { entry }, key);
4875
4998
  }
4876
4999
  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);
5000
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "collapsed_explore", children: /* @__PURE__ */ jsx11(CollapsedExploreNode, { entry, registry }) }, key);
4878
5001
  }
4879
5002
  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);
5003
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "grouped_tool_use", children: /* @__PURE__ */ jsx11(GroupedToolCallRow, { payload: entry.payload }) }, key);
4881
5004
  }
4882
5005
  if (entry.kind === "system_summary") {
4883
5006
  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);
5007
+ return /* @__PURE__ */ jsx11("div", { className: "lax-timeline__item", "data-kind": "system_summary", children: /* @__PURE__ */ jsx11(SystemSummaryNode, { summaryText: summary }) }, key);
4885
5008
  }
4886
5009
  const nodeId = entry.sourceNodeIds[0];
4887
5010
  if (!nodeId) return null;
4888
- return /* @__PURE__ */ jsx10(TimelineItem, { nodeId, registry }, key);
5011
+ return /* @__PURE__ */ jsx11(TimelineItem, { nodeId, registry }, key);
4889
5012
  }
4890
5013
 
4891
5014
  // src/view/TimelineEntries.tsx
4892
- import { Fragment as Fragment2, jsx as jsx11 } from "react/jsx-runtime";
5015
+ import { Fragment as Fragment2, jsx as jsx12 } from "react/jsx-runtime";
4893
5016
  function TimelineEntries({ registry }) {
4894
5017
  const projectedItems = useProjectedTimeline();
4895
- return /* @__PURE__ */ jsx11(Fragment2, { children: projectedItems.map((item) => renderProjectedTimelineItem(item, registry)) });
5018
+ return /* @__PURE__ */ jsx12(Fragment2, { children: projectedItems.map((item) => renderProjectedTimelineItem(item, registry)) });
4896
5019
  }
4897
5020
 
4898
5021
  // src/view/Timeline.tsx
4899
5022
  import { useStore as useStore6 } from "zustand";
4900
5023
  import { useShallow as useShallow5 } from "zustand/shallow";
4901
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
5024
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
4902
5025
  function useContentCharCount() {
4903
5026
  const store = useSessionStoreApi();
4904
5027
  return useStore6(
@@ -4914,15 +5037,15 @@ function useContentCharCount() {
4914
5037
  })
4915
5038
  );
4916
5039
  }
4917
- function Timeline() {
5040
+ function Timeline({ showTaskListFooter = true }) {
4918
5041
  const registry = useNodeRegistry();
4919
5042
  const store = useSessionStoreApi();
4920
5043
  const { verbose } = useSessionViewOptions();
4921
5044
  const { startedAt } = useStore6(store, (s) => s.tree.meta);
4922
5045
  const contentCharCount = useContentCharCount();
4923
- return /* @__PURE__ */ jsxs9("div", { className: "lax-timeline", "data-testid": "lax-timeline", children: [
4924
- /* @__PURE__ */ jsx12(TimelineEntries, { registry }),
4925
- /* @__PURE__ */ jsx12(
5046
+ return /* @__PURE__ */ jsxs10("div", { className: "lax-timeline", "data-testid": "lax-timeline", children: [
5047
+ /* @__PURE__ */ jsx13(TimelineEntries, { registry }),
5048
+ /* @__PURE__ */ jsx13(
4926
5049
  Spinner,
4927
5050
  {
4928
5051
  startedAt,
@@ -4930,7 +5053,7 @@ function Timeline() {
4930
5053
  verbose
4931
5054
  }
4932
5055
  ),
4933
- /* @__PURE__ */ jsx12(TaskListFooter, {})
5056
+ showTaskListFooter ? /* @__PURE__ */ jsx13(TaskListFooter, {}) : null
4934
5057
  ] });
4935
5058
  }
4936
5059
 
@@ -4982,7 +5105,7 @@ function estimateProjectedEntrySize(entry, fallback = DEFAULT_ESTIMATE_SIZE) {
4982
5105
  import { useRef as useRef7 } from "react";
4983
5106
  import { useVirtualizer } from "@tanstack/react-virtual";
4984
5107
  import { useStore as useStore7 } from "zustand";
4985
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
5108
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4986
5109
  var DEFAULT_VIRTUALIZE_THRESHOLD = 500;
4987
5110
  function useContentCharCount2() {
4988
5111
  const store = useSessionStoreApi();
@@ -4997,7 +5120,8 @@ function useContentCharCount2() {
4997
5120
  });
4998
5121
  }
4999
5122
  function VirtualTimeline({
5000
- estimateSize = DEFAULT_ESTIMATE_SIZE
5123
+ estimateSize = DEFAULT_ESTIMATE_SIZE,
5124
+ showTaskListFooter = true
5001
5125
  }) {
5002
5126
  const items = useProjectedTimeline();
5003
5127
  const registry = useNodeRegistry();
@@ -5015,14 +5139,14 @@ function VirtualTimeline({
5015
5139
  },
5016
5140
  overscan: 5
5017
5141
  });
5018
- return /* @__PURE__ */ jsxs10(
5142
+ return /* @__PURE__ */ jsxs11(
5019
5143
  "div",
5020
5144
  {
5021
5145
  ref: parentRef,
5022
5146
  className: "lax-virtual-timeline",
5023
5147
  "data-testid": "lax-virtual-timeline",
5024
5148
  children: [
5025
- /* @__PURE__ */ jsx13(
5149
+ /* @__PURE__ */ jsx14(
5026
5150
  "div",
5027
5151
  {
5028
5152
  className: "lax-virtual-timeline__inner",
@@ -5034,7 +5158,7 @@ function VirtualTimeline({
5034
5158
  children: virtualizer.getVirtualItems().map((virtualRow) => {
5035
5159
  const item = items[virtualRow.index];
5036
5160
  if (!item) return null;
5037
- return /* @__PURE__ */ jsx13(
5161
+ return /* @__PURE__ */ jsx14(
5038
5162
  "div",
5039
5163
  {
5040
5164
  className: "lax-virtual-timeline__item lax-timeline__item",
@@ -5055,7 +5179,7 @@ function VirtualTimeline({
5055
5179
  })
5056
5180
  }
5057
5181
  ),
5058
- /* @__PURE__ */ jsx13(
5182
+ /* @__PURE__ */ jsx14(
5059
5183
  Spinner,
5060
5184
  {
5061
5185
  startedAt,
@@ -5063,27 +5187,28 @@ function VirtualTimeline({
5063
5187
  verbose
5064
5188
  }
5065
5189
  ),
5066
- /* @__PURE__ */ jsx13(TaskListFooter, {})
5190
+ showTaskListFooter ? /* @__PURE__ */ jsx14(TaskListFooter, {}) : null
5067
5191
  ]
5068
5192
  }
5069
5193
  );
5070
5194
  }
5071
5195
 
5072
5196
  // src/view/SessionTimeline.tsx
5073
- import { jsx as jsx14 } from "react/jsx-runtime";
5197
+ import { jsx as jsx15 } from "react/jsx-runtime";
5074
5198
  function SessionTimeline({
5075
5199
  virtualized = false,
5076
5200
  virtualizeThreshold = DEFAULT_VIRTUALIZE_THRESHOLD,
5077
- groupParallelTools = false
5201
+ groupParallelTools = false,
5202
+ showTaskListFooter = true
5078
5203
  }) {
5079
5204
  const ids = useTimeline();
5080
5205
  if (groupParallelTools) {
5081
- return /* @__PURE__ */ jsx14(Timeline, {});
5206
+ return /* @__PURE__ */ jsx15(Timeline, { showTaskListFooter });
5082
5207
  }
5083
5208
  if (virtualized && ids.length > virtualizeThreshold) {
5084
- return /* @__PURE__ */ jsx14(VirtualTimeline, {});
5209
+ return /* @__PURE__ */ jsx15(VirtualTimeline, { showTaskListFooter });
5085
5210
  }
5086
- return /* @__PURE__ */ jsx14(Timeline, {});
5211
+ return /* @__PURE__ */ jsx15(Timeline, { showTaskListFooter });
5087
5212
  }
5088
5213
 
5089
5214
  export {
@@ -5093,6 +5218,7 @@ export {
5093
5218
  classifyEvent,
5094
5219
  streamChunk,
5095
5220
  reduceTree,
5221
+ reduceAgentLoopEvent,
5096
5222
  reduceEvents,
5097
5223
  getMainStageIds,
5098
5224
  getChildIds,
@@ -5168,4 +5294,4 @@ export {
5168
5294
  VirtualTimeline,
5169
5295
  SessionTimeline
5170
5296
  };
5171
- //# sourceMappingURL=chunk-G2KYJCMM.js.map
5297
+ //# sourceMappingURL=chunk-3B4WC67E.js.map