@runtypelabs/cli 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -990,6 +990,9 @@ var require_generated_model_routing = __commonJS({
990
990
  "grok-4-fast-reasoning": ["vercel"],
991
991
  "grok-4.1-fast-non-reasoning": ["vercel"],
992
992
  "grok-4.1-fast-reasoning": ["vercel"],
993
+ "grok-4.20-multi-agent-beta": ["vercel"],
994
+ "grok-4.20-non-reasoning-beta": ["vercel"],
995
+ "grok-4.20-reasoning-beta": ["vercel"],
993
996
  "grok-beta": ["xai"],
994
997
  "grok-code-fast-1": ["vercel", "xai"],
995
998
  "grok-vision-beta": ["xai"],
@@ -1030,6 +1033,7 @@ var require_generated_model_routing = __commonJS({
1030
1033
  "Llama-4-Scout-17B-16E-Instruct": ["togetherai"],
1031
1034
  "longcat-flash-chat": ["vercel"],
1032
1035
  "longcat-flash-thinking": ["vercel"],
1036
+ "longcat-flash-thinking-2601": ["vercel"],
1033
1037
  "magistral-medium": ["vercel"],
1034
1038
  "magistral-small": ["vercel"],
1035
1039
  "mercury-2": ["vercel"],
@@ -1115,7 +1119,6 @@ var require_generated_model_routing = __commonJS({
1115
1119
  "openchat-3.5-1210": ["togetherai"],
1116
1120
  "OpenHermes-2-Mistral-7B": ["togetherai"],
1117
1121
  "OpenHermes-2p5-Mistral-7B": ["togetherai"],
1118
- "orchestrator-8b": ["mixlayer"],
1119
1122
  "phi-2": ["togetherai"],
1120
1123
  "pixtral-12b": ["vercel"],
1121
1124
  "pixtral-large": ["vercel"],
@@ -1140,10 +1143,6 @@ var require_generated_model_routing = __commonJS({
1140
1143
  "Qwen2.5-Coder-32B-Instruct": ["togetherai"],
1141
1144
  "Qwen2.5-VL-72B-Instruct": ["togetherai"],
1142
1145
  "qwen3-235b-a22b-thinking": ["vercel"],
1143
- "qwen3-30b-a3b-instruct": ["mixlayer"],
1144
- "qwen3-30b-a3b-thinking": ["mixlayer"],
1145
- "qwen3-32b": ["mixlayer"],
1146
- "qwen3-8b": ["mixlayer"],
1147
1146
  "qwen3-coder": ["vercel"],
1148
1147
  "qwen3-coder-30b-a3b": ["vercel"],
1149
1148
  "qwen3-coder-next": ["vercel"],
@@ -1158,6 +1157,8 @@ var require_generated_model_routing = __commonJS({
1158
1157
  "qwen3-next-80b-a3b-thinking": ["vercel"],
1159
1158
  "qwen3-vl-instruct": ["vercel"],
1160
1159
  "qwen3-vl-thinking": ["vercel"],
1160
+ "qwen3.5-122b-a10b": ["mixlayer"],
1161
+ "qwen3.5-27b": ["mixlayer"],
1161
1162
  "qwen3.5-35b-a3b": ["mixlayer"],
1162
1163
  "qwen3.5-397b-a17b": ["mixlayer"],
1163
1164
  "qwen3.5-9b": ["mixlayer"],
@@ -1432,6 +1433,9 @@ var require_generated_model_routing = __commonJS({
1432
1433
  "grok-4": ["vercel", "xai"],
1433
1434
  "grok-4-1-fast-non-reasoning": ["vercel"],
1434
1435
  "grok-4-1-fast-reasoning": ["vercel"],
1436
+ "grok-4-20-multi-agent-beta": ["vercel"],
1437
+ "grok-4-20-non-reasoning-beta": ["vercel"],
1438
+ "grok-4-20-reasoning-beta": ["vercel"],
1435
1439
  "grok-4-fast": ["xai"],
1436
1440
  "grok-4-fast-non-reasoning": ["vercel"],
1437
1441
  "grok-4-fast-reasoning": ["vercel"],
@@ -1467,6 +1471,7 @@ var require_generated_model_routing = __commonJS({
1467
1471
  "llama3-3-70b": ["vercel"],
1468
1472
  "longcat-flash": ["vercel"],
1469
1473
  "longcat-flash-chat": ["vercel"],
1474
+ "longcat-flash-thinking-2601": ["vercel"],
1470
1475
  "magistral-medium": ["vercel"],
1471
1476
  "magistral-small": ["vercel"],
1472
1477
  "mercury-2": ["vercel"],
@@ -1552,7 +1557,6 @@ var require_generated_model_routing = __commonJS({
1552
1557
  "openchat-3-5-1210": ["togetherai"],
1553
1558
  "openhermes-2-mistral-7b": ["togetherai"],
1554
1559
  "openhermes-2p5-mistral-7b": ["togetherai"],
1555
- "orchestrator-8b": ["mixlayer"],
1556
1560
  "phi-2": ["togetherai"],
1557
1561
  "pixtral-12b": ["vercel"],
1558
1562
  "pixtral-large": ["vercel"],
@@ -1576,15 +1580,14 @@ var require_generated_model_routing = __commonJS({
1576
1580
  "qwen2-5-coder-32b-instruct": ["togetherai"],
1577
1581
  "qwen2-5-vl-72b-instruct": ["togetherai"],
1578
1582
  "qwen3-235b-a22b": ["vercel"],
1579
- "qwen3-30b-a3b": ["mixlayer"],
1580
- "qwen3-30b-a3b-instruct": ["mixlayer"],
1581
- "qwen3-32b": ["mixlayer", "vercel"],
1583
+ "qwen3-32b": ["vercel"],
1584
+ "qwen3-5-122b-a10b": ["mixlayer"],
1585
+ "qwen3-5-27b": ["mixlayer"],
1582
1586
  "qwen3-5-35b-a3b": ["mixlayer"],
1583
1587
  "qwen3-5-397b-a17b": ["mixlayer"],
1584
1588
  "qwen3-5-9b": ["mixlayer"],
1585
1589
  "qwen3-5-flash": ["vercel"],
1586
1590
  "qwen3-5-plus": ["vercel"],
1587
- "qwen3-8b": ["mixlayer"],
1588
1591
  "qwen3-coder": ["vercel"],
1589
1592
  "qwen3-coder-30b-a3b": ["vercel"],
1590
1593
  "qwen3-coder-next": ["vercel"],
@@ -2649,6 +2652,15 @@ var require_generated_model_routing = __commonJS({
2649
2652
  "grok-4-1-fast-reasoning": {
2650
2653
  "vercel": "xai/grok-4.1-fast-reasoning"
2651
2654
  },
2655
+ "grok-4-20-multi-agent-beta": {
2656
+ "vercel": "xai/grok-4.20-multi-agent-beta"
2657
+ },
2658
+ "grok-4-20-non-reasoning-beta": {
2659
+ "vercel": "xai/grok-4.20-non-reasoning-beta"
2660
+ },
2661
+ "grok-4-20-reasoning-beta": {
2662
+ "vercel": "xai/grok-4.20-reasoning-beta"
2663
+ },
2652
2664
  "grok-4-fast": {
2653
2665
  "xai": "x/grok-4-fast"
2654
2666
  },
@@ -2664,6 +2676,15 @@ var require_generated_model_routing = __commonJS({
2664
2676
  "grok-4.1-fast-reasoning": {
2665
2677
  "vercel": "xai/grok-4.1-fast-reasoning"
2666
2678
  },
2679
+ "grok-4.20-multi-agent-beta": {
2680
+ "vercel": "xai/grok-4.20-multi-agent-beta"
2681
+ },
2682
+ "grok-4.20-non-reasoning-beta": {
2683
+ "vercel": "xai/grok-4.20-non-reasoning-beta"
2684
+ },
2685
+ "grok-4.20-reasoning-beta": {
2686
+ "vercel": "xai/grok-4.20-reasoning-beta"
2687
+ },
2667
2688
  "grok-beta": {
2668
2689
  "xai": "grok-beta"
2669
2690
  },
@@ -2830,6 +2851,9 @@ var require_generated_model_routing = __commonJS({
2830
2851
  "longcat-flash-thinking": {
2831
2852
  "vercel": "meituan/longcat-flash-thinking"
2832
2853
  },
2854
+ "longcat-flash-thinking-2601": {
2855
+ "vercel": "meituan/longcat-flash-thinking-2601"
2856
+ },
2833
2857
  "magistral-medium": {
2834
2858
  "vercel": "mistral/magistral-medium"
2835
2859
  },
@@ -3195,9 +3219,6 @@ var require_generated_model_routing = __commonJS({
3195
3219
  "OpenHermes-2p5-Mistral-7B": {
3196
3220
  "togetherai": "teknium/OpenHermes-2p5-Mistral-7B"
3197
3221
  },
3198
- "orchestrator-8b": {
3199
- "mixlayer": "nvidia/orchestrator-8b"
3200
- },
3201
3222
  "phi-2": {
3202
3223
  "togetherai": "microsoft/phi-2"
3203
3224
  },
@@ -3321,19 +3342,15 @@ var require_generated_model_routing = __commonJS({
3321
3342
  "qwen3-235b-a22b-thinking": {
3322
3343
  "vercel": "alibaba/qwen3-235b-a22b-thinking"
3323
3344
  },
3324
- "qwen3-30b-a3b": {
3325
- "mixlayer": "qwen/qwen3-30b-a3b-thinking"
3326
- },
3327
- "qwen3-30b-a3b-instruct": {
3328
- "mixlayer": "qwen/qwen3-30b-a3b-instruct"
3329
- },
3330
- "qwen3-30b-a3b-thinking": {
3331
- "mixlayer": "qwen/qwen3-30b-a3b-thinking"
3332
- },
3333
3345
  "qwen3-32b": {
3334
- "mixlayer": "qwen/qwen3-32b",
3335
3346
  "vercel": "alibaba/qwen-3-32b"
3336
3347
  },
3348
+ "qwen3-5-122b-a10b": {
3349
+ "mixlayer": "qwen/qwen3.5-122b-a10b"
3350
+ },
3351
+ "qwen3-5-27b": {
3352
+ "mixlayer": "qwen/qwen3.5-27b"
3353
+ },
3337
3354
  "qwen3-5-35b-a3b": {
3338
3355
  "mixlayer": "qwen/qwen3.5-35b-a3b"
3339
3356
  },
@@ -3349,9 +3366,6 @@ var require_generated_model_routing = __commonJS({
3349
3366
  "qwen3-5-plus": {
3350
3367
  "vercel": "alibaba/qwen3.5-plus"
3351
3368
  },
3352
- "qwen3-8b": {
3353
- "mixlayer": "qwen/qwen3-8b"
3354
- },
3355
3369
  "qwen3-coder": {
3356
3370
  "vercel": "alibaba/qwen3-coder"
3357
3371
  },
@@ -3403,6 +3417,12 @@ var require_generated_model_routing = __commonJS({
3403
3417
  "qwen3-vl-thinking": {
3404
3418
  "vercel": "alibaba/qwen3-vl-thinking"
3405
3419
  },
3420
+ "qwen3.5-122b-a10b": {
3421
+ "mixlayer": "qwen/qwen3.5-122b-a10b"
3422
+ },
3423
+ "qwen3.5-27b": {
3424
+ "mixlayer": "qwen/qwen3.5-27b"
3425
+ },
3406
3426
  "qwen3.5-35b-a3b": {
3407
3427
  "mixlayer": "qwen/qwen3.5-35b-a3b"
3408
3428
  },
@@ -8327,9 +8347,8 @@ import { useState as useState22, useEffect as useEffect20, useRef as useRef8, us
8327
8347
  import { execSync } from "child_process";
8328
8348
  import * as fs4 from "fs";
8329
8349
  import open4 from "open";
8330
- import { Box as Box26, Text as Text28, useApp as useApp4, useInput as useInput11, useStdout as useStdout5 } from "ink";
8331
- import { StreamOutput, StatusBar, ErrorDisplay as ErrorDisplay3, theme as theme30 } from "@runtypelabs/ink-components";
8332
- import { LoadingAnimation } from "@runtypelabs/terminal-animations";
8350
+ import { Box as Box28, Text as Text28, useApp as useApp4, useInput as useInput11, useStdout as useStdout5 } from "ink";
8351
+ import { StatusBar, theme as theme31 } from "@runtypelabs/ink-components";
8333
8352
 
8334
8353
  // src/ink/marathon/useMarathonStream.ts
8335
8354
  import { useState as useState10, useRef as useRef3, useMemo, useCallback as useCallback2 } from "react";
@@ -8382,21 +8401,155 @@ var INITIAL_STATE = {
8382
8401
  contextCompaction: null
8383
8402
  };
8384
8403
  var MAX_RAW_EVENTS = 15e3;
8404
+ var TOOL_PREVIEW_FIELD_LIMIT = 2;
8405
+ var TOOL_PREVIEW_VALUE_MAX_CHARS = 48;
8406
+ var TOOL_PREVIEW_MAX_CHARS = 120;
8407
+ var HEAVY_PREVIEW_KEYS = /* @__PURE__ */ new Set([
8408
+ "content",
8409
+ "code",
8410
+ "script",
8411
+ "body",
8412
+ "prompt",
8413
+ "input",
8414
+ "stdout",
8415
+ "stderr",
8416
+ "output",
8417
+ "text",
8418
+ "diff",
8419
+ "patch",
8420
+ "html",
8421
+ "markdown"
8422
+ ]);
8423
+ var HEAVY_EVENT_FIELDS = ["parameters", "result", "finalOutput"];
8424
+ var INLINE_TOOL_NAMES = /* @__PURE__ */ new Set(["write_file", "read_file"]);
8425
+ var MAX_INLINE_TOOL_STRING_CHARS = 160;
8426
+ var MAX_INLINE_TOOL_KEYS = 6;
8385
8427
  function appendContentLine(content, line) {
8386
8428
  if (!content) return line;
8387
8429
  const separator = content.endsWith("\n") ? "" : "\n";
8388
8430
  return `${content}${separator}${line}`;
8389
8431
  }
8390
- function pushRawEvent(prev, type, data, writer) {
8391
- const event = { timestamp: Date.now(), type, data };
8392
- if (writer?.current) {
8393
- try {
8394
- writer.current(event);
8395
- } catch {
8432
+ function truncatePreviewText(text, maxChars = TOOL_PREVIEW_VALUE_MAX_CHARS) {
8433
+ if (text.length <= maxChars) return text;
8434
+ return `${text.slice(0, Math.max(0, maxChars - 1))}\u2026`;
8435
+ }
8436
+ function clampPreview(preview) {
8437
+ if (preview.length <= TOOL_PREVIEW_MAX_CHARS) return preview;
8438
+ return `${preview.slice(0, Math.max(0, TOOL_PREVIEW_MAX_CHARS - 1))}\u2026`;
8439
+ }
8440
+ function isLargeToolValue(value) {
8441
+ if (typeof value === "string") return value.length > MAX_INLINE_TOOL_STRING_CHARS;
8442
+ if (Array.isArray(value)) {
8443
+ return value.length > 8 || value.some((item) => item != null && typeof item === "object");
8444
+ }
8445
+ if (value && typeof value === "object") {
8446
+ return Object.keys(value).length > MAX_INLINE_TOOL_KEYS;
8447
+ }
8448
+ return false;
8449
+ }
8450
+ function shouldKeepToolPayloadInline(toolName, payload) {
8451
+ if (payload == null) return false;
8452
+ if (INLINE_TOOL_NAMES.has(toolName)) return true;
8453
+ if (typeof payload === "string") return payload.length <= MAX_INLINE_TOOL_STRING_CHARS;
8454
+ if (typeof payload !== "object") return true;
8455
+ const entries = Object.entries(payload);
8456
+ if (entries.length > MAX_INLINE_TOOL_KEYS) return false;
8457
+ return entries.every(([key, value]) => !HEAVY_PREVIEW_KEYS.has(key) && !isLargeToolValue(value));
8458
+ }
8459
+ function buildPersistedEventData(data, listData) {
8460
+ if (!listData) return data;
8461
+ const omittedFields = HEAVY_EVENT_FIELDS.filter((field) => data[field] !== void 0);
8462
+ if (omittedFields.length === 0) return data;
8463
+ return {
8464
+ ...listData,
8465
+ omittedFields
8466
+ };
8467
+ }
8468
+ function setStoredToolPayloadField(payloads, toolId, field, value) {
8469
+ const next = { ...payloads.get(toolId) ?? {} };
8470
+ if (value === void 0) {
8471
+ delete next[field];
8472
+ } else {
8473
+ next[field] = value;
8474
+ }
8475
+ if (next.parameters === void 0 && next.result === void 0 && next.streamedInput === void 0) {
8476
+ payloads.delete(toolId);
8477
+ } else {
8478
+ payloads.set(toolId, next);
8479
+ }
8480
+ }
8481
+ function prioritizePreviewEntries(entries) {
8482
+ const light = [];
8483
+ const heavy = [];
8484
+ for (const entry of entries) {
8485
+ if (HEAVY_PREVIEW_KEYS.has(entry[0])) {
8486
+ heavy.push(entry);
8487
+ } else {
8488
+ light.push(entry);
8489
+ }
8490
+ }
8491
+ return [...light, ...heavy].slice(0, TOOL_PREVIEW_FIELD_LIMIT);
8492
+ }
8493
+ function stringifyPreviewValue(value) {
8494
+ if (typeof value === "string") return JSON.stringify(truncatePreviewText(value));
8495
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
8496
+ if (value === null) return "null";
8497
+ if (Array.isArray(value)) return "[...]";
8498
+ if (typeof value === "object") return "{...}";
8499
+ return JSON.stringify(truncatePreviewText(String(value)));
8500
+ }
8501
+ function buildPreviewFromParameters(parameters) {
8502
+ if (!parameters) return void 0;
8503
+ const entries = Object.entries(parameters).filter(([, value]) => value !== void 0);
8504
+ if (entries.length === 0) return void 0;
8505
+ return clampPreview(prioritizePreviewEntries(entries).map(([key, value]) => `${key}=${stringifyPreviewValue(value)}`).join(" "));
8506
+ }
8507
+ function buildPreviewFromStreamedInput(input) {
8508
+ const scanWindow = input.length > 500 ? input.slice(0, 500) : input;
8509
+ const matches = Array.from(
8510
+ scanWindow.matchAll(/"([^"]+)"\s*:\s*("[^"\\]*(?:\\.[^"\\]*)*"|true|false|null|-?\d+(?:\.\d+)?)/g)
8511
+ );
8512
+ if (matches.length === 0) return void 0;
8513
+ return clampPreview(prioritizePreviewEntries(matches.map((match) => {
8514
+ const [, key, rawValue] = match;
8515
+ let value = rawValue;
8516
+ if (value.startsWith('"')) {
8517
+ try {
8518
+ value = JSON.stringify(truncatePreviewText(JSON.parse(value)));
8519
+ } catch {
8520
+ value = truncatePreviewText(value);
8521
+ }
8396
8522
  }
8523
+ return [key, value];
8524
+ })).map((match) => {
8525
+ const [key, value] = match;
8526
+ return `${key}=${value}`;
8527
+ }).join(" "));
8528
+ }
8529
+ function buildToolPreview(params) {
8530
+ if (params.existingPreview && params.streamedInput) return params.existingPreview;
8531
+ return (params.streamedInput ? buildPreviewFromStreamedInput(params.streamedInput) : void 0) || buildPreviewFromParameters(params.parameters);
8532
+ }
8533
+ function pushRawEvent(eventsRef, type, data, writer, listData) {
8534
+ const event = { timestamp: Date.now(), type, data, ...listData ? { listData } : {} };
8535
+ if (writer?.current) {
8536
+ const w = writer.current;
8537
+ const jsonlEvent = {
8538
+ timestamp: event.timestamp,
8539
+ type: event.type,
8540
+ data: buildPersistedEventData(event.data, listData)
8541
+ };
8542
+ setTimeout(() => {
8543
+ try {
8544
+ w(jsonlEvent);
8545
+ } catch {
8546
+ }
8547
+ }, 0);
8548
+ }
8549
+ eventsRef.current.push(event);
8550
+ if (eventsRef.current.length > MAX_RAW_EVENTS) {
8551
+ eventsRef.current = eventsRef.current.slice(-MAX_RAW_EVENTS);
8397
8552
  }
8398
- const events = [...prev.rawEvents, event];
8399
- return events.length > MAX_RAW_EVENTS ? events.slice(-MAX_RAW_EVENTS) : events;
8400
8553
  }
8401
8554
  function formatContextCompactionCompleteLine(event) {
8402
8555
  const modeLabel = event.mode === "auto" ? "automatically" : "with compact mode";
@@ -8418,27 +8571,57 @@ function formatContextNoticeLine(event) {
8418
8571
  var toolCounter = 0;
8419
8572
  function useMarathonStream() {
8420
8573
  const [state, setState] = useState10(INITIAL_STATE);
8574
+ const rawEventsRef = useRef3([]);
8421
8575
  const stateRef = useRef3(state);
8422
8576
  stateRef.current = state;
8423
8577
  const eventWriterRef = useRef3(null);
8578
+ const toolPayloadsRef = useRef3(/* @__PURE__ */ new Map());
8579
+ const hydrateToolEntry = useCallback2((tool) => {
8580
+ const payload = toolPayloadsRef.current.get(tool.id);
8581
+ if (!payload) return tool;
8582
+ return {
8583
+ ...tool,
8584
+ ...payload.parameters !== void 0 ? { parameters: payload.parameters } : {},
8585
+ ...payload.result !== void 0 ? { result: payload.result } : {},
8586
+ ...payload.streamedInput !== void 0 ? { streamedInput: payload.streamedInput } : {}
8587
+ };
8588
+ }, []);
8589
+ const getHydratedState = useCallback2(
8590
+ () => ({
8591
+ ...stateRef.current,
8592
+ tools: stateRef.current.tools.map((tool) => hydrateToolEntry(tool)),
8593
+ rawEvents: rawEventsRef.current
8594
+ }),
8595
+ [hydrateToolEntry]
8596
+ );
8424
8597
  const callbacks = useMemo(
8425
8598
  () => ({
8426
8599
  onAgentStart() {
8600
+ pushRawEvent(rawEventsRef, "agent_start", {}, eventWriterRef);
8427
8601
  setState((prev) => ({
8428
8602
  ...prev,
8429
8603
  phase: "thinking",
8430
- thinkingStartedAt: Date.now(),
8431
- rawEvents: pushRawEvent(prev, "agent_start", {}, eventWriterRef)
8604
+ thinkingStartedAt: Date.now()
8432
8605
  }));
8433
8606
  },
8434
8607
  onIterationStart(event) {
8608
+ pushRawEvent(rawEventsRef, "agent_iteration_start", { ...event }, eventWriterRef);
8435
8609
  setState((prev) => ({
8436
8610
  ...prev,
8437
- currentIteration: event.iteration,
8438
- rawEvents: pushRawEvent(prev, "agent_iteration_start", { ...event }, eventWriterRef)
8611
+ currentIteration: event.iteration
8439
8612
  }));
8440
8613
  },
8441
8614
  onTurnDelta(event) {
8615
+ pushRawEvent(
8616
+ rawEventsRef,
8617
+ "agent_turn_delta",
8618
+ {
8619
+ contentType: event.contentType,
8620
+ delta: event.delta,
8621
+ deltaLength: event.delta.length
8622
+ },
8623
+ eventWriterRef
8624
+ );
8442
8625
  setState((prev) => ({
8443
8626
  ...prev,
8444
8627
  ...event.contentType === "text" ? {
@@ -8446,29 +8629,70 @@ function useMarathonStream() {
8446
8629
  content: appendTranscriptText(prev.content, event.delta, prev.lastTranscriptKind),
8447
8630
  lastTranscriptKind: "text",
8448
8631
  thinkingStartedAt: null
8632
+ } : event.contentType === "tool_input" ? {
8633
+ phase: "tool",
8634
+ thinkingStartedAt: null
8449
8635
  } : {
8450
8636
  phase: "thinking",
8451
8637
  reasoning: prev.reasoning + event.delta,
8452
8638
  thinkingStartedAt: prev.thinkingStartedAt ?? Date.now()
8453
- },
8454
- rawEvents: pushRawEvent(prev, "agent_turn_delta", {
8455
- contentType: event.contentType,
8456
- delta: event.delta,
8457
- deltaLength: event.delta.length
8458
- }, eventWriterRef)
8639
+ }
8459
8640
  }));
8460
8641
  },
8461
8642
  onTurnComplete(event) {
8643
+ pushRawEvent(rawEventsRef, "agent_turn_complete", { ...event }, eventWriterRef);
8462
8644
  setState((prev) => ({
8463
8645
  ...prev,
8464
- totalCost: event.cost != null ? prev.totalCost + event.cost : prev.totalCost,
8465
- rawEvents: pushRawEvent(prev, "agent_turn_complete", { ...event }, eventWriterRef)
8646
+ totalCost: event.cost != null ? prev.totalCost + event.cost : prev.totalCost
8466
8647
  }));
8467
8648
  },
8468
8649
  onToolStart(event) {
8469
- const uniqueId = `tool-${++toolCounter}`;
8650
+ const { parameters: _params, ...listData } = event;
8651
+ pushRawEvent(rawEventsRef, "agent_tool_start", { ...event }, eventWriterRef, listData);
8470
8652
  setState((prev) => {
8653
+ const existingIndex = prev.tools.findIndex(
8654
+ (tool) => tool.sourceToolCallId === event.toolCallId && tool.status === "running"
8655
+ );
8656
+ const nextPhase = event.parameters ? "executing" : "input_streaming";
8657
+ if (existingIndex >= 0) {
8658
+ const tools = prev.tools.map((tool, index) => {
8659
+ if (index !== existingIndex) return tool;
8660
+ const hydratedTool = hydrateToolEntry(tool);
8661
+ const parameters = event.parameters ?? hydratedTool.parameters;
8662
+ const streamedInput = hydratedTool.streamedInput;
8663
+ const inlineParameters2 = parameters && shouldKeepToolPayloadInline(event.toolName, parameters) ? parameters : void 0;
8664
+ setStoredToolPayloadField(
8665
+ toolPayloadsRef.current,
8666
+ tool.id,
8667
+ "parameters",
8668
+ inlineParameters2 ? void 0 : parameters
8669
+ );
8670
+ return {
8671
+ ...tool,
8672
+ name: event.toolName,
8673
+ toolType: event.toolType,
8674
+ parameters: inlineParameters2,
8675
+ phase: nextPhase,
8676
+ inputPreview: buildToolPreview({ streamedInput, parameters, existingPreview: tool.inputPreview })
8677
+ };
8678
+ });
8679
+ return {
8680
+ ...prev,
8681
+ phase: "tool",
8682
+ thinkingStartedAt: null,
8683
+ tools
8684
+ };
8685
+ }
8686
+ const uniqueId = `tool-${++toolCounter}`;
8471
8687
  const sequence = prev.tools.length + 1;
8688
+ const inputPreview = buildToolPreview({ parameters: event.parameters });
8689
+ const inlineParameters = event.parameters && shouldKeepToolPayloadInline(event.toolName, event.parameters) ? event.parameters : void 0;
8690
+ setStoredToolPayloadField(
8691
+ toolPayloadsRef.current,
8692
+ uniqueId,
8693
+ "parameters",
8694
+ inlineParameters ? void 0 : event.parameters
8695
+ );
8472
8696
  return {
8473
8697
  ...prev,
8474
8698
  phase: "tool",
@@ -8484,25 +8708,104 @@ function useMarathonStream() {
8484
8708
  name: event.toolName,
8485
8709
  toolType: event.toolType,
8486
8710
  status: "running",
8487
- parameters: event.parameters,
8711
+ phase: nextPhase,
8712
+ parameters: inlineParameters,
8713
+ inputPreview,
8488
8714
  startedAt: Date.now()
8489
8715
  }
8490
- ],
8491
- rawEvents: pushRawEvent(prev, "agent_tool_start", { ...event }, eventWriterRef)
8716
+ ]
8717
+ };
8718
+ });
8719
+ },
8720
+ onToolInputDelta(event) {
8721
+ pushRawEvent(rawEventsRef, "agent_tool_input_delta", { ...event }, eventWriterRef);
8722
+ setState((prev) => {
8723
+ let matched = false;
8724
+ const tools = prev.tools.map((tool) => {
8725
+ if (matched || tool.sourceToolCallId !== event.toolCallId || tool.status !== "running") {
8726
+ return tool;
8727
+ }
8728
+ matched = true;
8729
+ const hydratedTool = hydrateToolEntry(tool);
8730
+ const streamedInput = `${hydratedTool.streamedInput || ""}${event.delta || ""}`;
8731
+ const inlineStreamedInput = shouldKeepToolPayloadInline(tool.name, streamedInput) ? streamedInput : void 0;
8732
+ setStoredToolPayloadField(
8733
+ toolPayloadsRef.current,
8734
+ tool.id,
8735
+ "streamedInput",
8736
+ inlineStreamedInput ? void 0 : streamedInput
8737
+ );
8738
+ return {
8739
+ ...tool,
8740
+ phase: "input_streaming",
8741
+ streamedInput: inlineStreamedInput,
8742
+ inputPreview: buildToolPreview({ streamedInput, parameters: hydratedTool.parameters, existingPreview: tool.inputPreview })
8743
+ };
8744
+ });
8745
+ return {
8746
+ ...prev,
8747
+ phase: "tool",
8748
+ thinkingStartedAt: null,
8749
+ tools
8750
+ };
8751
+ });
8752
+ },
8753
+ onToolInputComplete(event) {
8754
+ pushRawEvent(rawEventsRef, "agent_tool_input_complete", { ...event }, eventWriterRef);
8755
+ setState((prev) => {
8756
+ let matched = false;
8757
+ const tools = prev.tools.map((tool) => {
8758
+ if (matched || tool.sourceToolCallId !== event.toolCallId || tool.status !== "running") {
8759
+ return tool;
8760
+ }
8761
+ matched = true;
8762
+ const parameters = event.parameters;
8763
+ const inlineParameters = parameters && shouldKeepToolPayloadInline(tool.name, parameters) ? parameters : void 0;
8764
+ setStoredToolPayloadField(
8765
+ toolPayloadsRef.current,
8766
+ tool.id,
8767
+ "parameters",
8768
+ inlineParameters ? void 0 : parameters
8769
+ );
8770
+ return {
8771
+ ...tool,
8772
+ phase: "executing",
8773
+ parameters: inlineParameters,
8774
+ inputPreview: buildToolPreview({ streamedInput: hydrateToolEntry(tool).streamedInput, parameters, existingPreview: tool.inputPreview })
8775
+ };
8776
+ });
8777
+ return {
8778
+ ...prev,
8779
+ phase: "tool",
8780
+ thinkingStartedAt: null,
8781
+ tools
8492
8782
  };
8493
8783
  });
8494
8784
  },
8785
+ onToolDelta(event) {
8786
+ pushRawEvent(rawEventsRef, "agent_tool_delta", { ...event }, eventWriterRef);
8787
+ },
8495
8788
  onToolComplete(event) {
8789
+ const { result: _result, ...listData } = event;
8790
+ pushRawEvent(rawEventsRef, "agent_tool_complete", { ...event }, eventWriterRef, listData);
8496
8791
  setState((prev) => {
8497
8792
  let matched = false;
8498
8793
  const tools = prev.tools.map((t) => {
8499
8794
  if (!matched && t.sourceToolCallId === event.toolCallId && t.status === "running") {
8500
8795
  matched = true;
8796
+ const inlineResult = shouldKeepToolPayloadInline(t.name, event.result) ? event.result : void 0;
8797
+ setStoredToolPayloadField(
8798
+ toolPayloadsRef.current,
8799
+ t.id,
8800
+ "result",
8801
+ inlineResult === void 0 ? event.result : void 0
8802
+ );
8501
8803
  return {
8502
8804
  ...t,
8503
8805
  status: event.success ? "complete" : "error",
8806
+ phase: "executing",
8504
8807
  executionTime: event.executionTime,
8505
- result: event.result
8808
+ result: inlineResult
8506
8809
  };
8507
8810
  }
8508
8811
  return t;
@@ -8510,19 +8813,18 @@ function useMarathonStream() {
8510
8813
  return {
8511
8814
  ...prev,
8512
8815
  phase: "streaming",
8513
- tools,
8514
- rawEvents: pushRawEvent(prev, "agent_tool_complete", { ...event }, eventWriterRef)
8816
+ tools
8515
8817
  };
8516
8818
  });
8517
8819
  },
8518
8820
  onIterationComplete(event) {
8821
+ pushRawEvent(rawEventsRef, "agent_iteration_complete", { ...event }, eventWriterRef);
8519
8822
  setState((prev) => ({
8520
8823
  ...prev,
8521
8824
  totalCost: event.cost != null ? prev.totalCost + event.cost : prev.totalCost,
8522
8825
  totalInputTokens: prev.totalInputTokens + (event.tokens?.input ?? 0),
8523
8826
  totalOutputTokens: prev.totalOutputTokens + (event.tokens?.output ?? 0),
8524
- sessionCount: prev.sessionCount + 1,
8525
- rawEvents: pushRawEvent(prev, "agent_iteration_complete", { ...event }, eventWriterRef)
8827
+ sessionCount: prev.sessionCount + 1
8526
8828
  }));
8527
8829
  },
8528
8830
  onAgentPaused(event) {
@@ -8536,6 +8838,8 @@ function useMarathonStream() {
8536
8838
  } catch {
8537
8839
  }
8538
8840
  }
8841
+ const { parameters: _awaitParamsRaw, ...awaitListData } = event;
8842
+ pushRawEvent(rawEventsRef, "agent_await", { ...event }, eventWriterRef, awaitListData);
8539
8843
  setState((prev) => ({
8540
8844
  ...prev,
8541
8845
  phase: "tool",
@@ -8544,48 +8848,140 @@ function useMarathonStream() {
8544
8848
  if (t.status !== "running") return t;
8545
8849
  const isMatch = !event.toolId || t.sourceToolCallId === event.toolId || event.toolName != null && t.name === event.toolName;
8546
8850
  if (!isMatch) return t;
8851
+ const hydratedTool = hydrateToolEntry(t);
8852
+ const parameters = awaitParams ?? hydratedTool.parameters;
8853
+ const streamedInput = hydratedTool.streamedInput;
8854
+ const inlineParameters = parameters && shouldKeepToolPayloadInline(t.name, parameters) ? parameters : void 0;
8855
+ setStoredToolPayloadField(
8856
+ toolPayloadsRef.current,
8857
+ t.id,
8858
+ "parameters",
8859
+ inlineParameters ? void 0 : parameters
8860
+ );
8547
8861
  return {
8548
8862
  ...t,
8549
- status: "complete",
8550
- executionTime: Date.now() - t.startedAt,
8551
- // Backfill parameters if they were stripped from agent_tool_start
8552
- ...awaitParams && !t.parameters ? { parameters: awaitParams } : {}
8863
+ localToolCallId: event.toolId,
8864
+ phase: "executing_locally",
8865
+ localExecutionStartedAt: Date.now(),
8866
+ parameters: inlineParameters,
8867
+ inputPreview: buildToolPreview({
8868
+ streamedInput,
8869
+ parameters,
8870
+ existingPreview: t.inputPreview
8871
+ })
8872
+ };
8873
+ })
8874
+ }));
8875
+ },
8876
+ onLocalToolExecutionStart(event) {
8877
+ const { parameters: _params, ...listData } = event;
8878
+ pushRawEvent(rawEventsRef, "local_tool_execution_start", { ...event }, eventWriterRef, listData);
8879
+ setState((prev) => ({
8880
+ ...prev,
8881
+ phase: "tool",
8882
+ thinkingStartedAt: null,
8883
+ tools: prev.tools.map((tool) => {
8884
+ const isMatch = tool.status === "running" && (tool.localToolCallId === event.toolCallId || tool.sourceToolCallId === event.toolCallId || tool.name === event.toolName);
8885
+ if (!isMatch) return tool;
8886
+ const inlineParameters = event.parameters && shouldKeepToolPayloadInline(tool.name, event.parameters) ? event.parameters : void 0;
8887
+ setStoredToolPayloadField(
8888
+ toolPayloadsRef.current,
8889
+ tool.id,
8890
+ "parameters",
8891
+ inlineParameters ? void 0 : event.parameters
8892
+ );
8893
+ return {
8894
+ ...tool,
8895
+ localToolCallId: event.toolCallId,
8896
+ phase: "executing_locally",
8897
+ localExecutionStartedAt: Date.parse(event.startedAt) || Date.now(),
8898
+ parameters: inlineParameters,
8899
+ inputPreview: buildToolPreview({
8900
+ streamedInput: hydrateToolEntry(tool).streamedInput,
8901
+ parameters: event.parameters,
8902
+ existingPreview: tool.inputPreview
8903
+ })
8904
+ };
8905
+ })
8906
+ }));
8907
+ },
8908
+ onLocalToolExecutionComplete(event) {
8909
+ const { result: _result, parameters: _params, ...listData } = event;
8910
+ pushRawEvent(rawEventsRef, "local_tool_execution_complete", { ...event }, eventWriterRef, listData);
8911
+ setState((prev) => ({
8912
+ ...prev,
8913
+ tools: prev.tools.map((tool) => {
8914
+ const isMatch = tool.status === "running" && (tool.localToolCallId === event.toolCallId || tool.sourceToolCallId === event.toolCallId || tool.name === event.toolName);
8915
+ if (!isMatch) return tool;
8916
+ const inlineParameters = event.parameters && shouldKeepToolPayloadInline(tool.name, event.parameters) ? event.parameters : void 0;
8917
+ const inlineResult = shouldKeepToolPayloadInline(tool.name, event.result) ? event.result : void 0;
8918
+ setStoredToolPayloadField(
8919
+ toolPayloadsRef.current,
8920
+ tool.id,
8921
+ "parameters",
8922
+ inlineParameters ? void 0 : event.parameters
8923
+ );
8924
+ setStoredToolPayloadField(
8925
+ toolPayloadsRef.current,
8926
+ tool.id,
8927
+ "result",
8928
+ inlineResult === void 0 ? event.result : void 0
8929
+ );
8930
+ return {
8931
+ ...tool,
8932
+ status: event.success ? "complete" : "error",
8933
+ phase: "executing",
8934
+ parameters: inlineParameters,
8935
+ result: inlineResult,
8936
+ executionTime: event.durationMs
8553
8937
  };
8554
- }),
8555
- rawEvents: pushRawEvent(prev, "agent_await", { ...event }, eventWriterRef)
8938
+ })
8556
8939
  }));
8557
8940
  },
8558
8941
  onAgentComplete(event) {
8942
+ const { finalOutput: _output, ...listData } = event;
8943
+ pushRawEvent(rawEventsRef, "agent_complete", { ...event }, eventWriterRef, listData);
8559
8944
  setState((prev) => ({
8560
8945
  ...prev,
8561
8946
  phase: "complete",
8562
8947
  content: prev.content || event.finalOutput || "",
8563
- // Mark any still-running local tools as complete (they never get tool_complete from the API)
8948
+ // If any running local tools remain, close them out using the best
8949
+ // duration we have available from local execution state.
8564
8950
  tools: prev.tools.map(
8565
- (t) => t.status === "running" ? { ...t, status: "complete", executionTime: Date.now() - t.startedAt } : t
8951
+ (t) => t.status === "running" ? {
8952
+ ...t,
8953
+ status: "complete",
8954
+ executionTime: Date.now() - (t.localExecutionStartedAt ?? t.startedAt)
8955
+ } : t
8566
8956
  ),
8567
8957
  totalCost: event.totalCost != null ? event.totalCost : prev.totalCost,
8568
8958
  totalInputTokens: event.totalTokens?.input ?? prev.totalInputTokens,
8569
- totalOutputTokens: event.totalTokens?.output ?? prev.totalOutputTokens,
8570
- rawEvents: pushRawEvent(prev, "agent_complete", { ...event }, eventWriterRef)
8959
+ totalOutputTokens: event.totalTokens?.output ?? prev.totalOutputTokens
8571
8960
  }));
8572
8961
  },
8573
8962
  onError(event) {
8963
+ pushRawEvent(rawEventsRef, "agent_error", { ...event }, eventWriterRef);
8574
8964
  setState((prev) => ({
8575
8965
  ...prev,
8576
8966
  phase: "error",
8577
8967
  error: new Error(event.error.message),
8578
8968
  // Mark any still-running tools as errored so spinners stop
8579
8969
  tools: prev.tools.map(
8580
- (t) => t.status === "running" ? { ...t, status: "error", executionTime: Date.now() - t.startedAt } : t
8581
- ),
8582
- rawEvents: pushRawEvent(prev, "agent_error", { ...event }, eventWriterRef)
8970
+ (t) => t.status === "running" ? {
8971
+ ...t,
8972
+ status: "error",
8973
+ executionTime: Date.now() - (t.localExecutionStartedAt ?? t.startedAt)
8974
+ } : t
8975
+ )
8583
8976
  }));
8584
8977
  }
8585
8978
  }),
8586
8979
  []
8980
+ // rawEventsRef and eventWriterRef are stable refs — safe to omit
8587
8981
  );
8588
8982
  const reset = useCallback2(() => {
8983
+ rawEventsRef.current = [];
8984
+ toolPayloadsRef.current.clear();
8589
8985
  setState(INITIAL_STATE);
8590
8986
  }, []);
8591
8987
  const setCheckpoint = useCallback2(() => {
@@ -8600,6 +8996,7 @@ function useMarathonStream() {
8600
8996
  }));
8601
8997
  }, []);
8602
8998
  const startContextCompaction = useCallback2((event) => {
8999
+ pushRawEvent(rawEventsRef, "marathon_context_compaction_start", { ...event }, eventWriterRef);
8603
9000
  setState((prev) => ({
8604
9001
  ...prev,
8605
9002
  contextCompaction: {
@@ -8617,28 +9014,28 @@ function useMarathonStream() {
8617
9014
  afterTokens: event.afterTokens,
8618
9015
  breakdown: event.breakdown,
8619
9016
  startedAt: Date.now()
8620
- },
8621
- rawEvents: pushRawEvent(prev, "marathon_context_compaction_start", { ...event }, eventWriterRef)
9017
+ }
8622
9018
  }));
8623
9019
  }, []);
8624
9020
  const finishContextCompaction = useCallback2((event) => {
9021
+ pushRawEvent(rawEventsRef, "marathon_context_compaction_complete", { ...event }, eventWriterRef);
8625
9022
  setState((prev) => ({
8626
9023
  ...prev,
8627
9024
  contextCompaction: null,
8628
9025
  content: appendContentLine(prev.content, formatContextCompactionCompleteLine(event)),
8629
- lastTranscriptKind: "text",
8630
- rawEvents: pushRawEvent(prev, "marathon_context_compaction_complete", { ...event }, eventWriterRef)
9026
+ lastTranscriptKind: "text"
8631
9027
  }));
8632
9028
  }, []);
8633
9029
  const reportContextNotice = useCallback2((event) => {
9030
+ pushRawEvent(rawEventsRef, "marathon_context_notice", { ...event }, eventWriterRef);
8634
9031
  setState((prev) => ({
8635
9032
  ...prev,
8636
9033
  content: appendContentLine(prev.content, formatContextNoticeLine(event)),
8637
- lastTranscriptKind: "text",
8638
- rawEvents: pushRawEvent(prev, "marathon_context_notice", { ...event }, eventWriterRef)
9034
+ lastTranscriptKind: "text"
8639
9035
  }));
8640
9036
  }, []);
8641
9037
  const resetForNewSession = useCallback2(() => {
9038
+ toolPayloadsRef.current.clear();
8642
9039
  setState((prev) => ({
8643
9040
  ...prev,
8644
9041
  phase: "idle",
@@ -8653,11 +9050,11 @@ function useMarathonStream() {
8653
9050
  }));
8654
9051
  }, []);
8655
9052
  const showError = useCallback2((error) => {
9053
+ pushRawEvent(rawEventsRef, "agent_error", { message: error.message }, eventWriterRef);
8656
9054
  setState((prev) => ({
8657
9055
  ...prev,
8658
9056
  phase: "error",
8659
- error,
8660
- rawEvents: pushRawEvent(prev, "agent_error", { message: error.message }, eventWriterRef)
9057
+ error
8661
9058
  }));
8662
9059
  }, []);
8663
9060
  const setEventWriter = useCallback2((writer) => {
@@ -8665,7 +9062,10 @@ function useMarathonStream() {
8665
9062
  }, []);
8666
9063
  return {
8667
9064
  state,
9065
+ rawEventsRef,
8668
9066
  callbacks,
9067
+ getHydratedState,
9068
+ hydrateToolEntry,
8669
9069
  reset,
8670
9070
  setCheckpoint,
8671
9071
  markPendingStart,
@@ -9083,189 +9483,76 @@ function ScreenTabs({ activeTab, width, shortcutHint }) {
9083
9483
  ] });
9084
9484
  }
9085
9485
 
9086
- // src/ink/marathon/ThinkingIndicator.tsx
9087
- import { useState as useState12, useEffect as useEffect12 } from "react";
9088
- import { Spinner as Spinner4, theme as theme12 } from "@runtypelabs/ink-components";
9486
+ // src/ink/marathon/ToolsPanel.tsx
9487
+ import { useState as useState12, useEffect as useEffect12, useMemo as useMemo2, memo } from "react";
9488
+ import { Box as Box11, Text as Text12 } from "ink";
9489
+ import { Spinner as Spinner4, theme as theme13 } from "@runtypelabs/ink-components";
9490
+
9491
+ // src/ink/marathon/Scrollbar.tsx
9492
+ import { Box as Box10, Text as Text11 } from "ink";
9493
+ import { theme as theme12 } from "@runtypelabs/ink-components";
9089
9494
  import { jsx as jsx11 } from "react/jsx-runtime";
9090
- function ThinkingIndicator({ startedAt }) {
9091
- const [elapsed, setElapsed] = useState12(
9092
- () => startedAt ? Math.floor((Date.now() - startedAt) / 1e3) : 0
9093
- );
9094
- useEffect12(() => {
9095
- const start = startedAt ?? Date.now();
9096
- setElapsed(Math.floor((Date.now() - start) / 1e3));
9097
- const interval = setInterval(() => {
9098
- setElapsed(Math.floor((Date.now() - start) / 1e3));
9099
- }, 1e3);
9100
- return () => clearInterval(interval);
9101
- }, [startedAt]);
9102
- return /* @__PURE__ */ jsx11(Spinner4, { label: `Thinking... ${elapsed}s`, color: theme12.textMuted });
9495
+ function Scrollbar({ totalLines, visibleLines, scrollOffset, height }) {
9496
+ if (totalLines <= visibleLines) return null;
9497
+ const maxScroll = Math.max(0, totalLines - visibleLines);
9498
+ const clampedOffset = Math.min(scrollOffset, maxScroll);
9499
+ const thumbSize = Math.max(1, Math.round(visibleLines / totalLines * height));
9500
+ const thumbStart = maxScroll > 0 ? Math.round(clampedOffset / maxScroll * (height - thumbSize)) : 0;
9501
+ return /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", marginLeft: 1, children: Array.from({ length: height }, (_, i) => {
9502
+ const isThumb = i >= thumbStart && i < thumbStart + thumbSize;
9503
+ return /* @__PURE__ */ jsx11(Text11, { color: isThumb ? theme12.textMuted : theme12.border, children: isThumb ? "\u2588" : "\u2502" }, i);
9504
+ }) });
9103
9505
  }
9104
9506
 
9105
- // src/ink/marathon/ContextCompactionIndicator.tsx
9106
- import { useEffect as useEffect13, useState as useState13 } from "react";
9107
- import { Spinner as Spinner5, theme as theme13 } from "@runtypelabs/ink-components";
9108
- import { jsx as jsx12 } from "react/jsx-runtime";
9109
- function buildCompactionLabel(compaction, elapsedSeconds) {
9110
- const modeLabel = compaction.mode === "auto" ? "auto" : "manual";
9111
- const strategyLabel = compaction.strategy === "provider_native" ? "native" : "summary";
9112
- const thresholdLabel = formatCompactionThresholdLabel(compaction);
9113
- if (thresholdLabel) {
9114
- return `Compacting context for session ${compaction.sessionIndex} (${modeLabel}, ${strategyLabel}, ${thresholdLabel})... ${elapsedSeconds}s`;
9115
- }
9116
- return `Compacting context for session ${compaction.sessionIndex} (${modeLabel}, ${strategyLabel})... ${elapsedSeconds}s`;
9117
- }
9118
- function ContextCompactionIndicator({
9119
- compaction
9120
- }) {
9121
- const [elapsed, setElapsed] = useState13(
9122
- () => Math.floor((Date.now() - compaction.startedAt) / 1e3)
9123
- );
9124
- useEffect13(() => {
9125
- const start = compaction.startedAt;
9126
- setElapsed(Math.floor((Date.now() - start) / 1e3));
9127
- const interval = setInterval(() => {
9128
- setElapsed(Math.floor((Date.now() - start) / 1e3));
9129
- }, 1e3);
9130
- return () => clearInterval(interval);
9131
- }, [compaction.startedAt]);
9132
- return /* @__PURE__ */ jsx12(Spinner5, { label: buildCompactionLabel(compaction, elapsed), color: theme13.warning });
9507
+ // src/ink/marathon/ToolsPanel.tsx
9508
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
9509
+ function getVisibleToolWindow(total, selectedIndex, maxVisibleLines) {
9510
+ const toolSlots = Math.floor(maxVisibleLines / 2);
9511
+ const half = Math.floor(toolSlots / 2);
9512
+ let start = selectedIndex - half;
9513
+ let end = start + toolSlots;
9514
+ if (start < 0) {
9515
+ start = 0;
9516
+ end = Math.min(total, toolSlots);
9517
+ }
9518
+ if (end > total) {
9519
+ end = total;
9520
+ start = Math.max(0, end - toolSlots);
9521
+ }
9522
+ return { start, end };
9523
+ }
9524
+ function visibleToolsFingerprint(tools, selectedIndex, maxVisibleLines) {
9525
+ if (tools.length === 0) return `0:${selectedIndex}:${maxVisibleLines}`;
9526
+ const { start, end } = getVisibleToolWindow(tools.length, selectedIndex, maxVisibleLines);
9527
+ let fp = `${tools.length}:${selectedIndex}:${maxVisibleLines}:${start}:${end}`;
9528
+ for (let i = start; i < end; i++) {
9529
+ const tool = tools[i];
9530
+ fp += `|${tool.id}:${tool.name}:${tool.status}:${tool.phase ?? ""}:${tool.inputPreview ?? ""}:${tool.executionTime ?? ""}:${tool.startedAt}:${tool.localExecutionStartedAt ?? ""}`;
9531
+ }
9532
+ return fp;
9133
9533
  }
9134
-
9135
- // src/ink/marathon/ToolPanel.tsx
9136
- import { useState as useState14, useEffect as useEffect14, useMemo as useMemo2 } from "react";
9137
- import { Box as Box10, Text as Text11 } from "ink";
9138
- import { Spinner as Spinner6, theme as theme14 } from "@runtypelabs/ink-components";
9139
- import { Fragment, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
9140
- function formatCharCount(chars) {
9141
- if (chars >= 1e3) {
9142
- return `${(chars / 1e3).toFixed(1)}k chars`;
9534
+ function formatElapsed(tool, now) {
9535
+ if (tool.executionTime != null) {
9536
+ return `${(tool.executionTime / 1e3).toFixed(1)}s`;
9143
9537
  }
9144
- return `${chars} chars`;
9538
+ const startedAt = tool.localExecutionStartedAt ?? tool.startedAt;
9539
+ return `${((now - startedAt) / 1e3).toFixed(1)}s`;
9145
9540
  }
9146
9541
  var STATUS_ICONS = {
9147
9542
  complete: "\u2713",
9148
9543
  error: "\u2717"
9149
9544
  };
9150
- var FILE_TYPE_ICONS = {
9151
- written: "\u270E",
9152
- read: "\u2022",
9153
- plan: "\u2691"
9154
- };
9155
- function formatElapsed(tool, now) {
9156
- if (tool.executionTime != null) {
9157
- return `${(tool.executionTime / 1e3).toFixed(1)}s`;
9158
- }
9159
- return `${((now - tool.startedAt) / 1e3).toFixed(1)}s`;
9160
- }
9161
9545
  function getStatusColor(status) {
9162
- if (status === "error") return theme14.danger;
9163
- if (status === "complete") return theme14.success;
9164
- return theme14.accent;
9546
+ if (status === "error") return theme13.danger;
9547
+ if (status === "complete") return theme13.success;
9548
+ return theme13.accent;
9165
9549
  }
9166
- function ToolPanel({ tools, reasoning, files, maxHeight }) {
9167
- const [now, setNow] = useState14(Date.now());
9168
- const hasRunning = tools.some((t) => t.status === "running");
9169
- useEffect14(() => {
9170
- if (!hasRunning) return;
9171
- const interval = setInterval(() => setNow(Date.now()), 100);
9172
- return () => clearInterval(interval);
9173
- }, [hasRunning]);
9174
- const { visibleTools, visibleFiles } = useMemo2(() => {
9175
- if (!maxHeight) return { visibleTools: tools, visibleFiles: files || [] };
9176
- const reasoningRows = reasoning && reasoning.length > 0 ? 1 : 0;
9177
- const headerRows = 1;
9178
- const filesList = files || [];
9179
- const fileHeaderRows = filesList.length > 0 ? 2 : 0;
9180
- const availableRows = maxHeight - headerRows - reasoningRows - fileHeaderRows;
9181
- const fileRows = filesList.length > 0 ? Math.min(filesList.length, Math.max(3, Math.floor(availableRows * 0.3))) : 0;
9182
- const toolRows = Math.max(1, availableRows - fileRows);
9183
- const vTools = tools.length <= toolRows ? tools : tools.slice(-toolRows);
9184
- const vFiles = filesList.length <= fileRows ? filesList : filesList.slice(-fileRows);
9185
- return { visibleTools: vTools, visibleFiles: vFiles };
9186
- }, [tools, files, maxHeight, reasoning]);
9187
- const hiddenToolCount = tools.length - visibleTools.length;
9188
- const hiddenFileCount = (files || []).length - visibleFiles.length;
9189
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", backgroundColor: theme14.background, children: [
9190
- /* @__PURE__ */ jsx13(Text11, { bold: true, color: theme14.accent, children: "Activity" }),
9191
- reasoning && reasoning.length > 0 && /* @__PURE__ */ jsxs10(Text11, { color: theme14.textMuted, children: [
9192
- "Reasoning: ",
9193
- formatCharCount(reasoning.length)
9194
- ] }),
9195
- hiddenToolCount > 0 && /* @__PURE__ */ jsxs10(Text11, { color: theme14.textSubtle, children: [
9196
- " ",
9197
- hiddenToolCount,
9198
- " earlier hidden"
9199
- ] }),
9200
- visibleTools.map((tool) => {
9201
- const statusColor = getStatusColor(tool.status);
9202
- const elapsed = formatElapsed(tool, now);
9203
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", flexWrap: "nowrap", children: [
9204
- /* @__PURE__ */ jsx13(Box10, { width: 2, flexShrink: 0, children: tool.status === "running" ? /* @__PURE__ */ jsx13(Spinner6, { label: "", color: theme14.spinner }) : /* @__PURE__ */ jsx13(Text11, { color: statusColor, children: STATUS_ICONS[tool.status] || "?" }) }),
9205
- /* @__PURE__ */ jsx13(Box10, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ jsx13(Text11, { color: theme14.text, wrap: "truncate", children: tool.name }) }),
9206
- /* @__PURE__ */ jsx13(Box10, { flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text11, { color: theme14.textMuted, children: [
9207
- " ",
9208
- elapsed
9209
- ] }) })
9210
- ] }, tool.id);
9211
- }),
9212
- visibleFiles.length > 0 && /* @__PURE__ */ jsxs10(Fragment, { children: [
9213
- /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
9214
- /* @__PURE__ */ jsx13(Text11, { bold: true, color: theme14.accent, children: "Files" }),
9215
- hiddenFileCount > 0 && /* @__PURE__ */ jsxs10(Text11, { color: theme14.textSubtle, children: [
9216
- " +",
9217
- hiddenFileCount
9218
- ] })
9219
- ] }),
9220
- visibleFiles.map((file) => {
9221
- const icon = FILE_TYPE_ICONS[file.type] || "\u2022";
9222
- const filename = file.path.split("/").pop() || file.path;
9223
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", flexWrap: "nowrap", children: [
9224
- /* @__PURE__ */ jsx13(Box10, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text11, { color: file.type === "written" ? theme14.success : theme14.textMuted, children: icon }) }),
9225
- /* @__PURE__ */ jsx13(Box10, { flexShrink: 1, children: /* @__PURE__ */ jsx13(Text11, { color: theme14.text, wrap: "truncate", children: filename }) })
9226
- ] }, file.path);
9227
- })
9228
- ] })
9229
- ] });
9230
- }
9231
-
9232
- // src/ink/marathon/ToolsPanel.tsx
9233
- import { useState as useState15, useEffect as useEffect15, useMemo as useMemo3 } from "react";
9234
- import { Box as Box12, Text as Text13 } from "ink";
9235
- import { Spinner as Spinner7, theme as theme16 } from "@runtypelabs/ink-components";
9236
-
9237
- // src/ink/marathon/Scrollbar.tsx
9238
- import { Box as Box11, Text as Text12 } from "ink";
9239
- import { theme as theme15 } from "@runtypelabs/ink-components";
9240
- import { jsx as jsx14 } from "react/jsx-runtime";
9241
- function Scrollbar({ totalLines, visibleLines, scrollOffset, height }) {
9242
- if (totalLines <= visibleLines) return null;
9243
- const maxScroll = Math.max(0, totalLines - visibleLines);
9244
- const clampedOffset = Math.min(scrollOffset, maxScroll);
9245
- const thumbSize = Math.max(1, Math.round(visibleLines / totalLines * height));
9246
- const thumbStart = maxScroll > 0 ? Math.round(clampedOffset / maxScroll * (height - thumbSize)) : 0;
9247
- return /* @__PURE__ */ jsx14(Box11, { flexDirection: "column", marginLeft: 1, children: Array.from({ length: height }, (_, i) => {
9248
- const isThumb = i >= thumbStart && i < thumbStart + thumbSize;
9249
- return /* @__PURE__ */ jsx14(Text12, { color: isThumb ? theme15.textMuted : theme15.border, children: isThumb ? "\u2588" : "\u2502" }, i);
9250
- }) });
9251
- }
9252
-
9253
- // src/ink/marathon/ToolsPanel.tsx
9254
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
9255
- function formatElapsed2(tool, now) {
9256
- if (tool.executionTime != null) {
9257
- return `${(tool.executionTime / 1e3).toFixed(1)}s`;
9258
- }
9259
- return `${((now - tool.startedAt) / 1e3).toFixed(1)}s`;
9260
- }
9261
- var STATUS_ICONS2 = {
9262
- complete: "\u2713",
9263
- error: "\u2717"
9264
- };
9265
- function getStatusColor2(status) {
9266
- if (status === "error") return theme16.danger;
9267
- if (status === "complete") return theme16.success;
9268
- return theme16.accent;
9550
+ function getPhaseLabel(tool) {
9551
+ if (tool.status === "complete") return "complete";
9552
+ if (tool.status === "error") return "error";
9553
+ if (tool.phase === "input_streaming") return "assembling input";
9554
+ if (tool.phase === "executing_locally") return "executing locally";
9555
+ return "running";
9269
9556
  }
9270
9557
  function wrapLines(lines, width) {
9271
9558
  if (width <= 0) return lines;
@@ -9283,6 +9570,16 @@ function wrapLines(lines, width) {
9283
9570
  }
9284
9571
  function buildToolDetailSections(tool) {
9285
9572
  const sections = [];
9573
+ sections.push(`Status: ${getPhaseLabel(tool)}`);
9574
+ if (tool.inputPreview) {
9575
+ sections.push(`Preview: ${tool.inputPreview}`);
9576
+ sections.push("");
9577
+ }
9578
+ if (tool.streamedInput) {
9579
+ sections.push("Streamed input:");
9580
+ sections.push(tool.streamedInput);
9581
+ sections.push("");
9582
+ }
9286
9583
  if (tool.parameters && Object.keys(tool.parameters).length > 0) {
9287
9584
  sections.push("Parameters:");
9288
9585
  sections.push(JSON.stringify(tool.parameters, null, 2));
@@ -9316,57 +9613,52 @@ function ToolDetailView({
9316
9613
  scrollOffset = 0,
9317
9614
  width = 120
9318
9615
  }) {
9319
- const separator = theme16.separator ?? " \xB7 ";
9320
- const statusColor = getStatusColor2(tool.status);
9321
- const { lines, totalLines, clampedOffset } = useMemo3(() => {
9322
- const allLines = getToolDetailLines(tool, width);
9323
- const total = allLines.length;
9324
- const clamped = Math.min(scrollOffset, Math.max(0, total - maxVisibleLines));
9325
- const start = clamped;
9326
- const end = Math.min(total, start + maxVisibleLines);
9327
- return { lines: allLines.slice(start, end), totalLines: total, clampedOffset: clamped };
9328
- }, [tool, maxVisibleLines, scrollOffset, width]);
9329
- return /* @__PURE__ */ jsxs11(
9330
- Box12,
9331
- {
9332
- flexDirection: "column",
9333
- paddingX: theme16.panelPaddingX,
9334
- paddingY: theme16.panelPaddingY,
9335
- children: [
9336
- /* @__PURE__ */ jsxs11(Box12, { marginBottom: theme16.sectionGap, children: [
9337
- tool.status === "running" ? /* @__PURE__ */ jsx15(Spinner7, { label: "", color: theme16.spinner }) : /* @__PURE__ */ jsx15(Text13, { color: statusColor, children: STATUS_ICONS2[tool.status] || "?" }),
9338
- /* @__PURE__ */ jsx15(Text13, { color: theme16.textMuted, children: ` [${tool.sequence}] ` }),
9339
- /* @__PURE__ */ jsx15(Text13, { bold: true, color: theme16.text, children: tool.name }),
9340
- /* @__PURE__ */ jsxs11(Text13, { color: theme16.textMuted, children: [
9341
- " ",
9342
- formatElapsed2(tool, now)
9343
- ] }),
9344
- /* @__PURE__ */ jsxs11(Text13, { color: theme16.textSubtle, children: [
9345
- separator,
9346
- "c: copy"
9347
- ] }),
9348
- /* @__PURE__ */ jsxs11(Text13, { color: theme16.textSubtle, children: [
9349
- separator,
9350
- "Esc: close"
9351
- ] })
9352
- ] }),
9353
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", flexGrow: 1, children: [
9354
- /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx15(Text13, { color: theme16.text, wrap: "truncate", children: line }, i)) }),
9355
- /* @__PURE__ */ jsx15(
9356
- Scrollbar,
9357
- {
9358
- totalLines,
9359
- visibleLines: maxVisibleLines,
9360
- scrollOffset: clampedOffset,
9361
- height: lines.length
9362
- }
9363
- )
9364
- ] })
9365
- ]
9366
- }
9616
+ const separator = theme13.separator ?? " \xB7 ";
9617
+ const statusColor = getStatusColor(tool.status);
9618
+ const allLines = useMemo2(() => getToolDetailLines(tool, width), [tool, width]);
9619
+ const totalLines = allLines.length;
9620
+ const clampedOffset = Math.min(scrollOffset, Math.max(0, totalLines - maxVisibleLines));
9621
+ const lines = useMemo2(
9622
+ () => allLines.slice(clampedOffset, clampedOffset + maxVisibleLines),
9623
+ [allLines, clampedOffset, maxVisibleLines]
9367
9624
  );
9625
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingX: theme13.panelPaddingX, paddingY: theme13.panelPaddingY, children: [
9626
+ /* @__PURE__ */ jsxs10(Box11, { marginBottom: theme13.sectionGap, children: [
9627
+ tool.status === "running" ? /* @__PURE__ */ jsx12(Spinner4, { label: "", color: theme13.spinner }) : /* @__PURE__ */ jsx12(Text12, { color: statusColor, children: STATUS_ICONS[tool.status] || "?" }),
9628
+ /* @__PURE__ */ jsx12(Text12, { color: theme13.textMuted, children: ` [${tool.sequence}] ` }),
9629
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: theme13.text, children: tool.name }),
9630
+ /* @__PURE__ */ jsxs10(Text12, { color: theme13.textMuted, children: [
9631
+ separator,
9632
+ getPhaseLabel(tool)
9633
+ ] }),
9634
+ /* @__PURE__ */ jsxs10(Text12, { color: theme13.textMuted, children: [
9635
+ " ",
9636
+ formatElapsed(tool, now)
9637
+ ] }),
9638
+ /* @__PURE__ */ jsxs10(Text12, { color: theme13.textSubtle, children: [
9639
+ separator,
9640
+ "c: copy"
9641
+ ] }),
9642
+ /* @__PURE__ */ jsxs10(Text12, { color: theme13.textSubtle, children: [
9643
+ separator,
9644
+ "Esc: close"
9645
+ ] })
9646
+ ] }),
9647
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", flexGrow: 1, children: [
9648
+ /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx12(Text12, { color: theme13.text, wrap: "truncate", children: line }, i)) }),
9649
+ /* @__PURE__ */ jsx12(
9650
+ Scrollbar,
9651
+ {
9652
+ totalLines,
9653
+ visibleLines: maxVisibleLines,
9654
+ scrollOffset: clampedOffset,
9655
+ height: lines.length
9656
+ }
9657
+ )
9658
+ ] })
9659
+ ] });
9368
9660
  }
9369
- function ToolsPanel({
9661
+ var ToolsPanel = memo(function ToolsPanel2({
9370
9662
  tools,
9371
9663
  maxVisibleLines = 50,
9372
9664
  selectedIndex,
@@ -9374,40 +9666,36 @@ function ToolsPanel({
9374
9666
  detailScrollOffset = 0,
9375
9667
  width = 120
9376
9668
  }) {
9377
- const separator = theme16.separator ?? " \xB7 ";
9378
- const [now, setNow] = useState15(Date.now());
9669
+ const separator = theme13.separator ?? " \xB7 ";
9670
+ const [now, setNow] = useState12(Date.now());
9379
9671
  const hasRunning = tools.some((t) => t.status === "running");
9380
- useEffect15(() => {
9672
+ useEffect12(() => {
9381
9673
  if (!hasRunning) return;
9382
9674
  const interval = setInterval(() => setNow(Date.now()), 100);
9383
9675
  return () => clearInterval(interval);
9384
9676
  }, [hasRunning]);
9385
- const { rows, hiddenAbove } = useMemo3(() => {
9677
+ const { rows, hiddenAbove } = useMemo2(() => {
9386
9678
  if (tools.length === 0) return { rows: [], hiddenAbove: 0 };
9387
- const total = tools.length;
9388
- const half = Math.floor(maxVisibleLines / 2);
9389
- let start = selectedIndex - half;
9390
- let end = start + maxVisibleLines;
9391
- if (start < 0) {
9392
- start = 0;
9393
- end = Math.min(total, maxVisibleLines);
9394
- }
9395
- if (end > total) {
9396
- end = total;
9397
- start = Math.max(0, end - maxVisibleLines);
9398
- }
9679
+ const { start, end } = getVisibleToolWindow(tools.length, selectedIndex, maxVisibleLines);
9399
9680
  const visible = tools.slice(start, end);
9400
9681
  return {
9401
9682
  rows: visible.map((tool, i) => ({
9402
9683
  globalIndex: start + i,
9403
9684
  key: tool.id,
9404
- tool
9685
+ status: tool.status,
9686
+ phase: tool.phase,
9687
+ sequence: tool.sequence,
9688
+ name: tool.name,
9689
+ inputPreview: tool.inputPreview,
9690
+ executionTime: tool.executionTime,
9691
+ startedAt: tool.startedAt,
9692
+ localExecutionStartedAt: tool.localExecutionStartedAt
9405
9693
  })),
9406
9694
  hiddenAbove: start
9407
9695
  };
9408
9696
  }, [tools, maxVisibleLines, selectedIndex]);
9409
9697
  if (detailTool) {
9410
- return /* @__PURE__ */ jsx15(
9698
+ return /* @__PURE__ */ jsx12(
9411
9699
  ToolDetailView,
9412
9700
  {
9413
9701
  tool: detailTool,
@@ -9419,102 +9707,125 @@ function ToolsPanel({
9419
9707
  );
9420
9708
  }
9421
9709
  if (tools.length === 0) {
9422
- return /* @__PURE__ */ jsx15(
9423
- Box12,
9424
- {
9425
- flexDirection: "column",
9426
- paddingX: theme16.panelPaddingX,
9427
- paddingY: theme16.panelPaddingY,
9428
- children: /* @__PURE__ */ jsx15(Text13, { color: theme16.textSubtle, children: "No tool calls yet..." })
9429
- }
9430
- );
9431
- }
9432
- return /* @__PURE__ */ jsxs11(
9433
- Box12,
9434
- {
9435
- flexDirection: "column",
9436
- paddingX: theme16.panelPaddingX,
9437
- paddingY: theme16.panelPaddingY,
9438
- children: [
9439
- /* @__PURE__ */ jsxs11(Box12, { marginBottom: 0, children: [
9440
- /* @__PURE__ */ jsx15(Text13, { bold: true, color: theme16.accent, children: "Tools" }),
9441
- /* @__PURE__ */ jsxs11(Text13, { color: theme16.textSubtle, children: [
9442
- separator,
9443
- tools.length,
9444
- " calls"
9710
+ return /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", paddingX: theme13.panelPaddingX, paddingY: theme13.panelPaddingY, children: /* @__PURE__ */ jsx12(Text12, { color: theme13.textSubtle, children: "No tool calls yet..." }) });
9711
+ }
9712
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingX: theme13.panelPaddingX, paddingY: theme13.panelPaddingY, children: [
9713
+ /* @__PURE__ */ jsxs10(Box11, { marginBottom: 0, children: [
9714
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: theme13.accent, children: "Tools" }),
9715
+ /* @__PURE__ */ jsxs10(Text12, { color: theme13.textSubtle, children: [
9716
+ separator,
9717
+ tools.length,
9718
+ " calls"
9719
+ ] })
9720
+ ] }),
9721
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", children: [
9722
+ /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", flexGrow: 1, children: rows.map((row) => {
9723
+ const isSelected = row.globalIndex === selectedIndex;
9724
+ const statusColor = getStatusColor(row.status);
9725
+ const elapsed = formatElapsed({
9726
+ status: row.status,
9727
+ phase: row.phase,
9728
+ sequence: row.sequence,
9729
+ name: row.name,
9730
+ startedAt: row.startedAt,
9731
+ localExecutionStartedAt: row.localExecutionStartedAt,
9732
+ executionTime: row.executionTime
9733
+ }, now);
9734
+ const phaseLabel = getPhaseLabel({
9735
+ status: row.status,
9736
+ phase: row.phase
9737
+ });
9738
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", children: [
9739
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", flexWrap: "nowrap", children: [
9740
+ /* @__PURE__ */ jsx12(Box11, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx12(Text12, { color: isSelected ? theme13.accentActive : theme13.textSubtle, children: isSelected ? "\u203A" : " " }) }),
9741
+ /* @__PURE__ */ jsx12(Box11, { width: 2, flexShrink: 0, children: row.status === "running" ? /* @__PURE__ */ jsx12(Spinner4, { label: "", color: theme13.spinner }) : /* @__PURE__ */ jsx12(Text12, { color: statusColor, children: STATUS_ICONS[row.status] || "?" }) }),
9742
+ /* @__PURE__ */ jsx12(Box11, { width: 5, flexShrink: 0, children: /* @__PURE__ */ jsxs10(Text12, { color: theme13.textMuted, children: [
9743
+ "[",
9744
+ row.sequence,
9745
+ "] "
9746
+ ] }) }),
9747
+ /* @__PURE__ */ jsx12(Box11, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ jsx12(
9748
+ Text12,
9749
+ {
9750
+ color: isSelected ? theme13.accentActive : theme13.text,
9751
+ bold: isSelected,
9752
+ wrap: "truncate",
9753
+ children: row.name
9754
+ }
9755
+ ) }),
9756
+ /* @__PURE__ */ jsx12(Box11, { width: 24, flexShrink: 0, justifyContent: "flex-end", children: /* @__PURE__ */ jsx12(Text12, { color: theme13.textMuted, children: `${phaseLabel} ${elapsed}` }) })
9757
+ ] }),
9758
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", flexWrap: "nowrap", children: [
9759
+ /* @__PURE__ */ jsx12(Box11, { width: 9, flexShrink: 0 }),
9760
+ /* @__PURE__ */ jsx12(Text12, { color: theme13.textSubtle, children: "\u2514 " }),
9761
+ /* @__PURE__ */ jsx12(Box11, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme13.textMuted, wrap: "truncate", children: row.inputPreview || "" }) })
9445
9762
  ] })
9446
- ] }),
9447
- /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", children: [
9448
- /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", flexGrow: 1, children: rows.map((row) => {
9449
- const isSelected = row.globalIndex === selectedIndex;
9450
- const statusColor = getStatusColor2(row.tool.status);
9451
- const elapsed = formatElapsed2(row.tool, now);
9452
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", flexWrap: "nowrap", children: [
9453
- /* @__PURE__ */ jsx15(Box12, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx15(Text13, { color: isSelected ? theme16.accentActive : theme16.textSubtle, children: isSelected ? "\u203A" : " " }) }),
9454
- /* @__PURE__ */ jsx15(Box12, { width: 2, flexShrink: 0, children: row.tool.status === "running" ? /* @__PURE__ */ jsx15(Spinner7, { label: "", color: theme16.spinner }) : /* @__PURE__ */ jsx15(Text13, { color: statusColor, children: STATUS_ICONS2[row.tool.status] || "?" }) }),
9455
- /* @__PURE__ */ jsx15(Box12, { width: 5, flexShrink: 0, children: /* @__PURE__ */ jsxs11(Text13, { color: theme16.textMuted, children: [
9456
- "[",
9457
- row.tool.sequence,
9458
- "] "
9459
- ] }) }),
9460
- /* @__PURE__ */ jsx15(Box12, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ jsx15(
9461
- Text13,
9462
- {
9463
- color: isSelected ? theme16.accentActive : theme16.text,
9464
- bold: isSelected,
9465
- wrap: "truncate",
9466
- children: row.tool.name
9467
- }
9468
- ) }),
9469
- /* @__PURE__ */ jsx15(Box12, { width: 7, flexShrink: 0, justifyContent: "flex-end", children: /* @__PURE__ */ jsx15(Text13, { color: theme16.textMuted, children: elapsed }) })
9470
- ] }, row.key);
9471
- }) }),
9472
- /* @__PURE__ */ jsx15(
9473
- Scrollbar,
9474
- {
9475
- totalLines: tools.length,
9476
- visibleLines: rows.length,
9477
- scrollOffset: hiddenAbove,
9478
- height: rows.length
9479
- }
9480
- )
9481
- ] })
9482
- ]
9483
- }
9484
- );
9485
- }
9763
+ ] }, row.key);
9764
+ }) }),
9765
+ /* @__PURE__ */ jsx12(
9766
+ Scrollbar,
9767
+ {
9768
+ totalLines: tools.length * 2,
9769
+ visibleLines: rows.length * 2,
9770
+ scrollOffset: hiddenAbove * 2,
9771
+ height: rows.length * 2
9772
+ }
9773
+ )
9774
+ ] })
9775
+ ] });
9776
+ }, (prev, next) => {
9777
+ return visibleToolsFingerprint(prev.tools, prev.selectedIndex, prev.maxVisibleLines ?? 50) === visibleToolsFingerprint(next.tools, next.selectedIndex, next.maxVisibleLines ?? 50) && prev.detailTool === next.detailTool && prev.detailScrollOffset === next.detailScrollOffset && prev.width === next.width;
9778
+ });
9486
9779
 
