mixdog 0.9.87 → 0.9.89

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.
Files changed (167) hide show
  1. package/package.json +13 -7
  2. package/scripts/tool-failures.mjs +60 -24
  3. package/src/agents/heavy-worker/AGENT.md +3 -4
  4. package/src/agents/worker/AGENT.md +1 -2
  5. package/src/cli.mjs +8 -0
  6. package/src/defaults/cycle3-review-prompt.md +4 -4
  7. package/src/rules/agent/00-core.md +3 -0
  8. package/src/rules/agent/42-cycle3-agent.md +5 -6
  9. package/src/rules/lead/01-general.md +2 -0
  10. package/src/rules/shared/01-tool.md +29 -35
  11. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +3 -51
  12. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +59 -0
  13. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -2
  14. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +67 -0
  15. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +57 -4
  16. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +2 -3
  17. package/src/runtime/agent/orchestrator/config.mjs +29 -11
  18. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -1
  19. package/src/runtime/agent/orchestrator/internal-agents.mjs +1 -1
  20. package/src/runtime/agent/orchestrator/mcp/client.mjs +0 -24
  21. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +3 -3
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +25 -17
  23. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +321 -22
  24. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +35 -22
  25. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +28 -23
  26. package/src/runtime/agent/orchestrator/providers/gemini.mjs +10 -2
  27. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +0 -1
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +0 -1
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +2 -5
  30. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +27 -0
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +23 -0
  32. package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +329 -0
  33. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +23 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +107 -20
  35. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +4 -3
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +124 -12
  37. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +19 -2
  38. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +110 -75
  41. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +96 -113
  42. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +160 -11
  43. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +18 -1
  44. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +147 -117
  45. package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +88 -0
  46. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +50 -8
  47. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +1 -1
  49. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +47 -6
  50. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +485 -24
  51. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +100 -2
  52. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -0
  53. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +80 -14
  54. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +142 -2
  55. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +128 -32
  56. package/src/runtime/agent/orchestrator/session/manager.mjs +5 -1
  57. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +77 -4
  58. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +70 -39
  59. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +91 -0
  60. package/src/runtime/agent/orchestrator/session/store/listing.mjs +141 -64
  61. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +253 -0
  62. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +211 -28
  63. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +313 -0
  64. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +622 -53
  65. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +43 -10
  66. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +53 -8
  67. package/src/runtime/agent/orchestrator/session/store/summary-rebuild-worker.mjs +20 -0
  68. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +113 -9
  69. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +2 -0
  70. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +476 -52
  71. package/src/runtime/agent/orchestrator/session/store.mjs +721 -196
  72. package/src/runtime/agent/orchestrator/session/token-native.mjs +2 -4
  73. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +19 -0
  74. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +4 -6
  75. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  76. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +146 -90
  77. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +6 -0
  78. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +93 -17
  79. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -0
  80. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +7 -7
  81. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +6 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +17 -1
  83. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +85 -5
  84. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +52 -0
  85. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +54 -51
  86. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +14 -15
  87. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +132 -8
  88. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +86 -3
  89. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +6 -1
  90. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +210 -38
  91. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +23 -1
  92. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +87 -19
  93. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
  94. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -8
  95. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +12 -2
  96. package/src/runtime/media/lanes.mjs +2 -2
  97. package/src/runtime/media/renditions.mjs +101 -17
  98. package/src/runtime/media/renditions.test.mjs +54 -0
  99. package/src/runtime/media/store.mjs +111 -43
  100. package/src/runtime/media/store.test.mjs +53 -11
  101. package/src/runtime/memory/index.mjs +26 -50
  102. package/src/runtime/memory/lib/core-memory-candidates.mjs +2 -7
  103. package/src/runtime/memory/lib/core-memory-store.mjs +2 -9
  104. package/src/runtime/memory/lib/cycle-scheduler.mjs +10 -0
  105. package/src/runtime/memory/lib/embedding-provider.mjs +3 -3
  106. package/src/runtime/memory/lib/embedding-worker.mjs +60 -15
  107. package/src/runtime/memory/lib/http-router.mjs +4 -0
  108. package/src/runtime/memory/lib/ko-morph.mjs +49 -6
  109. package/src/runtime/memory/lib/memory-action-handlers.mjs +21 -18
  110. package/src/runtime/memory/lib/memory-cycle2.mjs +3 -3
  111. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -3
  112. package/src/runtime/memory/lib/memory-recall-store.mjs +24 -0
  113. package/src/runtime/memory/lib/query-handlers.mjs +26 -28
  114. package/src/runtime/memory/tool-defs.mjs +3 -3
  115. package/src/runtime/shared/atomic-file.mjs +5 -1
  116. package/src/runtime/shared/child-guardian.mjs +73 -2
  117. package/src/runtime/shared/provider-api-key.mjs +22 -5
  118. package/src/runtime/shared/tool-execution-contract.mjs +53 -0
  119. package/src/session-runtime/config-helpers.mjs +6 -5
  120. package/src/session-runtime/lifecycle-api.mjs +16 -4
  121. package/src/session-runtime/media-api.mjs +31 -16
  122. package/src/session-runtime/prewarm.mjs +2 -24
  123. package/src/session-runtime/runtime-core.mjs +33 -15
  124. package/src/session-runtime/runtime-tunables.mjs +0 -3
  125. package/src/session-runtime/session-lifecycle.mjs +0 -6
  126. package/src/session-runtime/session-title.mjs +228 -0
  127. package/src/session-runtime/session-turn-api.mjs +11 -7
  128. package/src/session-runtime/workflow-agents-api.mjs +16 -0
  129. package/src/session-runtime/workflow.mjs +7 -5
  130. package/src/standalone/agent-tool/job-views.mjs +90 -36
  131. package/src/standalone/agent-tool/spawn-flow.mjs +25 -216
  132. package/src/standalone/agent-tool/worker-index.mjs +8 -0
  133. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  134. package/src/standalone/agent-tool.mjs +17 -17
  135. package/src/standalone/agent-watchdog-registry.mjs +19 -2
  136. package/src/standalone/channel-daemon.mjs +8 -0
  137. package/src/standalone/explore-tool.mjs +1 -1
  138. package/src/standalone/memory-runtime-proxy.mjs +4 -7
  139. package/src/standalone/projects.mjs +4 -1
  140. package/src/standalone/usage-dashboard.mjs +25 -3
  141. package/src/tui/App.jsx +5 -3
  142. package/src/tui/app/resume-picker.mjs +7 -2
  143. package/src/tui/app/route-pickers.mjs +11 -117
  144. package/src/tui/app/slash-dispatch.mjs +13 -3
  145. package/src/tui/app/transcript-row-estimate.mjs +1 -1
  146. package/src/tui/app/transcript-window.mjs +17 -93
  147. package/src/tui/app/use-transcript-scroll.mjs +32 -2
  148. package/src/tui/app/use-transcript-window.mjs +64 -114
  149. package/src/tui/components/Markdown.jsx +19 -2
  150. package/src/tui/components/Message.jsx +8 -3
  151. package/src/tui/components/ToolExecution.jsx +7 -3
  152. package/src/tui/dist/index.mjs +243 -223
  153. package/src/tui/engine/live-share.mjs +15 -5
  154. package/src/tui/engine/render-timing.mjs +2 -2
  155. package/src/tui/engine/session-api-ext.mjs +74 -4
  156. package/src/tui/engine/session-flow.mjs +3 -1
  157. package/src/tui/engine.mjs +2 -2
  158. package/src/tui/hooks/useSharedTick.mjs +0 -2
  159. package/src/tui/index.jsx +5 -5
  160. package/src/ui/statusline-agents.mjs +44 -11
  161. package/src/vendor/statusline/bin/statusline-route.mjs +26 -3
  162. package/src/workflows/default/WORKFLOW.md +3 -3
  163. package/src/workflows/solo/WORKFLOW.md +5 -3
  164. package/src/runtime/media/index.mjs +0 -18
  165. package/src/runtime/memory/lib/embedding-warmup.mjs +0 -68
  166. package/src/standalone/agent-shard/shard-child.mjs +0 -300
  167. package/src/standalone/agent-shard/shard-pool.mjs +0 -443
@@ -1006,14 +1006,15 @@ async function updateJsonAtomic(filePath, mutator, opts = {}) {
1006
1006
  return next;
1007
1007
  }, opts);
1008
1008
  }