9487
9780
  // src/ink/marathon/EventStreamPanel.tsx
9488
- import { useMemo as useMemo4 } from "react";
9489
- import { Box as Box13, Text as Text14 } from "ink";
9490
- import { theme as theme17 } from "@runtypelabs/ink-components";
9491
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
9781
+ import { useMemo as useMemo3, memo as memo2 } from "react";
9782
+ import { Box as Box12, Text as Text13 } from "ink";
9783
+ import { theme as theme14 } from "@runtypelabs/ink-components";
9784
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
9492
9785
  var EVENT_COLORS = {
9493
- agent_start: theme17.success,
9494
- agent_complete: theme17.success,
9495
- agent_error: theme17.danger,
9496
- agent_await: theme17.warning,
9497
- agent_iteration_start: theme17.accent,
9498
- agent_iteration_complete: theme17.accent,
9499
- agent_turn_delta: theme17.textMuted,
9500
- agent_turn_complete: theme17.accentActive,
9501
- agent_tool_start: theme17.warning,
9502
- agent_tool_complete: theme17.warning,
9503
- marathon_context_compaction_start: theme17.warning,
9504
- marathon_context_compaction_complete: theme17.success,
9505
- marathon_context_notice: theme17.warning
9786
+ agent_start: theme14.success,
9787
+ agent_complete: theme14.success,
9788
+ agent_error: theme14.danger,
9789
+ agent_await: theme14.warning,
9790
+ agent_iteration_start: theme14.accent,
9791
+ agent_iteration_complete: theme14.accent,
9792
+ agent_turn_delta: theme14.textMuted,
9793
+ agent_turn_complete: theme14.accentActive,
9794
+ agent_tool_start: theme14.warning,
9795
+ agent_tool_delta: theme14.warning,
9796
+ agent_tool_complete: theme14.warning,
9797
+ local_tool_execution_start: theme14.warning,
9798
+ local_tool_execution_complete: theme14.warning,
9799
+ marathon_context_compaction_start: theme14.warning,
9800
+ marathon_context_compaction_complete: theme14.success,
9801
+ marathon_context_notice: theme14.warning
9506
9802
  };
9507
9803
  function formatTimestamp(ts, start) {
9508
9804
  if (!start) return "0.000s";
9509
9805
  const elapsed = (ts - start) / 1e3;
9510
9806
  return `${elapsed.toFixed(3)}s`;
9511
9807
  }
9808
+ var eventDataCache = /* @__PURE__ */ new WeakMap();
9809
+ var EVENT_SUMMARY_MAX_CHARS = 140;
9810
+ var HEAVY_FIELDS = /* @__PURE__ */ new Set(["result", "parameters", "finalOutput"]);
9512
9811
  function formatEventData(data) {
9513
- const filtered = { ...data };
9514
- delete filtered.type;
9812
+ const cached = eventDataCache.get(data);
9813
+ if (cached !== void 0) return cached;
9814
+ const filtered = {};
9815
+ for (const key of Object.keys(data)) {
9816
+ if (key === "type") continue;
9817
+ if (HEAVY_FIELDS.has(key)) continue;
9818
+ filtered[key] = data[key];
9819
+ }
9515
9820
  const keys = Object.keys(filtered);
9516
- if (keys.length === 0) return "";
9517
- return JSON.stringify(filtered);
9821
+ if (keys.length === 0) {
9822
+ eventDataCache.set(data, "");
9823
+ return "";
9824
+ }
9825
+ const result = JSON.stringify(filtered);
9826
+ const summary = result.length > EVENT_SUMMARY_MAX_CHARS ? `${result.slice(0, EVENT_SUMMARY_MAX_CHARS - 1)}\u2026` : result;
9827
+ eventDataCache.set(data, summary);
9828
+ return summary;
9518
9829
  }