1009
- var _execFileAsync, RETRY_CODES, LOCK_WAIT_CODES, DEFAULT_BACKOFFS_MS, DEFAULT_LOCK_TIMEOUT_MS, _OWNER_TOKEN, _cachedUserSid;
1009
+ var _execFileAsync, RETRY_CODES, LOCK_WAIT_CODES, DEFAULT_BACKOFFS_MS, _configuredLockTimeoutMs, DEFAULT_LOCK_TIMEOUT_MS, _OWNER_TOKEN, _cachedUserSid;
1010
1010
  var init_atomic_file = __esm({
1011
1011
  "src/runtime/shared/atomic-file.mjs"() {
1012
1012
  _execFileAsync = promisify(execFile2);
1013
1013
  RETRY_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY", "EEXIST"]);
1014
1014
  LOCK_WAIT_CODES = /* @__PURE__ */ new Set(["EEXIST", "EPERM", "EACCES", "EBUSY"]);
1015
1015
  DEFAULT_BACKOFFS_MS = Object.freeze([25, 50, 100, 200, 400, 800, 1200, 1600]);
1016
- DEFAULT_LOCK_TIMEOUT_MS = 8e3;
1016
+ _configuredLockTimeoutMs = Number(process.env.MIXDOG_LOCK_TIMEOUT_MS);
1017
+ DEFAULT_LOCK_TIMEOUT_MS = Number.isFinite(_configuredLockTimeoutMs) && _configuredLockTimeoutMs >= 0 ? _configuredLockTimeoutMs : 2e3;
1017
1018
  _OWNER_TOKEN = randomBytes2(12).toString("hex");
1018
1019
  _cachedUserSid = null;
1019
1020
  }
@@ -1224,27 +1225,33 @@ import { createRequire as createRequire2 } from "node:module";
1224
1225
  function providerSecretEnv(provider) {
1225
1226
  return `MIXDOG_AGENT_${String(provider || "").replace(/[.\s]+/g, "_").toUpperCase()}_APIKEY`;
1226
1227
  }
1227
- function getAgentApiKey(provider) {
1228
+ function environmentApiKey(provider) {
1228
1229
  const id = String(provider || "").trim();
1229
1230
  const standardEnv = AGENT_PROVIDER_ENV[id];
1230
1231
  if (standardEnv && process.env[standardEnv]) return process.env[standardEnv];
1231
1232
  const mixdogEnv = providerSecretEnv(id);
1232
1233
  if (process.env[mixdogEnv]) return process.env[mixdogEnv];
1234
+ for (const alias of AGENT_PROVIDER_ENV_ALIASES[id] || []) {
1235
+ if (process.env[alias]) return process.env[alias];
1236
+ }
1237
+ return null;
1238
+ }
1239
+ function getAgentApiKey(provider) {
1240
+ const id = String(provider || "").trim();
1241
+ const environment = environmentApiKey(id);
1242
+ if (environment) return environment;
1233
1243
  try {
1234
1244
  const stored = getSecret(`agent.${id}.apiKey`);
1235
1245
  if (stored) return stored;
1236
1246
  } catch {
1237
1247
  }
1238
- for (const alias of AGENT_PROVIDER_ENV_ALIASES[id] || []) {
1239
- if (process.env[alias]) return process.env[alias];
1240
- }
1241
1248
  return null;
1242
1249
  }
1243
- var require2, getSecret, AGENT_PROVIDER_ENV, AGENT_PROVIDER_ENV_ALIASES;
1250
+ var require2, getSecret, hasSecret, AGENT_PROVIDER_ENV, AGENT_PROVIDER_ENV_ALIASES;
1244
1251
  var init_provider_api_key = __esm({
1245
1252
  "src/runtime/shared/provider-api-key.mjs"() {
1246
1253
  require2 = createRequire2(import.meta.url);
1247
- ({ getSecret } = require2("../../lib/keychain-cjs.cjs"));
1254
+ ({ getSecret, hasSecret } = require2("../../lib/keychain-cjs.cjs"));
1248
1255
  AGENT_PROVIDER_ENV = Object.freeze({
1249
1256
  openai: "OPENAI_API_KEY",
1250
1257
  anthropic: "ANTHROPIC_API_KEY",
@@ -2614,7 +2621,7 @@ import { performance as performance4 } from "node:perf_hooks";
2614
2621
  import { format } from "node:util";
2615
2622
 
2616
2623
  // src/tui/App.jsx
2617
- import React21, { useState as useState11, useCallback as useCallback12, useEffect as useEffect13, useLayoutEffect as useLayoutEffect4, useMemo as useMemo4, useRef as useRef13 } from "react";
2624
+ import React21, { useState as useState11, useCallback as useCallback12, useEffect as useEffect13, useLayoutEffect as useLayoutEffect5, useMemo as useMemo4, useRef as useRef13 } from "react";
2618
2625
  import { Box as Box19, Text as Text21, useApp, useInput as useInput6, useStdin as useStdin3, useStdout as useStdout2 } from "../../../vendor/ink/build/index.js";
2619
2626
 
2620
2627
  // src/tui/themes/base.mjs
@@ -9592,7 +9599,7 @@ import { homedir as homedir2 } from "node:os";
9592
9599
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, statSync, writeFileSync as writeFileSync2 } from "node:fs";
9593
9600
  import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
9594
9601
  var MIXDOG_HOME = process.env.MIXDOG_HOME || join3(homedir2(), ".mixdog");
9595
- var PROJECTS_FILE = join3(MIXDOG_HOME, "projects.json");
9602
+ var PROJECTS_FILE = process.env.MIXDOG_PROJECTS_FILE ? resolve2(process.env.MIXDOG_PROJECTS_FILE) : join3(MIXDOG_HOME, "projects.json");
9596
9603
  function toAbsolute(rawPath) {
9597
9604
  const text = String(rawPath || "").trim();
9598
9605
  if (!text) return "";
@@ -9632,6 +9639,7 @@ function writeStore(store) {
9632
9639
  }
9633
9640
  function ensureProjectIdMarker(absPath, name) {
9634
9641
  try {
9642
+ if (process.env.MIXDOG_DISABLE_PROJECT_MARKERS === "1") return;
9635
9643
  if (!absPath) return;
9636
9644
  try {
9637
9645
  if (!statSync(absPath).isDirectory()) return;
@@ -12743,7 +12751,7 @@ function estimateTranscriptItemRows(item, columns, toolOutputExpanded, attachedT
12743
12751
  case "user":
12744
12752
  return 1 + estimateWrappedRows(item.text, columns, 4);
12745
12753
  case "assistant":
12746
- return 1 + (item.streaming ? measureStreamingMarkdownRenderedRows(item.text, columns, item.id) : measureMarkdownRenderedRows(item.text, columns));
12754
+ return 1 + (item.streaming ? measureStreamingMarkdownRenderedRows(item.text, columns, item.id) : measureMarkdownRenderedRows(item.text, columns, { trimPartialFences: true }));
12747
12755
  case "tool": {
12748
12756
  const TOOL_MARGIN_TOP = attachedTool ? 0 : 1;
12749
12757
  if (shouldSuppressFullyFailedToolItem(item)) return 0;
@@ -12996,7 +13004,7 @@ function carryTranscriptMeasuredRowsCache(prevItem, nextItem) {
12996
13004
  function measuredTranscriptRows(item, columns, toolOutputExpanded) {
12997
13005
  if (!TRANSCRIPT_MEASURED_ROWS || !item) return null;
12998
13006
  if (shouldSuppressFullyFailedToolItemCached(item)) return 0;
12999
- if (item.kind === "assistant" && item.streaming) return null;
13007
+ if (item.kind === "assistant") return null;
13000
13008
  const entry = transcriptMeasuredRowsCache.get(item);
13001
13009
  if (!entry) return null;
13002
13010
  if (entry.rows <= 0) return null;
@@ -13006,10 +13014,6 @@ function measuredTranscriptRows(item, columns, toolOutputExpanded) {
13006
13014
  return entry.rows;
13007
13015
  }
13008
13016
  var STREAMING_ROW_QUANTUM = 1;
13009
- var streamingBottomPinned = false;
13010
- function setStreamingBottomPinned(pinned) {
13011
- streamingBottomPinned = !!pinned;
13012
- }
13013
13017
  function assistantTextForStreamingRowEstimate(text) {
13014
13018
  return streamingLayoutText(text);
13015
13019
  }
@@ -13051,21 +13055,6 @@ function streamingEstimateRows(item, columns, toolOutputExpanded) {
13051
13055
  });
13052
13056
  return rows;
13053
13057
  }
13054
- function streamingTailMountedGrowth(items, columns, toolOutputExpanded) {
13055
- const list = Array.isArray(items) ? items : [];
13056
- const tail = list.length > 0 ? list[list.length - 1] : null;
13057
- if (!tail || tail.kind !== "assistant" || !tail.streaming) return null;
13058
- const toolExpanded = toolOutputExpanded ? 1 : 0;
13059
- const idEntry = streamingMeasuredRowsById.get(tail.id);
13060
- if (!idEntry || !(idEntry.rows > 0) || idEntry.columns !== columns || idEntry.toolExpanded !== toolExpanded) {
13061
- return null;
13062
- }
13063
- const live = streamingEstimateRows(tail, columns, toolOutputExpanded);
13064
- if (streamingBottomPinned) return { tailRows: Math.max(idEntry.rows, live), delta: 0 };
13065
- const baseline = idEntry.estimateRows > 0 ? idEntry.estimateRows : idEntry.rows;
13066
- const delta = Math.max(0, live - baseline);
13067
- return { tailRows: idEntry.rows, delta };
13068
- }
13069
13058
  var suppressFullyFailedCache = /* @__PURE__ */ new WeakMap();
13070
13059
  function shouldSuppressFullyFailedToolItemCached(item) {
13071
13060
  if (!item || typeof item !== "object") return shouldSuppressFullyFailedToolItem(item);
@@ -13079,18 +13068,8 @@ function estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded, att
13079
13068
  if (!item) return Math.max(1, Math.ceil(estimateTranscriptItemRows(item, columns, toolOutputExpanded)));
13080
13069
  if (shouldSuppressFullyFailedToolItemCached(item)) return 0;
13081
13070
  if (item.kind === "assistant" && item.streaming) {
13082
- const toolExpanded2 = toolOutputExpanded ? 1 : 0;
13083
- const idEntry = streamingMeasuredRowsById.get(item.id);
13084
- if (idEntry && idEntry.rows > 0 && idEntry.columns === columns && idEntry.toolExpanded === toolExpanded2) {
13085
- if (streamingBottomPinned) {
13086
- return Math.max(idEntry.rows, streamingEstimateRows(item, columns, toolOutputExpanded));
13087
- }
13088
- return idEntry.rows;
13089
- }
13090
- if (idEntry) {
13071
+ if (streamingMeasuredRowsById.has(item.id)) {
13091
13072
  streamingMeasuredRowsById.delete(item.id);
13092
- streamingEstimateHighWaterById.delete(item.id);
13093
- streamingTailEstimateById.delete(item.id);
13094
13073
  }
13095
13074
  return streamingEstimateRows(item, columns, toolOutputExpanded);
13096
13075
  }
@@ -13203,6 +13182,11 @@ function appendTranscriptRow(prefix, tailValue) {
13203
13182
  return Object.freeze({
13204
13183
  length: prefix.length + 1,
13205
13184
  atIndex: (index) => index === prefix.length ? tailValue : Number(prefix[index]) || 0,
13185
+ at: (index) => {
13186
+ const resolved = index < 0 ? prefix.length + 1 + index : index;
13187
+ if (resolved < 0 || resolved > prefix.length) return void 0;
13188
+ return resolved === prefix.length ? tailValue : prefix[resolved];
13189
+ },
13206
13190
  slice: (start = 0, end = prefix.length + 1) => {
13207
13191
  const values = [];
13208
13192
  const lo = Math.max(0, start < 0 ? prefix.length + 1 + start : start);
@@ -13776,12 +13760,12 @@ function useMouseInput({
13776
13760
  }
13777
13761
 
13778
13762
  // src/tui/app/use-transcript-scroll.mjs
13779
- import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef8 } from "react";
13763
+ import { useCallback as useCallback3, useEffect as useEffect8, useLayoutEffect as useLayoutEffect3, useRef as useRef8 } from "react";
13780
13764
 
13781
13765
  // src/tui/engine/render-timing.mjs
13782
13766
  var RENDER_ACK_HANG_GUARD_MS = 250;
13783
13767
  var RENDER_SETTLE_IDLE_MS = 64;
13784
- var TUI_RENDER_FPS = 60;
13768
+ var TUI_RENDER_FPS = 120;
13785
13769
  var TUI_FRAME_MS = Math.ceil(1e3 / TUI_RENDER_FPS);
13786
13770
  var pendingRenderAcks = [];
13787
13771
  var renderAckSeq = 0;
@@ -13881,6 +13865,7 @@ function useTranscriptScroll({
13881
13865
  selectionTextRef
13882
13866
  }) {
13883
13867
  const scrollAnimationRef = useRef8(null);
13868
+ const pendingReadbackRowsRef = useRef8(0);
13884
13869
  const selectionPaintRef = useRef8({ t: 0, rect: null, pending: null, timer: null });
13885
13870
  const scrollCoalesceRef = useRef8({ pendingRows: 0, timer: null });
13886
13871
  const selectionTextCaptureRef = useRef8(0);
@@ -13983,6 +13968,7 @@ function useTranscriptScroll({
13983
13968
  const resetTranscriptScroll = useCallback3(() => {
13984
13969
  cancelTranscriptFollow();
13985
13970
  stopSmoothScroll();
13971
+ pendingReadbackRowsRef.current = 0;
13986
13972
  scrollPositionRef.current = 0;
13987
13973
  scrollTargetRef.current = 0;
13988
13974
  transcriptAnchorRef.current = null;
@@ -13992,6 +13978,7 @@ function useTranscriptScroll({
13992
13978
  const armTranscriptFollow = useCallback3(() => {
13993
13979
  transcriptAnchorRef.current = null;
13994
13980
  transcriptAnchorDirtyRef.current = false;
13981
+ pendingReadbackRowsRef.current = 0;
13995
13982
  followingRef.current = true;
13996
13983
  stopSmoothScroll();
13997
13984
  }, [stopSmoothScroll]);
@@ -14174,6 +14161,11 @@ function useTranscriptScroll({
14174
14161
  }, []);
14175
14162
  const scrollTranscriptRows = useCallback3((deltaRows, options = {}) => {
14176
14163
  const maxTarget = Math.max(0, Number(maxScrollRowsRef.current) || 0);
14164
+ const publishedGeometry = transcriptGeomRef.current || {};
14165
+ const geometryMax = Math.max(
14166
+ 0,
14167
+ (Number(publishedGeometry.totalRows) || 0) - Math.max(1, Number(publishedGeometry.viewRows) || 1)
14168
+ );
14177
14169
  const historyState = store.getState?.() || {};
14178
14170
  const captureRestoreAnchor = () => {
14179
14171
  const geom = transcriptGeomRef.current || {};
@@ -14216,11 +14208,17 @@ function useTranscriptScroll({
14216
14208
  const BOTTOM_SNAP_ROWS = 3;
14217
14209
  if (deltaRows < 0 && target > 0 && target <= BOTTOM_SNAP_ROWS) target = 0;
14218
14210
  const appliedDelta = target - scrollTargetRef.current;
14211
+ const blockedReadbackIntent = deltaRows > 0 && appliedDelta === 0 && maxTarget === 0 && geometryMax > 0;
14212
+ if (blockedReadbackIntent) {
14213
+ pendingReadbackRowsRef.current += deltaRows;
14214
+ } else if (deltaRows < 0 || appliedDelta !== 0) {
14215
+ pendingReadbackRowsRef.current = 0;
14216
+ }
14219
14217
  if (appliedDelta !== 0 && dragRef.current.region === "transcript" && dragRef.current.rect) {
14220
14218
  flushPendingSelectionPaint();
14221
14219
  harvestStitchRowsNow(Number(scrollTargetRef.current) || 0);
14222
14220
  }
14223
- if (appliedDelta !== 0) cancelTranscriptFollow();
14221
+ if (appliedDelta !== 0 || blockedReadbackIntent) cancelTranscriptFollow();
14224
14222
  scrollTargetRef.current = target;
14225
14223
  if (appliedDelta !== 0) {
14226
14224
  if (target === 0) {
@@ -14279,6 +14277,12 @@ function useTranscriptScroll({
14279
14277
  scrollPositionRef.current = target;
14280
14278
  setScrollOffset(Math.round(target));
14281
14279
  }, [startSmoothScroll, stopSmoothScroll, paintSelectionRect, selectionPointAtCurrentScroll, withSelectionClip, cancelTranscriptFollow, buildSpanRect, harvestStitchRowsNow, cancelPendingSelectionPaint, flushPendingSelectionPaint]);
14280
+ useLayoutEffect3(() => {
14281
+ const pendingRows = Math.max(0, Number(pendingReadbackRowsRef.current) || 0);
14282
+ if (pendingRows <= 0 || Math.max(0, Number(maxScrollRowsRef.current) || 0) <= 0) return;
14283
+ pendingReadbackRowsRef.current = 0;
14284
+ scrollTranscriptRows(pendingRows);
14285
+ });
14282
14286
  const queueScrollCoalesced = useCallback3((deltaRows) => {
14283
14287
  const state = scrollCoalesceRef.current;
14284
14288
  state.pendingRows += deltaRows;
@@ -16169,10 +16173,19 @@ var StableMarkdownChunk = React13.memo(function StableMarkdownChunk2({
16169
16173
  }) {
16170
16174
  return /* @__PURE__ */ jsx13(Markdown, { themeEpoch, columns, children: text });
16171
16175
  });
16172
- function StreamingMarkdown({ children, themeEpoch = 0, columns, streamKey }) {
16176
+ function StreamingMarkdown({
16177
+ children,
16178
+ themeEpoch = 0,
16179
+ columns,
16180
+ streamKey,
16181
+ streaming = true
16182
+ }) {
16183
+ if (!streaming) {
16184
+ return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx13(Markdown, { themeEpoch, columns, trimPartialFences: true, children }) });
16185
+ }
16173
16186
  const parts = resolveStreamingMarkdownParts(children, streamKey);
16174
16187
  if (parts.plain) {
16175
- return /* @__PURE__ */ jsx13(Text13, { color: theme.text, wrap: "wrap", children: parts.unstableForRender });
16188
+ return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx13(Text13, { color: theme.text, wrap: "wrap", children: parts.unstableForRender }) });
16176
16189
  }
16177
16190
  const stableChunks = parts.stableChunks?.length ? parts.stableChunks : parts.stablePrefix ? [parts.stablePrefix] : [];
16178
16191
  return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", gap: 1, children: [
@@ -16206,7 +16219,16 @@ var AssistantMessage = React14.memo(function AssistantMessage2({
16206
16219
  const renderText = streaming && streamingWindowRows > 0 ? windowPlainStreamingText(text, bodyWidth, streamingWindowRows, assistantId) : text;
16207
16220
  return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", marginTop: 1, children: [
16208
16221
  /* @__PURE__ */ jsx14(Box12, { flexShrink: 0, minWidth: 2, children: /* @__PURE__ */ jsx14(Text14, { color: theme.text, children: TURN_MARKER }) }),
16209
- /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexShrink: 0, width: bodyWidth, children: streaming ? /* @__PURE__ */ jsx14(StreamingMarkdown, { themeEpoch, columns: bodyWidth, streamKey: assistantId, children: renderText }) : /* @__PURE__ */ jsx14(Markdown, { themeEpoch, columns: bodyWidth, children: text }) })
16222
+ /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexShrink: 0, width: bodyWidth, children: /* @__PURE__ */ jsx14(
16223
+ StreamingMarkdown,
16224
+ {
16225
+ themeEpoch,
16226
+ columns: bodyWidth,
16227
+ streamKey: assistantId,
16228
+ streaming,
16229
+ children: streaming ? renderText : text
16230
+ }
16231
+ ) })
16210
16232
  ] });
16211
16233
  });
16212
16234
  var UserMessage = React14.memo(function UserMessage2({ text, attached = false, columns, themeEpoch = 0 }) {
@@ -17086,14 +17108,15 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
17086
17108
  const trailingColor = expandHintColor;
17087
17109
  let labelOut;
17088
17110
  let summaryOut;
17111
+ const headerSummaryText = isShellSurface ? "" : summaryText;
17089
17112
  if (stringWidth9(labelText) >= avail) {
17090
17113
  labelOut = truncateToWidth(labelText, avail);
17091
17114
  summaryOut = "";
17092
17115
  } else {
17093
17116
  labelOut = labelText;
17094
- const summaryBudget = avail - stringWidth9(labelText) - (summaryText ? stringWidth9(" ()") : 0);
17117
+ const summaryBudget = avail - stringWidth9(labelText) - (headerSummaryText ? stringWidth9(" ()") : 0);
17095
17118
  const summaryWidth = Math.max(0, Math.min(summaryBudget, SUMMARY_MAX_CHARS));
17096
- const truncatedSummary = summaryText && summaryWidth > 0 ? truncateToWidth(summaryText, summaryWidth) : "";
17119
+ const truncatedSummary = headerSummaryText && summaryWidth > 0 ? truncateToWidth(headerSummaryText, summaryWidth) : "";
17097
17120
  summaryOut = truncatedSummary ? ` (${truncatedSummary})` : "";
17098
17121
  }
17099
17122
  return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
@@ -18041,11 +18064,12 @@ function createResumePicker({
18041
18064
  }
18042
18065
  const items = sessions.map((s) => {
18043
18066
  const preview = String(s.preview || "").replace(/\n/g, " ").trim();
18067
+ const title = String(s.title || "").replace(/\s+/g, " ").trim();
18044
18068
  const count = formatSessionMessageCount2(s.messageCount);
18045
18069
  return {
18046
18070
  value: s.id,
18047
18071
  label: `${formatSessionUpdatedAt2(s.updatedAt)} ${count}`,
18048
- description: preview || "(no message)"
18072
+ description: title || preview || "(no message)"
18049
18073
  };
18050
18074
  });
18051
18075
  setPicker({
@@ -18055,7 +18079,9 @@ function createResumePicker({
18055
18079
  labelWidth: 21,
18056
18080
  onSelect: (value) => {
18057
18081
  setPicker(null);
18058
- void store.resume(value).then((ok) => store.pushNotice(ok ? `Resumed ${value}` : "Couldn\u2019t resume chat.", ok ? "info" : "warn")).catch((e) => store.pushNotice(`Couldn\u2019t resume chat: ${e?.message || e}`, "error"));
18082
+ const selected = sessions.find((session) => session.id === value);
18083
+ const resumedName = String(selected?.title || selected?.preview || value).replace(/\s+/g, " ").trim();
18084
+ void store.resume(value).then((ok) => store.pushNotice(ok ? `Resumed ${resumedName || value}` : "Couldn\u2019t resume chat.", ok ? "info" : "warn")).catch((e) => store.pushNotice(`Couldn\u2019t resume chat: ${e?.message || e}`, "error"));
18059
18085
  },
18060
18086
  onCancel: () => {
18061
18087
  setPicker(null);
@@ -21116,118 +21142,26 @@ function createRoutePickers({
21116
21142
  closeUsagePanel();
21117
21143
  setPicker({
21118
21144
  title: "Workflow",
21119
- description: "Active workflow and its agents.",
21120
- help: returnTo ? "\u2191/\u2193 Select \xB7 Enter Open \xB7 Esc Settings" : "\u2191/\u2193 Select \xB7 Enter Open \xB7 Esc Back",
21145
+ description: "Select active workflow.",
21146
+ help: returnTo ? "\u2191/\u2193 Select \xB7 Enter Choose \xB7 Esc Settings" : "\u2191/\u2193 Select \xB7 Enter Choose \xB7 Esc Back",
21121
21147
  labelWidth: 18,
21122
- initialIndex: Math.max(0, items.findIndex((item) => item.value === (options.initialWorkflowId || ""))),
21123
21148
  items,
21124
21149
  onSelect: (_value, item) => {
21125
21150
  const workflow = item?._workflow;
21126
21151
  if (!workflow) return;
21127
- openWorkflowDetailPicker(workflow.id, { returnTo });
21128
- },
21129
- onCancel: () => {
21130
21152
  setPicker(null);
21131
- if (returnTo) returnTo();
21132
- }
21133
- });
21134
- };
21135
- const openWorkflowDetailPicker = (workflowId, options = {}) => {
21136
- const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
21137
- let workflows = [];
21138
- try {
21139
- workflows = store.listWorkflows?.() || [];
21140
- } catch (e) {
21141
- store.pushNotice(`could not list workflows: ${e?.message || e}`, "error");
21142
- return;
21143
- }
21144
- const workflow = workflows.find((item) => item.id === workflowId);
21145
- if (!workflow) {
21146
- store.pushNotice(`workflow "${workflowId}" not found`, "warn");
21147
- openWorkflowPicker({ returnTo });
21148
- return;
21149
- }
21150
- let agents = [];
21151
- try {
21152
- agents = store.listAgents?.() || [];
21153
- } catch (e) {
21154
- store.pushNotice(`could not list agents: ${e?.message || e}`, "error");
21155
- agents = [];
21156
- }
21157
- const packAgents = workflow.agentsConfigured === true ? (workflow.agents || []).map((id) => agents.find((agent) => agent.id === id) || { id, label: id, route: null, description: "" }) : agents.filter((agent) => agent.custom !== true);
21158
- const routeOverrides = options.routeOverrides && typeof options.routeOverrides === "object" ? options.routeOverrides : {};
21159
- const items = [
21160
- {
21161
- value: "__activate",
21162
- label: workflow.active ? "Active" : "Activate",
21163
- marker: workflow.active ? "\u2713" : "",
21164
- markerColor: theme.success,
21165
- description: workflow.active ? "This workflow is active." : "Make this the active workflow."
21166
- },
21167
- ...packAgents.length ? packAgents.map((agent) => ({
21168
- value: agent.id,
21169
- label: agent.label,
21170
- metaParts: agentModelParts2(routeOverrides[agent.id] || agent.route),
21171
- description: agent.description || agent.definition?.description || "",
21172
- _agent: agent
21173
- })) : [{
21174
- value: "__lead-only",
21175
- label: "Lead only",
21176
- description: "This workflow delegates to no agents."
21177
- }]
21178
- ];
21179
- setPicker({
21180
- title: workflow.name,
21181
- description: workflow.description || "Workflow activation and agents.",
21182
- help: "\u2191/\u2193 Select \xB7 Enter Choose \xB7 Esc Back",
21183
- indexMode: "always",
21184
- labelWidth: 18,
21185
- metaWidth: 33,
21186
- initialIndex: Math.max(0, items.findIndex((item) => item.value === (options.initialValue || "__activate"))),
21187
- items,
21188
- onSelect: (_value, item) => {
21189
- if (item?.value === "__activate") {
21190
- if (workflow.active) {
21191
- store.pushNotice(`${workflow.name} is already the active workflow`, "info");
21153
+ void store.setWorkflow?.(workflow.id).then((result) => {
21154
+ if (!result) {
21155
+ store.pushNotice("Workflow switch is already running.", "warn");
21192
21156
  return;
21193
21157
  }
21194
- setPicker(null);
21195
- void store.setWorkflow?.(workflow.id).then((result) => {
21196
- if (!result) {
21197
- store.pushNotice("Workflow switch is already running.", "warn");
21198
- return;
21199
- }
21200
- store.pushNotice(workflowSwitchNotice2(result), "info");
21201
- openWorkflowPicker({ returnTo, initialWorkflowId: workflow.id });
21202
- }).catch((e) => store.pushNotice(`Couldn\u2019t switch workflow: ${e?.message || e}`, "error"));
21203
- return;
21204
- }
21205
- const agent = item?._agent;
21206
- if (!agent) return;
21207
- void openModelPicker({
21208
- title: `${agent.label} Model`,
21209
- providerDescription: "Choose a provider for this agent.",
21210
- currentRoute: agent.route || null,
21211
- returnTo: () => openWorkflowDetailPicker(workflow.id, { returnTo, initialValue: agent.id }),
21212
- onImmediateSelect: (routeInput) => {
21213
- openWorkflowDetailPicker(workflow.id, {
21214
- returnTo,
21215
- routeOverrides: { [agent.id]: routeInput },
21216
- initialValue: agent.id
21217
- });
21218
- },
21219
- onSelectRoute: async (routeInput) => {
21220
- const result = await store.setAgentRoute?.(agent.id, routeInput);
21221
- if (!result) {
21222
- store.pushNotice("Agent model save is already running.", "warn");
21223
- return;
21224
- }
21225
- store.pushNotice(`${agent.label} model set to ${agentModelProfile2(result)}`, "info");
21226
- }
21227
- });
21158
+ store.pushNotice(workflowSwitchNotice2(result), "info");
21159
+ if (returnTo) returnTo();
21160
+ }).catch((e) => store.pushNotice(`Couldn\u2019t switch workflow: ${e?.message || e}`, "error"));
21228
21161
  },
21229
21162
  onCancel: () => {
21230
- openWorkflowPicker({ returnTo, initialWorkflowId: workflow.id });
21163
+ setPicker(null);
21164
+ if (returnTo) returnTo();
21231
21165
  }
21232
21166
  });
21233
21167
  };
@@ -21821,12 +21755,17 @@ function createSlashDispatch({
21821
21755
  if (cmd !== "usage") closeUsagePanel();
21822
21756
  switch (cmd) {
21823
21757
  case "clear":
21824
- if (state.busy) {
21825
- store.pushNotice("wait for the current turn to finish before /clear", "warn");
21758
+ if (state.busy || state.commandBusy) {
21759
+ store.pushNotice(`wait for the current session command to finish before /${rawName === "new" ? "new" : "clear"}`, "warn");
21826
21760
  return false;
21827
21761
  }
21828
21762
  if (rawName === "new") {
21829
- void store.newSession().then(() => {
21763
+ void store.newSession().then((created) => {
21764
+ if (created === false) {
21765
+ store.pushNotice("new session is already running", "warn");
21766
+ return;
21767
+ }
21768
+ store.forceRenderRepaint?.();
21830
21769
  }).catch((e) => store.pushNotice(`new session failed: ${e?.message || e}`, "error"));
21831
21770
  } else {
21832
21771
  void store.clear().then(() => {
@@ -22510,7 +22449,7 @@ function useTerminalChrome({ stdout: stdout2, isRawModeSupported, setResizeState
22510
22449
  }
22511
22450
 
22512
22451
  // src/tui/app/use-transcript-window.mjs
22513
- import { useCallback as useCallback10, useEffect as useEffect12, useLayoutEffect as useLayoutEffect3, useMemo as useMemo3, useRef as useRef12, useState as useState10 } from "react";
22452
+ import { useCallback as useCallback10, useEffect as useEffect12, useLayoutEffect as useLayoutEffect4, useMemo as useMemo3, useRef as useRef12, useState as useState10 } from "react";
22514
22453
  var HARVEST_BUMP_STREAK_LIMIT = 10;
22515
22454
  var TUI_DEBUG = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_TUI_DEBUG || ""));
22516
22455
  var harvestBreakerLogged = false;
@@ -22526,6 +22465,7 @@ function traceHarvestBreaker(changedKeys) {
22526
22465
  function useTranscriptWindow({
22527
22466
  items: settledItems,
22528
22467
  structureRevision,
22468
+ sessionKey = "",
22529
22469
  streamingTail,
22530
22470
  themeEpoch,
22531
22471
  frameColumns,
@@ -22567,6 +22507,39 @@ function useTranscriptWindow({
22567
22507
  forceNext: false
22568
22508
  });
22569
22509
  const transcriptMeasureItemsRef = useRef12(/* @__PURE__ */ new Map());
22510
+ const normalizedSessionKey = String(sessionKey ?? "");
22511
+ const transcriptSessionKeyRef = useRef12(normalizedSessionKey);
22512
+ if (transcriptSessionKeyRef.current !== normalizedSessionKey) {
22513
+ transcriptSessionKeyRef.current = normalizedSessionKey;
22514
+ transcriptTotalRowsRef.current = 0;
22515
+ harvestBumpStreakRef.current = { count: 0, timer: null };
22516
+ committedMaxScrollRowsRef.current = 0;
22517
+ incrementalRowIndexCacheRef.current = null;
22518
+ transcriptItemsCacheRef.current = null;
22519
+ prevViewportGeomRef.current = { contentHeight: 0, floatingPanelRows: 0 };
22520
+ transcriptItemElsRef.current.clear();
22521
+ transcriptMeasureRefCache.current.clear();
22522
+ transcriptMeasureItemsRef.current.clear();
22523
+ harvestGateRef.current = {
22524
+ inputs: null,
22525
+ skippedForDrag: false,
22526
+ forceNext: false
22527
+ };
22528
+ transcriptAnchorRef.current = null;
22529
+ transcriptAnchorDirtyRef.current = false;
22530
+ scrollTargetRef.current = 0;
22531
+ scrollPositionRef.current = 0;
22532
+ maxScrollRowsRef.current = 0;
22533
+ transcriptGeomRef.current = {};
22534
+ followingRef.current = true;
22535
+ selectionLayoutRef.current = null;
22536
+ if (hasStreamingRowStateToPrune()) {
22537
+ pruneStreamingMeasuredRowsById(/* @__PURE__ */ new Set());
22538
+ }
22539
+ }
22540
+ useLayoutEffect4(() => {
22541
+ if (scrollOffset !== 0) setScrollOffset(0);
22542
+ }, [normalizedSessionKey, setScrollOffset]);
22570
22543
  const transcriptMeasureRef = useCallback10((item) => {
22571
22544
  if (!TRANSCRIPT_MEASURED_ROWS || !item || item.id == null) return void 0;
22572
22545
  if (shouldSuppressFullyFailedToolItem(item)) {
@@ -22593,7 +22566,6 @@ function useTranscriptWindow({
22593
22566
  const streamingTailItem = streamingTail?.kind === "assistant" && streamingTail.streaming ? streamingTail : null;
22594
22567
  const scrolledUpRowsForPin = Math.max(0, Number(scrollTargetRef.current) || 0);
22595
22568
  const transcriptPinnedForStreaming = followingRef.current || scrolledUpRowsForPin === 0;
22596
- setStreamingBottomPinned(transcriptPinnedForStreaming);
22597
22569
  const tailRows = streamingTailItem ? estimateTranscriptItemRowsCached(streamingTailItem, frameColumns, toolOutputExpanded) : 0;
22598
22570
  const tailSig = streamingTailItem ? `${streamingTailItem.id}:${tailRows}` : "_";
22599
22571
  const revision = Math.max(0, Number(structureRevision) || 0);
@@ -22714,19 +22686,6 @@ function useTranscriptWindow({
22714
22686
  contentHeight: lockViewRows,
22715
22687
  floatingPanelRows: Number(floatingPanelRows) || 0
22716
22688
  };
22717
- if (scrolledUp && !followingRef.current) {
22718
- const growth = streamingTailMountedGrowth(
22719
- streamingTailItem ? [streamingTailItem] : transcriptItems,
22720
- frameColumns,
22721
- toolOutputExpanded
22722
- );
22723
- if (growth && growth.delta > 0) {
22724
- const maxOffsetKeepingTailMounted = growth.tailRows + TRANSCRIPT_WINDOW_OVERSCAN_ROWS - 1;
22725
- if (renderScrollOffset <= maxOffsetKeepingTailMounted) {
22726
- renderScrollOffset += Math.min(growth.delta, maxOffsetKeepingTailMounted - renderScrollOffset);
22727
- }
22728
- }
22729
- }
22730
22689
  const transcriptWindow = useMemo3(() => transcriptRenderWindow(transcriptItems, {
22731
22690
  scrollOffset: renderScrollOffset,
22732
22691
  viewportHeight: transcriptContentHeight,
@@ -22743,12 +22702,8 @@ function useTranscriptWindow({
22743
22702
  for (let i = 0; i < mountedSlice.length; i++) {
22744
22703
  const it = mountedSlice[i];
22745
22704
  if (!it || shouldSuppressFullyFailedToolItem(it)) continue;
22746
- if (it.kind === "assistant" && it.streaming) {
22747
- const idPrev = streamingMeasuredRowsById.get(it.id);
22748
- if (!idPrev || idPrev.columns !== frameColumns || idPrev.toolExpanded !== toolExpandedFlag) {
22749
- holdCommittedMax = true;
22750
- break;
22751
- }
22705
+ if (it.kind === "assistant") {
22706
+ continue;
22752
22707
  } else {
22753
22708
  const prev = transcriptMeasuredRowsCache.get(it);
22754
22709
  if (!prev || prev.columns !== frameColumns || prev.toolExpanded !== toolExpandedFlag || prev.variantKey !== transcriptItemVariantKey(it)) {
@@ -22811,7 +22766,7 @@ function useTranscriptWindow({
22811
22766
  transcriptGuardRows,
22812
22767
  themeEpoch
22813
22768
  };
22814
- useLayoutEffect3(() => {
22769
+ useLayoutEffect4(() => {
22815
22770
  if (!TRANSCRIPT_MEASURED_ROWS) return;
22816
22771
  const gate = harvestGateRef.current;
22817
22772
  if (dragRef.current.active) {
@@ -22848,27 +22803,14 @@ function useTranscriptWindow({
22848
22803
  }
22849
22804
  continue;
22850
22805
  }
22851
- const isStreamingAssistant = item.kind === "assistant" && !!item.streaming;
22806
+ if (item.kind === "assistant") {
22807
+ transcriptMeasuredRowsCache.delete(item);
22808
+ continue;
22809
+ }
22852
22810
  const measured = Math.max(1, rawMeasured);
22853
22811
  const variantKey = transcriptItemVariantKey(item);
22854
- if (isStreamingAssistant) {
22855
- const idPrev = streamingMeasuredRowsById.get(item.id);
22856
- const estimateAtMeasure = streamingEstimateRows(item, frameColumns, toolOutputExpanded);
22857
- if (!idPrev || idPrev.rows !== measured || idPrev.columns !== frameColumns || idPrev.toolExpanded !== toolExpandedFlag) {
22858
- streamingMeasuredRowsById.set(item.id, {
22859
- rows: measured,
22860
- columns: frameColumns,
22861
- toolExpanded: toolExpandedFlag,
22862
- estimateRows: estimateAtMeasure
22863
- });
22864
- changed = true;
22865
- changedKeys.push(`${key}=${measured}s`);
22866
- } else {
22867
- idPrev.estimateRows = estimateAtMeasure;
22868
- }
22869
- }
22870
22812
  const prev = transcriptMeasuredRowsCache.get(item);
22871
- if (!isStreamingAssistant && prev && prev.rows === measured && prev.columns === frameColumns && prev.toolExpanded === toolExpandedFlag && prev.variantKey === variantKey) {
22813
+ if (prev && prev.rows === measured && prev.columns === frameColumns && prev.toolExpanded === toolExpandedFlag && prev.variantKey === variantKey) {
22872
22814
  continue;
22873
22815
  }
22874
22816
  transcriptMeasuredRowsCache.set(item, {
@@ -22877,12 +22819,10 @@ function useTranscriptWindow({
22877
22819
  toolExpanded: toolExpandedFlag,
22878
22820
  variantKey
22879
22821
  });
22880
- if (!isStreamingAssistant) {
22881
- const estimateRows = prev ? -1 : estimateTranscriptItemRowsCached(item, frameColumns, toolOutputExpanded);
22882
- if (prev || measured !== estimateRows) {
22883
- changed = true;
22884
- changedKeys.push(`${key}=${measured}`);
22885
- }
22822
+ const estimateRows = prev ? -1 : estimateTranscriptItemRowsCached(item, frameColumns, toolOutputExpanded);
22823
+ if (prev || measured !== estimateRows) {
22824
+ changed = true;
22825
+ changedKeys.push(`${key}=${measured}`);
22886
22826
  }
22887
22827
  }
22888
22828
  if (changed) {
@@ -22917,7 +22857,7 @@ function useTranscriptWindow({
22917
22857
  pruneStreamingMeasuredRowsById(new Set(liveItems.keys()));
22918
22858
  }
22919
22859
  });
22920
- useLayoutEffect3(() => {
22860
+ useLayoutEffect4(() => {
22921
22861
  const totalRows = Math.max(0, Number(transcriptWindow.totalRows) || 0);
22922
22862
  const previousTotalRows = Math.max(0, Number(transcriptTotalRowsRef.current) || 0);
22923
22863
  transcriptTotalRowsRef.current = totalRows;
@@ -22928,7 +22868,7 @@ function useTranscriptWindow({
22928
22868
  const currentPosition = Math.max(0, Number(scrollPositionRef.current) || 0);
22929
22869
  const currentOffset = Math.max(0, Number(scrollOffset) || 0);
22930
22870
  const maxRows = Math.max(0, Number(transcriptWindow.maxScrollRows) || 0);
22931
- const nearBottom = followingRef.current || currentTarget === 0;
22871
+ const nearBottom = followingRef.current;
22932
22872
  const pinnedToBottom = nearBottom;
22933
22873
  const activeReadingAnchor = !!transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current && !followingRef.current && !nearBottom;
22934
22874
  const followOnGrowth = followingRef.current && rowDelta > 0 && !activeReadingAnchor;
@@ -22988,7 +22928,7 @@ function useTranscriptWindow({
22988
22928
  scrollPositionRef.current = Math.max(0, Math.min(maxRows, currentPosition + appliedDelta));
22989
22929
  setScrollOffset(Math.max(0, Math.round(desired)));
22990
22930
  }, [transcriptWindow.totalRows, transcriptWindow.maxScrollRows, transcriptRowIndex, transcriptContentHeight, scrollOffset, stopSmoothScroll]);
22991
- useLayoutEffect3(() => {
22931
+ useLayoutEffect4(() => {
22992
22932
  if (transcriptAnchorRef.current || transcriptAnchorDirtyRef.current || followingRef.current) return;
22993
22933
  const currentTarget = Math.max(0, Number(scrollTargetRef.current) || 0);
22994
22934
  const currentPosition = Math.max(0, Number(scrollPositionRef.current) || 0);
@@ -23000,7 +22940,7 @@ function useTranscriptWindow({
23000
22940
  scrollPositionRef.current = 0;
23001
22941
  setScrollOffset(0);
23002
22942
  }, [scrollOffset, stopSmoothScroll]);
23003
- useLayoutEffect3(() => {
22943
+ useLayoutEffect4(() => {
23004
22944
  const top = Math.max(0, Number(transcriptViewportRef.current?.top) || 0);
23005
22945
  const next = {
23006
22946
  top,
@@ -24016,7 +23956,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
24016
23956
  setMeasuredRowsVersion,
24017
23957
  clearStitchBuffer
24018
23958
  });
24019
- useLayoutEffect4(() => {
23959
+ useLayoutEffect5(() => {
24020
23960
  const count = state.items.length;
24021
23961
  const previousCount = lastItemsCountRef.current;
24022
23962
  lastItemsCountRef.current = count;
@@ -24025,7 +23965,6 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
24025
23965
  return;
24026
23966
  }
24027
23967
  if (count === previousCount || dragRef.current.active) return;
24028
- if (scrollTargetRef.current === 0 || followingRef.current) followingRef.current = true;
24029
23968
  }, [state.items.length, resetTranscriptScroll]);
24030
23969
  const {
24031
23970
  requestExit,
@@ -24343,6 +24282,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
24343
24282
  } = useTranscriptWindow({
24344
24283
  items: state.transcriptViewItems || state.items,
24345
24284
  structureRevision: state.transcriptViewItems ? state.transcriptViewRevision : state.structureRevision,
24285
+ sessionKey: state.sessionId || "",
24346
24286
  // Historical pages are contiguous settled windows. Keep the independently
24347
24287
  // growing live tail hidden until paging forward reaches the live window.
24348
24288
  streamingTail: state.transcriptViewItems ? null : state.streamingTail,
@@ -24599,6 +24539,27 @@ function isLikelyToolCompletionWrapper(text) {
24599
24539
  const quotedCount = quotedLines.filter((line) => /^> /.test(line)).length;
24600
24540
  return quotedCount / quotedLines.length >= 0.8;
24601
24541
  }
24542
+ var INTERNAL_TRANSCRIPT_CONTEXT_RE = /^<(?:system-reminder|task-notification|skill|memory-context|mcp-instructions|available-deferred-tools|event)\b/i;
24543
+ var INTERNAL_TRANSCRIPT_SYNTHETIC_RE = /^(?:\[mixdog-runtime\]|A previous model worked on this task and produced the compacted handoff summary below\b|Re-attached after compaction\b|Reference files:\s)/i;
24544
+ var INTERNAL_TRANSCRIPT_ASYNC_HEAD_RE = /^The async (?:agent task|shell task|\w+ execution) \S+ has (?:finished|completed)\b/i;
24545
+ var INTERNAL_TRANSCRIPT_INTERRUPT_RE = /^\[request interrupted by user(?: for tool use)?\]$/i;
24546
+ function isInternalTranscriptDisplayText(text) {
24547
+ const value = String(text ?? "").trim();
24548
+ if (!value) return false;
24549
+ if (INTERNAL_TRANSCRIPT_CONTEXT_RE.test(value) || INTERNAL_TRANSCRIPT_SYNTHETIC_RE.test(value) || INTERNAL_TRANSCRIPT_INTERRUPT_RE.test(value) || isInternalRuntimeNotificationText(value) || isModelVisibleToolCompletionWrapper(value) || isLikelyToolCompletionWrapper(value)) {
24550
+ return true;
24551
+ }
24552
+ if (INTERNAL_TRANSCRIPT_ASYNC_HEAD_RE.test(value) && !/\bResult:\s*(?:\r?\n|$)/i.test(value)) {
24553
+ return true;
24554
+ }
24555
+ const resultSplit = /\r?\n(?:[ \t]*\r?\n)?Result:[ \t]*\r?\n/i.exec(value);
24556
+ if (!resultSplit) return false;
24557
+ const preamble = value.slice(0, resultSplit.index).trim();
24558
+ const instructionLike = MODEL_VISIBLE_COMPLETION_INSTRUCTION_RE.test(preamble) || MODEL_VISIBLE_COMPLETION_REVIEW_RE.test(preamble) || MODEL_VISIBLE_COMPLETION_ASYNC_HEADER_RE.test(preamble);
24559
+ if (!instructionLike) return false;
24560
+ const normalizedBody = value.slice(resultSplit.index + resultSplit[0].length).split(/\r?\n/).map((line) => line.replace(/^>\s?/, "")).join("\n").trim();
24561
+ return isInternalRuntimeNotificationText(normalizedBody);
24562
+ }
24602
24563
  function modelVisibleToolCompletionMessage(text, meta = {}) {
24603
24564
  const message = String(text || "").trim();
24604
24565
  if (!message) return "";
@@ -27101,7 +27062,6 @@ function createSessionFlow(bag) {
27101
27062
  if (isQueuedEntryVisible(entry)) restored.push(entry);
27102
27063
  }
27103
27064
  if (restored.length > 0) set({ queued: [...getState().queued, ...restored] });
27104
- void drain();
27105
27065
  }
27106
27066
  async function autoClearBeforeSubmit() {
27107
27067
  flushDeferredClearedSessionUi();
@@ -28660,9 +28620,7 @@ function restoredMessageItemUpperBound(message) {
28660
28620
  function restoredUserTranscriptItems(message, nextId2) {
28661
28621
  if (message?.meta === "skill" || message?.meta === "hook") return [];
28662
28622
  const text = (typeof message?.content === "string" ? message.content : toolResultText(message?.content)).trim();
28663
- if (/^<(?:system-reminder|skill|memory-context|mcp-instructions|available-deferred-tools|event)\b/i.test(text)) {
28664
- return [];
28665
- }
28623
+ if (isInternalTranscriptDisplayText(text)) return [];
28666
28624
  if (!text) return [];
28667
28625
  const synthetic = parseSyntheticAgentMessage(text);
28668
28626
  if (!synthetic) {
@@ -28735,6 +28693,44 @@ function restoreTranscriptItems(messages, {
28735
28693
  }
28736
28694
  return restored.length > limit ? restored.slice(-limit) : restored;
28737
28695
  }
28696
+ function sessionContextSnapshotProjection(session, contextStatus) {
28697
+ if (!contextStatus) return {};
28698
+ const usedSource = String(contextStatus.usedSource || "").toLowerCase();
28699
+ const estimatedTokens = Math.max(0, Number(
28700
+ contextStatus.currentEstimatedTokens ?? contextStatus.usedTokens ?? 0
28701
+ ));
28702
+ const lastApiRequestTokens = Math.max(0, Number(
28703
+ contextStatus.lastApiRequestTokens ?? contextStatus.usage?.lastContextTokens ?? 0
28704
+ ));
28705
+ return {
28706
+ stats: {
28707
+ currentContextTokens: usedSource === "last_api_request" ? lastApiRequestTokens : 0,
28708
+ currentEstimatedContextTokens: estimatedTokens,
28709
+ currentContextSource: usedSource === "last_api_request" ? "last_api_request" : estimatedTokens > 0 ? "estimated" : null,
28710
+ currentContextUpdatedAt: Date.now()
28711
+ },
28712
+ contextWindow: Math.max(
28713
+ 0,
28714
+ Number(session?.contextWindow || contextStatus.effectiveContextWindow || 0)
28715
+ ),
28716
+ rawContextWindow: Math.max(
28717
+ 0,
28718
+ Number(session?.rawContextWindow || contextStatus.rawContextWindow || 0)
28719
+ ),
28720
+ effectiveContextWindowPercent: Number(
28721
+ session?.effectiveContextWindowPercent ?? contextStatus.effectiveContextWindowPercent ?? 0
28722
+ ),
28723
+ displayContextWindow: Math.max(0, Number(contextStatus.contextWindow || 0)),
28724
+ compactBoundaryTokens: Math.max(
28725
+ 0,
28726
+ Number(contextStatus.compaction?.boundaryTokens || 0)
28727
+ ),
28728
+ autoCompactTokenLimit: Math.max(
28729
+ 0,
28730
+ Number(contextStatus.compaction?.triggerTokens || 0)
28731
+ )
28732
+ };
28733
+ }
28738
28734
  function createEngineApiB(bag) {
28739
28735
  const {
28740
28736
  runtime,
@@ -29003,6 +28999,12 @@ function createEngineApiB(bag) {
29003
28999
  getUsageDashboard: async (options = {}) => {
29004
29000
  return await runtime.getUsageDashboard?.(options);
29005
29001
  },
29002
+ consumeCodexRateLimitResetCredit: async (options = {}) => {
29003
+ if (typeof runtime.consumeCodexRateLimitResetCredit !== "function") {
29004
+ throw new Error("Codex reset is unavailable");
29005
+ }
29006
+ return await runtime.consumeCodexRateLimitResetCredit(options);
29007
+ },
29006
29008
  getTurnReviewDiff: async () => {
29007
29009
  return await runtime.getTurnReviewDiff?.() ?? { supported: false, files: [], patch: "" };
29008
29010
  },
@@ -29130,6 +29132,7 @@ function createEngineApiB(bag) {
29130
29132
  listMediaLanes: () => runtime.listMediaLanes?.(),
29131
29133
  listMediaAssets: (options) => runtime.listMediaAssets?.(options),
29132
29134
  readMediaAsset: (id, options) => runtime.readMediaAsset?.(id, options),
29135
+ cacheMediaThumbnail: (id, input) => runtime.cacheMediaThumbnail?.(id, input),
29133
29136
  resolveMediaFile: (id, options) => runtime.resolveMediaFile?.(id, options),
29134
29137
  getMediaJob: (id) => runtime.getMediaJob?.(id),
29135
29138
  listMediaJobs: () => runtime.listMediaJobs?.(),
@@ -29139,7 +29142,7 @@ function createEngineApiB(bag) {
29139
29142
  cancelMediaJob: (id) => runtime.cancelMediaJob?.(id),
29140
29143
  deleteMediaAsset: (id) => runtime.deleteMediaAsset?.(id),
29141
29144
  openMediaAsset: (id) => runtime.openMediaAsset?.(id),
29142
- openMediaFolder: () => runtime.openMediaFolder?.(),
29145
+ openMediaFolder: (id) => runtime.openMediaFolder?.(id),
29143
29146
  setWebhookConfig: async (patch) => {
29144
29147
  const result = await runtime.setWebhookConfig(patch);
29145
29148
  pushNotice("webhook config updated", "info");
@@ -29235,6 +29238,9 @@ function createEngineApiB(bag) {
29235
29238
  listSessions: (options) => {
29236
29239
  return runtime.listSessions(options);
29237
29240
  },
29241
+ renameSessionTitle: (id, title) => {
29242
+ return runtime.renameSessionTitle?.(id, title) ?? false;
29243
+ },
29238
29244
  // Desktop sidebar watcher hook: EngineHost fs.watches this directory so
29239
29245
  // heartbeat sidecar create/delete pushes instant working/dot updates.
29240
29246
  // Without it the watcher silently no-ops and the sidebar falls back to
@@ -29337,6 +29343,7 @@ function createEngineApiB(bag) {
29337
29343
  sessionId: null,
29338
29344
  stats: { ...getState().stats }
29339
29345
  });
29346
+ flushEmitImmediate();
29340
29347
  try {
29341
29348
  await runtime.newSession();
29342
29349
  clearUiActivityBeforeContextSync();
@@ -29351,6 +29358,7 @@ function createEngineApiB(bag) {
29351
29358
  } finally {
29352
29359
  flags.pendingSessionReset = false;
29353
29360
  set({ commandBusy: false });
29361
+ flushEmitImmediate();
29354
29362
  }
29355
29363
  },
29356
29364
  // Desktop split panes: read-only transcript + route peek of an idle
@@ -29361,6 +29369,7 @@ function createEngineApiB(bag) {
29361
29369
  if (!session) return null;
29362
29370
  const sessionId = String(session.id || id);
29363
29371
  const limit = Number(options.transcriptItemLimit);
29372
+ const contextStatus = runtime.contextStatusForSession?.(session) || null;
29364
29373
  return {
29365
29374
  sessionId,
29366
29375
  items: restoreTranscriptItems(session.messages, {
@@ -29373,7 +29382,8 @@ function createEngineApiB(bag) {
29373
29382
  fast: session.fast === true,
29374
29383
  cwd: session.cwd || "",
29375
29384
  desktopSession: session.desktopSession || null,
29376
- workflow: session.workflow || null
29385
+ workflow: session.workflow || null,
29386
+ ...sessionContextSnapshotProjection(session, contextStatus)
29377
29387
  };
29378
29388
  },
29379
29389
  resume: async (id, options = {}) => {
@@ -30342,9 +30352,19 @@ function createLiveShare({
30342
30352
  onRemoteSubmit,
30343
30353
  onRemoteAbort,
30344
30354
  onOwnerClosed,
30345
- viewerApply
30355
+ viewerApply,
30356
+ viewerRetryMinMs = LIVE_CONNECT_RETRY_MIN_MS,
30357
+ viewerRetryMaxMs = LIVE_CONNECT_RETRY_MAX_MS
30346
30358
  }) {
30347
30359
  let disposed = false;
30360
+ const clientRetryMinimumMs = Math.max(
30361
+ LIVE_CONNECT_RETRY_MIN_MS,
30362
+ Number(viewerRetryMinMs) || LIVE_CONNECT_RETRY_MIN_MS
30363
+ );
30364
+ const clientRetryMaximumMs = Math.max(
30365
+ clientRetryMinimumMs,
30366
+ Number(viewerRetryMaxMs) || LIVE_CONNECT_RETRY_MAX_MS
30367
+ );
30348
30368
  let server = null;
30349
30369
  let serverId = "";
30350
30370
  let serverPath = "";
@@ -30631,7 +30651,7 @@ function createLiveShare({
30631
30651
  let lastSyncRequestAt = 0;
30632
30652
  let clientRetryTimer = null;
30633
30653
  let clientRetryId = "";
30634
- let clientRetryDelayMs = LIVE_CONNECT_RETRY_MIN_MS;
30654
+ let clientRetryDelayMs = clientRetryMinimumMs;
30635
30655
  const viewerSyncWaiters = /* @__PURE__ */ new Set();
30636
30656
  const settleViewerSync = (id, synced) => {
30637
30657
  for (const waiter of [...viewerSyncWaiters]) {
@@ -30721,8 +30741,8 @@ function createLiveShare({
30721
30741
  const target = String(id || "");
30722
30742
  if (disposed || !target || client || clientRetryTimer || String(viewerSessionId() || "") !== target) return;
30723
30743
  const delay2 = clientRetryDelayMs;
30724
- clientRetryDelayMs = Math.min(LIVE_CONNECT_RETRY_MAX_MS, Math.max(
30725
- LIVE_CONNECT_RETRY_MIN_MS,
30744
+ clientRetryDelayMs = Math.min(clientRetryMaximumMs, Math.max(
30745
+ clientRetryMinimumMs,
30726
30746
  clientRetryDelayMs * 2
30727
30747
  ));
30728
30748
  clientRetryId = target;
@@ -30769,7 +30789,7 @@ function createLiveShare({
30769
30789
  };
30770
30790
  const stopClient = () => {
30771
30791
  clearClientRetry();
30772
- clientRetryDelayMs = LIVE_CONNECT_RETRY_MIN_MS;
30792
+ clientRetryDelayMs = clientRetryMinimumMs;
30773
30793
  const closing = client;
30774
30794
  const closingId = clientId;
30775
30795
  const wasUp = clientUp;
@@ -30805,7 +30825,7 @@ function createLiveShare({
30805
30825
  if (client !== socket) return;
30806
30826
  clientUp = true;
30807
30827
  clearClientRetry();
30808
- clientRetryDelayMs = LIVE_CONNECT_RETRY_MIN_MS;
30828
+ clientRetryDelayMs = clientRetryMinimumMs;
30809
30829
  });
30810
30830
  const down = (ownerClosed) => {
30811
30831
  const wasUp = clientUp && client === socket;
@@ -31748,7 +31768,7 @@ async function createEngineSession({
31748
31768
  return true;
31749
31769
  };
31750
31770
  const pushUserOrSyntheticItem = (text, id = nextId(), origin = "user", extra = null) => {
31751
- if (origin === "injected" && isLikelyToolCompletionWrapper(text)) return;
31771
+ if (origin === "injected" && isInternalTranscriptDisplayText(text)) return;
31752
31772
  if (isModelVisibleToolCompletionWrapper(text)) return;
31753
31773
  if (isLateToolAnnouncement(text)) return;
31754
31774
  if (upsertSyntheticToolItem(text, id)) return;
@@ -32703,7 +32723,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
32703
32723
  if (mouseTracking && typeof instance.getSelectionRows === "function") {
32704
32724
  store.getRenderSelectionRows = instance.getSelectionRows;
32705
32725
  }
32706
- if (mouseTracking && typeof instance.forceFullRepaint === "function") {
32726
+ if (typeof instance.forceFullRepaint === "function") {
32707
32727
  store.forceRenderRepaint = instance.forceFullRepaint;
32708
32728
  }
32709
32729
  await waitUntilExit();