9519
9830
  function formatEventDetailCopyText(event, startTime) {
9520
9831
  return `${formatTimestamp(event.timestamp, startTime)} ${event.type}
@@ -9547,41 +9858,40 @@ function EventDetailView({
9547
9858
  scrollOffset = 0,
9548
9859
  width = 120
9549
9860
  }) {
9550
- const separator = theme17.separator ?? " \xB7 ";
9551
- const { lines, totalLines, clampedOffset } = useMemo4(() => {
9552
- const allLines = getEventDetailLines(event, width);
9553
- const total = allLines.length;
9554
- const clamped = Math.min(scrollOffset, Math.max(0, total - maxVisibleLines));
9555
- const start = clamped;
9556
- const end = Math.min(total, start + maxVisibleLines);
9557
- return { lines: allLines.slice(start, end), totalLines: total, clampedOffset: clamped };
9558
- }, [event, maxVisibleLines, scrollOffset, width]);
9559
- const color = EVENT_COLORS[event.type] || theme17.text;
9560
- return /* @__PURE__ */ jsxs12(
9561
- Box13,
9861
+ const separator = theme14.separator ?? " \xB7 ";
9862
+ const allLines = useMemo3(() => getEventDetailLines(event, width), [event, width]);
9863
+ const totalLines = allLines.length;
9864
+ const clampedOffset = Math.min(scrollOffset, Math.max(0, totalLines - maxVisibleLines));
9865
+ const lines = useMemo3(
9866
+ () => allLines.slice(clampedOffset, clampedOffset + maxVisibleLines),
9867
+ [allLines, clampedOffset, maxVisibleLines]
9868
+ );
9869
+ const color = EVENT_COLORS[event.type] || theme14.text;
9870
+ return /* @__PURE__ */ jsxs11(
9871
+ Box12,
9562
9872
  {
9563
9873
  flexDirection: "column",
9564
- paddingX: theme17.panelPaddingX,
9565
- paddingY: theme17.panelPaddingY,
9874
+ paddingX: theme14.panelPaddingX,
9875
+ paddingY: theme14.panelPaddingY,
9566
9876
  children: [
9567
- /* @__PURE__ */ jsxs12(Box13, { marginBottom: theme17.sectionGap, children: [
9568
- /* @__PURE__ */ jsxs12(Text14, { color: theme17.textMuted, children: [
9877
+ /* @__PURE__ */ jsxs11(Box12, { marginBottom: theme14.sectionGap, children: [
9878
+ /* @__PURE__ */ jsxs11(Text13, { color: theme14.textMuted, children: [
9569
9879
  formatTimestamp(event.timestamp, startTime),
9570
9880
  " "
9571
9881
  ] }),
9572
- /* @__PURE__ */ jsx16(Text14, { bold: true, color, children: event.type }),
9573
- /* @__PURE__ */ jsxs12(Text14, { color: theme17.textSubtle, children: [
9882
+ /* @__PURE__ */ jsx13(Text13, { bold: true, color, children: event.type }),
9883
+ /* @__PURE__ */ jsxs11(Text13, { color: theme14.textSubtle, children: [
9574
9884
  separator,
9575
9885
  "c: copy"
9576
9886
  ] }),
9577
- /* @__PURE__ */ jsxs12(Text14, { color: theme17.textSubtle, children: [
9887
+ /* @__PURE__ */ jsxs11(Text13, { color: theme14.textSubtle, children: [
9578
9888
  separator,
9579
9889
  "Esc: close"
9580
9890
  ] })
9581
9891
  ] }),
9582
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", flexGrow: 1, children: [
9583
- /* @__PURE__ */ jsx16(Box13, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx16(Text14, { color: theme17.text, wrap: "truncate", children: line }, i)) }),
9584
- /* @__PURE__ */ jsx16(
9892
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", flexGrow: 1, children: [
9893
+ /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx13(Text13, { color: theme14.text, wrap: "truncate", children: line }, i)) }),
9894
+ /* @__PURE__ */ jsx13(
9585
9895
  Scrollbar,
9586
9896
  {
9587
9897
  totalLines,
@@ -9595,7 +9905,7 @@ function EventDetailView({
9595
9905
  }
9596
9906
  );
9597
9907
  }
9598
- function EventStreamPanel({
9908
+ var EventStreamPanel = memo2(function EventStreamPanel2({
9599
9909
  events,
9600
9910
  startTime,
9601
9911
  maxVisibleLines = 50,
@@ -9606,8 +9916,8 @@ function EventStreamPanel({
9606
9916
  activeFilterCount = 0,
9607
9917
  totalTypeCount = 0
9608
9918
  }) {
9609
- const separator = theme17.separator ?? " \xB7 ";
9610
- const { rows, hiddenAbove } = useMemo4(() => {
9919
+ const separator = theme14.separator ?? " \xB7 ";
9920
+ const { rows, hiddenAbove } = useMemo3(() => {
9611
9921
  if (events.length === 0) return { rows: [], hiddenAbove: 0 };
9612
9922
  const total = events.length;
9613
9923
  const half = Math.floor(maxVisibleLines / 2);
@@ -9628,14 +9938,14 @@ function EventStreamPanel({
9628
9938
  key: `${start + i}`,
9629
9939
  time: formatTimestamp(evt.timestamp, startTime),
9630
9940
  type: evt.type,
9631
- color: EVENT_COLORS[evt.type] || theme17.text,
9632
- data: formatEventData(evt.data)
9941
+ color: EVENT_COLORS[evt.type] || theme14.text,
9942
+ data: formatEventData(evt.listData ?? evt.data)
9633
9943
  })),
9634
9944
  hiddenAbove: start
9635
9945
  };
9636
9946
  }, [events, startTime, maxVisibleLines, selectedIndex]);
9637
9947
  if (detailEvent) {
9638
- return /* @__PURE__ */ jsx16(
9948
+ return /* @__PURE__ */ jsx13(
9639
9949
  EventDetailView,
9640
9950
  {
9641
9951
  event: detailEvent,
@@ -9647,46 +9957,46 @@ function EventStreamPanel({
9647
9957
  );
9648
9958
  }
9649
9959
  if (events.length === 0) {
9650
- return /* @__PURE__ */ jsx16(
9651
- Box13,
9960
+ return /* @__PURE__ */ jsx13(
9961
+ Box12,
9652
9962
  {
9653
9963
  flexDirection: "column",
9654
- paddingX: theme17.panelPaddingX,
9655
- paddingY: theme17.panelPaddingY,
9656
- children: /* @__PURE__ */ jsx16(Text14, { color: theme17.textSubtle, children: "Waiting for events..." })
9964
+ paddingX: theme14.panelPaddingX,
9965
+ paddingY: theme14.panelPaddingY,
9966
+ children: /* @__PURE__ */ jsx13(Text13, { color: theme14.textSubtle, children: "Waiting for events..." })
9657
9967
  }
9658
9968
  );
9659
9969
  }
9660
- return /* @__PURE__ */ jsxs12(
9661
- Box13,
9970
+ return /* @__PURE__ */ jsxs11(
9971
+ Box12,
9662
9972
  {
9663
9973
  flexDirection: "column",
9664
- paddingX: theme17.panelPaddingX,
9665
- paddingY: theme17.panelPaddingY,
9974
+ paddingX: theme14.panelPaddingX,
9975
+ paddingY: theme14.panelPaddingY,
9666
9976
  children: [
9667
- /* @__PURE__ */ jsxs12(Box13, { marginBottom: 0, children: [
9668
- /* @__PURE__ */ jsx16(Text14, { bold: true, color: theme17.accent, children: "Event Stream" }),
9669
- /* @__PURE__ */ jsxs12(Text14, { color: theme17.textSubtle, children: [
9977
+ /* @__PURE__ */ jsxs11(Box12, { marginBottom: 0, children: [
9978
+ /* @__PURE__ */ jsx13(Text13, { bold: true, color: theme14.accent, children: "Event Stream" }),
9979
+ /* @__PURE__ */ jsxs11(Text13, { color: theme14.textSubtle, children: [
9670
9980
  separator,
9671
9981
  events.length,
9672
9982
  " events",
9673
9983
  activeFilterCount > 0 && ` (${activeFilterCount}/${totalTypeCount} types)`
9674
9984
  ] }),
9675
- /* @__PURE__ */ jsx16(Text14, { color: theme17.textSubtle, children: separator }),
9676
- /* @__PURE__ */ jsx16(Text14, { color: theme17.info, children: "f" }),
9677
- /* @__PURE__ */ jsx16(Text14, { color: theme17.textSubtle, children: ": filter" })
9985
+ /* @__PURE__ */ jsx13(Text13, { color: theme14.textSubtle, children: separator }),
9986
+ /* @__PURE__ */ jsx13(Text13, { color: theme14.info, children: "f" }),
9987
+ /* @__PURE__ */ jsx13(Text13, { color: theme14.textSubtle, children: ": filter" })
9678
9988
  ] }),
9679
- /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
9680
- /* @__PURE__ */ jsx16(Box13, { flexDirection: "column", flexGrow: 1, children: rows.map((row) => {
9989
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", children: [
9990
+ /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", flexGrow: 1, children: rows.map((row) => {
9681
9991
  const isSelected = row.globalIndex === selectedIndex;
9682
- return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", flexWrap: "nowrap", children: [
9683
- /* @__PURE__ */ jsx16(Box13, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text14, { color: isSelected ? theme17.accentActive : theme17.textSubtle, children: isSelected ? "\u203A" : " " }) }),
9684
- /* @__PURE__ */ jsx16(Box13, { width: 9, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text14, { color: theme17.textMuted, children: row.time }) }),
9685
- /* @__PURE__ */ jsx16(Box13, { width: 28, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text14, { color: isSelected ? theme17.accentActive : row.color, bold: isSelected, children: row.type }) }),
9686
- /* @__PURE__ */ jsx16(Box13, { flexShrink: 1, children: /* @__PURE__ */ jsx16(Text14, { color: theme17.textMuted, wrap: "truncate", children: row.data }) })
9992
+ return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", flexWrap: "nowrap", children: [
9993
+ /* @__PURE__ */ jsx13(Box12, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text13, { color: isSelected ? theme14.accentActive : theme14.textSubtle, children: isSelected ? "\u203A" : " " }) }),
9994
+ /* @__PURE__ */ jsx13(Box12, { width: 9, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text13, { color: theme14.textMuted, children: row.time }) }),
9995
+ /* @__PURE__ */ jsx13(Box12, { width: 32, flexShrink: 0, children: /* @__PURE__ */ jsx13(Text13, { color: isSelected ? theme14.accentActive : row.color, bold: isSelected, children: row.type }) }),
9996
+ /* @__PURE__ */ jsx13(Box12, { flexShrink: 1, children: /* @__PURE__ */ jsx13(Text13, { color: theme14.textMuted, wrap: "truncate", children: row.data }) })
9687
9997
  ] }, row.key);
9688
9998
  }) }),
9689
- /* @__PURE__ */ jsx16(
9999
+ /* @__PURE__ */ jsx13(
9690
10000
  Scrollbar,
9691
10001
  {
9692
10002
  totalLines: events.length,
@@ -9699,18 +10009,20 @@ function EventStreamPanel({
9699
10009
  ]
9700
10010
  }
9701
10011
  );
9702
- }
10012
+ }, (prev, next) => {
10013
+ return prev.events.length === next.events.length && prev.selectedIndex === next.selectedIndex && prev.maxVisibleLines === next.maxVisibleLines && prev.startTime === next.startTime && prev.detailEvent === next.detailEvent && prev.detailScrollOffset === next.detailScrollOffset && prev.width === next.width && prev.activeFilterCount === next.activeFilterCount && prev.totalTypeCount === next.totalTypeCount;
10014
+ });
9703
10015
 
9704
10016
  // src/ink/marathon/EventTypeFilter.tsx
9705
- import { useState as useState16, useCallback as useCallback4 } from "react";
9706
- import { Box as Box14, Text as Text15, useInput as useInput5, useStdout } from "ink";
9707
- import { theme as theme18 } from "@runtypelabs/ink-components";
9708
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
10017
+ import { useState as useState13, useCallback as useCallback4 } from "react";
10018
+ import { Box as Box13, Text as Text14, useInput as useInput5, useStdout } from "ink";
10019
+ import { theme as theme15 } from "@runtypelabs/ink-components";
10020
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
9709
10021
  var MAX_VISIBLE = 14;
9710
10022
  function EventTypeFilter({ eventTypes, selectedTypes, onApply, onCancel }) {
9711
10023
  const { stdout } = useStdout();
9712
- const [localSelected, setLocalSelected] = useState16(() => new Set(selectedTypes));
9713
- const [selectedIndex, setSelectedIndex] = useState16(0);
10024
+ const [localSelected, setLocalSelected] = useState13(() => new Set(selectedTypes));
10025
+ const [selectedIndex, setSelectedIndex] = useState13(0);
9714
10026
  const contentWidth = (stdout?.columns ?? 120) - 4;
9715
10027
  const pad = useCallback4(
9716
10028
  (text) => text.length < contentWidth ? text + " ".repeat(contentWidth - text.length) : text,
@@ -9769,60 +10081,60 @@ function EventTypeFilter({ eventTypes, selectedTypes, onApply, onCancel }) {
9769
10081
  return;
9770
10082
  }
9771
10083
  });
9772
- const separator = theme18.separator ?? " \xB7 ";
9773
- return /* @__PURE__ */ jsxs13(
9774
- Box14,
10084
+ const separator = theme15.separator ?? " \xB7 ";
10085
+ return /* @__PURE__ */ jsxs12(
10086
+ Box13,
9775
10087
  {
9776
10088
  borderStyle: "single",
9777
- borderColor: theme18.border,
9778
- backgroundColor: theme18.surfaceElevated,
9779
- paddingX: theme18.panelPaddingX,
9780
- paddingY: theme18.panelPaddingY,
10089
+ borderColor: theme15.border,
10090
+ backgroundColor: theme15.surfaceElevated,
10091
+ paddingX: theme15.panelPaddingX,
10092
+ paddingY: theme15.panelPaddingY,
9781
10093
  flexDirection: "column",
9782
10094
  children: [
9783
- /* @__PURE__ */ jsxs13(Box14, { marginBottom: 1, children: [
9784
- /* @__PURE__ */ jsx17(Text15, { color: theme18.accent, bold: true, children: "Filter Event Types" }),
9785
- /* @__PURE__ */ jsxs13(Text15, { color: theme18.textSubtle, children: [
10095
+ /* @__PURE__ */ jsxs12(Box13, { marginBottom: 1, children: [
10096
+ /* @__PURE__ */ jsx14(Text14, { color: theme15.accent, bold: true, children: "Filter Event Types" }),
10097
+ /* @__PURE__ */ jsxs12(Text14, { color: theme15.textSubtle, children: [
9786
10098
  " ",
9787
10099
  localSelected.size === 0 ? "all" : `${localSelected.size}/${eventTypes.length}`
9788
10100
  ] })
9789
10101
  ] }),
9790
- /* @__PURE__ */ jsx17(Box14, { height: 1, children: /* @__PURE__ */ jsx17(Text15, { color: theme18.textSubtle, children: pad(showScrollUp ? " ..." : "") }) }),
9791
- /* @__PURE__ */ jsx17(Box14, { flexDirection: "column", height: MAX_VISIBLE, children: eventTypes.length === 0 ? /* @__PURE__ */ jsx17(Text15, { color: theme18.textSubtle, children: pad(" No event types") }) : visibleTypes.map((type, i) => {
10102
+ /* @__PURE__ */ jsx14(Box13, { height: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme15.textSubtle, children: pad(showScrollUp ? " ..." : "") }) }),
10103
+ /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", height: MAX_VISIBLE, children: eventTypes.length === 0 ? /* @__PURE__ */ jsx14(Text14, { color: theme15.textSubtle, children: pad(" No event types") }) : visibleTypes.map((type, i) => {
9792
10104
  const globalIndex = windowStart + i;
9793
10105
  const isHighlighted = globalIndex === selectedIndex;
9794
10106
  const isChecked = localSelected.has(type);
9795
- const color = EVENT_COLORS[type] || theme18.text;
10107
+ const color = EVENT_COLORS[type] || theme15.text;
9796
10108
  const checkbox = isChecked ? "[\u2713]" : "[ ]";
9797
10109
  const indicator = isHighlighted ? "\u203A " : " ";
9798
- return /* @__PURE__ */ jsxs13(Box14, { children: [
9799
- /* @__PURE__ */ jsx17(
9800
- Text15,
10110
+ return /* @__PURE__ */ jsxs12(Box13, { children: [
10111
+ /* @__PURE__ */ jsx14(
10112
+ Text14,
9801
10113
  {
9802
- color: isHighlighted ? theme18.accentActive : theme18.textMuted,
10114
+ color: isHighlighted ? theme15.accentActive : theme15.textMuted,
9803
10115
  bold: isHighlighted,
9804
10116
  children: indicator
9805
10117
  }
9806
10118
  ),
9807
- /* @__PURE__ */ jsxs13(Text15, { color: isHighlighted ? theme18.accentActive : theme18.textSubtle, children: [
10119
+ /* @__PURE__ */ jsxs12(Text14, { color: isHighlighted ? theme15.accentActive : theme15.textSubtle, children: [
9808
10120
  checkbox,
9809
10121
  " "
9810
10122
  ] }),
9811
- /* @__PURE__ */ jsx17(Text15, { color: isHighlighted ? theme18.accentActive : color, children: pad(type) })
10123
+ /* @__PURE__ */ jsx14(Text14, { color: isHighlighted ? theme15.accentActive : color, children: pad(type) })
9812
10124
  ] }, type);
9813
10125
  }) }),
9814
- /* @__PURE__ */ jsx17(Box14, { height: 1, children: /* @__PURE__ */ jsx17(Text15, { color: theme18.textSubtle, children: pad(showScrollDown ? " ..." : "") }) }),
9815
- /* @__PURE__ */ jsx17(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { color: theme18.textSubtle, children: ["Space toggle", "a: all/none", "Enter apply", "Esc cancel"].join(separator) }) })
10126
+ /* @__PURE__ */ jsx14(Box13, { height: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme15.textSubtle, children: pad(showScrollDown ? " ..." : "") }) }),
10127
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme15.textSubtle, children: ["Space toggle", "a: all/none", "Enter apply", "Esc cancel"].join(separator) }) })
9816
10128
  ]
9817
10129
  }
9818
10130
  );
9819
10131
  }
9820
10132
 
9821
10133
  // src/ink/marathon/ReasoningPanel.tsx
9822
- import { useMemo as useMemo5 } from "react";
9823
- import { Box as Box15, Text as Text16 } from "ink";
9824
- import { theme as theme19 } from "@runtypelabs/ink-components";
9825
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
10134
+ import { useMemo as useMemo4 } from "react";
10135
+ import { Box as Box14, Text as Text15 } from "ink";
10136
+ import { theme as theme16 } from "@runtypelabs/ink-components";
10137
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
9826
10138
  function wrapLines3(lines, width) {
9827
10139
  if (width <= 0) return lines;
9828
10140
  const result = [];
@@ -9837,7 +10149,7 @@ function wrapLines3(lines, width) {
9837
10149
  }
9838
10150
  return result;
9839
10151
  }
9840
- function formatCharCount2(chars) {
10152
+ function formatCharCount(chars) {
9841
10153
  if (chars >= 1e3) {
9842
10154
  return `${(chars / 1e3).toFixed(1)}k`;
9843
10155
  }
@@ -9854,50 +10166,51 @@ function ReasoningPanel({
9854
10166
  scrollOffset = 0,
9855
10167
  width = 120
9856
10168
  }) {
9857
- const separator = theme19.separator ?? " \xB7 ";
9858
- const { lines, totalLines, clampedOffset } = useMemo5(() => {
9859
- const trimmed = reasoning.trimEnd();
9860
- if (!trimmed) return { lines: [], totalLines: 0, clampedOffset: 0 };
9861
- const allLines = getReasoningLines(trimmed, width);
9862
- const total = allLines.length;
9863
- const clamped = Math.min(scrollOffset, Math.max(0, total - maxVisibleLines));
9864
- const start = clamped;
9865
- const end = Math.min(total, start + maxVisibleLines);
9866
- return { lines: allLines.slice(start, end), totalLines: total, clampedOffset: clamped };
9867
- }, [reasoning, maxVisibleLines, scrollOffset, width]);
9868
- if (!reasoning.trim()) {
9869
- return /* @__PURE__ */ jsx18(
9870
- Box15,
10169
+ const separator = theme16.separator ?? " \xB7 ";
10170
+ const trimmedReasoning = useMemo4(() => reasoning.trimEnd(), [reasoning]);
10171
+ const allLines = useMemo4(
10172
+ () => trimmedReasoning ? getReasoningLines(trimmedReasoning, width) : [],
10173
+ [trimmedReasoning, width]
10174
+ );
10175
+ const totalLines = allLines.length;
10176
+ const clampedOffset = Math.min(scrollOffset, Math.max(0, totalLines - maxVisibleLines));
10177
+ const lines = useMemo4(
10178
+ () => allLines.slice(clampedOffset, clampedOffset + maxVisibleLines),
10179
+ [allLines, clampedOffset, maxVisibleLines]
10180
+ );
10181
+ if (!trimmedReasoning) {
10182
+ return /* @__PURE__ */ jsx15(
10183
+ Box14,
9871
10184
  {
9872
10185
  flexDirection: "column",
9873
- paddingX: theme19.panelPaddingX,
9874
- paddingY: theme19.panelPaddingY,
9875
- children: /* @__PURE__ */ jsx18(Text16, { color: theme19.textSubtle, children: "No reasoning yet..." })
10186
+ paddingX: theme16.panelPaddingX,
10187
+ paddingY: theme16.panelPaddingY,
10188
+ children: /* @__PURE__ */ jsx15(Text15, { color: theme16.textSubtle, children: "No reasoning yet..." })
9876
10189
  }
9877
10190
  );
9878
10191
  }
9879
- return /* @__PURE__ */ jsxs14(
9880
- Box15,
10192
+ return /* @__PURE__ */ jsxs13(
10193
+ Box14,
9881
10194
  {
9882
10195
  flexDirection: "column",
9883
- paddingX: theme19.panelPaddingX,
9884
- paddingY: theme19.panelPaddingY,
10196
+ paddingX: theme16.panelPaddingX,
10197
+ paddingY: theme16.panelPaddingY,
9885
10198
  children: [
9886
- /* @__PURE__ */ jsxs14(Box15, { marginBottom: 0, children: [
9887
- /* @__PURE__ */ jsx18(Text16, { bold: true, color: theme19.accent, children: "Reasoning" }),
9888
- /* @__PURE__ */ jsxs14(Text16, { color: theme19.textSubtle, children: [
10199
+ /* @__PURE__ */ jsxs13(Box14, { marginBottom: 0, children: [
10200
+ /* @__PURE__ */ jsx15(Text15, { bold: true, color: theme16.accent, children: "Reasoning" }),
10201
+ /* @__PURE__ */ jsxs13(Text15, { color: theme16.textSubtle, children: [
9889
10202
  separator,
9890
- formatCharCount2(reasoning.length),
10203
+ formatCharCount(reasoning.length),
9891
10204
  " chars"
9892
10205
  ] }),
9893
- /* @__PURE__ */ jsxs14(Text16, { color: theme19.textSubtle, children: [
10206
+ /* @__PURE__ */ jsxs13(Text15, { color: theme16.textSubtle, children: [
9894
10207
  separator,
9895
10208
  "c: copy"
9896
10209
  ] })
9897
10210
  ] }),
9898
- /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", flexGrow: 1, children: [
9899
- /* @__PURE__ */ jsx18(Box15, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx18(Text16, { color: theme19.textMuted, wrap: "truncate", children: line || " " }, i)) }),
9900
- /* @__PURE__ */ jsx18(
10211
+ /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", flexGrow: 1, children: [
10212
+ /* @__PURE__ */ jsx15(Box14, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx15(Text15, { color: theme16.textMuted, wrap: "truncate", children: line || " " }, i)) }),
10213
+ /* @__PURE__ */ jsx15(
9901
10214
  Scrollbar,
9902
10215
  {
9903
10216
  totalLines,
@@ -9913,14 +10226,14 @@ function ReasoningPanel({
9913
10226
  }
9914
10227
 
9915
10228
  // src/ink/marathon/FilesPanel.tsx
9916
- import { useMemo as useMemo6 } from "react";
9917
- import { Box as Box16, Text as Text17 } from "ink";
9918
- import { theme as theme20, renderMarkdownToTerminal } from "@runtypelabs/ink-components";
9919
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
10229
+ import { useMemo as useMemo5 } from "react";
10230
+ import { Box as Box15, Text as Text16 } from "ink";
10231
+ import { theme as theme17, renderMarkdownToTerminal } from "@runtypelabs/ink-components";
10232
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
9920
10233
  var TYPE_COLORS = {
9921
- plan: theme20.accent,
9922
- written: theme20.success,
9923
- read: theme20.textMuted
10234
+ plan: theme17.accent,
10235
+ written: theme17.success,
10236
+ read: theme17.textMuted
9924
10237
  };
9925
10238
  var TYPE_LABELS = {
9926
10239
  plan: "plan",
@@ -9937,6 +10250,7 @@ function shortenPath(filePath, maxWidth) {
9937
10250
  return ".../" + segments[segments.length - 1];
9938
10251
  }
9939
10252
  var ANSI_RE = /\x1B\[[0-9;]*m/g;
10253
+ var ANSI_TEST_RE = /\x1B\[[0-9;]*m/;
9940
10254
  function visibleLength(str) {
9941
10255
  return str.replace(ANSI_RE, "").length;
9942
10256
  }
@@ -9947,7 +10261,7 @@ function wrapLines4(lines, width) {
9947
10261
  if (visibleLength(line) <= width) {
9948
10262
  result.push(line);
9949
10263
  } else {
9950
- if (!ANSI_RE.test(line)) {
10264
+ if (!ANSI_TEST_RE.test(line)) {
9951
10265
  for (let i = 0; i < line.length; i += width) {
9952
10266
  result.push(line.slice(i, i + width));
9953
10267
  }
@@ -9998,55 +10312,59 @@ function FileContentView({
9998
10312
  scrollOffset = 0,
9999
10313
  width = 120
10000
10314
  }) {
10001
- const separator = theme20.separator ?? " \xB7 ";
10315
+ const separator = theme17.separator ?? " \xB7 ";
10002
10316
  const contentWidth = Math.max(20, width - 4);
10003
10317
  const isMd = isMarkdownFile(file.path);
10004
- const { lines, displayTotalLines, clampedOffset } = useMemo6(() => {
10005
- const displayContent = isMd ? renderMarkdownToTerminal(content) : content;
10006
- const rawLines = displayContent.split("\n");
10007
- const allLines = wrapLines4(rawLines, contentWidth);
10008
- const total = allLines.length;
10009
- const clamped = Math.min(scrollOffset, Math.max(0, total - maxVisibleLines));
10010
- const start = clamped;
10011
- const end = Math.min(total, start + maxVisibleLines);
10012
- return { lines: allLines.slice(start, end), displayTotalLines: total, clampedOffset: clamped };
10013
- }, [content, isMd, maxVisibleLines, scrollOffset, contentWidth]);
10318
+ const displayContent = useMemo5(
10319
+ () => isMd ? renderMarkdownToTerminal(content) : content,
10320
+ [content, isMd]
10321
+ );
10322
+ const allLines = useMemo5(
10323
+ () => wrapLines4(displayContent.split("\n"), contentWidth),
10324
+ [displayContent, contentWidth]
10325
+ );
10326
+ const displayTotalLines = allLines.length;
10327
+ const clampedOffset = Math.min(scrollOffset, Math.max(0, displayTotalLines - maxVisibleLines));
10328
+ const lines = useMemo5(
10329
+ () => allLines.slice(clampedOffset, clampedOffset + maxVisibleLines),
10330
+ [allLines, clampedOffset, maxVisibleLines]
10331
+ );
10014
10332
  const filename = file.path.split("/").pop() || file.path;
10015
- return /* @__PURE__ */ jsxs15(
10016
- Box16,
10333
+ return /* @__PURE__ */ jsxs14(
10334
+ Box15,
10017
10335
  {
10018
10336
  flexDirection: "column",
10019
- paddingX: theme20.panelPaddingX,
10020
- paddingY: theme20.panelPaddingY,
10337
+ paddingX: theme17.panelPaddingX,
10338
+ paddingY: theme17.panelPaddingY,
10021
10339
  flexGrow: 1,
10022
10340
  children: [
10023
- /* @__PURE__ */ jsxs15(Box16, { marginBottom: theme20.sectionGap, children: [
10024
- /* @__PURE__ */ jsx19(Text17, { bold: true, color: theme20.accent, children: filename }),
10025
- /* @__PURE__ */ jsxs15(Text17, { color: theme20.textSubtle, children: [
10341
+ /* @__PURE__ */ jsxs14(Box15, { marginBottom: theme17.sectionGap, children: [
10342
+ /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme17.accent, children: filename }),
10343
+ /* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
10026
10344
  separator,
10027
10345
  totalLines,
10028
10346
  " lines"
10029
10347
  ] }),
10030
- truncated && /* @__PURE__ */ jsxs15(Text17, { color: theme20.warning, children: [
10348
+ truncated && /* @__PURE__ */ jsxs14(Text16, { color: theme17.warning, children: [
10031
10349
  separator,
10032
10350
  "truncated"
10033
10351
  ] }),
10034
- source === "tool" && /* @__PURE__ */ jsxs15(Text17, { color: theme20.textSubtle, children: [
10352
+ source === "tool" && /* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
10035
10353
  separator,
10036
10354
  "from tool call"
10037
10355
  ] }),
10038
- /* @__PURE__ */ jsxs15(Text17, { color: theme20.textSubtle, children: [
10356
+ /* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
10039
10357
  separator,
10040
10358
  "c: copy"
10041
10359
  ] }),
10042
- /* @__PURE__ */ jsxs15(Text17, { color: theme20.textSubtle, children: [
10360
+ /* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
10043
10361
  separator,
10044
10362
  "Esc: back"
10045
10363
  ] })
10046
10364
  ] }),
10047
- /* @__PURE__ */ jsxs15(Box16, { flexDirection: "row", flexGrow: 1, children: [
10048
- /* @__PURE__ */ jsx19(Box16, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx19(Text17, { color: theme20.text, wrap: "truncate", children: line || " " }, i)) }),
10049
- /* @__PURE__ */ jsx19(
10365
+ /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", flexGrow: 1, children: [
10366
+ /* @__PURE__ */ jsx16(Box15, { flexDirection: "column", flexGrow: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx16(Text16, { color: theme17.text, wrap: "truncate", children: line || " " }, i)) }),
10367
+ /* @__PURE__ */ jsx16(
10050
10368
  Scrollbar,
10051
10369
  {
10052
10370
  totalLines: displayTotalLines,
@@ -10072,9 +10390,9 @@ function FilesPanel({
10072
10390
  detailScrollOffset = 0,
10073
10391
  width = 120
10074
10392
  }) {
10075
- const separator = theme20.separator ?? " \xB7 ";
10393
+ const separator = theme17.separator ?? " \xB7 ";
10076
10394
  if (detailFile && detailContent !== null) {
10077
- return /* @__PURE__ */ jsx19(
10395
+ return /* @__PURE__ */ jsx16(
10078
10396
  FileContentView,
10079
10397
  {
10080
10398
  file: detailFile,
@@ -10089,7 +10407,7 @@ function FilesPanel({
10089
10407
  );
10090
10408
  }
10091
10409
  const pathMaxWidth = Math.max(20, width - 14);
10092
- const { rows, hiddenAbove } = useMemo6(() => {
10410
+ const { rows, hiddenAbove } = useMemo5(() => {
10093
10411
  if (files.length === 0) return { rows: [], hiddenAbove: 0 };
10094
10412
  const total = files.length;
10095
10413
  const half = Math.floor(maxVisibleLines / 2);
@@ -10114,47 +10432,47 @@ function FilesPanel({
10114
10432
  };
10115
10433
  }, [files, maxVisibleLines, selectedIndex]);
10116
10434
  if (files.length === 0) {
10117
- return /* @__PURE__ */ jsx19(
10118
- Box16,
10435
+ return /* @__PURE__ */ jsx16(
10436
+ Box15,
10119
10437
  {
10120
10438
  flexDirection: "column",
10121
- paddingX: theme20.panelPaddingX,
10122
- paddingY: theme20.panelPaddingY,
10123
- children: /* @__PURE__ */ jsx19(Text17, { color: theme20.textSubtle, children: "No files tracked yet..." })
10439
+ paddingX: theme17.panelPaddingX,
10440
+ paddingY: theme17.panelPaddingY,
10441
+ children: /* @__PURE__ */ jsx16(Text16, { color: theme17.textSubtle, children: "No files tracked yet..." })
10124
10442
  }
10125
10443
  );
10126
10444
  }
10127
- return /* @__PURE__ */ jsxs15(
10128
- Box16,
10445
+ return /* @__PURE__ */ jsxs14(
10446
+ Box15,
10129
10447
  {
10130
10448
  flexDirection: "column",
10131
- paddingX: theme20.panelPaddingX,
10132
- paddingY: theme20.panelPaddingY,
10449
+ paddingX: theme17.panelPaddingX,
10450
+ paddingY: theme17.panelPaddingY,
10133
10451
  children: [
10134
- /* @__PURE__ */ jsxs15(Box16, { marginBottom: 0, children: [
10135
- /* @__PURE__ */ jsx19(Text17, { bold: true, color: theme20.accent, children: "Files" }),
10136
- /* @__PURE__ */ jsxs15(Text17, { color: theme20.textSubtle, children: [
10452
+ /* @__PURE__ */ jsxs14(Box15, { marginBottom: 0, children: [
10453
+ /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme17.accent, children: "Files" }),
10454
+ /* @__PURE__ */ jsxs14(Text16, { color: theme17.textSubtle, children: [
10137
10455
  separator,
10138
10456
  files.length,
10139
10457
  " files"
10140
10458
  ] })
10141
10459
  ] }),
10142
- /* @__PURE__ */ jsxs15(Box16, { flexDirection: "row", children: [
10143
- /* @__PURE__ */ jsx19(Box16, { flexDirection: "column", flexGrow: 1, children: rows.map((row) => {
10460
+ /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", children: [
10461
+ /* @__PURE__ */ jsx16(Box15, { flexDirection: "column", flexGrow: 1, children: rows.map((row) => {
10144
10462
  const isSelected = row.globalIndex === selectedIndex;
10145
- const typeColor = TYPE_COLORS[row.file.type] || theme20.text;
10463
+ const typeColor = TYPE_COLORS[row.file.type] || theme17.text;
10146
10464
  const typeLabel = TYPE_LABELS[row.file.type] || row.file.type;
10147
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "row", flexWrap: "nowrap", children: [
10148
- /* @__PURE__ */ jsx19(Box16, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx19(Text17, { color: isSelected ? theme20.accentActive : theme20.textSubtle, children: isSelected ? "\u203A" : " " }) }),
10149
- /* @__PURE__ */ jsx19(Box16, { width: 8, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text17, { color: typeColor, children: [
10465
+ return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "row", flexWrap: "nowrap", children: [
10466
+ /* @__PURE__ */ jsx16(Box15, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text16, { color: isSelected ? theme17.accentActive : theme17.textSubtle, children: isSelected ? "\u203A" : " " }) }),
10467
+ /* @__PURE__ */ jsx16(Box15, { width: 8, flexShrink: 0, children: /* @__PURE__ */ jsxs14(Text16, { color: typeColor, children: [
10150
10468
  "[",
10151
10469
  typeLabel,
10152
10470
  "]"
10153
10471
  ] }) }),
10154
- /* @__PURE__ */ jsx19(Box16, { flexShrink: 1, children: /* @__PURE__ */ jsx19(
10155
- Text17,
10472
+ /* @__PURE__ */ jsx16(Box15, { flexShrink: 1, children: /* @__PURE__ */ jsx16(
10473
+ Text16,
10156
10474
  {
10157
- color: isSelected ? theme20.accentActive : theme20.text,
10475
+ color: isSelected ? theme17.accentActive : theme17.text,
10158
10476
  bold: isSelected,
10159
10477
  wrap: "truncate",
10160
10478
  children: shortenPath(row.file.path, pathMaxWidth)
@@ -10162,7 +10480,7 @@ function FilesPanel({
10162
10480
  ) })
10163
10481
  ] }, row.key);
10164
10482
  }) }),
10165
- /* @__PURE__ */ jsx19(
10483
+ /* @__PURE__ */ jsx16(
10166
10484
  Scrollbar,
10167
10485
  {
10168
10486
  totalLines: files.length,
@@ -10178,10 +10496,10 @@ function FilesPanel({
10178
10496
  }
10179
10497
 
10180
10498
  // src/ink/marathon/HelpPanel.tsx
10181
- import { useMemo as useMemo7 } from "react";
10182
- import { Box as Box17, Text as Text18 } from "ink";
10183
- import { theme as theme21 } from "@runtypelabs/ink-components";
10184
- import { Fragment as Fragment2, jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
10499
+ import { useMemo as useMemo6 } from "react";
10500
+ import { Box as Box16, Text as Text17 } from "ink";
10501
+ import { theme as theme18 } from "@runtypelabs/ink-components";
10502
+ import { Fragment, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
10185
10503
  var NAVIGATION_SHORTCUTS = [
10186
10504
  { key: "Tab", desc: "Next screen" },
10187
10505
  { key: "Esc", desc: "Back / close detail / close help" },
@@ -10252,37 +10570,37 @@ function HelpPanel({ width, height, isCheckpoint, scrollOffset }) {
10252
10570
  const dialogWidth = Math.min(width - 4, 60);
10253
10571
  const contentHeight = Math.min(height - 2, isCheckpoint ? 38 : 28) - 8;
10254
10572
  const visibleRows = Math.max(1, contentHeight);
10255
- const allLines = useMemo7(() => buildLines(isCheckpoint), [isCheckpoint]);
10573
+ const allLines = useMemo6(() => buildLines(isCheckpoint), [isCheckpoint]);
10256
10574
  const maxScroll = Math.max(0, allLines.length - visibleRows);
10257
10575
  const clampedOffset = Math.min(scrollOffset, maxScroll);
10258
10576
  const visibleLines = allLines.slice(clampedOffset, clampedOffset + visibleRows);
10259
- return /* @__PURE__ */ jsxs16(
10260
- Box17,
10577
+ return /* @__PURE__ */ jsxs15(
10578
+ Box16,
10261
10579
  {
10262
10580
  width: dialogWidth,
10263
10581
  height: Math.min(height - 2, isCheckpoint ? 38 : 28),
10264
10582
  flexDirection: "column",
10265
10583
  borderStyle: "round",
10266
- borderColor: theme21.accent,
10267
- backgroundColor: theme21.surfaceElevated,
10584
+ borderColor: theme18.accent,
10585
+ backgroundColor: theme18.surfaceElevated,
10268
10586
  paddingX: 2,
10269
10587
  paddingY: 1,
10270
10588
  children: [
10271
- /* @__PURE__ */ jsx20(Text18, { bold: true, color: theme21.accent, children: "Keyboard Shortcuts" }),
10272
- /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", flexGrow: 1, marginTop: 1, children: [
10273
- /* @__PURE__ */ jsx20(Box17, { flexDirection: "column", flexGrow: 1, children: visibleLines.map((line, idx) => {
10589
+ /* @__PURE__ */ jsx17(Text17, { bold: true, color: theme18.accent, children: "Keyboard Shortcuts" }),
10590
+ /* @__PURE__ */ jsxs15(Box16, { flexDirection: "row", flexGrow: 1, marginTop: 1, children: [
10591
+ /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", flexGrow: 1, children: visibleLines.map((line, idx) => {
10274
10592
  if (line.type === "header") {
10275
- return /* @__PURE__ */ jsx20(Text18, { bold: true, color: theme21.accentActive, children: line.title }, idx);
10593
+ return /* @__PURE__ */ jsx17(Text17, { bold: true, color: theme18.accentActive, children: line.title }, idx);
10276
10594
  }
10277
10595
  if (line.type === "item") {
10278
- return /* @__PURE__ */ jsxs16(Text18, { children: [
10279
- /* @__PURE__ */ jsx20(Text18, { color: theme21.textSubtle, children: ` ${line.key.padEnd(16)}` }),
10280
- /* @__PURE__ */ jsx20(Text18, { color: theme21.textMuted, children: line.desc })
10596
+ return /* @__PURE__ */ jsxs15(Text17, { children: [
10597
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.textSubtle, children: ` ${line.key.padEnd(16)}` }),
10598
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.textMuted, children: line.desc })
10281
10599
  ] }, idx);
10282
10600
  }
10283
- return /* @__PURE__ */ jsx20(Text18, { children: " " }, idx);
10601
+ return /* @__PURE__ */ jsx17(Text17, { children: " " }, idx);
10284
10602
  }) }),
10285
- /* @__PURE__ */ jsx20(
10603
+ /* @__PURE__ */ jsx17(
10286
10604
  Scrollbar,
10287
10605
  {
10288
10606
  totalLines: allLines.length,
@@ -10292,15 +10610,15 @@ function HelpPanel({ width, height, isCheckpoint, scrollOffset }) {
10292
10610
  }
10293
10611
  )
10294
10612
  ] }),
10295
- /* @__PURE__ */ jsxs16(Box17, { marginTop: 1, children: [
10296
- /* @__PURE__ */ jsx20(Text18, { color: theme21.info, children: "Esc" }),
10297
- /* @__PURE__ */ jsx20(Text18, { color: theme21.muted, children: " or " }),
10298
- /* @__PURE__ */ jsx20(Text18, { color: theme21.info, children: "?" }),
10299
- /* @__PURE__ */ jsx20(Text18, { color: theme21.muted, children: " to close" }),
10300
- isCheckpoint && /* @__PURE__ */ jsxs16(Fragment2, { children: [
10301
- /* @__PURE__ */ jsx20(Text18, { color: theme21.muted, children: " (use " }),
10302
- /* @__PURE__ */ jsx20(Text18, { color: theme21.info, children: "/help" }),
10303
- /* @__PURE__ */ jsx20(Text18, { color: theme21.muted, children: " during checkpoint)" })
10613
+ /* @__PURE__ */ jsxs15(Box16, { marginTop: 1, children: [
10614
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.info, children: "Esc" }),
10615
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.muted, children: " or " }),
10616
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.info, children: "?" }),
10617
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.muted, children: " to close" }),
10618
+ isCheckpoint && /* @__PURE__ */ jsxs15(Fragment, { children: [
10619
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.muted, children: " (use " }),
10620
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.info, children: "/help" }),
10621
+ /* @__PURE__ */ jsx17(Text17, { color: theme18.muted, children: " during checkpoint)" })
10304
10622
  ] })
10305
10623
  ] })
10306
10624
  ]
@@ -10736,20 +11054,37 @@ function createEventLogWriter(stateFilePath2, initialSessionIndex) {
10736
11054
  let sessionIndex = initialSessionIndex;
10737
11055
  let currentPath = path3.join(eventsDir, sessionFileName(sessionIndex));
10738
11056
  let dirCreated = false;
11057
+ let stream = null;
10739
11058
  function ensureDir() {
10740
11059
  if (!dirCreated) {
10741
11060
  fs3.mkdirSync(eventsDir, { recursive: true });
10742
11061
  dirCreated = true;
10743
11062
  }
10744
11063
  }
11064
+ function ensureStream() {
11065
+ if (!stream || stream.destroyed) {
11066
+ ensureDir();
11067
+ stream = fs3.createWriteStream(currentPath, { flags: "a" });
11068
+ stream.on("error", () => {
11069
+ });
11070
+ }
11071
+ }
10745
11072
  return {
10746
11073
  append(event) {
10747
- ensureDir();
10748
- fs3.appendFileSync(currentPath, JSON.stringify(event) + "\n");
11074
+ ensureStream();
11075
+ stream.write(JSON.stringify(event) + "\n");
10749
11076
  },
10750
11077
  flush() {
11078
+ if (stream && !stream.destroyed) {
11079
+ stream.end();
11080
+ stream = null;
11081
+ }
10751
11082
  },
10752
11083
  setSessionIndex(nextIndex) {
11084
+ if (stream && !stream.destroyed) {
11085
+ stream.end();
11086
+ stream = null;
11087
+ }
10753
11088
  sessionIndex = nextIndex;
10754
11089
  currentPath = path3.join(eventsDir, sessionFileName(sessionIndex));
10755
11090
  }
@@ -10774,14 +11109,14 @@ function readSessionEventLog(stateFilePath2, sessionIndex) {
10774
11109
  }
10775
11110
 
10776
11111
  // src/ink/marathon/CheckpointPrompt.tsx
10777
- import { useState as useState21, useEffect as useEffect19, useRef as useRef7 } from "react";
10778
- import { Box as Box22, Text as Text24 } from "ink";
11112
+ import { useState as useState18, useEffect as useEffect16, useRef as useRef7 } from "react";
11113
+ import { Box as Box21, Text as Text23 } from "ink";
10779
11114
 
10780
11115
  // src/ink/marathon/BlinkingTextInput.tsx
10781
- import { useState as useState17, useEffect as useEffect16, useRef as useRef5 } from "react";
10782
- import { Text as Text19, useInput as useInput6 } from "ink";
11116
+ import { useState as useState14, useEffect as useEffect13, useRef as useRef5 } from "react";
11117
+ import { Text as Text18, useInput as useInput6 } from "ink";
10783
11118
  import chalk12 from "chalk";
10784
- import { jsx as jsx21 } from "react/jsx-runtime";
11119
+ import { jsx as jsx18 } from "react/jsx-runtime";
10785
11120
  var CURSOR_BLINK_MS = 800;
10786
11121
  function BlinkingTextInput({
10787
11122
  value: originalValue,
@@ -10794,14 +11129,14 @@ function BlinkingTextInput({
10794
11129
  onChange,
10795
11130
  onSubmit
10796
11131
  }) {
10797
- const [state, setState] = useState17({
11132
+ const [state, setState] = useState14({
10798
11133
  cursorOffset: (originalValue || "").length,
10799
11134
  cursorWidth: 0
10800
11135
  });
10801
- const [blinkVisible, setBlinkVisible] = useState17(true);
11136
+ const [blinkVisible, setBlinkVisible] = useState14(true);
10802
11137
  const blinkRef = useRef5(null);
10803
11138
  const { cursorOffset, cursorWidth } = state;
10804
- useEffect16(() => {
11139
+ useEffect13(() => {
10805
11140
  if (!focus || !showCursor || !blinkCursor) {
10806
11141
  setBlinkVisible(true);
10807
11142
  return;
@@ -10816,10 +11151,10 @@ function BlinkingTextInput({
10816
11151
  }
10817
11152
  };
10818
11153
  }, [focus, showCursor, blinkCursor]);
10819
- useEffect16(() => {
11154
+ useEffect13(() => {
10820
11155
  setBlinkVisible(true);
10821
11156
  }, [originalValue, cursorOffset]);
10822
- useEffect16(() => {
11157
+ useEffect13(() => {
10823
11158
  setState((previousState) => {
10824
11159
  if (!focus || !showCursor) {
10825
11160
  return previousState;
@@ -10905,19 +11240,19 @@ function BlinkingTextInput({
10905
11240
  },
10906
11241
  { isActive: focus }
10907
11242
  );
10908
- return /* @__PURE__ */ jsx21(Text19, { children: placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue });
11243
+ return /* @__PURE__ */ jsx18(Text18, { children: placeholder ? value.length > 0 ? renderedValue : renderedPlaceholder : renderedValue });
10909
11244
  }
10910
11245
 
10911
11246
  // src/ink/marathon/CheckpointPrompt.tsx
10912
- import { theme as theme26 } from "@runtypelabs/ink-components";
11247
+ import { theme as theme23 } from "@runtypelabs/ink-components";
10913
11248
 
10914
11249
  // src/ink/talk/ModelPicker.tsx
10915
- import { useState as useState18, useMemo as useMemo8, useCallback as useCallback5, useEffect as useEffect17 } from "react";
10916
- import { Box as Box18, Text as Text20, useInput as useInput7, useStdout as useStdout2 } from "ink";
11250
+ import { useState as useState15, useMemo as useMemo7, useCallback as useCallback5, useEffect as useEffect14 } from "react";
11251
+ import { Box as Box17, Text as Text19, useInput as useInput7, useStdout as useStdout2 } from "ink";
10917
11252
  import TextInput2 from "ink-text-input";
10918
11253
  import open3 from "open";
10919
- import { theme as theme22 } from "@runtypelabs/ink-components";
10920
- import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
11254
+ import { theme as theme19 } from "@runtypelabs/ink-components";
11255
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
10921
11256
  var DEFAULT_MODELS = [
10922
11257
  // Anthropic
10923
11258
  { label: "Claude Opus 4.6", value: "claude-opus-4-6", group: "Anthropic" },
@@ -10951,21 +11286,21 @@ var DEFAULT_MODELS = [
10951
11286
  var MAX_VISIBLE2 = 12;
10952
11287
  function ModelPicker({ currentModel, onSelect, onCancel, models }) {
10953
11288
  const { stdout } = useStdout2();
10954
- const [search, setSearch] = useState18("");
11289
+ const [search, setSearch] = useState15("");
10955
11290
  const availableModels = models && models.length > 0 ? models : DEFAULT_MODELS;
10956
11291
  const contentWidth = (stdout?.columns ?? 120) - 4;
10957
11292
  const pad = useCallback5(
10958
11293
  (text) => text.length < contentWidth ? text + " ".repeat(contentWidth - text.length) : text,
10959
11294
  [contentWidth]
10960
11295
  );
10961
- const filtered = useMemo8(() => {
11296
+ const filtered = useMemo7(() => {
10962
11297
  if (!search.trim()) return availableModels;
10963
11298
  const q = search.toLowerCase();
10964
11299
  return availableModels.filter(
10965
11300
  (m) => m.label.toLowerCase().includes(q) || m.value.toLowerCase().includes(q) || m.group.toLowerCase().includes(q)
10966
11301
  );
10967
11302
  }, [availableModels, search]);
10968
- const [selectedIndex, setSelectedIndex] = useState18(() => {
11303
+ const [selectedIndex, setSelectedIndex] = useState15(() => {
10969
11304
  const currentIndex = availableModels.findIndex((model) => model.value === currentModel);
10970
11305
  return currentIndex >= 0 ? currentIndex : 0;
10971
11306
  });
@@ -10974,7 +11309,7 @@ function ModelPicker({ currentModel, onSelect, onCancel, models }) {
10974
11309
  setSearch(value);
10975
11310
  setSelectedIndex(0);
10976
11311
  };
10977
- useEffect17(() => {
11312
+ useEffect14(() => {
10978
11313
  if (!search.trim()) {
10979
11314
  const currentIndex = filtered.findIndex((model) => model.value === currentModel);
10980
11315
  setSelectedIndex(currentIndex >= 0 ? currentIndex : 0);
@@ -11032,27 +11367,27 @@ function ModelPicker({ currentModel, onSelect, onCancel, models }) {
11032
11367
  }
11033
11368
  const showScrollUp = modelWindowStart > 0;
11034
11369
  const showScrollDown = modelWindowEnd < filtered.length;
11035
- return /* @__PURE__ */ jsxs17(
11036
- Box18,
11370
+ return /* @__PURE__ */ jsxs16(
11371
+ Box17,
11037
11372
  {
11038
11373
  borderStyle: "single",
11039
- borderColor: theme22.border,
11040
- backgroundColor: theme22.surfaceElevated,
11041
- paddingX: theme22.panelPaddingX,
11042
- paddingY: theme22.panelPaddingY,
11374
+ borderColor: theme19.border,
11375
+ backgroundColor: theme19.surfaceElevated,
11376
+ paddingX: theme19.panelPaddingX,
11377
+ paddingY: theme19.panelPaddingY,
11043
11378
  flexDirection: "column",
11044
11379
  children: [
11045
- /* @__PURE__ */ jsxs17(Box18, { marginBottom: 1, children: [
11046
- /* @__PURE__ */ jsx22(Text20, { color: theme22.accent, bold: true, children: "Select Model" }),
11047
- /* @__PURE__ */ jsxs17(Text20, { color: theme22.textSubtle, children: [
11380
+ /* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
11381
+ /* @__PURE__ */ jsx19(Text19, { color: theme19.accent, bold: true, children: "Select Model" }),
11382
+ /* @__PURE__ */ jsxs16(Text19, { color: theme19.textSubtle, children: [
11048
11383
  " ",
11049
11384
  filtered.length,
11050
11385
  " models"
11051
11386
  ] })
11052
11387
  ] }),
11053
- /* @__PURE__ */ jsxs17(Box18, { marginBottom: 1, children: [
11054
- /* @__PURE__ */ jsx22(Text20, { color: theme22.textSubtle, children: "/ " }),
11055
- /* @__PURE__ */ jsx22(
11388
+ /* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
11389
+ /* @__PURE__ */ jsx19(Text19, { color: theme19.textSubtle, children: "/ " }),
11390
+ /* @__PURE__ */ jsx19(
11056
11391
  TextInput2,
11057
11392
  {
11058
11393
  value: search,
@@ -11061,35 +11396,35 @@ function ModelPicker({ currentModel, onSelect, onCancel, models }) {
11061
11396
  }
11062
11397
  )
11063
11398
  ] }),
11064
- /* @__PURE__ */ jsx22(Box18, { height: 1, children: /* @__PURE__ */ jsx22(Text20, { color: theme22.textSubtle, children: pad(showScrollUp ? " ..." : "") }) }),
11065
- /* @__PURE__ */ jsx22(Box18, { flexDirection: "column", height: MAX_VISIBLE2, children: filtered.length === 0 ? /* @__PURE__ */ jsx22(Text20, { color: theme22.textSubtle, children: " No matching models" }) : rows.map((row) => {
11399
+ /* @__PURE__ */ jsx19(Box17, { height: 1, children: /* @__PURE__ */ jsx19(Text19, { color: theme19.textSubtle, children: pad(showScrollUp ? " ..." : "") }) }),
11400
+ /* @__PURE__ */ jsx19(Box17, { flexDirection: "column", height: MAX_VISIBLE2, children: filtered.length === 0 ? /* @__PURE__ */ jsx19(Text19, { color: theme19.textSubtle, children: " No matching models" }) : rows.map((row) => {
11066
11401
  if (row.type === "group") {
11067
- return /* @__PURE__ */ jsx22(Box18, { children: /* @__PURE__ */ jsx22(Text20, { color: theme22.textSubtle, dimColor: true, children: pad(` ${row.label}`) }) }, `group-${row.label}`);
11402
+ return /* @__PURE__ */ jsx19(Box17, { children: /* @__PURE__ */ jsx19(Text19, { color: theme19.textSubtle, dimColor: true, children: pad(` ${row.label}`) }) }, `group-${row.label}`);
11068
11403
  }
11069
11404
  const { model, isHighlighted, isCurrent } = row;
11070
11405
  const indicator = isHighlighted ? "\u203A " : " ";
11071
11406
  const suffix = isCurrent ? " *" : "";
11072
- return /* @__PURE__ */ jsx22(Box18, { children: /* @__PURE__ */ jsx22(
11073
- Text20,
11407
+ return /* @__PURE__ */ jsx19(Box17, { children: /* @__PURE__ */ jsx19(
11408
+ Text19,
11074
11409
  {
11075
- color: isHighlighted ? theme22.accentActive : isCurrent ? theme22.accent : theme22.textMuted,
11410
+ color: isHighlighted ? theme19.accentActive : isCurrent ? theme19.accent : theme19.textMuted,
11076
11411
  bold: isHighlighted,
11077
11412
  children: pad(`${indicator}${model.label}${suffix}`)
11078
11413
  }
11079
11414
  ) }, model.value);
11080
11415
  }) }),
11081
- /* @__PURE__ */ jsx22(Box18, { height: 1, children: /* @__PURE__ */ jsx22(Text20, { color: theme22.textSubtle, children: pad(showScrollDown ? " ..." : "") }) }),
11082
- /* @__PURE__ */ jsx22(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx22(Text20, { color: theme22.textSubtle, children: ["\u2191\u2193 navigate", "Enter select", "Tab manage models", "Esc cancel"].join(theme22.separator ?? " \xB7 ") }) })
11416
+ /* @__PURE__ */ jsx19(Box17, { height: 1, children: /* @__PURE__ */ jsx19(Text19, { color: theme19.textSubtle, children: pad(showScrollDown ? " ..." : "") }) }),
11417
+ /* @__PURE__ */ jsx19(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text19, { color: theme19.textSubtle, children: ["\u2191\u2193 navigate", "Enter select", "Tab manage models", "Esc cancel"].join(theme19.separator ?? " \xB7 ") }) })
11083
11418
  ]
11084
11419
  }
11085
11420
  );
11086
11421
  }
11087
11422
 
11088
11423
  // src/ink/marathon/CommandPicker.tsx
11089
- import { useState as useState19, useMemo as useMemo9, useCallback as useCallback6 } from "react";
11090
- import { Box as Box19, Text as Text21, useInput as useInput8, useStdout as useStdout3 } from "ink";
11424
+ import { useState as useState16, useMemo as useMemo8, useCallback as useCallback6 } from "react";
11425
+ import { Box as Box18, Text as Text20, useInput as useInput8, useStdout as useStdout3 } from "ink";
11091
11426
  import TextInput3 from "ink-text-input";
11092
- import { theme as theme23 } from "@runtypelabs/ink-components";
11427
+ import { theme as theme20 } from "@runtypelabs/ink-components";
11093
11428
 
11094
11429
  // src/ink/marathon/types.ts
11095
11430
  var MARATHON_CHECKPOINT_COMMANDS = [
@@ -11115,17 +11450,17 @@ var MARATHON_CHECKPOINT_COMMANDS = [
11115
11450
  ];
11116
11451
 
11117
11452
  // src/ink/marathon/CommandPicker.tsx
11118
- import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
11453
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
11119
11454
  function CommandPicker({ onSelect, onCancel, initialFilter = "" }) {
11120
11455
  const { stdout } = useStdout3();
11121
- const [search, setSearch] = useState19(initialFilter);
11122
- const [selectedIndex, setSelectedIndex] = useState19(0);
11456
+ const [search, setSearch] = useState16(initialFilter);
11457
+ const [selectedIndex, setSelectedIndex] = useState16(0);
11123
11458
  const contentWidth = (stdout?.columns ?? 120) - 4;
11124
11459
  const pad = useCallback6(
11125
11460
  (text) => text.length < contentWidth ? text + " ".repeat(contentWidth - text.length) : text,
11126
11461
  [contentWidth]
11127
11462
  );
11128
- const filtered = useMemo9(() => {
11463
+ const filtered = useMemo8(() => {
11129
11464
  if (!search.trim()) return MARATHON_CHECKPOINT_COMMANDS;
11130
11465
  const q = search.toLowerCase();
11131
11466
  return MARATHON_CHECKPOINT_COMMANDS.filter(
@@ -11157,27 +11492,27 @@ function CommandPicker({ onSelect, onCancel, initialFilter = "" }) {
11157
11492
  return;
11158
11493
  }
11159
11494
  });
11160
- return /* @__PURE__ */ jsxs18(
11161
- Box19,
11495
+ return /* @__PURE__ */ jsxs17(
11496
+ Box18,
11162
11497
  {
11163
11498
  borderStyle: "single",
11164
- borderColor: theme23.border,
11165
- backgroundColor: theme23.surfaceElevated,
11166
- paddingX: theme23.panelPaddingX,
11167
- paddingY: theme23.panelPaddingY,
11499
+ borderColor: theme20.border,
11500
+ backgroundColor: theme20.surfaceElevated,
11501
+ paddingX: theme20.panelPaddingX,
11502
+ paddingY: theme20.panelPaddingY,
11168
11503
  flexDirection: "column",
11169
11504
  children: [
11170
- /* @__PURE__ */ jsxs18(Box19, { marginBottom: 1, children: [
11171
- /* @__PURE__ */ jsx23(Text21, { color: theme23.accent, bold: true, children: "Commands" }),
11172
- /* @__PURE__ */ jsxs18(Text21, { color: theme23.textSubtle, children: [
11505
+ /* @__PURE__ */ jsxs17(Box18, { marginBottom: 1, children: [
11506
+ /* @__PURE__ */ jsx20(Text20, { color: theme20.accent, bold: true, children: "Commands" }),
11507
+ /* @__PURE__ */ jsxs17(Text20, { color: theme20.textSubtle, children: [
11173
11508
  " ",
11174
11509
  filtered.length,
11175
11510
  " available"
11176
11511
  ] })
11177
11512
  ] }),
11178
- /* @__PURE__ */ jsxs18(Box19, { marginBottom: 1, children: [
11179
- /* @__PURE__ */ jsx23(Text21, { color: theme23.textSubtle, children: "/ " }),
11180
- /* @__PURE__ */ jsx23(
11513
+ /* @__PURE__ */ jsxs17(Box18, { marginBottom: 1, children: [
11514
+ /* @__PURE__ */ jsx20(Text20, { color: theme20.textSubtle, children: "/ " }),
11515
+ /* @__PURE__ */ jsx20(
11181
11516
  TextInput3,
11182
11517
  {
11183
11518
  value: search,
@@ -11186,29 +11521,29 @@ function CommandPicker({ onSelect, onCancel, initialFilter = "" }) {
11186
11521
  }
11187
11522
  )
11188
11523
  ] }),
11189
- /* @__PURE__ */ jsx23(Box19, { flexDirection: "column", children: filtered.length === 0 ? /* @__PURE__ */ jsx23(Text21, { color: theme23.textSubtle, children: pad(" No matching commands") }) : filtered.map((cmd, index) => {
11524
+ /* @__PURE__ */ jsx20(Box18, { flexDirection: "column", children: filtered.length === 0 ? /* @__PURE__ */ jsx20(Text20, { color: theme20.textSubtle, children: pad(" No matching commands") }) : filtered.map((cmd, index) => {
11190
11525
  const isHighlighted = index === selectedIndex;
11191
11526
  const indicator = isHighlighted ? "\u203A " : " ";
11192
11527
  const nameCol = cmd.name.padEnd(14);
11193
- return /* @__PURE__ */ jsx23(Box19, { children: /* @__PURE__ */ jsx23(
11194
- Text21,
11528
+ return /* @__PURE__ */ jsx20(Box18, { children: /* @__PURE__ */ jsx20(
11529
+ Text20,
11195
11530
  {
11196
- color: isHighlighted ? theme23.accentActive : theme23.textMuted,
11531
+ color: isHighlighted ? theme20.accentActive : theme20.textMuted,
11197
11532
  bold: isHighlighted,
11198
11533
  children: pad(`${indicator}${nameCol}${cmd.description}`)
11199
11534
  }
11200
11535
  ) }, cmd.name);
11201
11536
  }) }),
11202
- /* @__PURE__ */ jsx23(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx23(Text21, { color: theme23.textSubtle, children: ["\u2191\u2193 navigate", "Enter select", "Esc cancel"].join(theme23.separator ?? " \xB7 ") }) })
11537
+ /* @__PURE__ */ jsx20(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx20(Text20, { color: theme20.textSubtle, children: ["\u2191\u2193 navigate", "Enter select", "Esc cancel"].join(theme20.separator ?? " \xB7 ") }) })
11203
11538
  ]
11204
11539
  }
11205
11540
  );
11206
11541
  }
11207
11542
 
11208
11543
  // src/ink/marathon/TextArea.tsx
11209
- import { useState as useState20, useCallback as useCallback7, useRef as useRef6, useEffect as useEffect18, useMemo as useMemo10 } from "react";
11210
- import { Box as Box20, Text as Text22, useInput as useInput9, useStdout as useStdout4 } from "ink";
11211
- import { theme as theme24 } from "@runtypelabs/ink-components";
11544
+ import { useState as useState17, useCallback as useCallback7, useRef as useRef6, useEffect as useEffect15, useMemo as useMemo9 } from "react";
11545
+ import { Box as Box19, Text as Text21, useInput as useInput9, useStdout as useStdout4 } from "ink";
11546
+ import { theme as theme21 } from "@runtypelabs/ink-components";
11212
11547
 
11213
11548
  // src/ink/marathon/text-area-editing.ts
11214
11549
  function ensureLines(lines) {
@@ -11272,7 +11607,7 @@ function deleteToLineStart(lines, cursor) {
11272
11607
  }
11273
11608
 
11274
11609
  // src/ink/marathon/TextArea.tsx
11275
- import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
11610
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
11276
11611
  function splitLines(text) {
11277
11612
  const lines = text.split("\n");
11278
11613
  return lines.length === 0 ? [""] : lines;
@@ -11290,20 +11625,20 @@ function TextArea({
11290
11625
  const { stdout } = useStdout4();
11291
11626
  const terminalWidth = stdout?.columns ?? 120;
11292
11627
  const contentWidth = Math.max(20, terminalWidth - 6);
11293
- const separator = theme24.separator ?? " \xB7 ";
11294
- const [lines, setLines] = useState20(() => splitLines(defaultValue));
11295
- const [cursor, setCursor] = useState20(() => {
11628
+ const separator = theme21.separator ?? " \xB7 ";
11629
+ const [lines, setLines] = useState17(() => splitLines(defaultValue));
11630
+ const [cursor, setCursor] = useState17(() => {
11296
11631
  const l = splitLines(defaultValue);
11297
11632
  return { line: l.length - 1, col: l[l.length - 1].length };
11298
11633
  });
11299
- const [scrollOffset, setScrollOffset] = useState20(0);
11300
- const [blinkVisible, setBlinkVisible] = useState20(true);
11634
+ const [scrollOffset, setScrollOffset] = useState17(0);
11635
+ const [blinkVisible, setBlinkVisible] = useState17(true);
11301
11636
  const blinkRef = useRef6(null);
11302
11637
  const linesRef = useRef6(lines);
11303
11638
  linesRef.current = lines;
11304
11639
  const cursorRef = useRef6(cursor);
11305
11640
  cursorRef.current = cursor;
11306
- useEffect18(() => {
11641
+ useEffect15(() => {
11307
11642
  blinkRef.current = setInterval(() => {
11308
11643
  setBlinkVisible((v) => !v);
11309
11644
  }, CURSOR_BLINK_MS);
@@ -11314,10 +11649,10 @@ function TextArea({
11314
11649
  }
11315
11650
  };
11316
11651
  }, []);
11317
- useEffect18(() => {
11652
+ useEffect15(() => {
11318
11653
  setBlinkVisible(true);
11319
11654
  }, [lines, cursor]);
11320
- useEffect18(() => {
11655
+ useEffect15(() => {
11321
11656
  const visibleStart = scrollOffset;
11322
11657
  const visibleEnd = scrollOffset + maxHeight - 1;
11323
11658
  if (cursor.line < visibleStart) {
@@ -11442,7 +11777,7 @@ function TextArea({
11442
11777
  [cursor, onCancel, onSubmit, maxHeight]
11443
11778
  );
11444
11779
  useInput9(handleInput);
11445
- const visibleLines = useMemo10(() => {
11780
+ const visibleLines = useMemo9(() => {
11446
11781
  const start = scrollOffset;
11447
11782
  const end = Math.min(start + maxHeight, lines.length);
11448
11783
  const result = [];
@@ -11459,28 +11794,28 @@ function TextArea({
11459
11794
  }
11460
11795
  return result;
11461
11796
  }, [lines, cursor, scrollOffset, maxHeight]);
11462
- return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
11463
- /* @__PURE__ */ jsxs19(
11464
- Box20,
11797
+ return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", children: [
11798
+ /* @__PURE__ */ jsxs18(
11799
+ Box19,
11465
11800
  {
11466
11801
  borderStyle: "single",
11467
- borderColor: theme24.borderActive,
11468
- backgroundColor: theme24.surface,
11802
+ borderColor: theme21.borderActive,
11803
+ backgroundColor: theme21.surface,
11469
11804
  flexDirection: "column",
11470
11805
  paddingX: 1,
11471
11806
  children: [
11472
- title && /* @__PURE__ */ jsxs19(Box20, { marginBottom: 1, children: [
11473
- /* @__PURE__ */ jsx24(Text22, { color: theme24.accent, bold: true, children: title }),
11474
- /* @__PURE__ */ jsxs19(Text22, { color: theme24.textSubtle, children: [
11807
+ title && /* @__PURE__ */ jsxs18(Box19, { marginBottom: 1, children: [
11808
+ /* @__PURE__ */ jsx21(Text21, { color: theme21.accent, bold: true, children: title }),
11809
+ /* @__PURE__ */ jsxs18(Text21, { color: theme21.textSubtle, children: [
11475
11810
  " ",
11476
11811
  lines.length,
11477
11812
  " lines"
11478
11813
  ] })
11479
11814
  ] }),
11480
- /* @__PURE__ */ jsxs19(Box20, { flexDirection: "row", height: maxHeight, children: [
11481
- /* @__PURE__ */ jsx24(Box20, { flexDirection: "column", flexGrow: 1, children: visibleLines.map((vl, idx) => /* @__PURE__ */ jsxs19(Box20, { children: [
11482
- /* @__PURE__ */ jsx24(Text22, { color: theme24.textSubtle, children: vl.lineIndex >= 0 ? `${String(vl.lineIndex + 1).padStart(3)} ` : " " }),
11483
- vl.hasCursor ? /* @__PURE__ */ jsx24(
11815
+ /* @__PURE__ */ jsxs18(Box19, { flexDirection: "row", height: maxHeight, children: [
11816
+ /* @__PURE__ */ jsx21(Box19, { flexDirection: "column", flexGrow: 1, children: visibleLines.map((vl, idx) => /* @__PURE__ */ jsxs18(Box19, { children: [
11817
+ /* @__PURE__ */ jsx21(Text21, { color: theme21.textSubtle, children: vl.lineIndex >= 0 ? `${String(vl.lineIndex + 1).padStart(3)} ` : " " }),
11818
+ vl.hasCursor ? /* @__PURE__ */ jsx21(
11484
11819
  CursorLine,
11485
11820
  {
11486
11821
  content: vl.content,
@@ -11488,9 +11823,9 @@ function TextArea({
11488
11823
  maxWidth: contentWidth - 6,
11489
11824
  visible: blinkVisible
11490
11825
  }
11491
- ) : /* @__PURE__ */ jsx24(Text22, { color: theme24.text, children: vl.content.slice(0, contentWidth - 6) })
11826
+ ) : /* @__PURE__ */ jsx21(Text21, { color: theme21.text, children: vl.content.slice(0, contentWidth - 6) })
11492
11827
  ] }, idx)) }),
11493
- /* @__PURE__ */ jsx24(
11828
+ /* @__PURE__ */ jsx21(
11494
11829
  Scrollbar,
11495
11830
  {
11496
11831
  totalLines: lines.length,
@@ -11503,7 +11838,7 @@ function TextArea({
11503
11838
  ]
11504
11839
  }
11505
11840
  ),
11506
- /* @__PURE__ */ jsx24(Text22, { color: theme24.textSubtle, children: [
11841
+ /* @__PURE__ */ jsx21(Text21, { color: theme21.textSubtle, children: [
11507
11842
  "Ctrl+S: submit",
11508
11843
  "Esc: cancel",
11509
11844
  "Enter: newline",
@@ -11523,22 +11858,22 @@ function CursorLine({
11523
11858
  const before = truncated.slice(0, col);
11524
11859
  const cursorChar = truncated[col] ?? " ";
11525
11860
  const after = truncated.slice(col + 1);
11526
- return /* @__PURE__ */ jsxs19(Text22, { children: [
11527
- /* @__PURE__ */ jsx24(Text22, { color: theme24.text, children: before }),
11528
- visible ? /* @__PURE__ */ jsx24(Text22, { color: theme24.background, backgroundColor: theme24.accentActive, children: cursorChar }) : /* @__PURE__ */ jsx24(Text22, { color: theme24.text, children: cursorChar }),
11529
- /* @__PURE__ */ jsx24(Text22, { color: theme24.text, children: after })
11861
+ return /* @__PURE__ */ jsxs18(Text21, { children: [
11862
+ /* @__PURE__ */ jsx21(Text21, { color: theme21.text, children: before }),
11863
+ visible ? /* @__PURE__ */ jsx21(Text21, { color: theme21.background, backgroundColor: theme21.accentActive, children: cursorChar }) : /* @__PURE__ */ jsx21(Text21, { color: theme21.text, children: cursorChar }),
11864
+ /* @__PURE__ */ jsx21(Text21, { color: theme21.text, children: after })
11530
11865
  ] });
11531
11866
  }
11532
11867
 
11533
11868
  // src/ink/marathon/ReflectEditor.tsx
11534
- import { jsx as jsx25 } from "react/jsx-runtime";
11869
+ import { jsx as jsx22 } from "react/jsx-runtime";
11535
11870
  var DEFAULT_REFLECT_PROMPT = `Before continuing, step back and reflect:
11536
11871
  - What has been accomplished so far?
11537
11872
  - What isn't working or is blocking progress?
11538
11873
  - What approach should be taken next and why?
11539
11874
  - Are there any assumptions that should be reconsidered?`;
11540
11875
  function ReflectEditor({ onSubmit, onCancel, maxHeight = 10 }) {
11541
- return /* @__PURE__ */ jsx25(
11876
+ return /* @__PURE__ */ jsx22(
11542
11877
  TextArea,
11543
11878
  {
11544
11879
  title: "Reflect",
@@ -11551,9 +11886,9 @@ function ReflectEditor({ onSubmit, onCancel, maxHeight = 10 }) {
11551
11886
  }
11552
11887
 
11553
11888
  // src/ink/marathon/CheckpointRecap.tsx
11554
- import { Box as Box21, Text as Text23 } from "ink";
11555
- import { theme as theme25 } from "@runtypelabs/ink-components";
11556
- import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
11889
+ import { Box as Box20, Text as Text22 } from "ink";
11890
+ import { theme as theme22 } from "@runtypelabs/ink-components";
11891
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
11557
11892
  function formatTokenCount2(value) {
11558
11893
  return value.toLocaleString("en-US");
11559
11894
  }
@@ -11568,26 +11903,26 @@ function CheckpointRecap({
11568
11903
  isTerminal
11569
11904
  }) {
11570
11905
  const label = isTerminal ? `\u2713 Session ${sessionNumber} complete` : `Session ${sessionNumber} complete`;
11571
- const separator = theme25.separator ?? " \xB7 ";
11906
+ const separator = theme22.separator ?? " \xB7 ";
11572
11907
  const usageSummary = [
11573
11908
  `Tools: ${toolCallsMade}`,
11574
11909
  `Tokens: ${formatTokenCount2(tokensInput)} in${separator}${formatTokenCount2(tokensOutput)} out`,
11575
11910
  ...reasoningTokens && reasoningTokens > 0 ? [`Reasoning: ${formatTokenCount2(reasoningTokens)}`] : [],
11576
11911
  `Cost: $${cost.toFixed(4)}`
11577
11912
  ].join(separator);
11578
- return /* @__PURE__ */ jsxs20(
11579
- Box21,
11913
+ return /* @__PURE__ */ jsxs19(
11914
+ Box20,
11580
11915
  {
11581
11916
  borderStyle: "single",
11582
- borderColor: theme25.border,
11583
- backgroundColor: theme25.background,
11917
+ borderColor: theme22.border,
11918
+ backgroundColor: theme22.background,
11584
11919
  flexDirection: "column",
11585
- paddingX: theme25.panelPaddingX,
11586
- paddingY: theme25.panelPaddingY,
11920
+ paddingX: theme22.panelPaddingX,
11921
+ paddingY: theme22.panelPaddingY,
11587
11922
  children: [
11588
- /* @__PURE__ */ jsx26(Text23, { color: isTerminal ? theme25.accentActive : theme25.accent, children: label }),
11589
- /* @__PURE__ */ jsx26(Text23, { color: theme25.textMuted, children: usageSummary }),
11590
- historyWarning && /* @__PURE__ */ jsx26(Text23, { color: theme25.warning, children: historyWarning })
11923
+ /* @__PURE__ */ jsx23(Text22, { color: isTerminal ? theme22.accentActive : theme22.accent, children: label }),
11924
+ /* @__PURE__ */ jsx23(Text22, { color: theme22.textMuted, children: usageSummary }),
11925
+ historyWarning && /* @__PURE__ */ jsx23(Text22, { color: theme22.warning, children: historyWarning })
11591
11926
  ]
11592
11927
  }
11593
11928
  );
@@ -11609,7 +11944,7 @@ function getCheckpointCountdownText(state) {
11609
11944
  }
11610
11945
 
11611
11946
  // src/ink/marathon/CheckpointPrompt.tsx
11612
- import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
11947
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
11613
11948
  function CheckpointPrompt({
11614
11949
  onSubmit,
11615
11950
  onToggleHelp,
@@ -11621,18 +11956,18 @@ function CheckpointPrompt({
11621
11956
  recap,
11622
11957
  isReviewing = false
11623
11958
  }) {
11624
- const [value, setValue] = useState21("");
11625
- const [remaining, setRemaining] = useState21(timeout);
11626
- const [isTyping, setIsTyping] = useState21(false);
11627
- const [showModelPicker, setShowModelPicker] = useState21(false);
11628
- const [showReflectEditor, setShowReflectEditor] = useState21(false);
11629
- const [showCommandPicker, setShowCommandPicker] = useState21(false);
11630
- const [pendingModel, setPendingModel] = useState21(void 0);
11631
- const [pendingSandbox, setPendingSandbox] = useState21(void 0);
11632
- const [pendingToolsToggle, setPendingToolsToggle] = useState21(false);
11633
- const [pendingStop, setPendingStop] = useState21(false);
11959
+ const [value, setValue] = useState18("");
11960
+ const [remaining, setRemaining] = useState18(timeout);
11961
+ const [isTyping, setIsTyping] = useState18(false);
11962
+ const [showModelPicker, setShowModelPicker] = useState18(false);
11963
+ const [showReflectEditor, setShowReflectEditor] = useState18(false);
11964
+ const [showCommandPicker, setShowCommandPicker] = useState18(false);
11965
+ const [pendingModel, setPendingModel] = useState18(void 0);
11966
+ const [pendingSandbox, setPendingSandbox] = useState18(void 0);
11967
+ const [pendingToolsToggle, setPendingToolsToggle] = useState18(false);
11968
+ const [pendingStop, setPendingStop] = useState18(false);
11634
11969
  const timerRef = useRef7(null);
11635
- const separator = theme26.separator ?? " \xB7 ";
11970
+ const separator = theme23.separator ?? " \xB7 ";
11636
11971
  const hasPendingChanges = pendingModel !== void 0 || pendingSandbox !== void 0 || pendingToolsToggle || pendingStop;
11637
11972
  const clearInputDraft = () => {
11638
11973
  setValue("");
@@ -11656,7 +11991,7 @@ function CheckpointPrompt({
11656
11991
  resetDraft();
11657
11992
  onSubmit(result);
11658
11993
  };
11659
- useEffect19(() => {
11994
+ useEffect16(() => {
11660
11995
  const title = "Marathon";
11661
11996
  const body = isTerminal ? "Marathon complete" : `Session ${recap?.sessionNumber ?? "?"} complete`;
11662
11997
  process.stderr.write(`\x1B]777;notify;${title};${body}\x07`);
@@ -11676,7 +12011,7 @@ function CheckpointPrompt({
11676
12011
  setIsTyping(false);
11677
12012
  }
11678
12013
  };
11679
- useEffect19(() => {
12014
+ useEffect16(() => {
11680
12015
  if (shouldPauseCheckpointTimer({
11681
12016
  isTerminal,
11682
12017
  timeout,
@@ -11821,43 +12156,43 @@ ${trimmed}` : trimmed);
11821
12156
  if (isTerminal) return "Enter: exit";
11822
12157
  return "Enter: continue";
11823
12158
  })();
11824
- return /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", flexShrink: 0, children: [
11825
- showCommandPicker ? /* @__PURE__ */ jsx27(
12159
+ return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", flexShrink: 0, children: [
12160
+ showCommandPicker ? /* @__PURE__ */ jsx24(
11826
12161
  CommandPicker,
11827
12162
  {
11828
12163
  onSelect: handleCommandSelect,
11829
12164
  onCancel: handleCommandCancel
11830
12165
  }
11831
- ) : showModelPicker ? /* @__PURE__ */ jsx27(
12166
+ ) : showModelPicker ? /* @__PURE__ */ jsx24(
11832
12167
  ModelPicker,
11833
12168
  {
11834
12169
  currentModel,
11835
12170
  onSelect: handleModelSelect,
11836
12171
  onCancel: handleModelCancel
11837
12172
  }
11838
- ) : showReflectEditor ? /* @__PURE__ */ jsx27(
12173
+ ) : showReflectEditor ? /* @__PURE__ */ jsx24(
11839
12174
  ReflectEditor,
11840
12175
  {
11841
12176
  onSubmit: handleReflectSubmit,
11842
12177
  onCancel: handleReflectCancel
11843
12178
  }
11844
- ) : /* @__PURE__ */ jsxs21(
11845
- Box22,
12179
+ ) : /* @__PURE__ */ jsxs20(
12180
+ Box21,
11846
12181
  {
11847
12182
  borderStyle: "bold",
11848
12183
  borderLeft: true,
11849
12184
  borderRight: false,
11850
12185
  borderTop: false,
11851
12186
  borderBottom: false,
11852
- borderColor: theme26.accent,
11853
- backgroundColor: theme26.backgroundDeep,
12187
+ borderColor: theme23.accent,
12188
+ backgroundColor: theme23.backgroundDeep,
11854
12189
  flexDirection: "column",
11855
12190
  paddingX: 1,
11856
12191
  paddingY: 1,
11857
12192
  children: [
11858
- /* @__PURE__ */ jsxs21(Box22, { children: [
11859
- /* @__PURE__ */ jsx27(Text24, { color: theme26.accentActive, children: "> " }),
11860
- /* @__PURE__ */ jsx27(Box22, { flexGrow: 1, children: /* @__PURE__ */ jsx27(
12193
+ /* @__PURE__ */ jsxs20(Box21, { children: [
12194
+ /* @__PURE__ */ jsx24(Text23, { color: theme23.accentActive, children: "> " }),
12195
+ /* @__PURE__ */ jsx24(Box21, { flexGrow: 1, children: /* @__PURE__ */ jsx24(
11861
12196
  BlinkingTextInput,
11862
12197
  {
11863
12198
  value,
@@ -11866,100 +12201,383 @@ ${trimmed}` : trimmed);
11866
12201
  placeholder: isTerminal ? "Send new instructions to continue marathon, or press enter to exit..." : "Guide next iteration..."
11867
12202
  }
11868
12203
  ) }),
11869
- countdownText && /* @__PURE__ */ jsxs21(Text24, { color: theme26.textSubtle, children: [
12204
+ countdownText && /* @__PURE__ */ jsxs20(Text23, { color: theme23.textSubtle, children: [
12205
+ " ",
12206
+ countdownText
12207
+ ] })
12208
+ ] }),
12209
+ /* @__PURE__ */ jsx24(Text23, { color: theme23.textSubtle, children: [
12210
+ enterHint,
12211
+ "/: commands",
12212
+ "/help"
12213
+ ].join(separator) }),
12214
+ pendingSummary.length > 0 && /* @__PURE__ */ jsx24(Text23, { color: theme23.textMuted, children: `Pending: ${pendingSummary.join(separator)}` })
12215
+ ]
12216
+ }
12217
+ ),
12218
+ /* @__PURE__ */ jsx24(CheckpointRecap, { ...recap, isTerminal })
12219
+ ] });
12220
+ }
12221
+
12222
+ // src/ink/marathon/SessionActionMenu.tsx
12223
+ import { Box as Box22, Text as Text24, useInput as useInput10 } from "ink";
12224
+ import { theme as theme24 } from "@runtypelabs/ink-components";
12225
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
12226
+ var MENU_ITEMS = [
12227
+ { key: "c", label: "Copy session JSON" },
12228
+ { key: "o", label: "Open session JSON in editor" },
12229
+ { key: "f", label: "Open marathon folder in file manager" },
12230
+ { key: "d", label: "Open agent in Runtype dashboard" }
12231
+ ];
12232
+ function SessionActionMenu({
12233
+ onCopySession,
12234
+ onOpenStateFile,
12235
+ onOpenFolder,
12236
+ onOpenDashboard,
12237
+ onClose,
12238
+ hasDashboard,
12239
+ hasStateFile
12240
+ }) {
12241
+ useInput10((input, key) => {
12242
+ if (key.escape) {
12243
+ onClose();
12244
+ return;
12245
+ }
12246
+ if (input === "c") {
12247
+ onCopySession();
12248
+ return;
12249
+ }
12250
+ if (input === "o" && hasStateFile) {
12251
+ onOpenStateFile();
12252
+ return;
12253
+ }
12254
+ if (input === "f" && hasStateFile) {
12255
+ onOpenFolder();
12256
+ return;
12257
+ }
12258
+ if (input === "d" && hasDashboard) {
12259
+ onOpenDashboard();
12260
+ return;
12261
+ }
12262
+ });
12263
+ return /* @__PURE__ */ jsxs21(
12264
+ Box22,
12265
+ {
12266
+ flexDirection: "column",
12267
+ borderStyle: "round",
12268
+ borderColor: theme24.accent,
12269
+ backgroundColor: theme24.surfaceElevated,
12270
+ paddingX: 2,
12271
+ paddingY: 1,
12272
+ width: 50,
12273
+ children: [
12274
+ /* @__PURE__ */ jsx25(Text24, { bold: true, color: theme24.accent, children: "Session" }),
12275
+ /* @__PURE__ */ jsx25(Box22, { flexDirection: "column", marginTop: 1, children: MENU_ITEMS.map((item) => {
12276
+ const dimmed = item.key === "o" && !hasStateFile || item.key === "f" && !hasStateFile || item.key === "d" && !hasDashboard;
12277
+ return /* @__PURE__ */ jsxs21(Text24, { children: [
12278
+ /* @__PURE__ */ jsx25(Text24, { color: dimmed ? theme24.textSubtle : theme24.accentActive, children: ` ${item.key} ` }),
12279
+ /* @__PURE__ */ jsx25(Text24, { color: dimmed ? theme24.textSubtle : theme24.textMuted, children: item.label })
12280
+ ] }, item.key);
12281
+ }) }),
12282
+ /* @__PURE__ */ jsxs21(Box22, { marginTop: 1, children: [
12283
+ /* @__PURE__ */ jsx25(Text24, { color: theme24.info, children: "Esc" }),
12284
+ /* @__PURE__ */ jsx25(Text24, { color: theme24.muted, children: " to close" })
12285
+ ] })
12286
+ ]
12287
+ }
12288
+ );
12289
+ }
12290
+
12291
+ // src/ink/marathon/UpgradeModal.tsx
12292
+ import { Box as Box23, Text as Text25 } from "ink";
12293
+ import { theme as theme25 } from "@runtypelabs/ink-components";
12294
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
12295
+ function UpgradeModal({ prompt, width }) {
12296
+ return /* @__PURE__ */ jsxs22(
12297
+ Box23,
12298
+ {
12299
+ width,
12300
+ flexDirection: "column",
12301
+ borderStyle: "round",
12302
+ borderColor: theme25.warning,
12303
+ backgroundColor: theme25.surfaceElevated,
12304
+ paddingX: 2,
12305
+ paddingY: 1,
12306
+ children: [
12307
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.warning, bold: true, children: "Upgrade available" }),
12308
+ /* @__PURE__ */ jsx26(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx26(Text25, { color: theme25.label, children: prompt.message }) }),
12309
+ (prompt.code || prompt.limitType || prompt.retryAfter) && /* @__PURE__ */ jsxs22(Box23, { flexDirection: "column", marginTop: 1, children: [
12310
+ prompt.code && /* @__PURE__ */ jsxs22(Text25, { color: theme25.muted, children: [
12311
+ "Code: ",
12312
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.label, children: prompt.code })
12313
+ ] }),
12314
+ prompt.limitType && /* @__PURE__ */ jsxs22(Text25, { color: theme25.muted, children: [
12315
+ "Limit: ",
12316
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.label, children: prompt.limitType })
12317
+ ] }),
12318
+ prompt.retryAfter && /* @__PURE__ */ jsxs22(Text25, { color: theme25.muted, children: [
12319
+ "Retry after: ",
12320
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.label, children: prompt.retryAfter })
12321
+ ] })
12322
+ ] }),
12323
+ /* @__PURE__ */ jsxs22(Box23, { marginTop: 1, children: [
12324
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.info, children: "Enter" }),
12325
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.muted, children: " open billing " }),
12326
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.info, children: "Esc" }),
12327
+ /* @__PURE__ */ jsx26(Text25, { color: theme25.muted, children: " close" })
12328
+ ] }),
12329
+ /* @__PURE__ */ jsx26(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx26(Text25, { color: theme25.dimLabel, children: "Opens your dashboard billing page" }) })
12330
+ ]
12331
+ }
12332
+ );
12333
+ }
12334
+
12335
+ // src/ink/marathon/OverviewActivityPane.tsx
12336
+ import { memo as memo4 } from "react";
12337
+ import { Box as Box25 } from "ink";
12338
+ import { theme as theme27 } from "@runtypelabs/ink-components";
12339
+
12340
+ // src/ink/marathon/ToolPanel.tsx
12341
+ import { useState as useState19, useEffect as useEffect17, useMemo as useMemo10, memo as memo3 } from "react";
12342
+ import { Box as Box24, Text as Text26 } from "ink";
12343
+ import { Spinner as Spinner5, theme as theme26 } from "@runtypelabs/ink-components";
12344
+ import { Fragment as Fragment2, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
12345
+ var TOOL_PANEL_TIMER_MS = 500;
12346
+ function formatCharCount2(chars) {
12347
+ if (chars >= 1e3) {
12348
+ return `${(chars / 1e3).toFixed(1)}k chars`;
12349
+ }
12350
+ return `${chars} chars`;
12351
+ }
12352
+ var STATUS_ICONS2 = {
12353
+ complete: "\u2713",
12354
+ error: "\u2717"
12355
+ };
12356
+ var FILE_TYPE_ICONS = {
12357
+ written: "\u270E",
12358
+ read: "\u2022",
12359
+ plan: "\u2691"
12360
+ };
12361
+ function formatElapsed2(tool, now) {
12362
+ if (tool.executionTime != null) {
12363
+ return `${(tool.executionTime / 1e3).toFixed(1)}s`;
12364
+ }
12365
+ const startedAt = tool.localExecutionStartedAt ?? tool.startedAt;
12366
+ return `${((now - startedAt) / 1e3).toFixed(1)}s`;
12367
+ }
12368
+ function getStatusColor2(status) {
12369
+ if (status === "error") return theme26.danger;
12370
+ if (status === "complete") return theme26.success;
12371
+ return theme26.accent;
12372
+ }
12373
+ function getPhaseLabel2(tool) {
12374
+ if (tool.status === "complete") return "complete";
12375
+ if (tool.status === "error") return "error";
12376
+ if (tool.phase === "input_streaming") return "assembling input";
12377
+ if (tool.phase === "executing_locally") return "executing locally";
12378
+ return "running";
12379
+ }
12380
+ function toolsFingerprint(tools) {
12381
+ const tail = tools.length <= 10 ? tools : tools.slice(-10);
12382
+ let fp = String(tools.length);
12383
+ for (const t of tail) {
12384
+ fp += `|${t.id}:${t.name}:${t.status}:${t.phase ?? ""}:${t.inputPreview ?? ""}`;
12385
+ }
12386
+ return fp;
12387
+ }
12388
+ function filesFingerprint(files) {
12389
+ if (!files || files.length === 0) return "0";
12390
+ const tail = files.length <= 10 ? files : files.slice(-10);
12391
+ let fp = String(files.length);
12392
+ for (const file of tail) {
12393
+ fp += `|${file.path}:${file.type}`;
12394
+ }
12395
+ return fp;
12396
+ }
12397
+ var ToolPanel = memo3(function ToolPanel2({
12398
+ tools,
12399
+ reasoningCharCount = 0,
12400
+ files,
12401
+ maxHeight
12402
+ }) {
12403
+ const [now, setNow] = useState19(Date.now());
12404
+ const hasRunning = tools.some((t) => t.status === "running");
12405
+ useEffect17(() => {
12406
+ if (!hasRunning) return;
12407
+ const interval = setInterval(() => setNow(Date.now()), TOOL_PANEL_TIMER_MS);
12408
+ return () => clearInterval(interval);
12409
+ }, [hasRunning]);
12410
+ const { visibleTools, visibleFiles } = useMemo10(() => {
12411
+ if (!maxHeight) return { visibleTools: tools, visibleFiles: files || [] };
12412
+ const reasoningRows = reasoningCharCount > 0 ? 1 : 0;
12413
+ const headerRows = 1;
12414
+ const filesList = files || [];
12415
+ const fileHeaderRows = filesList.length > 0 ? 2 : 0;
12416
+ const availableRows = maxHeight - headerRows - reasoningRows - fileHeaderRows;
12417
+ const fileRows = filesList.length > 0 ? Math.min(filesList.length, Math.max(3, Math.floor(availableRows * 0.3))) : 0;
12418
+ const toolRows = Math.max(1, availableRows - fileRows);
12419
+ const toolSlots = Math.floor(toolRows / 2);
12420
+ const vTools = tools.length <= toolSlots ? tools : tools.slice(-toolSlots);
12421
+ const vFiles = filesList.length <= fileRows ? filesList : filesList.slice(-fileRows);
12422
+ return { visibleTools: vTools, visibleFiles: vFiles };
12423
+ }, [tools, files, maxHeight, reasoningCharCount]);
12424
+ const hiddenToolCount = tools.length - visibleTools.length;
12425
+ const hiddenFileCount = (files || []).length - visibleFiles.length;
12426
+ return /* @__PURE__ */ jsxs23(Box24, { flexDirection: "column", backgroundColor: theme26.background, children: [
12427
+ /* @__PURE__ */ jsx27(Text26, { bold: true, color: theme26.accent, children: "Activity" }),
12428
+ reasoningCharCount > 0 && /* @__PURE__ */ jsxs23(Text26, { color: theme26.textMuted, children: [
12429
+ "Reasoning: ",
12430
+ formatCharCount2(reasoningCharCount)
12431
+ ] }),
12432
+ hiddenToolCount > 0 && /* @__PURE__ */ jsxs23(Text26, { color: theme26.textSubtle, children: [
12433
+ " ",
12434
+ hiddenToolCount,
12435
+ " earlier hidden"
12436
+ ] }),
12437
+ visibleTools.map((tool) => {
12438
+ const statusColor = getStatusColor2(tool.status);
12439
+ const elapsed = formatElapsed2(tool, now);
12440
+ const phaseLabel = getPhaseLabel2(tool);
12441
+ return /* @__PURE__ */ jsxs23(Box24, { flexDirection: "column", children: [
12442
+ /* @__PURE__ */ jsxs23(Box24, { flexDirection: "row", flexWrap: "nowrap", children: [
12443
+ /* @__PURE__ */ jsx27(Box24, { width: 2, flexShrink: 0, children: tool.status === "running" ? /* @__PURE__ */ jsx27(Spinner5, { label: "", color: theme26.spinner }) : /* @__PURE__ */ jsx27(Text26, { color: statusColor, children: STATUS_ICONS2[tool.status] || "?" }) }),
12444
+ /* @__PURE__ */ jsx27(Box24, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ jsx27(Text26, { color: theme26.text, wrap: "truncate", children: tool.name }) }),
12445
+ /* @__PURE__ */ jsxs23(Box24, { flexShrink: 0, children: [
12446
+ /* @__PURE__ */ jsxs23(Text26, { color: theme26.textSubtle, children: [
11870
12447
  " ",
11871
- countdownText
12448
+ phaseLabel
12449
+ ] }),
12450
+ /* @__PURE__ */ jsxs23(Text26, { color: theme26.textMuted, children: [
12451
+ " ",
12452
+ elapsed
11872
12453
  ] })
11873
- ] }),
11874
- /* @__PURE__ */ jsx27(Text24, { color: theme26.textSubtle, children: [
11875
- enterHint,
11876
- "/: commands",
11877
- "/help"
11878
- ].join(separator) }),
11879
- pendingSummary.length > 0 && /* @__PURE__ */ jsx27(Text24, { color: theme26.textMuted, children: `Pending: ${pendingSummary.join(separator)}` })
11880
- ]
11881
- }
11882
- ),
11883
- /* @__PURE__ */ jsx27(CheckpointRecap, { ...recap, isTerminal })
12454
+ ] })
12455
+ ] }),
12456
+ /* @__PURE__ */ jsxs23(Box24, { flexDirection: "row", flexWrap: "nowrap", children: [
12457
+ /* @__PURE__ */ jsx27(Box24, { width: 2, flexShrink: 0 }),
12458
+ /* @__PURE__ */ jsx27(Text26, { color: theme26.textSubtle, children: "\u2514 " }),
12459
+ /* @__PURE__ */ jsx27(Box24, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ jsx27(Text26, { color: theme26.textMuted, wrap: "truncate", children: tool.inputPreview || "" }) })
12460
+ ] })
12461
+ ] }, tool.id);
12462
+ }),
12463
+ visibleFiles.length > 0 && /* @__PURE__ */ jsxs23(Fragment2, { children: [
12464
+ /* @__PURE__ */ jsxs23(Box24, { marginTop: 1, children: [
12465
+ /* @__PURE__ */ jsx27(Text26, { bold: true, color: theme26.accent, children: "Files" }),
12466
+ hiddenFileCount > 0 && /* @__PURE__ */ jsxs23(Text26, { color: theme26.textSubtle, children: [
12467
+ " +",
12468
+ hiddenFileCount
12469
+ ] })
12470
+ ] }),
12471
+ visibleFiles.map((file) => {
12472
+ const icon = FILE_TYPE_ICONS[file.type] || "\u2022";
12473
+ const filename = file.path.split("/").pop() || file.path;
12474
+ return /* @__PURE__ */ jsxs23(Box24, { flexDirection: "row", flexWrap: "nowrap", children: [
12475
+ /* @__PURE__ */ jsx27(Box24, { width: 2, flexShrink: 0, children: /* @__PURE__ */ jsx27(Text26, { color: file.type === "written" ? theme26.success : theme26.textMuted, children: icon }) }),
12476
+ /* @__PURE__ */ jsx27(Box24, { flexShrink: 1, children: /* @__PURE__ */ jsx27(Text26, { color: theme26.text, wrap: "truncate", children: filename }) })
12477
+ ] }, file.path);
12478
+ })
12479
+ ] })
11884
12480
  ] });
11885
- }
12481
+ }, (prev, next) => {
12482
+ return toolsFingerprint(prev.tools) === toolsFingerprint(next.tools) && prev.maxHeight === next.maxHeight && (prev.reasoningCharCount ?? 0) === (next.reasoningCharCount ?? 0) && filesFingerprint(prev.files) === filesFingerprint(next.files);
12483
+ });
11886
12484
 
11887
- // src/ink/marathon/SessionActionMenu.tsx
11888
- import { Box as Box23, Text as Text25, useInput as useInput10 } from "ink";
11889
- import { theme as theme27 } from "@runtypelabs/ink-components";
11890
- import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
11891
- var MENU_ITEMS = [
11892
- { key: "c", label: "Copy session JSON" },
11893
- { key: "o", label: "Open session JSON in editor" },
11894
- { key: "f", label: "Open marathon folder in file manager" },
11895
- { key: "d", label: "Open agent in Runtype dashboard" }
11896
- ];
11897
- function SessionActionMenu({
11898
- onCopySession,
11899
- onOpenStateFile,
11900
- onOpenFolder,
11901
- onOpenDashboard,
11902
- onClose,
11903
- hasDashboard,
11904
- hasStateFile
12485
+ // src/ink/marathon/OverviewActivityPane.tsx
12486
+ import { jsx as jsx28 } from "react/jsx-runtime";
12487
+ var OverviewActivityPane = memo4(function OverviewActivityPane2({
12488
+ tools,
12489
+ reasoningCharCount,
12490
+ files,
12491
+ maxHeight,
12492
+ stacked = false,
12493
+ width
11905
12494
  }) {
11906
- useInput10((input, key) => {
11907
- if (key.escape) {
11908
- onClose();
11909
- return;
11910
- }
11911
- if (input === "c") {
11912
- onCopySession();
11913
- return;
11914
- }
11915
- if (input === "o" && hasStateFile) {
11916
- onOpenStateFile();
11917
- return;
11918
- }
11919
- if (input === "f" && hasStateFile) {
11920
- onOpenFolder();
11921
- return;
11922
- }
11923
- if (input === "d" && hasDashboard) {
11924
- onOpenDashboard();
11925
- return;
11926
- }
11927
- });
11928
- return /* @__PURE__ */ jsxs22(
11929
- Box23,
12495
+ return /* @__PURE__ */ jsx28(
12496
+ Box25,
11930
12497
  {
11931
12498
  flexDirection: "column",
11932
- borderStyle: "round",
11933
- borderColor: theme27.accent,
11934
- backgroundColor: theme27.surfaceElevated,
11935
- paddingX: 2,
11936
- paddingY: 1,
11937
- width: 50,
11938
- children: [
11939
- /* @__PURE__ */ jsx28(Text25, { bold: true, color: theme27.accent, children: "Session" }),
11940
- /* @__PURE__ */ jsx28(Box23, { flexDirection: "column", marginTop: 1, children: MENU_ITEMS.map((item) => {
11941
- const dimmed = item.key === "o" && !hasStateFile || item.key === "f" && !hasStateFile || item.key === "d" && !hasDashboard;
11942
- return /* @__PURE__ */ jsxs22(Text25, { children: [
11943
- /* @__PURE__ */ jsx28(Text25, { color: dimmed ? theme27.textSubtle : theme27.accentActive, children: ` ${item.key} ` }),
11944
- /* @__PURE__ */ jsx28(Text25, { color: dimmed ? theme27.textSubtle : theme27.textMuted, children: item.label })
11945
- ] }, item.key);
11946
- }) }),
11947
- /* @__PURE__ */ jsxs22(Box23, { marginTop: 1, children: [
11948
- /* @__PURE__ */ jsx28(Text25, { color: theme27.info, children: "Esc" }),
11949
- /* @__PURE__ */ jsx28(Text25, { color: theme27.muted, children: " to close" })
11950
- ] })
11951
- ]
12499
+ width: stacked ? void 0 : width,
12500
+ flexShrink: 0,
12501
+ borderStyle: "single",
12502
+ borderColor: theme27.border,
12503
+ backgroundColor: theme27.background,
12504
+ paddingX: theme27.panelPaddingX,
12505
+ paddingY: theme27.panelPaddingY,
12506
+ children: /* @__PURE__ */ jsx28(
12507
+ ToolPanel,
12508
+ {
12509
+ tools,
12510
+ reasoningCharCount,
12511
+ files,
12512
+ maxHeight
12513
+ }
12514
+ )
11952
12515
  }
11953
12516
  );
12517
+ });
12518
+
12519
+ // src/ink/marathon/OverviewTranscriptPane.tsx
12520
+ import { memo as memo5 } from "react";
12521
+ import { Box as Box27 } from "ink";
12522
+ import { StreamOutput, ErrorDisplay as ErrorDisplay3 } from "@runtypelabs/ink-components";
12523
+ import { LoadingAnimation } from "@runtypelabs/terminal-animations";
12524
+
12525
+ // src/ink/marathon/ThinkingIndicator.tsx
12526
+ import { useState as useState20, useEffect as useEffect18 } from "react";
12527
+ import { Spinner as Spinner6, theme as theme28 } from "@runtypelabs/ink-components";
12528
+ import { jsx as jsx29 } from "react/jsx-runtime";
12529
+ function ThinkingIndicator({ startedAt }) {
12530
+ const [elapsed, setElapsed] = useState20(
12531
+ () => startedAt ? Math.floor((Date.now() - startedAt) / 1e3) : 0
12532
+ );
12533
+ useEffect18(() => {
12534
+ const start = startedAt ?? Date.now();
12535
+ setElapsed(Math.floor((Date.now() - start) / 1e3));
12536
+ const interval = setInterval(() => {
12537
+ setElapsed(Math.floor((Date.now() - start) / 1e3));
12538
+ }, 1e3);
12539
+ return () => clearInterval(interval);
12540
+ }, [startedAt]);
12541
+ return /* @__PURE__ */ jsx29(Spinner6, { label: `Thinking... ${elapsed}s`, color: theme28.textMuted });
12542
+ }
12543
+
12544
+ // src/ink/marathon/ContextCompactionIndicator.tsx
12545
+ import { useEffect as useEffect19, useState as useState21 } from "react";
12546
+ import { Spinner as Spinner7, theme as theme29 } from "@runtypelabs/ink-components";
12547
+ import { jsx as jsx30 } from "react/jsx-runtime";
12548
+ function buildCompactionLabel(compaction, elapsedSeconds) {
12549
+ const modeLabel = compaction.mode === "auto" ? "auto" : "manual";
12550
+ const strategyLabel = compaction.strategy === "provider_native" ? "native" : "summary";
12551
+ const thresholdLabel = formatCompactionThresholdLabel(compaction);
12552
+ if (thresholdLabel) {
12553
+ return `Compacting context for session ${compaction.sessionIndex} (${modeLabel}, ${strategyLabel}, ${thresholdLabel})... ${elapsedSeconds}s`;
12554
+ }
12555
+ return `Compacting context for session ${compaction.sessionIndex} (${modeLabel}, ${strategyLabel})... ${elapsedSeconds}s`;
12556
+ }
12557
+ function ContextCompactionIndicator({
12558
+ compaction
12559
+ }) {
12560
+ const [elapsed, setElapsed] = useState21(
12561
+ () => Math.floor((Date.now() - compaction.startedAt) / 1e3)
12562
+ );
12563
+ useEffect19(() => {
12564
+ const start = compaction.startedAt;
12565
+ setElapsed(Math.floor((Date.now() - start) / 1e3));
12566
+ const interval = setInterval(() => {
12567
+ setElapsed(Math.floor((Date.now() - start) / 1e3));
12568
+ }, 1e3);
12569
+ return () => clearInterval(interval);
12570
+ }, [compaction.startedAt]);
12571
+ return /* @__PURE__ */ jsx30(Spinner7, { label: buildCompactionLabel(compaction, elapsed), color: theme29.warning });
11954
12572
  }
11955
12573
 
11956
12574
  // src/ink/marathon/ReasoningBlock.tsx
11957
- import { Box as Box24, Text as Text26 } from "ink";
11958
- import { theme as theme28 } from "@runtypelabs/ink-components";
11959
- import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
11960
- var REASONING_LABEL_COLOR = theme28.accent;
11961
- var REASONING_TEXT_COLOR = theme28.textMuted;
11962
- var REASONING_HINT_COLOR = theme28.textSubtle;
12575
+ import { Box as Box26, Text as Text27 } from "ink";
12576
+ import { theme as theme30 } from "@runtypelabs/ink-components";
12577
+ import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
12578
+ var REASONING_LABEL_COLOR = theme30.accent;
12579
+ var REASONING_TEXT_COLOR = theme30.textMuted;
12580
+ var REASONING_HINT_COLOR = theme30.textSubtle;
11963
12581
  function renderReasoningLine(line) {
11964
12582
  return line ? `| ${line}` : "|";
11965
12583
  }
@@ -11972,17 +12590,17 @@ function ReasoningBlock({
11972
12590
  }) {
11973
12591
  const label = collapsed ? "Reasoning hidden" : "Reasoning";
11974
12592
  const hint = showToggleHint ? collapsed ? "r to show" : "r to hide" : null;
11975
- return /* @__PURE__ */ jsxs23(Box24, { flexDirection: "column", children: [
11976
- /* @__PURE__ */ jsxs23(Box24, { children: [
11977
- /* @__PURE__ */ jsx29(Text26, { color: REASONING_LABEL_COLOR, children: label }),
11978
- hint && /* @__PURE__ */ jsxs23(Text26, { color: REASONING_HINT_COLOR, children: [
12593
+ return /* @__PURE__ */ jsxs24(Box26, { flexDirection: "column", children: [
12594
+ /* @__PURE__ */ jsxs24(Box26, { children: [
12595
+ /* @__PURE__ */ jsx31(Text27, { color: REASONING_LABEL_COLOR, children: label }),
12596
+ hint && /* @__PURE__ */ jsxs24(Text27, { color: REASONING_HINT_COLOR, children: [
11979
12597
  " (",
11980
12598
  hint,
11981
12599
  ")"
11982
12600
  ] })
11983
12601
  ] }),
11984
- !collapsed && lines.map((line, index) => /* @__PURE__ */ jsx29(
11985
- Text26,
12602
+ !collapsed && lines.map((line, index) => /* @__PURE__ */ jsx31(
12603
+ Text27,
11986
12604
  {
11987
12605
  color: REASONING_TEXT_COLOR,
11988
12606
  wrap: compact ? "truncate" : void 0,
@@ -11990,53 +12608,77 @@ function ReasoningBlock({
11990
12608
  },
11991
12609
  `${index}:${line}`
11992
12610
  )),
11993
- !collapsed && live && /* @__PURE__ */ jsx29(Text26, { color: REASONING_TEXT_COLOR, wrap: compact ? "truncate" : void 0, children: "| ..." })
12611
+ !collapsed && live && /* @__PURE__ */ jsx31(Text27, { color: REASONING_TEXT_COLOR, wrap: compact ? "truncate" : void 0, children: "| ..." })
11994
12612
  ] });
11995
12613
  }
11996
12614
 
11997
- // src/ink/marathon/UpgradeModal.tsx
11998
- import { Box as Box25, Text as Text27 } from "ink";
11999
- import { theme as theme29 } from "@runtypelabs/ink-components";
12000
- import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
12001
- function UpgradeModal({ prompt, width }) {
12002
- return /* @__PURE__ */ jsxs24(
12003
- Box25,
12004
- {
12005
- width,
12006
- flexDirection: "column",
12007
- borderStyle: "round",
12008
- borderColor: theme29.warning,
12009
- backgroundColor: theme29.surfaceElevated,
12010
- paddingX: 2,
12011
- paddingY: 1,
12012
- children: [
12013
- /* @__PURE__ */ jsx30(Text27, { color: theme29.warning, bold: true, children: "Upgrade available" }),
12014
- /* @__PURE__ */ jsx30(Box25, { marginTop: 1, children: /* @__PURE__ */ jsx30(Text27, { color: theme29.label, children: prompt.message }) }),
12015
- (prompt.code || prompt.limitType || prompt.retryAfter) && /* @__PURE__ */ jsxs24(Box25, { flexDirection: "column", marginTop: 1, children: [
12016
- prompt.code && /* @__PURE__ */ jsxs24(Text27, { color: theme29.muted, children: [
12017
- "Code: ",
12018
- /* @__PURE__ */ jsx30(Text27, { color: theme29.label, children: prompt.code })
12019
- ] }),
12020
- prompt.limitType && /* @__PURE__ */ jsxs24(Text27, { color: theme29.muted, children: [
12021
- "Limit: ",
12022
- /* @__PURE__ */ jsx30(Text27, { color: theme29.label, children: prompt.limitType })
12023
- ] }),
12024
- prompt.retryAfter && /* @__PURE__ */ jsxs24(Text27, { color: theme29.muted, children: [
12025
- "Retry after: ",
12026
- /* @__PURE__ */ jsx30(Text27, { color: theme29.label, children: prompt.retryAfter })
12027
- ] })
12028
- ] }),
12029
- /* @__PURE__ */ jsxs24(Box25, { marginTop: 1, children: [
12030
- /* @__PURE__ */ jsx30(Text27, { color: theme29.info, children: "Enter" }),
12031
- /* @__PURE__ */ jsx30(Text27, { color: theme29.muted, children: " open billing " }),
12032
- /* @__PURE__ */ jsx30(Text27, { color: theme29.info, children: "Esc" }),
12033
- /* @__PURE__ */ jsx30(Text27, { color: theme29.muted, children: " close" })
12034
- ] }),
12035
- /* @__PURE__ */ jsx30(Box25, { marginTop: 1, children: /* @__PURE__ */ jsx30(Text27, { color: theme29.dimLabel, children: "Opens your dashboard billing page" }) })
12036
- ]
12037
- }
12038
- );
12039
- }
12615
+ // src/ink/marathon/OverviewTranscriptPane.tsx
12616
+ import { Fragment as Fragment3, jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
12617
+ var UPGRADE_BROWSE_HINT_LINES = [
12618
+ "Upgrade modal dismissed.",
12619
+ "Use Shift+Left/Right or 1-9 to review previous marathon runs."
12620
+ ];
12621
+ var OverviewTranscriptPane = memo5(function OverviewTranscriptPane2({
12622
+ content,
12623
+ enableMarkdown,
12624
+ isStreaming,
12625
+ maxVisibleLines,
12626
+ scrollOffset,
12627
+ totalLines,
12628
+ paneWidth,
12629
+ contentHeight,
12630
+ showLoadingAnimation = false,
12631
+ hasReasoning = false,
12632
+ visibleReasoningLines = [],
12633
+ reasoningCollapsed = false,
12634
+ reasoningLive = false,
12635
+ showThinkingIndicator = false,
12636
+ thinkingStartedAt,
12637
+ contextCompaction = null,
12638
+ showUpgradeBrowseHint = false,
12639
+ error = null
12640
+ }) {
12641
+ if (showLoadingAnimation) {
12642
+ return /* @__PURE__ */ jsx32(Box27, { flexGrow: 1, minHeight: contentHeight, overflow: "hidden", children: /* @__PURE__ */ jsx32(LoadingAnimation, { width: paneWidth, height: contentHeight }) });
12643
+ }
12644
+ const maxScroll = Math.max(0, totalLines - maxVisibleLines);
12645
+ return /* @__PURE__ */ jsxs25(Fragment3, { children: [
12646
+ hasReasoning && /* @__PURE__ */ jsx32(Box27, { flexDirection: "column", marginBottom: reasoningCollapsed ? 0 : 1, children: /* @__PURE__ */ jsx32(
12647
+ ReasoningBlock,
12648
+ {
12649
+ lines: visibleReasoningLines,
12650
+ live: reasoningLive,
12651
+ collapsed: reasoningCollapsed,
12652
+ showToggleHint: true
12653
+ }
12654
+ ) }),
12655
+ showThinkingIndicator && /* @__PURE__ */ jsx32(ThinkingIndicator, { startedAt: thinkingStartedAt }),
12656
+ contextCompaction && /* @__PURE__ */ jsx32(ContextCompactionIndicator, { compaction: contextCompaction }),
12657
+ showUpgradeBrowseHint && /* @__PURE__ */ jsx32(Box27, { marginBottom: 1, children: /* @__PURE__ */ jsx32(ReasoningBlock, { lines: UPGRADE_BROWSE_HINT_LINES, compact: true }) }),
12658
+ /* @__PURE__ */ jsxs25(Box27, { flexDirection: "row", children: [
12659
+ /* @__PURE__ */ jsx32(Box27, { flexDirection: "column", flexGrow: 1, marginRight: 1, children: /* @__PURE__ */ jsx32(
12660
+ StreamOutput,
12661
+ {
12662
+ content,
12663
+ isStreaming,
12664
+ enableMarkdown,
12665
+ maxVisibleLines,
12666
+ scrollOffset
12667
+ }
12668
+ ) }),
12669
+ /* @__PURE__ */ jsx32(
12670
+ Scrollbar,
12671
+ {
12672
+ totalLines,
12673
+ visibleLines: maxVisibleLines,
12674
+ scrollOffset: maxScroll - Math.min(scrollOffset, maxScroll),
12675
+ height: maxVisibleLines
12676
+ }
12677
+ )
12678
+ ] }),
12679
+ error && /* @__PURE__ */ jsx32(ErrorDisplay3, { error })
12680
+ ] });
12681
+ });
12040
12682
 
12041
12683
  // src/ink/marathon/upgrade-modal-utils.ts
12042
12684
  var UPGRADE_LIMIT_TYPES = /* @__PURE__ */ new Set([
@@ -12192,7 +12834,7 @@ function isAllowedCheckpointBrowseKey(key, isBrowsingScreen) {
12192
12834
  }
12193
12835
 
12194
12836
  // src/ink/marathon/MarathonApp.tsx
12195
- import { Fragment as Fragment3, jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
12837
+ import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
12196
12838
  var TOOL_PANEL_WIDE = 48;
12197
12839
  var TOOL_PANEL_NARROW = 36;
12198
12840
  var NARROW_THRESHOLD = 100;
@@ -12211,22 +12853,23 @@ function cloneSessionSnapshot(snapshot) {
12211
12853
  ...tool.parameters ? { parameters: structuredClone(tool.parameters) } : {},
12212
12854
  ...tool.result !== void 0 ? { result: structuredClone(tool.result) } : {}
12213
12855
  })),
12214
- rawEvents: snapshot.rawEvents.map((event) => ({
12215
- ...event,
12216
- data: structuredClone(event.data)
12217
- }))
12856
+ rawEvents: [...snapshot.rawEvents]
12218
12857
  };
12219
12858
  }
12220
12859
  function upsertSessionSnapshots(snapshots, nextSnapshot) {
12221
- const bySessionIndex = /* @__PURE__ */ new Map();
12222
- for (const snapshot of snapshots) {
12223
- bySessionIndex.set(snapshot.sessionIndex, cloneSessionSnapshot(snapshot));
12224
- }
12225
- bySessionIndex.set(nextSnapshot.sessionIndex, cloneSessionSnapshot(nextSnapshot));
12226
- return Array.from(bySessionIndex.values()).sort((left, right) => left.sessionIndex - right.sessionIndex);
12860
+ const cloned = cloneSessionSnapshot(nextSnapshot);
12861
+ const existingIndex = snapshots.findIndex((s) => s.sessionIndex === nextSnapshot.sessionIndex);
12862
+ if (existingIndex >= 0) {
12863
+ const result2 = [...snapshots];
12864
+ result2[existingIndex] = cloned;
12865
+ return result2;
12866
+ }
12867
+ const result = [...snapshots, cloned];
12868
+ result.sort((left, right) => left.sessionIndex - right.sessionIndex);
12869
+ return result;
12227
12870
  }
12228
- function buildLiveSessionSnapshot(liveState, sessionIndex, model) {
12229
- const hasLiveState = liveState.phase === "thinking" || Boolean(liveState.content) || Boolean(liveState.reasoning) || liveState.tools.length > 0 || liveState.rawEvents.length > 0 || Boolean(liveState.contextCompaction?.active);
12871
+ function buildLiveSessionSnapshot(liveState, rawEvents, sessionIndex, model) {
12872
+ const hasLiveState = liveState.phase === "thinking" || Boolean(liveState.content) || Boolean(liveState.reasoning) || liveState.tools.length > 0 || rawEvents.length > 0 || Boolean(liveState.contextCompaction?.active);
12230
12873
  if (!hasLiveState) return void 0;
12231
12874
  return {
12232
12875
  sessionIndex,
@@ -12235,15 +12878,8 @@ function buildLiveSessionSnapshot(liveState, sessionIndex, model) {
12235
12878
  cost: liveState.totalCost,
12236
12879
  content: liveState.content,
12237
12880
  reasoning: liveState.reasoning,
12238
- tools: liveState.tools.map((tool) => ({
12239
- ...tool,
12240
- ...tool.parameters ? { parameters: structuredClone(tool.parameters) } : {},
12241
- ...tool.result !== void 0 ? { result: structuredClone(tool.result) } : {}
12242
- })),
12243
- rawEvents: liveState.rawEvents.map((event) => ({
12244
- ...event,
12245
- data: structuredClone(event.data)
12246
- }))
12881
+ tools: liveState.tools,
12882
+ rawEvents
12247
12883
  };
12248
12884
  }
12249
12885
  function copyToClipboard(text) {
@@ -12264,8 +12900,15 @@ function copyToClipboard(text) {
12264
12900
  return false;
12265
12901
  }
12266
12902
  }
12903
+ function isBlank(s) {
12904
+ for (let i = 0; i < s.length; i++) {
12905
+ const c = s.charCodeAt(i);
12906
+ if (c !== 32 && c !== 9 && c !== 10 && c !== 13) return false;
12907
+ }
12908
+ return true;
12909
+ }
12267
12910
  function shouldShowMarathonLoadingAnimation(params) {
12268
- return params.allowInitialInlineLoader && !params.content.trim() && !params.reasoning.trim() && params.toolCount === 0 && (params.phase === "idle" || params.phase === "thinking");
12911
+ return params.allowInitialInlineLoader && isBlank(params.content) && isBlank(params.reasoning) && params.toolCount === 0 && (params.phase === "idle" || params.phase === "thinking");
12269
12912
  }
12270
12913
  function MarathonApp({
12271
12914
  taskName,
@@ -12298,7 +12941,10 @@ function MarathonApp({
12298
12941
  }) {
12299
12942
  const {
12300
12943
  state,
12944
+ rawEventsRef,
12301
12945
  callbacks,
12946
+ getHydratedState,
12947
+ hydrateToolEntry,
12302
12948
  reset: _reset,
12303
12949
  setCheckpoint,
12304
12950
  markPendingStart,
@@ -12311,7 +12957,7 @@ function MarathonApp({
12311
12957
  } = useMarathonStream();
12312
12958
  const { exit } = useApp4();
12313
12959
  const { stdout } = useStdout5();
12314
- const separator = theme30.separator ?? " \xB7 ";
12960
+ const separator = theme31.separator ?? " \xB7 ";
12315
12961
  const checkpointResolveRef = useRef8(null);
12316
12962
  const [checkpointRecap, setCheckpointRecap] = useState22(null);
12317
12963
  const [currentModel, setCurrentModel] = useState22(model || "default");
@@ -12364,7 +13010,7 @@ function MarathonApp({
12364
13010
  useEffect20(() => {
12365
13011
  streamRef.current = {
12366
13012
  getCallbacks: () => callbacks,
12367
- getState: () => state,
13013
+ getState: () => getHydratedState(),
12368
13014
  appendSessionSnapshot: (snapshot) => {
12369
13015
  setSessionSnapshots((prev) => upsertSessionSnapshots(prev, snapshot));
12370
13016
  const snapshotKey = createSessionTabKey(snapshot.sessionIndex);
@@ -12421,7 +13067,7 @@ function MarathonApp({
12421
13067
  setCheckpoint,
12422
13068
  showError,
12423
13069
  startContextCompaction,
12424
- state,
13070
+ getHydratedState,
12425
13071
  streamRef,
12426
13072
  resetForNewSession
12427
13073
  ]);
@@ -12483,7 +13129,7 @@ function MarathonApp({
12483
13129
  content: state.content,
12484
13130
  reasoning: state.reasoning,
12485
13131
  tools: state.tools,
12486
- rawEvents: state.rawEvents
13132
+ rawEvents: rawEventsRef.current
12487
13133
  }, null, 2);
12488
13134
  }
12489
13135
  const ok = copyToClipboard(json);
@@ -12491,7 +13137,7 @@ function MarathonApp({
12491
13137
  } catch {
12492
13138
  showFlash("Copy failed");
12493
13139
  }
12494
- }, [sessionSnapshots, state.content, state.reasoning, state.tools, state.rawEvents, stateFilePath2]);
13140
+ }, [sessionSnapshots, state.content, state.reasoning, state.tools, stateFilePath2]);
12495
13141
  const handleOpenStateFile = useCallback8(() => {
12496
13142
  setShowSessionMenu(false);
12497
13143
  if (stateFilePath2 && fs4.existsSync(stateFilePath2)) {
@@ -12517,10 +13163,11 @@ function MarathonApp({
12517
13163
  void open4(agentPageUrl);
12518
13164
  }
12519
13165
  }, [agentPageUrl]);
12520
- const shouldShowLiveTab = Boolean(state.contextCompaction?.active) || state.phase !== "idle" && state.phase !== "checkpoint" && state.phase !== "complete" && (Boolean(state.content) || Boolean(state.reasoning) || state.tools.length > 0 || state.rawEvents.length > 0 || state.phase === "thinking" || state.phase === "error");
13166
+ const shouldShowLiveTab = Boolean(state.contextCompaction?.active) || state.phase !== "idle" && state.phase !== "checkpoint" && state.phase !== "complete" && (Boolean(state.content) || Boolean(state.reasoning) || state.tools.length > 0 || rawEventsRef.current.length > 0 || state.phase === "thinking" || state.phase === "error");
12521
13167
  const liveSessionSnapshot = useMemo11(
12522
- () => shouldShowLiveTab ? buildLiveSessionSnapshot(state, latestCompletedSessionIndex + 1, currentModel) : void 0,
13168
+ () => shouldShowLiveTab ? buildLiveSessionSnapshot(state, rawEventsRef.current, latestCompletedSessionIndex + 1, currentModel) : void 0,
12523
13169
  [currentModel, latestCompletedSessionIndex, shouldShowLiveTab, state]
13170
+ // rawEventsRef.current is read directly — the memo re-evaluates because `state` changes on every event
12524
13171
  );
12525
13172
  const liveSessionKey = liveSessionSnapshot ? createSessionTabKey(liveSessionSnapshot.sessionIndex, "live") : void 0;
12526
13173
  const latestSessionKey = getLatestSessionTabKey(sessionSnapshots, liveSessionSnapshot);
@@ -12544,26 +13191,63 @@ function MarathonApp({
12544
13191
  const displayedContent = displayedSessionSnapshot?.content ?? state.content;
12545
13192
  const displayedReasoning = displayedSessionSnapshot?.reasoning ?? state.reasoning;
12546
13193
  const displayedTools = displayedSessionSnapshot?.tools ?? state.tools;
13194
+ const liveRawEvents = rawEventsRef.current;
12547
13195
  const displayedEvents = useMemo11(() => {
12548
13196
  const snapshotEvents = displayedSessionSnapshot?.rawEvents;
12549
13197
  if (snapshotEvents && snapshotEvents.length > 0) return snapshotEvents;
12550
13198
  if (displayedSessionSnapshot && displayedSessionSnapshot.status !== "live" && stateFilePath2 && snapshotEvents?.length === 0) {
12551
13199
  return readSessionEventLog(stateFilePath2, displayedSessionSnapshot.sessionIndex);
12552
13200
  }
12553
- return snapshotEvents ?? state.rawEvents;
12554
- }, [displayedSessionSnapshot, state.rawEvents, stateFilePath2]);
12555
- const seenEventTypes = useMemo11(
12556
- () => Array.from(new Set(displayedEvents.map((e) => e.type))).sort(),
12557
- [displayedEvents]
12558
- );
13201
+ return snapshotEvents ?? liveRawEvents;
13202
+ }, [displayedSessionSnapshot, liveRawEvents, stateFilePath2]);
13203
+ const seenTypesRef = useRef8({
13204
+ set: /* @__PURE__ */ new Set(),
13205
+ lastLen: 0,
13206
+ eventsRef: null
13207
+ });
13208
+ const seenEventTypes = useMemo11(() => {
13209
+ const cache = seenTypesRef.current;
13210
+ if (cache.eventsRef !== displayedEvents) {
13211
+ cache.set = /* @__PURE__ */ new Set();
13212
+ cache.lastLen = 0;
13213
+ cache.eventsRef = displayedEvents;
13214
+ }
13215
+ const prevSize = cache.set.size;
13216
+ for (let i = cache.lastLen; i < displayedEvents.length; i++) {
13217
+ cache.set.add(displayedEvents[i].type);
13218
+ }
13219
+ cache.lastLen = displayedEvents.length;
13220
+ if (cache.set.size !== prevSize || prevSize === 0) {
13221
+ return Array.from(cache.set).sort();
13222
+ }
13223
+ return void 0;
13224
+ }, [displayedEvents]);
13225
+ const stableSeenEventTypesRef = useRef8([]);
13226
+ if (seenEventTypes !== void 0) {
13227
+ stableSeenEventTypesRef.current = seenEventTypes;
13228
+ }
12559
13229
  const filteredEvents = useMemo11(
12560
13230
  () => eventTypeFilter.size === 0 ? displayedEvents : displayedEvents.filter((e) => eventTypeFilter.has(e.type)),
12561
13231
  [displayedEvents, eventTypeFilter]
12562
13232
  );
12563
- const trackedFiles = useMemo11(
12564
- () => extractTrackedFiles(sessionSnapshots, displayedTools, planPath),
12565
- [sessionSnapshots, displayedTools, planPath]
13233
+ const fileToolFingerprint = useMemo11(
13234
+ () => displayedTools.filter((t) => t.name === "write_file" || t.name === "read_file").map((t) => `${t.id}:${t.name}:${String(t.parameters?.path ?? "")}:${t.startedAt}`).join("|"),
13235
+ [displayedTools]
12566
13236
  );
13237
+ const trackedFilesCacheRef = useRef8({
13238
+ snapshots: null,
13239
+ fingerprint: "",
13240
+ planPath: void 0,
13241
+ value: []
13242
+ });
13243
+ const trackedFilesCache = trackedFilesCacheRef.current;
13244
+ if (trackedFilesCache.snapshots !== sessionSnapshots || trackedFilesCache.fingerprint !== fileToolFingerprint || trackedFilesCache.planPath !== planPath) {
13245
+ trackedFilesCache.snapshots = sessionSnapshots;
13246
+ trackedFilesCache.fingerprint = fileToolFingerprint;
13247
+ trackedFilesCache.planPath = planPath;
13248
+ trackedFilesCache.value = extractTrackedFiles(sessionSnapshots, displayedTools, planPath);
13249
+ }
13250
+ const trackedFiles = trackedFilesCache.value;
12567
13251
  const latestLiveActivityRef = useRef8(void 0);
12568
13252
  const upgradePrompt = useMemo11(() => parseMarathonUpgradePrompt(state.error), [state.error]);
12569
13253
  const upgradePromptKey = useMemo11(
@@ -12614,7 +13298,7 @@ function MarathonApp({
12614
13298
  setUpgradeModalDismissed(false);
12615
13299
  }, [upgradePromptKey]);
12616
13300
  useEffect20(() => {
12617
- const liveActivityKey = liveSessionSnapshot ? `${liveSessionSnapshot.sessionIndex}:${state.phase}:${state.content.length}:${state.reasoning.length}:${state.tools.length}:${state.rawEvents.length}` : void 0;
13301
+ const liveActivityKey = liveSessionSnapshot ? `${liveSessionSnapshot.sessionIndex}:${state.phase}:${state.content.length}:${state.reasoning.length}:${state.tools.length}:${rawEventsRef.current.length}` : void 0;
12618
13302
  if (!liveActivityKey) {
12619
13303
  latestLiveActivityRef.current = void 0;
12620
13304
  return;
@@ -12629,7 +13313,6 @@ function MarathonApp({
12629
13313
  liveSessionSnapshot,
12630
13314
  state.content.length,
12631
13315
  state.phase,
12632
- state.rawEvents.length,
12633
13316
  state.reasoning.length,
12634
13317
  state.tools.length
12635
13318
  ]);
@@ -12653,14 +13336,9 @@ function MarathonApp({
12653
13336
  }
12654
13337
  setPreviewUrl(void 0);
12655
13338
  }, [displayedTools, selectedIsLive, followLatest, state.phase]);
12656
- useEffect20(() => {
12657
- if (showEventStream && filteredEvents.length > 0 && eventCursor === -1) {
12658
- setEventCursor(filteredEvents.length - 1);
12659
- }
12660
- }, [filteredEvents.length, eventCursor, showEventStream]);
12661
13339
  useEffect20(() => {
12662
13340
  if (allowInitialInlineLoader) return;
12663
- const hasVisibleSessionActivity = Boolean(state.content.trim()) || Boolean(state.reasoning.trim()) || state.tools.length > 0 || state.error !== null;
13341
+ const hasVisibleSessionActivity = !isBlank(state.content) || !isBlank(state.reasoning) || state.tools.length > 0 || state.error !== null;
12664
13342
  if (hasVisibleSessionActivity) {
12665
13343
  setAllowInitialInlineLoader(true);
12666
13344
  }
@@ -12701,7 +13379,7 @@ function MarathonApp({
12701
13379
  content: state.content,
12702
13380
  reasoning: state.reasoning,
12703
13381
  tools: state.tools,
12704
- rawEvents: state.rawEvents,
13382
+ rawEvents: rawEventsRef.current,
12705
13383
  sessionSnapshots
12706
13384
  });
12707
13385
  exit();
@@ -12858,7 +13536,8 @@ function MarathonApp({
12858
13536
  return;
12859
13537
  }
12860
13538
  if (_input === "c" && showEventStream) {
12861
- const evt = detailEvent ?? filteredEvents[eventCursor];
13539
+ const eCursor = eventCursor === -1 ? filteredEvents.length - 1 : eventCursor;
13540
+ const evt = detailEvent ?? filteredEvents[eCursor];
12862
13541
  if (evt) {
12863
13542
  const payload = detailEvent ? formatEventDetailCopyText(evt, filteredEvents[0]?.timestamp ?? null) : JSON.stringify(evt.data, null, 2);
12864
13543
  const ok = copyToClipboard(payload);
@@ -12874,7 +13553,8 @@ function MarathonApp({
12874
13553
  return;
12875
13554
  }
12876
13555
  if (_input === "c" && showToolsPanel) {
12877
- const tool = detailToolEntry ?? displayedTools[toolCursor];
13556
+ const selectedTool = displayedTools[toolCursor];
13557
+ const tool = detailToolEntry ?? (selectedTool ? hydrateToolEntry(selectedTool) : void 0);
12878
13558
  if (tool) {
12879
13559
  const payload = detailToolEntry ? formatToolDetailCopyText(tool) : JSON.stringify({ name: tool.name, parameters: tool.parameters, result: tool.result }, null, 2);
12880
13560
  const ok = copyToClipboard(payload);
@@ -12902,7 +13582,8 @@ function MarathonApp({
12902
13582
  return;
12903
13583
  }
12904
13584
  if (key.return && showEventStream && !detailEvent) {
12905
- const evt = filteredEvents[eventCursor];
13585
+ const eCursor = eventCursor === -1 ? filteredEvents.length - 1 : eventCursor;
13586
+ const evt = filteredEvents[eCursor];
12906
13587
  if (evt) {
12907
13588
  setDetailEvent(evt);
12908
13589
  setDetailScrollOffset(0);
@@ -12917,7 +13598,7 @@ function MarathonApp({
12917
13598
  if (key.return && showToolsPanel && !detailToolEntry) {
12918
13599
  const tool = displayedTools[toolCursor];
12919
13600
  if (tool) {
12920
- setDetailToolEntry(tool);
13601
+ setDetailToolEntry(hydrateToolEntry(tool));
12921
13602
  setToolDetailScrollOffset(0);
12922
13603
  }
12923
13604
  return;
@@ -12944,9 +13625,12 @@ function MarathonApp({
12944
13625
  } else if (showEventStream && detailEvent) {
12945
13626
  setDetailScrollOffset((prev) => Math.max(0, prev - SCROLL_STEP));
12946
13627
  } else if (showEventStream) {
12947
- setEventCursor((prev) => Math.max(0, prev - 1));
13628
+ setEventCursor((prev) => {
13629
+ const eff = prev === -1 ? filteredEvents.length - 1 : prev;
13630
+ return Math.max(0, eff - 1);
13631
+ });
12948
13632
  } else {
12949
- const contentLines = displayedContent.split("\n").length * 2;
13633
+ const contentLines = overviewTotalLines * 2;
12950
13634
  const visibleRows = state.phase === "checkpoint" && checkpointRecap ? adjustedContentHeight - checkpointPromptRows : transcriptRows;
12951
13635
  const maxOverviewScroll = Math.max(0, contentLines - visibleRows);
12952
13636
  setScrollOffset((prev) => Math.min(prev + SCROLL_STEP, maxOverviewScroll));
@@ -12958,29 +13642,26 @@ function MarathonApp({
12958
13642
  markCheckpointExploring();
12959
13643
  }
12960
13644
  if (showReasoningPanel) {
12961
- const totalReasoningLines = getReasoningLines(displayedReasoning, terminalWidth).length;
12962
- const maxReasoningScroll = Math.max(0, totalReasoningLines - (adjustedContentHeight - 3));
13645
+ const maxReasoningScroll = Math.max(0, reasoningDetailLineCount - (adjustedContentHeight - 3));
12963
13646
  setReasoningScrollOffset((prev) => Math.min(prev + SCROLL_STEP, maxReasoningScroll));
12964
13647
  } else if (showFilesPanel && detailFile && detailFileContent) {
12965
- const renderedLines = getFileRenderedLineCount(detailFile.path, detailFileContent, terminalWidth);
12966
- const maxFileScroll = Math.max(0, renderedLines - (adjustedContentHeight - 3));
13648
+ const maxFileScroll = Math.max(0, fileDetailRenderedLineCount - (adjustedContentHeight - 3));
12967
13649
  setFileDetailScrollOffset((prev) => Math.min(prev + SCROLL_STEP, maxFileScroll));
12968
13650
  } else if (showFilesPanel) {
12969
13651
  setFileCursor((prev) => Math.min(trackedFiles.length - 1, prev + 1));
12970
13652
  } else if (showToolsPanel && detailToolEntry) {
12971
- const totalToolLines = getToolDetailLines(detailToolEntry, terminalWidth).length;
12972
- const maxToolScroll = Math.max(0, totalToolLines - (adjustedContentHeight - 3));
13653
+ const maxToolScroll = Math.max(0, toolDetailLineCount - (adjustedContentHeight - 3));
12973
13654
  setToolDetailScrollOffset((prev) => Math.min(prev + SCROLL_STEP, maxToolScroll));
12974
13655
  } else if (showToolsPanel) {
12975
13656
  setToolCursor((prev) => Math.min(displayedTools.length - 1, prev + 1));
12976
13657
  } else if (showEventStream && detailEvent) {
12977
- const wrapWidth = Math.max(20, terminalWidth - 4);
12978
- const json = JSON.stringify(detailEvent.data, null, 2);
12979
- const wrappedLines = json.split("\n").reduce((count, line) => count + (line.length <= wrapWidth ? 1 : Math.ceil(line.length / wrapWidth)), 0);
12980
- const maxEventScroll = Math.max(0, wrappedLines - (adjustedContentHeight - 3));
13658
+ const maxEventScroll = Math.max(0, eventDetailLineCount - (adjustedContentHeight - 3));
12981
13659
  setDetailScrollOffset((prev) => Math.min(prev + SCROLL_STEP, maxEventScroll));
12982
13660
  } else if (showEventStream) {
12983
- setEventCursor((prev) => Math.min(filteredEvents.length - 1, prev + 1));
13661
+ setEventCursor((prev) => {
13662
+ const eff = prev === -1 ? filteredEvents.length - 1 : prev;
13663
+ return Math.min(filteredEvents.length - 1, eff + 1);
13664
+ });
12984
13665
  } else {
12985
13666
  setScrollOffset((prev) => Math.max(0, prev - SCROLL_STEP));
12986
13667
  }
@@ -12989,11 +13670,17 @@ function MarathonApp({
12989
13670
  if (showEventStream && !detailEvent) {
12990
13671
  const pageSize = Math.max(1, adjustedContentHeight - 3);
12991
13672
  if (key.pageUp) {
12992
- setEventCursor((prev) => Math.max(0, prev - pageSize));
13673
+ setEventCursor((prev) => {
13674
+ const eff = prev === -1 ? filteredEvents.length - 1 : prev;
13675
+ return Math.max(0, eff - pageSize);
13676
+ });
12993
13677
  return;
12994
13678
  }
12995
13679
  if (key.pageDown) {
12996
- setEventCursor((prev) => Math.min(filteredEvents.length - 1, prev + pageSize));
13680
+ setEventCursor((prev) => {
13681
+ const eff = prev === -1 ? filteredEvents.length - 1 : prev;
13682
+ return Math.min(filteredEvents.length - 1, eff + pageSize);
13683
+ });
12997
13684
  return;
12998
13685
  }
12999
13686
  if (key.home) {
@@ -13001,7 +13688,7 @@ function MarathonApp({
13001
13688
  return;
13002
13689
  }
13003
13690
  if (key.end) {
13004
- setEventCursor(filteredEvents.length - 1);
13691
+ setEventCursor(-1);
13005
13692
  return;
13006
13693
  }
13007
13694
  }
@@ -13040,7 +13727,7 @@ function MarathonApp({
13040
13727
  const tool = displayedTools[newIndex];
13041
13728
  if (tool) {
13042
13729
  setToolCursor(newIndex);
13043
- setDetailToolEntry(tool);
13730
+ setDetailToolEntry(hydrateToolEntry(tool));
13044
13731
  setToolDetailScrollOffset(0);
13045
13732
  }
13046
13733
  return;
@@ -13050,17 +13737,19 @@ function MarathonApp({
13050
13737
  const tool = displayedTools[newIndex];
13051
13738
  if (tool) {
13052
13739
  setToolCursor(newIndex);
13053
- setDetailToolEntry(tool);
13740
+ setDetailToolEntry(hydrateToolEntry(tool));
13054
13741
  setToolDetailScrollOffset(0);
13055
13742
  }
13056
13743
  return;
13057
13744
  }
13058
13745
  if (key.leftArrow && showEventStream && detailEvent) {
13059
- navigateToEvent(eventCursor - 1);
13746
+ const eCursor = eventCursor === -1 ? filteredEvents.length - 1 : eventCursor;
13747
+ navigateToEvent(eCursor - 1);
13060
13748
  return;
13061
13749
  }
13062
13750
  if (key.rightArrow && showEventStream && detailEvent) {
13063
- navigateToEvent(eventCursor + 1);
13751
+ const eCursor = eventCursor === -1 ? filteredEvents.length - 1 : eventCursor;
13752
+ navigateToEvent(eCursor + 1);
13064
13753
  return;
13065
13754
  }
13066
13755
  });
@@ -13074,9 +13763,10 @@ function MarathonApp({
13074
13763
  const totalCost = initialCost + state.totalCost;
13075
13764
  const hasTabs = allTabs.length > 0;
13076
13765
  const hasTools = displayedTools.length > 0;
13077
- const hasReasoning = Boolean(displayedReasoning.trim());
13766
+ const hasReasoning = !isBlank(displayedReasoning);
13078
13767
  const showThinkingIndicator = selectedIsLive && state.phase === "thinking" && !hasReasoning;
13079
13768
  const showContextCompactionIndicator = selectedIsLive && Boolean(state.contextCompaction?.active);
13769
+ const reasoningCharCount = displayedReasoning.length;
13080
13770
  const showLoadingAnimation = !showContextCompactionIndicator && shouldShowMarathonLoadingAnimation({
13081
13771
  allowInitialInlineLoader,
13082
13772
  content: displayedContent,
@@ -13163,7 +13853,7 @@ function MarathonApp({
13163
13853
  );
13164
13854
  })();
13165
13855
  const filesCenter = detailFile ? `${detailFile.path.split("/").pop()}${separator}${fileCursor + 1}/${trackedFiles.length}` : `${trackedFiles.length} files`;
13166
- const detailCenter = detailEvent ? `Event detail${separator}${eventCursor + 1}/${filteredEvents.length}` : "Event stream";
13856
+ const detailCenter = detailEvent ? `Event detail${separator}${eventCursor === -1 ? filteredEvents.length : eventCursor + 1}/${filteredEvents.length}` : "Event stream";
13167
13857
  const toolsCenter = detailToolEntry ? `Tool detail${separator}${toolCursor + 1}/${displayedTools.length}` : `${displayedTools.length} tool calls`;
13168
13858
  const goalText = goal || "";
13169
13859
  const goalExpandedRows = goalExpanded && goalText.length > terminalWidth - 4 ? Math.ceil(goalText.length / (terminalWidth - 4)) - 1 : 0;
@@ -13188,16 +13878,33 @@ function MarathonApp({
13188
13878
  3,
13189
13879
  adjustedContentHeight - reasoningHintRows - reasoningBodyRows - reasoningLiveRows - thinkingRows - compactionIndicatorRows
13190
13880
  );
13881
+ const reasoningDetailLineCount = useMemo11(() => {
13882
+ const trimmed = displayedReasoning.trimEnd();
13883
+ return trimmed ? getReasoningLines(trimmed, terminalWidth).length : 0;
13884
+ }, [displayedReasoning, terminalWidth]);
13885
+ const fileDetailRenderedLineCount = useMemo11(
13886
+ () => detailFile && detailFileContent ? getFileRenderedLineCount(detailFile.path, detailFileContent, terminalWidth) : 0,
13887
+ [detailFile, detailFileContent, terminalWidth]
13888
+ );
13889
+ const toolDetailLineCount = useMemo11(
13890
+ () => detailToolEntry ? getToolDetailLines(detailToolEntry, terminalWidth).length : 0,
13891
+ [detailToolEntry, terminalWidth]
13892
+ );
13893
+ const eventDetailLineCount = useMemo11(
13894
+ () => detailEvent ? getEventDetailLines(detailEvent, terminalWidth).length : 0,
13895
+ [detailEvent, terminalWidth]
13896
+ );
13897
+ const overviewTotalLines = useMemo11(() => displayedContent.split("\n").length, [displayedContent]);
13191
13898
  const upgradeModalWidth = Math.max(24, Math.min(terminalWidth - 6, 88));
13192
- const showUpgradeBrowseHint = canBrowseAfterUpgradeError && selectedIsLive && !displayedContent.trim() && !displayedReasoning.trim() && displayedTools.length === 0 && displayedEvents.length === 0;
13193
- return /* @__PURE__ */ jsxs25(
13194
- Box26,
13899
+ const showUpgradeBrowseHint = canBrowseAfterUpgradeError && selectedIsLive && isBlank(displayedContent) && isBlank(displayedReasoning) && displayedTools.length === 0 && displayedEvents.length === 0;
13900
+ return /* @__PURE__ */ jsxs26(
13901
+ Box28,
13195
13902
  {
13196
13903
  flexDirection: "column",
13197
13904
  height: terminalRows,
13198
13905
  width: terminalWidth,
13199
13906
  children: [
13200
- /* @__PURE__ */ jsx31(
13907
+ /* @__PURE__ */ jsx33(
13201
13908
  SessionHeader,
13202
13909
  {
13203
13910
  sessionName: taskName,
@@ -13217,8 +13924,8 @@ function MarathonApp({
13217
13924
  currentMilestone
13218
13925
  }
13219
13926
  ),
13220
- /* @__PURE__ */ jsx31(Box26, { marginLeft: 1, children: /* @__PURE__ */ jsx31(ScreenTabs, { activeTab: activeScreen, width: terminalWidth - 1, shortcutHint: "Tab: next screen" }) }),
13221
- hasTabs && /* @__PURE__ */ jsx31(Box26, { width: terminalWidth, marginBottom: 1, marginLeft: 1, children: /* @__PURE__ */ jsx31(
13927
+ /* @__PURE__ */ jsx33(Box28, { marginLeft: 1, children: /* @__PURE__ */ jsx33(ScreenTabs, { activeTab: activeScreen, width: terminalWidth - 1, shortcutHint: "Tab: next screen" }) }),
13928
+ hasTabs && /* @__PURE__ */ jsx33(Box28, { width: terminalWidth, marginBottom: 1, marginLeft: 1, children: /* @__PURE__ */ jsx33(
13222
13929
  SessionTabs,
13223
13930
  {
13224
13931
  tabs: visibleTabs.tabs,
@@ -13227,13 +13934,13 @@ function MarathonApp({
13227
13934
  shortcutHint: allTabs.length > 1 ? "Shift+\u2190/\u2192: select run" : void 0
13228
13935
  }
13229
13936
  ) }),
13230
- showFilesPanel ? /* @__PURE__ */ jsx31(
13231
- Box26,
13937
+ showFilesPanel ? /* @__PURE__ */ jsx33(
13938
+ Box28,
13232
13939
  {
13233
13940
  flexDirection: "column",
13234
13941
  height: adjustedContentHeight,
13235
13942
  overflow: "hidden",
13236
- children: /* @__PURE__ */ jsx31(
13943
+ children: /* @__PURE__ */ jsx33(
13237
13944
  FilesPanel,
13238
13945
  {
13239
13946
  files: trackedFiles,
@@ -13249,13 +13956,13 @@ function MarathonApp({
13249
13956
  }
13250
13957
  )
13251
13958
  }
13252
- ) : showReasoningPanel ? /* @__PURE__ */ jsx31(
13253
- Box26,
13959
+ ) : showReasoningPanel ? /* @__PURE__ */ jsx33(
13960
+ Box28,
13254
13961
  {
13255
13962
  flexDirection: "column",
13256
13963
  height: adjustedContentHeight,
13257
13964
  overflow: "hidden",
13258
- children: /* @__PURE__ */ jsx31(
13965
+ children: /* @__PURE__ */ jsx33(
13259
13966
  ReasoningPanel,
13260
13967
  {
13261
13968
  reasoning: displayedReasoning,
@@ -13265,34 +13972,34 @@ function MarathonApp({
13265
13972
  }
13266
13973
  )
13267
13974
  }
13268
- ) : showEventStream ? /* @__PURE__ */ jsx31(
13269
- Box26,
13975
+ ) : showEventStream ? /* @__PURE__ */ jsx33(
13976
+ Box28,
13270
13977
  {
13271
13978
  flexDirection: "column",
13272
13979
  height: adjustedContentHeight,
13273
13980
  overflow: "hidden",
13274
- children: /* @__PURE__ */ jsx31(
13981
+ children: /* @__PURE__ */ jsx33(
13275
13982
  EventStreamPanel,
13276
13983
  {
13277
13984
  events: filteredEvents,
13278
13985
  startTime: filteredEvents[0]?.timestamp ?? null,
13279
13986
  maxVisibleLines: adjustedContentHeight - 1,
13280
- selectedIndex: eventCursor,
13987
+ selectedIndex: eventCursor === -1 ? filteredEvents.length - 1 : eventCursor,
13281
13988
  detailEvent,
13282
13989
  detailScrollOffset,
13283
13990
  width: terminalWidth,
13284
13991
  activeFilterCount: eventTypeFilter.size,
13285
- totalTypeCount: seenEventTypes.length
13992
+ totalTypeCount: stableSeenEventTypesRef.current.length
13286
13993
  }
13287
13994
  )
13288
13995
  }
13289
- ) : showToolsPanel ? /* @__PURE__ */ jsx31(
13290
- Box26,
13996
+ ) : showToolsPanel ? /* @__PURE__ */ jsx33(
13997
+ Box28,
13291
13998
  {
13292
13999
  flexDirection: "column",
13293
14000
  height: adjustedContentHeight,
13294
14001
  overflow: "hidden",
13295
- children: /* @__PURE__ */ jsx31(
14002
+ children: /* @__PURE__ */ jsx33(
13296
14003
  ToolsPanel,
13297
14004
  {
13298
14005
  tools: displayedTools,
@@ -13304,14 +14011,14 @@ function MarathonApp({
13304
14011
  }
13305
14012
  )
13306
14013
  }
13307
- ) : state.phase === "checkpoint" && checkpointRecap ? /* @__PURE__ */ jsxs25(
13308
- Box26,
14014
+ ) : state.phase === "checkpoint" && checkpointRecap ? /* @__PURE__ */ jsxs26(
14015
+ Box28,
13309
14016
  {
13310
14017
  flexDirection: isStacked ? "column" : "row",
13311
14018
  height: adjustedContentHeight,
13312
14019
  children: [
13313
- /* @__PURE__ */ jsxs25(
13314
- Box26,
14020
+ /* @__PURE__ */ jsxs26(
14021
+ Box28,
13315
14022
  {
13316
14023
  flexDirection: "column",
13317
14024
  flexGrow: 1,
@@ -13319,33 +14026,20 @@ function MarathonApp({
13319
14026
  marginLeft: contentMarginLeft,
13320
14027
  marginRight: contentMarginRight,
13321
14028
  children: [
13322
- /* @__PURE__ */ jsxs25(Box26, { flexDirection: "row", flexGrow: 1, overflow: "hidden", marginBottom: 1, children: [
13323
- /* @__PURE__ */ jsx31(Box26, { flexDirection: "column", flexGrow: 1, marginRight: 1, children: /* @__PURE__ */ jsx31(
13324
- StreamOutput,
13325
- {
13326
- content: displayedContent,
13327
- isStreaming: false,
13328
- enableMarkdown: !plainText,
13329
- maxVisibleLines: adjustedContentHeight - checkpointPromptRows - 1,
13330
- scrollOffset
13331
- }
13332
- ) }),
13333
- (() => {
13334
- const checkpointVisibleRows = adjustedContentHeight - checkpointPromptRows - 1;
13335
- const checkpointTotalLines = displayedContent.split("\n").length;
13336
- const checkpointMaxScroll = Math.max(0, checkpointTotalLines - checkpointVisibleRows);
13337
- return /* @__PURE__ */ jsx31(
13338
- Scrollbar,
13339
- {
13340
- totalLines: checkpointTotalLines,
13341
- visibleLines: checkpointVisibleRows,
13342
- scrollOffset: checkpointMaxScroll - Math.min(scrollOffset, checkpointMaxScroll),
13343
- height: checkpointVisibleRows
13344
- }
13345
- );
13346
- })()
13347
- ] }),
13348
- /* @__PURE__ */ jsx31(
14029
+ /* @__PURE__ */ jsx33(Box28, { flexDirection: "row", flexGrow: 1, overflow: "hidden", marginBottom: 1, children: /* @__PURE__ */ jsx33(Box28, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx33(
14030
+ OverviewTranscriptPane,
14031
+ {
14032
+ content: displayedContent,
14033
+ enableMarkdown: !plainText,
14034
+ isStreaming: false,
14035
+ maxVisibleLines: adjustedContentHeight - checkpointPromptRows - 1,
14036
+ scrollOffset,
14037
+ totalLines: overviewTotalLines,
14038
+ paneWidth: transcriptPaneWidth,
14039
+ contentHeight: adjustedContentHeight
14040
+ }
14041
+ ) }) }),
14042
+ /* @__PURE__ */ jsx33(
13349
14043
  CheckpointPrompt,
13350
14044
  {
13351
14045
  onSubmit: handleCheckpointSubmit,
@@ -13363,137 +14057,74 @@ function MarathonApp({
13363
14057
  ]
13364
14058
  }
13365
14059
  ),
13366
- !isStacked && (hasTools || hasReasoning) && /* @__PURE__ */ jsx31(
13367
- Box26,
14060
+ !isStacked && (hasTools || hasReasoning) && /* @__PURE__ */ jsx33(
14061
+ OverviewActivityPane,
13368
14062
  {
13369
- flexDirection: "column",
13370
- width: toolPanelWidth,
13371
- flexShrink: 0,
13372
- borderStyle: "single",
13373
- borderColor: theme30.border,
13374
- backgroundColor: theme30.background,
13375
- paddingX: theme30.panelPaddingX,
13376
- paddingY: theme30.panelPaddingY,
13377
- children: /* @__PURE__ */ jsx31(
13378
- ToolPanel,
13379
- {
13380
- tools: displayedTools,
13381
- reasoning: displayedReasoning,
13382
- files: trackedFiles,
13383
- maxHeight: adjustedContentHeight - 2
13384
- }
13385
- )
14063
+ tools: displayedTools,
14064
+ reasoningCharCount,
14065
+ files: trackedFiles,
14066
+ maxHeight: adjustedContentHeight - 2,
14067
+ width: toolPanelWidth
13386
14068
  }
13387
14069
  )
13388
14070
  ]
13389
14071
  }
13390
- ) : /* @__PURE__ */ jsxs25(
13391
- Box26,
14072
+ ) : /* @__PURE__ */ jsxs26(
14073
+ Box28,
13392
14074
  {
13393
14075
  flexDirection: isStacked ? "column" : "row",
13394
14076
  height: adjustedContentHeight,
13395
14077
  overflow: "hidden",
13396
14078
  children: [
13397
- /* @__PURE__ */ jsx31(
13398
- Box26,
14079
+ /* @__PURE__ */ jsx33(
14080
+ Box28,
13399
14081
  {
13400
14082
  flexDirection: "column",
13401
14083
  flexGrow: 1,
13402
14084
  flexShrink: 1,
13403
14085
  marginLeft: contentMarginLeft,
13404
14086
  marginRight: contentMarginRight,
13405
- children: showLoadingAnimation ? /* @__PURE__ */ jsx31(
13406
- Box26,
14087
+ children: /* @__PURE__ */ jsx33(
14088
+ OverviewTranscriptPane,
13407
14089
  {
13408
- flexGrow: 1,
13409
- minHeight: adjustedContentHeight,
13410
- overflow: "hidden",
13411
- children: /* @__PURE__ */ jsx31(
13412
- LoadingAnimation,
13413
- {
13414
- width: transcriptPaneWidth,
13415
- height: adjustedContentHeight
13416
- }
13417
- )
14090
+ content: displayedContent,
14091
+ enableMarkdown: !plainText,
14092
+ isStreaming: selectedIsLive && state.phase === "streaming",
14093
+ maxVisibleLines: transcriptRows,
14094
+ scrollOffset,
14095
+ totalLines: overviewTotalLines,
14096
+ paneWidth: transcriptPaneWidth,
14097
+ contentHeight: adjustedContentHeight,
14098
+ showLoadingAnimation,
14099
+ hasReasoning,
14100
+ visibleReasoningLines,
14101
+ reasoningCollapsed,
14102
+ reasoningLive: selectedIsLive && state.phase === "thinking" && !reasoningCollapsed,
14103
+ showThinkingIndicator,
14104
+ thinkingStartedAt: state.thinkingStartedAt,
14105
+ contextCompaction: showContextCompactionIndicator ? state.contextCompaction : null,
14106
+ showUpgradeBrowseHint,
14107
+ error: selectedIsLive && !upgradePrompt ? state.error : null
13418
14108
  }
13419
- ) : /* @__PURE__ */ jsxs25(Fragment3, { children: [
13420
- hasReasoning && /* @__PURE__ */ jsx31(Box26, { flexDirection: "column", marginBottom: reasoningCollapsed ? 0 : 1, children: /* @__PURE__ */ jsx31(
13421
- ReasoningBlock,
13422
- {
13423
- lines: visibleReasoningLines,
13424
- live: selectedIsLive && state.phase === "thinking" && !reasoningCollapsed,
13425
- collapsed: reasoningCollapsed,
13426
- showToggleHint: true
13427
- }
13428
- ) }),
13429
- showThinkingIndicator && /* @__PURE__ */ jsx31(ThinkingIndicator, { startedAt: state.thinkingStartedAt }),
13430
- showContextCompactionIndicator && state.contextCompaction && /* @__PURE__ */ jsx31(ContextCompactionIndicator, { compaction: state.contextCompaction }),
13431
- showUpgradeBrowseHint && /* @__PURE__ */ jsx31(Box26, { marginBottom: 1, children: /* @__PURE__ */ jsx31(
13432
- ReasoningBlock,
13433
- {
13434
- lines: [
13435
- "Upgrade modal dismissed.",
13436
- "Use Shift+Left/Right or 1-9 to review previous marathon runs."
13437
- ],
13438
- compact: true
13439
- }
13440
- ) }),
13441
- /* @__PURE__ */ jsxs25(Box26, { flexDirection: "row", children: [
13442
- /* @__PURE__ */ jsx31(Box26, { flexDirection: "column", flexGrow: 1, marginRight: 1, children: /* @__PURE__ */ jsx31(
13443
- StreamOutput,
13444
- {
13445
- content: displayedContent,
13446
- isStreaming: selectedIsLive && state.phase === "streaming",
13447
- enableMarkdown: !plainText,
13448
- maxVisibleLines: transcriptRows,
13449
- scrollOffset
13450
- }
13451
- ) }),
13452
- (() => {
13453
- const overviewTotalLines = displayedContent.split("\n").length;
13454
- const overviewMaxScroll = Math.max(0, overviewTotalLines - transcriptRows);
13455
- return /* @__PURE__ */ jsx31(
13456
- Scrollbar,
13457
- {
13458
- totalLines: overviewTotalLines,
13459
- visibleLines: transcriptRows,
13460
- scrollOffset: overviewMaxScroll - Math.min(scrollOffset, overviewMaxScroll),
13461
- height: transcriptRows
13462
- }
13463
- );
13464
- })()
13465
- ] }),
13466
- selectedIsLive && state.error && !upgradePrompt && /* @__PURE__ */ jsx31(ErrorDisplay3, { error: state.error })
13467
- ] })
14109
+ )
13468
14110
  }
13469
14111
  ),
13470
- (hasTools || hasReasoning) && /* @__PURE__ */ jsx31(
13471
- Box26,
14112
+ (hasTools || hasReasoning) && /* @__PURE__ */ jsx33(
14113
+ OverviewActivityPane,
13472
14114
  {
13473
- flexDirection: "column",
13474
- width: isStacked ? void 0 : toolPanelWidth,
13475
- flexShrink: 0,
13476
- borderStyle: "single",
13477
- borderColor: theme30.border,
13478
- backgroundColor: theme30.background,
13479
- paddingX: theme30.panelPaddingX,
13480
- paddingY: theme30.panelPaddingY,
13481
- children: /* @__PURE__ */ jsx31(
13482
- ToolPanel,
13483
- {
13484
- tools: displayedTools,
13485
- reasoning: displayedReasoning,
13486
- files: trackedFiles,
13487
- maxHeight: adjustedContentHeight - 2
13488
- }
13489
- )
14115
+ tools: displayedTools,
14116
+ reasoningCharCount,
14117
+ files: trackedFiles,
14118
+ maxHeight: adjustedContentHeight - 2,
14119
+ stacked: isStacked,
14120
+ width: toolPanelWidth
13490
14121
  }
13491
14122
  )
13492
14123
  ]
13493
14124
  }
13494
14125
  ),
13495
- showHelpOverlay && /* @__PURE__ */ jsx31(
13496
- Box26,
14126
+ showHelpOverlay && /* @__PURE__ */ jsx33(
14127
+ Box28,
13497
14128
  {
13498
14129
  width: terminalWidth,
13499
14130
  marginTop: -adjustedContentHeight,
@@ -13501,8 +14132,8 @@ function MarathonApp({
13501
14132
  justifyContent: "center",
13502
14133
  alignItems: "center",
13503
14134
  flexShrink: 0,
13504
- backgroundColor: theme30.surfaceMuted,
13505
- children: /* @__PURE__ */ jsx31(
14135
+ backgroundColor: theme31.surfaceMuted,
14136
+ children: /* @__PURE__ */ jsx33(
13506
14137
  HelpPanel,
13507
14138
  {
13508
14139
  width: terminalWidth,
@@ -13513,8 +14144,8 @@ function MarathonApp({
13513
14144
  )
13514
14145
  }
13515
14146
  ),
13516
- showEventTypeFilter && /* @__PURE__ */ jsx31(
13517
- Box26,
14147
+ showEventTypeFilter && /* @__PURE__ */ jsx33(
14148
+ Box28,
13518
14149
  {
13519
14150
  width: terminalWidth,
13520
14151
  marginTop: -adjustedContentHeight,
@@ -13522,11 +14153,11 @@ function MarathonApp({
13522
14153
  justifyContent: "center",
13523
14154
  alignItems: "center",
13524
14155
  flexShrink: 0,
13525
- backgroundColor: theme30.surfaceMuted,
13526
- children: /* @__PURE__ */ jsx31(
14156
+ backgroundColor: theme31.surfaceMuted,
14157
+ children: /* @__PURE__ */ jsx33(
13527
14158
  EventTypeFilter,
13528
14159
  {
13529
- eventTypes: seenEventTypes,
14160
+ eventTypes: stableSeenEventTypesRef.current,
13530
14161
  selectedTypes: eventTypeFilter,
13531
14162
  onApply: (selected) => {
13532
14163
  setEventTypeFilter(selected);
@@ -13538,8 +14169,8 @@ function MarathonApp({
13538
14169
  )
13539
14170
  }
13540
14171
  ),
13541
- showSessionMenu && /* @__PURE__ */ jsx31(
13542
- Box26,
14172
+ showSessionMenu && /* @__PURE__ */ jsx33(
14173
+ Box28,
13543
14174
  {
13544
14175
  width: terminalWidth,
13545
14176
  marginTop: -adjustedContentHeight,
@@ -13547,8 +14178,8 @@ function MarathonApp({
13547
14178
  justifyContent: "center",
13548
14179
  alignItems: "center",
13549
14180
  flexShrink: 0,
13550
- backgroundColor: theme30.surfaceMuted,
13551
- children: /* @__PURE__ */ jsx31(
14181
+ backgroundColor: theme31.surfaceMuted,
14182
+ children: /* @__PURE__ */ jsx33(
13552
14183
  SessionActionMenu,
13553
14184
  {
13554
14185
  onCopySession: handleCopySession,
@@ -13562,8 +14193,8 @@ function MarathonApp({
13562
14193
  )
13563
14194
  }
13564
14195
  ),
13565
- showUpgradeModal && upgradePrompt && /* @__PURE__ */ jsx31(
13566
- Box26,
14196
+ showUpgradeModal && upgradePrompt && /* @__PURE__ */ jsx33(
14197
+ Box28,
13567
14198
  {
13568
14199
  marginTop: -adjustedContentHeight,
13569
14200
  marginBottom: -adjustedContentHeight,
@@ -13571,15 +14202,15 @@ function MarathonApp({
13571
14202
  justifyContent: "center",
13572
14203
  alignItems: "center",
13573
14204
  flexShrink: 0,
13574
- children: /* @__PURE__ */ jsx31(UpgradeModal, { prompt: upgradePrompt, width: upgradeModalWidth })
14205
+ children: /* @__PURE__ */ jsx33(UpgradeModal, { prompt: upgradePrompt, width: upgradeModalWidth })
13575
14206
  }
13576
14207
  ),
13577
- /* @__PURE__ */ jsx31(
14208
+ /* @__PURE__ */ jsx33(
13578
14209
  StatusBar,
13579
14210
  {
13580
- left: /* @__PURE__ */ jsxs25(Text28, { color: theme30.textMuted, children: [
14211
+ left: /* @__PURE__ */ jsxs26(Text28, { color: theme31.textMuted, children: [
13581
14212
  `Model: ${currentModel}${currentSandbox ? `${separator}Sandbox: ${currentSandbox}` : ""}`,
13582
- previewUrl && /* @__PURE__ */ jsx31(Text28, { color: theme30.accent, children: `${separator}\x1B]8;;${previewUrl}\x07preview\x1B]8;;\x07` })
14213
+ previewUrl && /* @__PURE__ */ jsx33(Text28, { color: theme31.accent, children: `${separator}\x1B]8;;${previewUrl}\x07preview\x1B]8;;\x07` })
13583
14214
  ] }),
13584
14215
  center: showFilesPanel ? filesCenter : showEventStream ? detailCenter : showToolsPanel ? toolsCenter : void 0,
13585
14216
  right: statusRight
@@ -13592,10 +14223,10 @@ function MarathonApp({
13592
14223
 
13593
14224
  // src/ink/marathon/MarathonStartupShell.tsx
13594
14225
  import { useEffect as useEffect21, useRef as useRef9, useState as useState23 } from "react";
13595
- import { Box as Box27, Text as Text29, useApp as useApp5, useInput as useInput12, useStdout as useStdout6 } from "ink";
13596
- import { theme as theme31 } from "@runtypelabs/ink-components";
14226
+ import { Box as Box29, Text as Text29, useApp as useApp5, useInput as useInput12, useStdout as useStdout6 } from "ink";
14227
+ import { theme as theme32 } from "@runtypelabs/ink-components";
13597
14228
  import { AnimatedVariant, StartupGridIconCompactLightInverted } from "@runtypelabs/terminal-animations";
13598
- import { jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
14229
+ import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
13599
14230
  var SCROLL_HINT = "\u2191\u2193 Enter 1-3";
13600
14231
  var PROMPT_COLUMN_MAX = 68;
13601
14232
  var MIN_HOLD_MS = 1500;
@@ -13863,17 +14494,17 @@ function MarathonStartupShell({
13863
14494
  const selectedChoice = prompt?.choices[selectedPromptIndex];
13864
14495
  const contentWidth = prompt || modelChoices || playbookConfirm ? promptColumnWidth : void 0;
13865
14496
  if (scene === "app" && marathonAppProps) {
13866
- return /* @__PURE__ */ jsx32(MarathonApp, { ...marathonAppProps });
14497
+ return /* @__PURE__ */ jsx34(MarathonApp, { ...marathonAppProps });
13867
14498
  }
13868
- return /* @__PURE__ */ jsxs26(Box27, { width: terminalWidth, height: terminalRows, backgroundColor: theme31.background, children: [
13869
- /* @__PURE__ */ jsx32(
13870
- Box27,
14499
+ return /* @__PURE__ */ jsxs27(Box29, { width: terminalWidth, height: terminalRows, backgroundColor: theme32.background, children: [
14500
+ /* @__PURE__ */ jsx34(
14501
+ Box29,
13871
14502
  {
13872
14503
  position: "absolute",
13873
14504
  width: terminalWidth,
13874
14505
  height: terminalRows,
13875
14506
  overflow: "hidden",
13876
- children: /* @__PURE__ */ jsx32(
14507
+ children: /* @__PURE__ */ jsx34(
13877
14508
  AnimatedVariant,
13878
14509
  {
13879
14510
  variantId: backgroundVariantId,
@@ -13883,8 +14514,8 @@ function MarathonStartupShell({
13883
14514
  )
13884
14515
  }
13885
14516
  ),
13886
- /* @__PURE__ */ jsx32(
13887
- Box27,
14517
+ /* @__PURE__ */ jsx34(
14518
+ Box29,
13888
14519
  {
13889
14520
  position: "absolute",
13890
14521
  width: terminalWidth,
@@ -13893,27 +14524,27 @@ function MarathonStartupShell({
13893
14524
  justifyContent: "flex-start",
13894
14525
  paddingX: 1,
13895
14526
  paddingY: 1,
13896
- children: /* @__PURE__ */ jsx32(StartupGridIconCompactLightInverted, { autoplay: false })
14527
+ children: /* @__PURE__ */ jsx34(StartupGridIconCompactLightInverted, { autoplay: false })
13897
14528
  }
13898
14529
  ),
13899
- /* @__PURE__ */ jsx32(
13900
- Box27,
14530
+ /* @__PURE__ */ jsx34(
14531
+ Box29,
13901
14532
  {
13902
14533
  width: terminalWidth,
13903
14534
  height: terminalRows,
13904
14535
  flexDirection: "column",
13905
14536
  justifyContent: "center",
13906
14537
  alignItems: "center",
13907
- children: /* @__PURE__ */ jsxs26(
13908
- Box27,
14538
+ children: /* @__PURE__ */ jsxs27(
14539
+ Box29,
13909
14540
  {
13910
14541
  flexDirection: "column",
13911
14542
  alignItems: prompt || modelChoices || playbookConfirm ? "stretch" : "center",
13912
- backgroundColor: theme31.background,
14543
+ backgroundColor: theme32.background,
13913
14544
  paddingX: 2,
13914
14545
  paddingY: 1,
13915
14546
  children: [
13916
- !prompt && !modelChoices && !playbookConfirm && /* @__PURE__ */ jsx32(Text29, { color: theme31.textMuted, children: statusMessage }),
14547
+ !prompt && !modelChoices && !playbookConfirm && /* @__PURE__ */ jsx34(Text29, { color: theme32.textMuted, children: statusMessage }),
13917
14548
  playbookConfirm && !modelChoices && (() => {
13918
14549
  const milestoneCount = playbookConfirm.milestoneNames.length;
13919
14550
  const hasEdits = JSON.stringify(playbookConfirm.milestoneModels) !== JSON.stringify(playbookConfirm.originalModels);
@@ -13922,33 +14553,33 @@ function MarathonStartupShell({
13922
14553
  "Use single model instead",
13923
14554
  ...hasEdits ? ["Discard changes"] : []
13924
14555
  ];
13925
- return /* @__PURE__ */ jsxs26(Box27, { width: contentWidth, flexDirection: "column", marginTop: 1, children: [
13926
- /* @__PURE__ */ jsxs26(Text29, { bold: true, color: theme31.text, children: [
14556
+ return /* @__PURE__ */ jsxs27(Box29, { width: contentWidth, flexDirection: "column", marginTop: 1, children: [
14557
+ /* @__PURE__ */ jsxs27(Text29, { bold: true, color: theme32.text, children: [
13927
14558
  "Playbook: ",
13928
14559
  playbookConfirm.name
13929
14560
  ] }),
13930
- /* @__PURE__ */ jsx32(Box27, { marginTop: 1, flexDirection: "column", children: playbookConfirm.milestoneNames.map((milestone, i) => {
14561
+ /* @__PURE__ */ jsx34(Box29, { marginTop: 1, flexDirection: "column", children: playbookConfirm.milestoneNames.map((milestone, i) => {
13931
14562
  const isSelected = i === playbookConfirm.selectedIndex;
13932
14563
  const model = playbookConfirm.milestoneModels[milestone];
13933
14564
  const wasEdited = model !== playbookConfirm.originalModels[milestone];
13934
- return /* @__PURE__ */ jsxs26(Box27, { children: [
13935
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.accent : theme31.textSubtle, children: isSelected ? "\u203A " : " " }),
13936
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.text : theme31.textSubtle, bold: isSelected, children: milestone.padEnd(16) }),
13937
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.accent : theme31.textMuted, children: model }),
13938
- wasEdited && /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: " (edited)" })
14565
+ return /* @__PURE__ */ jsxs27(Box29, { children: [
14566
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.accent : theme32.textSubtle, children: isSelected ? "\u203A " : " " }),
14567
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.text : theme32.textSubtle, bold: isSelected, children: milestone.padEnd(16) }),
14568
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.accent : theme32.textMuted, children: model }),
14569
+ wasEdited && /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: " (edited)" })
13939
14570
  ] }, milestone);
13940
14571
  }) }),
13941
- /* @__PURE__ */ jsx32(Box27, { marginTop: 1, flexDirection: "column", children: actions.map((label, ai) => {
14572
+ /* @__PURE__ */ jsx34(Box29, { marginTop: 1, flexDirection: "column", children: actions.map((label, ai) => {
13942
14573
  const isSelected = playbookConfirm.selectedIndex === milestoneCount + ai;
13943
- return /* @__PURE__ */ jsxs26(Box27, { children: [
13944
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.accent : theme31.textSubtle, children: isSelected ? "\u203A " : " " }),
13945
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.text : theme31.textSubtle, bold: isSelected, children: label })
14574
+ return /* @__PURE__ */ jsxs27(Box29, { children: [
14575
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.accent : theme32.textSubtle, children: isSelected ? "\u203A " : " " }),
14576
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.text : theme32.textSubtle, bold: isSelected, children: label })
13946
14577
  ] }, label);
13947
14578
  }) }),
13948
- /* @__PURE__ */ jsx32(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text29, { color: theme31.border, children: "\u2191\u2193 navigate \xB7 Enter select" }) })
14579
+ /* @__PURE__ */ jsx34(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { color: theme32.border, children: "\u2191\u2193 navigate \xB7 Enter select" }) })
13949
14580
  ] });
13950
14581
  })(),
13951
- modelChoices && /* @__PURE__ */ jsx32(Box27, { width: contentWidth, flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx32(
14582
+ modelChoices && /* @__PURE__ */ jsx34(Box29, { width: contentWidth, flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx34(
13952
14583
  ModelPicker,
13953
14584
  {
13954
14585
  currentModel,
@@ -13966,27 +14597,27 @@ function MarathonStartupShell({
13966
14597
  }
13967
14598
  }
13968
14599
  ) }),
13969
- prompt && /* @__PURE__ */ jsxs26(Box27, { width: contentWidth, flexDirection: "column", marginTop: 1, children: [
13970
- /* @__PURE__ */ jsx32(Text29, { bold: true, color: theme31.text, children: promptShellModel?.heading }),
13971
- promptShellModel?.task && /* @__PURE__ */ jsxs26(Box27, { marginTop: 1, children: [
13972
- /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: "task " }),
13973
- /* @__PURE__ */ jsx32(Text29, { color: theme31.text, children: promptShellModel.task })
14600
+ prompt && /* @__PURE__ */ jsxs27(Box29, { width: contentWidth, flexDirection: "column", marginTop: 1, children: [
14601
+ /* @__PURE__ */ jsx34(Text29, { bold: true, color: theme32.text, children: promptShellModel?.heading }),
14602
+ promptShellModel?.task && /* @__PURE__ */ jsxs27(Box29, { marginTop: 1, children: [
14603
+ /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: "task " }),
14604
+ /* @__PURE__ */ jsx34(Text29, { color: theme32.text, children: promptShellModel.task })
13974
14605
  ] }),
13975
- promptShellModel?.filePath && /* @__PURE__ */ jsxs26(Box27, { children: [
13976
- /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: "state " }),
13977
- /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: promptShellModel.filePath })
14606
+ promptShellModel?.filePath && /* @__PURE__ */ jsxs27(Box29, { children: [
14607
+ /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: "state " }),
14608
+ /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: promptShellModel.filePath })
13978
14609
  ] }),
13979
- promptShellModel?.metaLine && /* @__PURE__ */ jsx32(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: promptShellModel.metaLine }) }),
13980
- /* @__PURE__ */ jsx32(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: prompt.hint }) }),
13981
- /* @__PURE__ */ jsx32(Box27, { flexDirection: "column", marginTop: 1, children: prompt.choices.map((choice, index) => {
14610
+ promptShellModel?.metaLine && /* @__PURE__ */ jsx34(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: promptShellModel.metaLine }) }),
14611
+ /* @__PURE__ */ jsx34(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: prompt.hint }) }),
14612
+ /* @__PURE__ */ jsx34(Box29, { flexDirection: "column", marginTop: 1, children: prompt.choices.map((choice, index) => {
13982
14613
  const isSelected = index === selectedPromptIndex;
13983
- return /* @__PURE__ */ jsxs26(Box27, { marginTop: index === 0 ? 0 : 1, children: [
13984
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.accent : theme31.textSubtle, bold: isSelected, children: isSelected ? "\u203A " : " " }),
13985
- /* @__PURE__ */ jsx32(Text29, { color: isSelected ? theme31.text : theme31.textSubtle, bold: isSelected, children: `${index + 1} ${choice.label}` })
14614
+ return /* @__PURE__ */ jsxs27(Box29, { marginTop: index === 0 ? 0 : 1, children: [
14615
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.accent : theme32.textSubtle, bold: isSelected, children: isSelected ? "\u203A " : " " }),
14616
+ /* @__PURE__ */ jsx34(Text29, { color: isSelected ? theme32.text : theme32.textSubtle, bold: isSelected, children: `${index + 1} ${choice.label}` })
13986
14617
  ] }, choice.value);
13987
14618
  }) }),
13988
- selectedChoice && /* @__PURE__ */ jsx32(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text29, { color: theme31.textSubtle, children: selectedChoice.description }) }),
13989
- /* @__PURE__ */ jsx32(Box27, { marginTop: 1, children: /* @__PURE__ */ jsx32(Text29, { color: theme31.border, children: SCROLL_HINT }) })
14619
+ selectedChoice && /* @__PURE__ */ jsx34(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { color: theme32.textSubtle, children: selectedChoice.description }) }),
14620
+ /* @__PURE__ */ jsx34(Box29, { marginTop: 1, children: /* @__PURE__ */ jsx34(Text29, { color: theme32.border, children: SCROLL_HINT }) })
13990
14621
  ] })
13991
14622
  ]
13992
14623
  }
@@ -16050,6 +16681,39 @@ function buildMarathonClientHeaders(devPlanOverride) {
16050
16681
  ...devPlanOverride ? { "X-Dev-Plan-Override": devPlanOverride } : {}
16051
16682
  };
16052
16683
  }
16684
+ function normalizeMarathonAgentArgument(agent) {
16685
+ const trimmed = agent.trim();
16686
+ if (trimmed.length < 2) return trimmed;
16687
+ const matchingQuotePairs = [
16688
+ ['"', '"'],
16689
+ ["'", "'"],
16690
+ ["\u201C", "\u201D"],
16691
+ ["\u2018", "\u2019"]
16692
+ ];
16693
+ for (const [openQuote, closeQuote] of matchingQuotePairs) {
16694
+ if (trimmed.startsWith(openQuote) && trimmed.endsWith(closeQuote)) {
16695
+ const unwrapped = trimmed.slice(openQuote.length, trimmed.length - closeQuote.length).trim();
16696
+ return unwrapped || trimmed;
16697
+ }
16698
+ }
16699
+ return trimmed;
16700
+ }
16701
+ function buildMarathonAutoCreatedAgentBootstrap(agentName, options = {}) {
16702
+ const normalizedModel = options.model?.trim();
16703
+ const normalizedToolIds = [...new Set((options.toolIds || []).map((toolId) => toolId.trim()).filter(Boolean))];
16704
+ const config2 = normalizedModel || normalizedToolIds.length > 0 ? {
16705
+ ...normalizedModel ? { model: normalizedModel } : {},
16706
+ ...normalizedToolIds.length > 0 ? {
16707
+ tools: {
16708
+ toolIds: normalizedToolIds
16709
+ }
16710
+ } : {}
16711
+ } : void 0;
16712
+ return {
16713
+ description: `Powering a marathon for ${agentName}`,
16714
+ ...config2 ? { config: config2 } : {}
16715
+ };
16716
+ }
16053
16717
  var DEFAULT_TOOL_OUTPUT_WARNING_CHARS = 4e4;
16054
16718
  var DEFAULT_TOOL_OUTPUT_OFFLOAD_CHARS = 1e5;
16055
16719
  function parseCompactStrategy(value) {
@@ -16087,6 +16751,7 @@ function resolveToolOutputGuardrails(rawThreshold) {
16087
16751
  };
16088
16752
  }
16089
16753
  async function taskAction(agent, options) {
16754
+ const normalizedAgent = normalizeMarathonAgentArgument(agent);
16090
16755
  if (!options.resume && !options.goal) {
16091
16756
  console.error(chalk16.red("Error: -g, --goal <text> is required for new tasks"));
16092
16757
  console.log(chalk16.gray(" Use --resume to continue an existing task without a new goal"));
@@ -16187,8 +16852,9 @@ async function taskAction(agent, options) {
16187
16852
  }
16188
16853
  process.exit(1);
16189
16854
  };
16190
- let agentId = agent;
16191
- if (!agent.startsWith("agent_")) {
16855
+ let agentId = normalizedAgent;
16856
+ let autoCreatedAgent = false;
16857
+ if (!normalizedAgent.startsWith("agent_")) {
16192
16858
  if (useStartupShell) {
16193
16859
  setStartupStatus("looking up agent");
16194
16860
  } else {
@@ -16197,7 +16863,7 @@ async function taskAction(agent, options) {
16197
16863
  try {
16198
16864
  const list = await client.agents.list();
16199
16865
  const found = list.data.find(
16200
- (a) => a.name.toLowerCase() === agent.toLowerCase()
16866
+ (a) => a.name.toLowerCase() === normalizedAgent.toLowerCase()
16201
16867
  );
16202
16868
  if (found) {
16203
16869
  agentId = found.id;
@@ -16208,13 +16874,14 @@ async function taskAction(agent, options) {
16208
16874
  }
16209
16875
  } else {
16210
16876
  if (useStartupShell) {
16211
- setStartupStatus(`creating agent ${agent}`);
16877
+ setStartupStatus(`creating agent ${normalizedAgent}`);
16212
16878
  } else {
16213
- console.log(chalk16.gray(`Creating agent "${agent}"...`));
16879
+ console.log(chalk16.gray(`Creating agent "${normalizedAgent}"...`));
16214
16880
  }
16215
16881
  try {
16216
- const created = await client.agents.create({ name: agent });
16882
+ const created = await client.agents.create({ name: normalizedAgent });
16217
16883
  agentId = created.id;
16884
+ autoCreatedAgent = true;
16218
16885
  if (useStartupShell) {
16219
16886
  setStartupStatus(`agent created: ${agentId}`);
16220
16887
  } else {
@@ -16223,7 +16890,7 @@ async function taskAction(agent, options) {
16223
16890
  } catch (createErr) {
16224
16891
  const errMsg = createErr instanceof Error ? createErr.message : String(createErr);
16225
16892
  await failBeforeMain([
16226
- chalk16.red(`Failed to create agent "${agent}"`),
16893
+ chalk16.red(`Failed to create agent "${normalizedAgent}"`),
16227
16894
  chalk16.red(errMsg)
16228
16895
  ]);
16229
16896
  }
@@ -16474,7 +17141,9 @@ async function taskAction(agent, options) {
16474
17141
  const remainingCost = maxCost ? maxCost - priorCost : void 0;
16475
17142
  const baseMessage = options.goal || (resumeRequested ? "Continue the task." : "");
16476
17143
  const sandboxPrompt = parsedSandbox ? createSandboxInstructions(parsedSandbox) : "";
16477
- const resolvedWorkflow = playbookWorkflow ?? detectDeployWorkflow(baseMessage, parsedSandbox, resumeState);
17144
+ const sandboxWorkflowSelection = playbookWorkflow ? void 0 : resolveSandboxWorkflowSelection(baseMessage, parsedSandbox, resumeState);
17145
+ parsedSandbox = sandboxWorkflowSelection?.sandboxProvider ?? parsedSandbox;
17146
+ const resolvedWorkflow = playbookWorkflow ?? sandboxWorkflowSelection?.workflow;
16478
17147
  const DEFAULT_MILESTONE_NAMES = ["research", "planning", "execution"];
16479
17148
  const EXTERNAL_MILESTONE_NAMES = ["research", "report"];
16480
17149
  const detectedVariant = resumeState?.workflowVariant ?? defaultWorkflow.classifyVariant?.(baseMessage);
@@ -16494,6 +17163,26 @@ ${rulesContext}`;
16494
17163
  defaultModel: options.model
16495
17164
  }, playbookMilestoneModels);
16496
17165
  if (resolvedModel) options.model = resolvedModel;
17166
+ if (autoCreatedAgent) {
17167
+ const bootstrapPayload = buildMarathonAutoCreatedAgentBootstrap(normalizedAgent, {
17168
+ model: options.model || agentConfigModel || defaultConfiguredModel,
17169
+ toolIds: resolvedToolIds
17170
+ });
17171
+ try {
17172
+ await client.agents.update(agentId, bootstrapPayload);
17173
+ } catch (error) {
17174
+ const warningMessage = error instanceof Error ? error.message : String(error);
17175
+ if (useStartupShell) {
17176
+ setStartupStatus("warning: could not persist initial agent settings");
17177
+ } else {
17178
+ console.log(
17179
+ chalk16.yellow(
17180
+ `Warning: created agent "${normalizedAgent}" but could not persist initial marathon settings (${warningMessage})`
17181
+ )
17182
+ );
17183
+ }
17184
+ }
17185
+ }
16497
17186
  let localTools = buildLocalTools(client, parsedSandbox, options, {
16498
17187
  taskName,
16499
17188
  stateDir: options.stateDir
@@ -17190,8 +17879,7 @@ ${details}`);
17190
17879
  }
17191
17880
  return resolved;
17192
17881
  }
17193
- function detectDeployWorkflow(message, sandboxProvider, resumeState) {
17194
- if (sandboxProvider !== "daytona") return void 0;
17882
+ function detectSandboxWorkflow(message, resumeState) {
17195
17883
  if (resumeState?.workflowVariant === "game") return gameWorkflow;
17196
17884
  if (resumeState?.workflowPhase === "design" || resumeState?.workflowPhase === "build" || resumeState?.workflowPhase === "verify") {
17197
17885
  return gameWorkflow;
@@ -17220,10 +17908,16 @@ function detectDeployWorkflow(message, sandboxProvider, resumeState) {
17220
17908
  /\bsandbox\b/,
17221
17909
  /\bpreview\s*url\b/,
17222
17910
  /\blive\s*preview\b/,
17223
- /\bhost\b.*\b(?:app|server|api|site)\b/,
17224
- /\b(?:app|server|api|site)\b.*\bhost\b/,
17225
- /\brun\b.*\bserver\b/,
17226
- /\blaunch\b.*\b(?:app|server|api|site)\b/
17911
+ /\bhost(?:ed|ing)?\b.*\b(?:app|server|api|site)\b/,
17912
+ /\b(?:app|server|api|site)\b.*\bhost(?:ed|ing)?\b/,
17913
+ /\b(?:run|launch|start)\b.*\b(?:app|server|api|site|web\s*app|web\s*server)\b/,
17914
+ /\bnpx\s+serve\b/,
17915
+ /\bserve(?:d|s|ing)?\b.*\b(?:app|site|preview|route|routes|localhost|url|urls)\b/,
17916
+ /\b(?:route|routes|preview|localhost|url|urls)\b.*\bserve(?:d|s|ing)?\b/,
17917
+ /\bverify\b.*\b(?:route|routes|preview|localhost|url|urls)\b/,
17918
+ /\bmulti[-\s]route\b/,
17919
+ /\b(?:serve|start|run|listen)\b.{0,40}\bport\s*\d{2,5}\b/,
17920
+ /\bport\s*\d{2,5}\b.{0,40}\b(?:serve|server|preview|localhost|route|routes)\b/
17227
17921
  ];
17228
17922
  if (deployPatterns.some((p) => p.test(lower))) {
17229
17923
  return deployWorkflow;
@@ -17233,15 +17927,34 @@ function detectDeployWorkflow(message, sandboxProvider, resumeState) {
17233
17927
  /\bcreate\b.*\b(?:web\s*app|website|api|server|express|hono|fastify)\b/,
17234
17928
  /\bmake\b.*\b(?:web\s*app|website|api|server|express|hono|fastify)\b/
17235
17929
  ];
17236
- const repoPatterns = [
17237
- /\b(?:file|repo|repository|codebase|project|directory|folder)\b/,
17238
- /\b(?:edit|modify|update|fix|refactor|change)\b/
17930
+ const repoEditPatterns = [
17931
+ /\b(?:edit|modify|update|fix|refactor|change|patch|rename|remove)\b.*\b(?:file|files|repo|repository|codebase|project|directory|folder|component|module|page|route|router)\b/,
17932
+ /\b(?:file|files|repo|repository|codebase|project|directory|folder|component|module|page|route|router)\b.*\b(?:edit|modify|update|fix|refactor|change|patch|rename|remove)\b/
17239
17933
  ];
17240
- if (webAppPatterns.some((p) => p.test(lower)) && !repoPatterns.some((p) => p.test(lower))) {
17934
+ if (webAppPatterns.some((p) => p.test(lower)) && !repoEditPatterns.some((p) => p.test(lower))) {
17241
17935
  return deployWorkflow;
17242
17936
  }
17243
17937
  return void 0;
17244
17938
  }
17939
+ function resolveSandboxWorkflowSelection(message, sandboxProvider, resumeState) {
17940
+ if (sandboxProvider && sandboxProvider !== "daytona") {
17941
+ return {
17942
+ workflow: void 0,
17943
+ sandboxProvider
17944
+ };
17945
+ }
17946
+ const workflow = detectSandboxWorkflow(message, resumeState);
17947
+ if (!workflow) {
17948
+ return {
17949
+ workflow: void 0,
17950
+ sandboxProvider
17951
+ };
17952
+ }
17953
+ return {
17954
+ workflow,
17955
+ sandboxProvider: sandboxProvider ?? "daytona"
17956
+ };
17957
+ }
17245
17958
  function applyTaskOptions(cmd) {
17246
17959
  return cmd.argument("<agent>", "Agent ID or name").option("-g, --goal <text>", "Goal message for the agent").option("--max-sessions <n>", "Maximum sessions", "50").option("--max-cost <n>", "Budget in USD").option("--model <modelId>", "Model ID to use (overrides agent config)").option("--name <name>", "Task name (used for state file, defaults to agent name)").option("--session <name>", "Resume a specific session by name").option("--state-dir <path>", "Directory for state files (default: ~/.runtype/projects/<hash>/marathons/)").option("--resume [message]", "Resume from existing local state, optionally with a new message").option("--fresh", "Start a new run and ignore any existing local state for this task").option("--compact", "Force compact-summary resume mode instead of replaying full history").option("--compact-strategy <strategy>", "Compaction strategy: auto (default), provider_native, or summary_fallback").option("--compact-threshold <value>", "Auto-compact when estimated context crosses this threshold (default: 80% fallback, 90% native; accepts percent like 90% or absolute token count like 120000)").option("--compact-instructions <text>", "Extra instructions for what a compact summary must preserve").option("--no-auto-compact", "Disable automatic context-aware history compaction").option("--track", "Sync progress to a Runtype record (visible in dashboard)").option("--debug", "Show debug output from each session").option("--json", "Output final result as JSON").option("--sandbox <provider>", "Enable sandbox code execution tool (cloudflare-worker, quickjs, or daytona)").option("--no-local-tools", "Disable built-in local tool execution (read_file, write_file, list_directory)").option("-t, --tools <tools...>", "Enable built-in tools (e.g., exa, firecrawl, dalle, openai_web_search, anthropic_web_search)").option("--plain-text", "Disable markdown rendering in output").option("--no-checkpoint", "Run all iterations without checkpoint pauses (fully autonomous)").option("--checkpoint-timeout <seconds>", "Auto-continue timeout in seconds (default: 10)", "10").option("--planning-model <modelId>", "Model to use during research/planning phases").option("--execution-model <modelId>", "Model to use during execution phase").option("--playbook <name>", "Load a playbook from .runtype/marathons/playbooks/").option("--offload-threshold <chars>", 'Offload tool outputs larger than this to files (default: 100000; use "off" or "0" to disable guardrails)').option("--tool-context <mode>", "Tool result storage: hot-tail (default), observation-mask, or full-inline").option("--tool-window <window>", 'Compaction window: "session" (default) or a number for last-N tool results (e.g. 10)').option("--runner-char <char>", "Custom runner emoji (default: \u{1F3C3})").option("--finish-char <char>", "Custom finish line emoji (default: \u{1F3C1})").option("--no-runner", "Hide the runner emoji from the header border").option("--no-finish", "Hide the finish line emoji from the header border").action(taskAction);
17247
17960
  }