mixdog 0.9.35 → 0.9.37
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/package.json +3 -2
- package/scripts/abort-recovery-test.mjs +118 -0
- package/scripts/compact-smoke.mjs +7 -7
- package/scripts/explore-bench-tmp.mjs +19 -0
- package/scripts/explore-bench.mjs +36 -6
- package/scripts/explore-prompt-policy-test.mjs +27 -0
- package/scripts/ingest-pure-conversation-smoke.mjs +11 -11
- package/scripts/openai-ws-early-settle-test.mjs +176 -0
- package/scripts/output-style-smoke.mjs +17 -17
- package/scripts/provider-toolcall-test.mjs +333 -14
- package/scripts/recall-bench-cases.json +3 -3
- package/scripts/recall-bench.mjs +1 -1
- package/scripts/recall-quality-cases.json +4 -4
- package/scripts/recall-usecase-cases.json +2 -2
- package/scripts/session-bench.mjs +13 -13
- package/scripts/steering-drain-buckets-test.mjs +72 -11
- package/scripts/submit-commandbusy-race-test.mjs +114 -0
- package/scripts/tool-smoke.mjs +83 -10
- package/src/app.mjs +2 -1
- package/src/defaults/cycle3-review-prompt.md +9 -0
- package/src/defaults/skills/setup/SKILL.md +93 -293
- package/src/lib/rules-builder.cjs +3 -2
- package/src/output-styles/default.md +2 -2
- package/src/output-styles/extreme-minimal.md +1 -1
- package/src/output-styles/minimal.md +1 -1
- package/src/output-styles/simple.md +2 -3
- package/src/rules/agent/30-explorer.md +15 -7
- package/src/rules/lead/01-general.md +4 -0
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +15 -3
- package/src/runtime/agent/orchestrator/config.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +35 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +27 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +36 -37
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +72 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +26 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -28
- package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +18 -15
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +113 -2
- package/src/runtime/agent/orchestrator/providers/registry.mjs +44 -5
- package/src/runtime/agent/orchestrator/providers/tool-stream-state.mjs +78 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +57 -31
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +28 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +26 -17
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +24 -16
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +18 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +35 -20
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +77 -31
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +25 -3
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +9 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +73 -25
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +17 -7
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +14 -12
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +5 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +56 -6
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -0
- package/src/runtime/channels/lib/worker-main.mjs +5 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +104 -0
- package/src/runtime/memory/lib/ko-morph.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle3.mjs +64 -4
- package/src/runtime/memory/lib/memory-text-utils.mjs +4 -4
- package/src/runtime/memory/lib/recall-format.mjs +3 -3
- package/src/runtime/shared/child-spawn-gate.mjs +15 -0
- package/src/runtime/shared/config.mjs +98 -12
- package/src/runtime/shared/process-listener-headroom.mjs +12 -0
- package/src/session-runtime/cwd-plugins.mjs +6 -3
- package/src/session-runtime/model-route-api.mjs +50 -2
- package/src/session-runtime/provider-auth-api.mjs +8 -1
- package/src/session-runtime/resource-api.mjs +22 -0
- package/src/session-runtime/runtime-core.mjs +13 -2
- package/src/session-runtime/settings-api.mjs +11 -2
- package/src/standalone/agent-tool.mjs +15 -9
- package/src/standalone/explore-tool.mjs +4 -1
- package/src/tui/App.jsx +6 -5
- package/src/tui/app/transcript-window.mjs +60 -10
- package/src/tui/app/use-transcript-window.mjs +2 -1
- package/src/tui/components/ContextPanel.jsx +4 -1
- package/src/tui/components/ToolExecution.jsx +15 -12
- package/src/tui/components/TranscriptItem.jsx +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +8 -8
- package/src/tui/dist/index.mjs +473 -155
- package/src/tui/engine/agent-job-feed.mjs +26 -6
- package/src/tui/engine/context-state.mjs +13 -4
- package/src/tui/engine/notification-plan.mjs +3 -3
- package/src/tui/engine/render-timing.mjs +104 -8
- package/src/tui/engine/session-api.mjs +58 -3
- package/src/tui/engine/session-flow.mjs +78 -28
- package/src/tui/engine/tool-card-results.mjs +28 -13
- package/src/tui/engine/turn.mjs +232 -156
- package/src/tui/engine.mjs +17 -8
- package/src/tui/index.jsx +10 -1
- package/src/workflows/default/WORKFLOW.md +8 -4
- package/src/workflows/solo/WORKFLOW.md +8 -4
package/src/tui/dist/index.mjs
CHANGED
|
@@ -6020,7 +6020,7 @@ function percent(value, total) {
|
|
|
6020
6020
|
function percentLabel(value, total) {
|
|
6021
6021
|
const pct = percent(value, total);
|
|
6022
6022
|
if (pct === null) return "N/A";
|
|
6023
|
-
return `${pct > 0 && pct < 1 ? pct.toFixed(1) : Math.
|
|
6023
|
+
return `${pct > 0 && pct < 1 ? pct.toFixed(1) : Math.floor(pct)}%`;
|
|
6024
6024
|
}
|
|
6025
6025
|
function usageColor(pct) {
|
|
6026
6026
|
if (!Number.isFinite(Number(pct))) return theme.text;
|
|
@@ -10479,6 +10479,7 @@ function computeTranscriptItemVariantKey(item) {
|
|
|
10479
10479
|
const count = Number(item.count ?? 0);
|
|
10480
10480
|
const completed = item.completedCount === void 0 ? "u" : Number(item.completedCount);
|
|
10481
10481
|
const errors = item.errorCount === void 0 ? "u" : Number(item.errorCount);
|
|
10482
|
+
const callErrors = item.callErrorCount === void 0 ? "u" : Number(item.callErrorCount);
|
|
10482
10483
|
const isError = item.isError ? 1 : 0;
|
|
10483
10484
|
const normalizedName = String(normalizeToolName(item.name) || "").toLowerCase();
|
|
10484
10485
|
const aggregate = item.aggregate ? 1 : 0;
|
|
@@ -10489,17 +10490,28 @@ function computeTranscriptItemVariantKey(item) {
|
|
|
10489
10490
|
const bgPrompt = textShapeFingerprint(bgArgs.prompt);
|
|
10490
10491
|
const bgMessage = textShapeFingerprint(bgArgs.message);
|
|
10491
10492
|
const bgError = textShapeFingerprint(bgArgs.error);
|
|
10492
|
-
return `x${expanded}:n${normalizedName}:g${aggregate}:r${resultShape}:R${rawShape}:c${count}:d${completed}:e${errors}:E${isError}:bt${bgType}:bs${bgStatus}:bk${bgTaskId}:bp${bgPrompt}:bm${bgMessage}:be${bgError}`;
|
|
10493
|
+
return `x${expanded}:n${normalizedName}:g${aggregate}:r${resultShape}:R${rawShape}:c${count}:d${completed}:e${errors}:ce${callErrors}:E${isError}:bt${bgType}:bs${bgStatus}:bk${bgTaskId}:bp${bgPrompt}:bm${bgMessage}:be${bgError}`;
|
|
10493
10494
|
}
|
|
10494
10495
|
return `x${expanded}:s${textShapeFingerprint(item.text ?? item.result ?? "")}`;
|
|
10495
10496
|
}
|
|
10496
10497
|
var transcriptRowsCache = /* @__PURE__ */ new WeakMap();
|
|
10497
10498
|
var transcriptMeasuredRowsCache = /* @__PURE__ */ new WeakMap();
|
|
10498
10499
|
var streamingMeasuredRowsById = /* @__PURE__ */ new Map();
|
|
10500
|
+
var streamingEstimateHighWaterById = /* @__PURE__ */ new Map();
|
|
10501
|
+
function hasStreamingRowStateToPrune() {
|
|
10502
|
+
return streamingMeasuredRowsById.size > 0 || streamingEstimateHighWaterById.size > 0;
|
|
10503
|
+
}
|
|
10499
10504
|
function pruneStreamingMeasuredRowsById(liveIds) {
|
|
10500
|
-
if (!liveIds
|
|
10501
|
-
|
|
10502
|
-
|
|
10505
|
+
if (!liveIds) return;
|
|
10506
|
+
if (streamingMeasuredRowsById.size > 0) {
|
|
10507
|
+
for (const id of streamingMeasuredRowsById.keys()) {
|
|
10508
|
+
if (!liveIds.has(id)) streamingMeasuredRowsById.delete(id);
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10511
|
+
if (streamingEstimateHighWaterById.size > 0) {
|
|
10512
|
+
for (const id of streamingEstimateHighWaterById.keys()) {
|
|
10513
|
+
if (!liveIds.has(id)) streamingEstimateHighWaterById.delete(id);
|
|
10514
|
+
}
|
|
10503
10515
|
}
|
|
10504
10516
|
}
|
|
10505
10517
|
function carryTranscriptMeasuredRowsCache(prevItem, nextItem) {
|
|
@@ -10534,7 +10546,17 @@ function streamingEstimateRows(item, columns, toolOutputExpanded) {
|
|
|
10534
10546
|
const trimmedText = assistantTextForStreamingRowEstimate(item.text);
|
|
10535
10547
|
const estimateItem = trimmedText === item.text ? item : { ...item, text: trimmedText };
|
|
10536
10548
|
const raw = Math.max(1, Math.ceil(estimateTranscriptItemRows(estimateItem, columns, toolOutputExpanded)));
|
|
10537
|
-
|
|
10549
|
+
const quantized = Math.ceil(raw / STREAMING_ROW_QUANTUM) * STREAMING_ROW_QUANTUM;
|
|
10550
|
+
const id = item?.id;
|
|
10551
|
+
if (id == null) return quantized;
|
|
10552
|
+
const toolExpanded = toolOutputExpanded ? 1 : 0;
|
|
10553
|
+
const prev = streamingEstimateHighWaterById.get(id);
|
|
10554
|
+
if (!prev || prev.columns !== columns || prev.toolExpanded !== toolExpanded) {
|
|
10555
|
+
streamingEstimateHighWaterById.set(id, { rows: quantized, columns, toolExpanded });
|
|
10556
|
+
return quantized;
|
|
10557
|
+
}
|
|
10558
|
+
if (quantized > prev.rows) prev.rows = quantized;
|
|
10559
|
+
return prev.rows;
|
|
10538
10560
|
}
|
|
10539
10561
|
function streamingTailMountedGrowth(items, columns, toolOutputExpanded) {
|
|
10540
10562
|
const list = Array.isArray(items) ? items : [];
|
|
@@ -10563,11 +10585,15 @@ function estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded) {
|
|
|
10563
10585
|
}
|
|
10564
10586
|
return idEntry.rows;
|
|
10565
10587
|
}
|
|
10566
|
-
if (idEntry)
|
|
10588
|
+
if (idEntry) {
|
|
10589
|
+
streamingMeasuredRowsById.delete(item.id);
|
|
10590
|
+
streamingEstimateHighWaterById.delete(item.id);
|
|
10591
|
+
}
|
|
10567
10592
|
return streamingEstimateRows(item, columns, toolOutputExpanded);
|
|
10568
10593
|
}
|
|
10569
|
-
if (item.kind === "assistant"
|
|
10570
|
-
streamingMeasuredRowsById.delete(item.id);
|
|
10594
|
+
if (item.kind === "assistant") {
|
|
10595
|
+
if (streamingMeasuredRowsById.has(item.id)) streamingMeasuredRowsById.delete(item.id);
|
|
10596
|
+
if (streamingEstimateHighWaterById.has(item.id)) streamingEstimateHighWaterById.delete(item.id);
|
|
10571
10597
|
}
|
|
10572
10598
|
const variantKey = transcriptItemVariantKey(item);
|
|
10573
10599
|
const toolExpanded = toolOutputExpanded ? 1 : 0;
|
|
@@ -11999,7 +12025,7 @@ function useTranscriptWindow({
|
|
|
11999
12025
|
if (!els.has(key)) refCache.delete(key);
|
|
12000
12026
|
}
|
|
12001
12027
|
}
|
|
12002
|
-
if (
|
|
12028
|
+
if (hasStreamingRowStateToPrune()) {
|
|
12003
12029
|
pruneStreamingMeasuredRowsById(new Set(liveItems.keys()));
|
|
12004
12030
|
}
|
|
12005
12031
|
});
|
|
@@ -14269,7 +14295,7 @@ function createOnboardingSteps({
|
|
|
14269
14295
|
}
|
|
14270
14296
|
|
|
14271
14297
|
// src/runtime/shared/config.mjs
|
|
14272
|
-
import { readFileSync as readFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
|
|
14298
|
+
import { readFileSync as readFileSync5, statSync as statSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
|
|
14273
14299
|
import { join as join6, dirname as dirname6 } from "path";
|
|
14274
14300
|
import { createRequire as createRequire2 } from "module";
|
|
14275
14301
|
|
|
@@ -14986,6 +15012,56 @@ var { getSecret: _getSecret, setSecret: _setSecret, deleteSecret: _deleteSecret,
|
|
|
14986
15012
|
var DATA_DIR = resolvePluginData();
|
|
14987
15013
|
var CONFIG_PATH = join6(DATA_DIR, "mixdog-config.json");
|
|
14988
15014
|
var GENERATED_KEY = "_generated";
|
|
15015
|
+
var _configReadCache = null;
|
|
15016
|
+
var CONFIG_READ_TTL_MS = (() => {
|
|
15017
|
+
const n = Number(process.env.MIXDOG_CONFIG_READ_TTL_MS);
|
|
15018
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
15019
|
+
})();
|
|
15020
|
+
function invalidateConfigReadCache() {
|
|
15021
|
+
_configReadCache = null;
|
|
15022
|
+
}
|
|
15023
|
+
function readConfigRawCached() {
|
|
15024
|
+
const now = Date.now();
|
|
15025
|
+
if (_configReadCache) {
|
|
15026
|
+
if (CONFIG_READ_TTL_MS > 0 && now - _configReadCache.atMs < CONFIG_READ_TTL_MS) {
|
|
15027
|
+
return _configReadCache.raw;
|
|
15028
|
+
}
|
|
15029
|
+
try {
|
|
15030
|
+
const st2 = statSync4(CONFIG_PATH);
|
|
15031
|
+
if (st2.mtimeMs === _configReadCache.mtimeMs && st2.size === _configReadCache.size) {
|
|
15032
|
+
_configReadCache.atMs = now;
|
|
15033
|
+
return _configReadCache.raw;
|
|
15034
|
+
}
|
|
15035
|
+
} catch {
|
|
15036
|
+
}
|
|
15037
|
+
}
|
|
15038
|
+
let st = null;
|
|
15039
|
+
try {
|
|
15040
|
+
st = statSync4(CONFIG_PATH);
|
|
15041
|
+
} catch (err) {
|
|
15042
|
+
if (err.code === "ENOENT") {
|
|
15043
|
+
_configReadCache = null;
|
|
15044
|
+
return null;
|
|
15045
|
+
}
|
|
15046
|
+
process.stderr.write(`[config] readConfigRawCached: unexpected stat error for ${CONFIG_PATH}: ${err.message}
|
|
15047
|
+
`);
|
|
15048
|
+
throw err;
|
|
15049
|
+
}
|
|
15050
|
+
let raw;
|
|
15051
|
+
try {
|
|
15052
|
+
raw = readFileSync5(CONFIG_PATH, "utf8");
|
|
15053
|
+
} catch (err) {
|
|
15054
|
+
if (err.code === "ENOENT") {
|
|
15055
|
+
_configReadCache = null;
|
|
15056
|
+
return null;
|
|
15057
|
+
}
|
|
15058
|
+
process.stderr.write(`[config] readJsonFile: unexpected read error for ${CONFIG_PATH}: ${err.message}
|
|
15059
|
+
`);
|
|
15060
|
+
throw err;
|
|
15061
|
+
}
|
|
15062
|
+
_configReadCache = { raw, mtimeMs: st.mtimeMs, size: st.size, atMs: now };
|
|
15063
|
+
return raw;
|
|
15064
|
+
}
|
|
14989
15065
|
function isPlainObject2(value) {
|
|
14990
15066
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
14991
15067
|
}
|
|
@@ -14996,18 +15072,24 @@ function stripGeneratedMarker(data) {
|
|
|
14996
15072
|
}
|
|
14997
15073
|
function readJsonFile(path2) {
|
|
14998
15074
|
let raw;
|
|
14999
|
-
|
|
15000
|
-
raw =
|
|
15001
|
-
}
|
|
15002
|
-
|
|
15003
|
-
|
|
15075
|
+
if (path2 === CONFIG_PATH) {
|
|
15076
|
+
raw = readConfigRawCached();
|
|
15077
|
+
} else {
|
|
15078
|
+
try {
|
|
15079
|
+
raw = readFileSync5(path2, "utf8");
|
|
15080
|
+
} catch (err) {
|
|
15081
|
+
if (err.code === "ENOENT") return null;
|
|
15082
|
+
process.stderr.write(`[config] readJsonFile: unexpected read error for ${path2}: ${err.message}
|
|
15004
15083
|
`);
|
|
15005
|
-
|
|
15084
|
+
throw err;
|
|
15085
|
+
}
|
|
15006
15086
|
}
|
|
15087
|
+
if (raw == null) return null;
|
|
15007
15088
|
try {
|
|
15008
15089
|
return JSON.parse(raw);
|
|
15009
15090
|
} catch (err) {
|
|
15010
15091
|
if (path2 === CONFIG_PATH) {
|
|
15092
|
+
invalidateConfigReadCache();
|
|
15011
15093
|
const corrupt = `${path2}.corrupt-${Date.now()}`;
|
|
15012
15094
|
try {
|
|
15013
15095
|
renameWithRetrySync(path2, corrupt);
|
|
@@ -15029,6 +15111,7 @@ function writeJsonFile(path2, data) {
|
|
|
15029
15111
|
}
|
|
15030
15112
|
writeJsonAtomicSync(path2, data, { lock: false, fsyncDir: true, mode: 384, secret: true });
|
|
15031
15113
|
if (path2 === CONFIG_PATH) {
|
|
15114
|
+
invalidateConfigReadCache();
|
|
15032
15115
|
try {
|
|
15033
15116
|
markUserDataInitialized(DATA_DIR);
|
|
15034
15117
|
} catch {
|
|
@@ -18879,12 +18962,12 @@ function clampFailureCount(errorCount, groupCount, isError) {
|
|
|
18879
18962
|
if (Number.isFinite(explicit)) return Math.max(0, Math.min(groupCount, Math.floor(explicit)));
|
|
18880
18963
|
return isError ? groupCount : 0;
|
|
18881
18964
|
}
|
|
18882
|
-
function toolStatusColor({ pending, groupCount,
|
|
18965
|
+
function toolStatusColor({ pending, groupCount, callFailedCount = 0, terminalStatus = "" }) {
|
|
18883
18966
|
if (pending) return theme.text;
|
|
18884
18967
|
const status = normalizeTerminalStatus(terminalStatus);
|
|
18885
18968
|
if (status === "cancelled") return theme.warning;
|
|
18886
|
-
if (
|
|
18887
|
-
if (groupCount > 1 &&
|
|
18969
|
+
if (callFailedCount <= 0) return theme.success;
|
|
18970
|
+
if (groupCount > 1 && callFailedCount < groupCount) return theme.mixdogOrange || theme.warning;
|
|
18888
18971
|
return theme.error;
|
|
18889
18972
|
}
|
|
18890
18973
|
|
|
@@ -18911,13 +18994,12 @@ function ResultBody({ lines, rawText, pathArg = "", isShell = false, columns, co
|
|
|
18911
18994
|
// src/tui/components/ToolExecution.jsx
|
|
18912
18995
|
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
18913
18996
|
var TOOL_BLINK_MS = 500;
|
|
18914
|
-
var TOOL_BLINK_LIMIT_MS = 3e3;
|
|
18915
18997
|
var TOOL_PENDING_SHOW_DELAY_MS = 1e3;
|
|
18916
18998
|
var TOOL_ANIM_TICK_MS = TOOL_BLINK_MS;
|
|
18917
18999
|
function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
|
|
18918
19000
|
return formatToolActionHeader(name, args, { pending, count });
|
|
18919
19001
|
}
|
|
18920
|
-
function ToolExecution({ name, args, result, rawResult, isError, errorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, doneCategories = null, headerFinalized = true, deferredDisplayReady = false }) {
|
|
19002
|
+
function ToolExecution({ name, args, result, rawResult, isError, errorCount, callErrorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, doneCategories = null, headerFinalized = true, deferredDisplayReady = false }) {
|
|
18921
19003
|
const rowWidth = Math.max(1, Number(columns || 80));
|
|
18922
19004
|
const groupCount = Math.max(1, Number(count || 1));
|
|
18923
19005
|
const doneCount = Math.max(0, Math.min(groupCount, Number(completedCount || (result == null ? 0 : groupCount))));
|
|
@@ -18933,14 +19015,14 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18933
19015
|
const hasVisibleProgress = doneCount > 0 || Boolean(String(rt || "").trim());
|
|
18934
19016
|
const pendingDisplayReady = !pending || !startedAtMs || pendingDelayElapsed || pendingAgeMs >= TOOL_PENDING_SHOW_DELAY_MS || hasVisibleProgress || deferredDisplayReady;
|
|
18935
19017
|
const blinkActive = pending && pendingDisplayReady;
|
|
18936
|
-
const
|
|
18937
|
-
const blinkOn = !blinkActive || blinkExpired ? true : Math.floor(nowMs / TOOL_BLINK_MS) % 2 === 0;
|
|
19018
|
+
const blinkOn = !blinkActive ? true : Math.floor(nowMs / TOOL_BLINK_MS) % 2 === 0;
|
|
18938
19019
|
const headerPending = pending || headerFinalized === false;
|
|
18939
19020
|
const hasResult = result != null && Boolean(String(rt || "").trim());
|
|
18940
19021
|
const hasRawResult = rawResult != null && Boolean(String(rawRt || "").trim());
|
|
18941
19022
|
const elapsedMs = startedAtMs ? Math.max(0, (pending ? nowMs : completedAtMs || nowMs) - startedAtMs) : 0;
|
|
18942
19023
|
const elapsed = elapsedMs >= 1e3 ? formatElapsed(elapsedMs) : "";
|
|
18943
19024
|
const failedCount = clampFailureCount(errorCount, groupCount, isError);
|
|
19025
|
+
const callFailedCount = clampFailureCount(callErrorCount, groupCount, false);
|
|
18944
19026
|
const displayGroupCount = groupCount;
|
|
18945
19027
|
const displayCategories = normalizeCountMap(categories || {});
|
|
18946
19028
|
const normalizedDoneCategories = doneCategories ? normalizeCountMap(doneCategories) : displayCategories;
|
|
@@ -18966,8 +19048,8 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18966
19048
|
detailText = "";
|
|
18967
19049
|
}
|
|
18968
19050
|
const aggregateTerminalStatus = pending ? "running" : resultTerminalStatus(rt) || (isError || failedCount > 0 ? "failed" : "completed");
|
|
18969
|
-
const dotColor2 = toolStatusColor({ pending, groupCount,
|
|
18970
|
-
const dotText2 = pending && !
|
|
19051
|
+
const dotColor2 = toolStatusColor({ pending, groupCount, callFailedCount, terminalStatus: aggregateTerminalStatus });
|
|
19052
|
+
const dotText2 = pending && !blinkOn ? " " : TURN_MARKER;
|
|
18971
19053
|
const gutter2 = 2;
|
|
18972
19054
|
const showHeaderExpandHint2 = hasRawResult;
|
|
18973
19055
|
const hintLabel2 = `ctrl+o ${expanded ? "collapse" : "expand"}`;
|
|
@@ -19066,12 +19148,12 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
19066
19148
|
const keepAgentDetail = (isError || agentFailureText) && !(agentHeaderFailure && !agentSurfaceBrief);
|
|
19067
19149
|
visibleDetailLines = keepAgentDetail ? [agentDetailLine] : [];
|
|
19068
19150
|
}
|
|
19069
|
-
const finalStatusColor = toolStatusColor({ pending, groupCount,
|
|
19151
|
+
const finalStatusColor = toolStatusColor({ pending, groupCount, callFailedCount, terminalStatus });
|
|
19070
19152
|
const dotColor = finalStatusColor;
|
|
19071
19153
|
const markerGlyph = isAgentResponse ? AGENT_RESPONSE_MARKER : isAgentSurfaceCard ? AGENT_CALL_MARKER : TURN_MARKER;
|
|
19072
19154
|
const isDirectionalMarker = isAgentResponse || isAgentSurfaceCard;
|
|
19073
19155
|
const markerText = isDirectionalMarker ? `${markerGlyph} ` : markerGlyph;
|
|
19074
|
-
const dotText = pending && !
|
|
19156
|
+
const dotText = pending && !blinkOn ? " " : markerText;
|
|
19075
19157
|
let labelText;
|
|
19076
19158
|
if (isAgentResponse) labelText = agentResponseTitle(parsedArgs);
|
|
19077
19159
|
else if (isBackgroundResponse) labelText = backgroundTaskResultTitle(normalizedName, backgroundMeta || parsedArgs);
|
|
@@ -19282,7 +19364,7 @@ var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpa
|
|
|
19282
19364
|
node = /* @__PURE__ */ jsx19(ToolHookDenialCard, { item, columns });
|
|
19283
19365
|
break;
|
|
19284
19366
|
}
|
|
19285
|
-
node = /* @__PURE__ */ jsx19(ToolExecution, { name: item.name, args: item.args, result: item.result, rawResult: item.rawResult, isError: item.isError, errorCount: item.errorCount, expanded: toolOutputExpanded || item.expanded, columns, attached: false, count: item.count, completedCount: item.completedCount, startedAt: item.startedAt, completedAt: item.completedAt, aggregate: item.aggregate, categories: item.categories, doneCategories: item.doneCategories, headerFinalized: item.headerFinalized, deferredDisplayReady: item.deferredDisplayReady
|
|
19367
|
+
node = /* @__PURE__ */ jsx19(ToolExecution, { name: item.name, args: item.args, result: item.result, rawResult: item.rawResult, isError: item.isError, errorCount: item.errorCount, callErrorCount: item.callErrorCount, expanded: toolOutputExpanded || item.expanded, columns, attached: false, count: item.count, completedCount: item.completedCount, startedAt: item.startedAt, completedAt: item.completedAt, aggregate: item.aggregate, categories: item.categories, doneCategories: item.doneCategories, headerFinalized: item.headerFinalized, deferredDisplayReady: item.deferredDisplayReady });
|
|
19286
19368
|
break;
|
|
19287
19369
|
}
|
|
19288
19370
|
case "notice":
|
|
@@ -20339,7 +20421,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20339
20421
|
const n = Number(value || 0);
|
|
20340
20422
|
const d = Number(total || 0);
|
|
20341
20423
|
if (!d) return "N/A";
|
|
20342
|
-
|
|
20424
|
+
const p = Math.max(0, Math.min(100, n / d * 100));
|
|
20425
|
+
return `${p > 0 && p < 1 ? p.toFixed(1) : Math.floor(p)}%`;
|
|
20343
20426
|
};
|
|
20344
20427
|
const fmt = (value) => {
|
|
20345
20428
|
const n = Number(value || 0);
|
|
@@ -20941,11 +21024,11 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20941
21024
|
}
|
|
20942
21025
|
}
|
|
20943
21026
|
if (!commandText) return false;
|
|
20944
|
-
if (state.commandBusy) {
|
|
20945
|
-
store.pushNotice("wait for the current command to finish", "warn");
|
|
20946
|
-
return false;
|
|
20947
|
-
}
|
|
20948
21027
|
if (commandText.startsWith("/")) {
|
|
21028
|
+
if (state.commandBusy) {
|
|
21029
|
+
store.pushNotice("wait for the current command to finish", "warn");
|
|
21030
|
+
return false;
|
|
21031
|
+
}
|
|
20949
21032
|
const [cmd, ...rest] = commandText.slice(1).split(/\s+/);
|
|
20950
21033
|
const accepted2 = runSlashCommand(cmd, rest.join(" ").trim());
|
|
20951
21034
|
if (accepted2 !== false) clearPastedImagesSnapshot();
|
|
@@ -22702,7 +22785,7 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
22702
22785
|
if (meta.kind === "update-notice") {
|
|
22703
22786
|
const ver = String(meta.version || "").trim();
|
|
22704
22787
|
const displayText = ver ? `mixdog v${ver} ready \u2014 restart to apply.` : trimmed;
|
|
22705
|
-
return { action: "notice", displayText, tone: meta.tone === "warn" ? "warn" : "info" };
|
|
22788
|
+
return { action: "notice", displayText, tone: meta.tone === "warn" ? "warn" : "info", transcript: false };
|
|
22706
22789
|
}
|
|
22707
22790
|
if (!isExecutionNotification(event, trimmed, parsed)) {
|
|
22708
22791
|
return { action: "enqueue", displayText: trimmed, modelContent: trimmed };
|
|
@@ -22719,9 +22802,68 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
22719
22802
|
}
|
|
22720
22803
|
|
|
22721
22804
|
// src/tui/engine/render-timing.mjs
|
|
22722
|
-
var
|
|
22723
|
-
var
|
|
22724
|
-
|
|
22805
|
+
var RENDER_ACK_HANG_GUARD_MS = 250;
|
|
22806
|
+
var RENDER_SETTLE_IDLE_MS = 64;
|
|
22807
|
+
var pendingRenderAcks = [];
|
|
22808
|
+
var renderAckSeq = 0;
|
|
22809
|
+
var scheduleRenderFrameAck = () => {
|
|
22810
|
+
const seq = ++renderAckSeq;
|
|
22811
|
+
setImmediate(() => notifyRenderFrame(seq));
|
|
22812
|
+
};
|
|
22813
|
+
var notifyRenderFrame = (seq = ++renderAckSeq) => {
|
|
22814
|
+
if (pendingRenderAcks.length === 0) return;
|
|
22815
|
+
const acks = pendingRenderAcks;
|
|
22816
|
+
pendingRenderAcks = [];
|
|
22817
|
+
for (const ack of acks) ack(seq);
|
|
22818
|
+
};
|
|
22819
|
+
var yieldToRenderer = ({ frames = 1 } = {}) => new Promise((resolve5) => {
|
|
22820
|
+
const minSeq = renderAckSeq;
|
|
22821
|
+
let remainingFrames = Math.max(1, Math.floor(Number(frames) || 1));
|
|
22822
|
+
let settled = false;
|
|
22823
|
+
let sawRealFrame = false;
|
|
22824
|
+
let timer2 = null;
|
|
22825
|
+
const finish = () => {
|
|
22826
|
+
if (settled) return;
|
|
22827
|
+
settled = true;
|
|
22828
|
+
if (timer2) clearTimeout(timer2);
|
|
22829
|
+
const idx = pendingRenderAcks.indexOf(onFrame);
|
|
22830
|
+
if (idx !== -1) pendingRenderAcks.splice(idx, 1);
|
|
22831
|
+
resolve5();
|
|
22832
|
+
};
|
|
22833
|
+
const onTimeout = () => {
|
|
22834
|
+
if (settled) return;
|
|
22835
|
+
if (!sawRealFrame && renderAckSeq > minSeq) {
|
|
22836
|
+
setImmediate(() => {
|
|
22837
|
+
if (!settled && !sawRealFrame) finish();
|
|
22838
|
+
});
|
|
22839
|
+
return;
|
|
22840
|
+
}
|
|
22841
|
+
finish();
|
|
22842
|
+
};
|
|
22843
|
+
const armWait = () => {
|
|
22844
|
+
if (settled) return;
|
|
22845
|
+
if (!pendingRenderAcks.includes(onFrame)) pendingRenderAcks.push(onFrame);
|
|
22846
|
+
if (timer2) clearTimeout(timer2);
|
|
22847
|
+
timer2 = setTimeout(onTimeout, sawRealFrame ? RENDER_SETTLE_IDLE_MS : RENDER_ACK_HANG_GUARD_MS);
|
|
22848
|
+
};
|
|
22849
|
+
const onFrame = (seq = 0) => {
|
|
22850
|
+
if (settled) return;
|
|
22851
|
+
if (seq <= minSeq) {
|
|
22852
|
+
if (!pendingRenderAcks.includes(onFrame)) pendingRenderAcks.push(onFrame);
|
|
22853
|
+
return;
|
|
22854
|
+
}
|
|
22855
|
+
sawRealFrame = true;
|
|
22856
|
+
if (timer2) {
|
|
22857
|
+
clearTimeout(timer2);
|
|
22858
|
+
timer2 = null;
|
|
22859
|
+
}
|
|
22860
|
+
const idx = pendingRenderAcks.indexOf(onFrame);
|
|
22861
|
+
if (idx !== -1) pendingRenderAcks.splice(idx, 1);
|
|
22862
|
+
remainingFrames -= 1;
|
|
22863
|
+
if (remainingFrames <= 0) finish();
|
|
22864
|
+
else armWait();
|
|
22865
|
+
};
|
|
22866
|
+
armWait();
|
|
22725
22867
|
});
|
|
22726
22868
|
|
|
22727
22869
|
// src/tui/engine/tool-result-status.mjs
|
|
@@ -22920,6 +23062,7 @@ function createToolCardResults({
|
|
|
22920
23062
|
patchItem,
|
|
22921
23063
|
markToolCallDone,
|
|
22922
23064
|
updateAgentJobCard,
|
|
23065
|
+
buildAgentJobCardPatch,
|
|
22923
23066
|
agentStatusState
|
|
22924
23067
|
}) {
|
|
22925
23068
|
function patchToolItem(id, patch) {
|
|
@@ -22940,7 +23083,9 @@ function createToolCardResults({
|
|
|
22940
23083
|
const aggregate = card.aggregate;
|
|
22941
23084
|
const callRec = aggregate && callId ? aggregate.calls.get(callId) : null;
|
|
22942
23085
|
const isMemoryCall = classifyToolCategory(callRec?.name || card?.name || "", callRec?.args || {}) === "Memory";
|
|
22943
|
-
const
|
|
23086
|
+
const isCallError = message?.isError === true || message?.toolKind === "error";
|
|
23087
|
+
const isResultError = /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText)) || isMemoryCall && memoryCoreResultErrorText(rawText) != null;
|
|
23088
|
+
const isError = isCallError || isResultError;
|
|
22944
23089
|
const text = isError ? toolErrorDisplay(rawText, card?.name || "tool") : rawText;
|
|
22945
23090
|
if (aggregate && card.itemId === aggregate.itemId) {
|
|
22946
23091
|
if (!callRec) return false;
|
|
@@ -22952,11 +23097,13 @@ function createToolCardResults({
|
|
|
22952
23097
|
callRec.summary = !isError ? summarizeToolResult(callRec.name, callRec.args, rawText, isError) : null;
|
|
22953
23098
|
assignAggregateSummaryOrder(aggregate, callRec);
|
|
22954
23099
|
callRec.isError = isError;
|
|
23100
|
+
callRec.isCallError = isCallError;
|
|
22955
23101
|
callRec.resultText = text;
|
|
22956
23102
|
callRec.resolved = true;
|
|
22957
23103
|
const allCalls = [...aggregate.calls.values()];
|
|
22958
23104
|
const completed = allCalls.filter((r) => r.resolved).length;
|
|
22959
23105
|
const errors = allCalls.filter((r) => r.isError).length;
|
|
23106
|
+
const callErrors = allCalls.filter((r) => r.isCallError).length;
|
|
22960
23107
|
const succeeded = completed - errors;
|
|
22961
23108
|
const detailText = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
|
|
22962
23109
|
const currentItem = getState().items.find((it) => it.id === card.itemId);
|
|
@@ -22970,6 +23117,7 @@ function createToolCardResults({
|
|
|
22970
23117
|
rawResult: rawResult || null,
|
|
22971
23118
|
isError: errors > 0,
|
|
22972
23119
|
errorCount: errors,
|
|
23120
|
+
callErrorCount: callErrors,
|
|
22973
23121
|
count: allCalls.length,
|
|
22974
23122
|
completedCount: visualCompleted,
|
|
22975
23123
|
doneCategories: aggregateDoneCategories(allCalls),
|
|
@@ -22979,9 +23127,10 @@ function createToolCardResults({
|
|
|
22979
23127
|
if (callId) done.add(callId);
|
|
22980
23128
|
return true;
|
|
22981
23129
|
}
|
|
22982
|
-
const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, results: [] };
|
|
23130
|
+
const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, callErrors: 0, results: [] };
|
|
22983
23131
|
group.completed = Math.min(group.count, group.completed + 1);
|
|
22984
23132
|
group.errors += isError ? 1 : 0;
|
|
23133
|
+
group.callErrors = (group.callErrors || 0) + (isCallError ? 1 : 0);
|
|
22985
23134
|
group.results.push({ text, isError });
|
|
22986
23135
|
toolGroups.set(card.itemId, group);
|
|
22987
23136
|
const resultText = groupedToolResultText(group);
|
|
@@ -22991,6 +23140,7 @@ function createToolCardResults({
|
|
|
22991
23140
|
text: displayResult,
|
|
22992
23141
|
isError: group.errors > 0,
|
|
22993
23142
|
errorCount: group.errors,
|
|
23143
|
+
callErrorCount: group.callErrors || 0,
|
|
22994
23144
|
count: group.count,
|
|
22995
23145
|
completedCount: group.completed,
|
|
22996
23146
|
completedAt: Date.now()
|
|
@@ -23000,19 +23150,11 @@ function createToolCardResults({
|
|
|
23000
23150
|
if (body) patch.rawResult = text || rawText;
|
|
23001
23151
|
const parsedAgent = parseAgentJob(rawText);
|
|
23002
23152
|
if (parsedAgent) {
|
|
23003
|
-
patch.args = agentArgsWithResultMetadata(getState().items.find((it) => it.id === card.itemId)?.args, parsedAgent);
|
|
23004
23153
|
set(agentStatusState({ force: true }));
|
|
23005
23154
|
}
|
|
23155
|
+
Object.assign(patch, buildAgentJobCardPatch(card.itemId, rawText, isError));
|
|
23006
23156
|
}
|
|
23007
23157
|
patchToolItem(card.itemId, patch);
|
|
23008
|
-
if (group.count <= 1) {
|
|
23009
|
-
const beforeAgent = getState().items.find((it) => it.id === card.itemId);
|
|
23010
|
-
updateAgentJobCard(card.itemId, rawText, isError);
|
|
23011
|
-
const afterAgent = getState().items.find((it) => it.id === card.itemId);
|
|
23012
|
-
if (afterAgent && beforeAgent && afterAgent !== beforeAgent) {
|
|
23013
|
-
carryTranscriptMeasuredRowsCache(beforeAgent, afterAgent);
|
|
23014
|
-
}
|
|
23015
|
-
}
|
|
23016
23158
|
card.done = true;
|
|
23017
23159
|
if (callId) done.add(callId);
|
|
23018
23160
|
return true;
|
|
@@ -23060,6 +23202,7 @@ function createToolCardResults({
|
|
|
23060
23202
|
const completed = allCalls.filter((r) => r.resolved).length;
|
|
23061
23203
|
const totalCompleted = completed;
|
|
23062
23204
|
const errors = allCalls.filter((r) => r.isError).length;
|
|
23205
|
+
const callErrors = allCalls.filter((r) => r.isCallError).length;
|
|
23063
23206
|
const succeeded = completed - errors;
|
|
23064
23207
|
const rawResult = aggregateRawResult(allCalls);
|
|
23065
23208
|
let displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
|
|
@@ -23073,6 +23216,7 @@ function createToolCardResults({
|
|
|
23073
23216
|
rawResult: rawResult || null,
|
|
23074
23217
|
isError: errors > 0,
|
|
23075
23218
|
errorCount: errors,
|
|
23219
|
+
callErrorCount: callErrors,
|
|
23076
23220
|
count: allCalls.length,
|
|
23077
23221
|
completedCount: totalCompleted,
|
|
23078
23222
|
doneCategories: aggregateDoneCategories(allCalls),
|
|
@@ -23093,7 +23237,7 @@ function createToolCardResults({
|
|
|
23093
23237
|
const currentItem = getState().items.find((it) => it.id === card.itemId);
|
|
23094
23238
|
resultText = withCancelledResultMarker(resultText, currentItem);
|
|
23095
23239
|
}
|
|
23096
|
-
patchToolItem(card.itemId, { result: resultText, text: resultText, isError: group.errors > 0, errorCount: group.errors, count: group.count, completedCount: group.completed, completedAt: Date.now() });
|
|
23240
|
+
patchToolItem(card.itemId, { result: resultText, text: resultText, isError: group.errors > 0, errorCount: group.errors, callErrorCount: group.callErrors || 0, count: group.count, completedCount: group.completed, completedAt: Date.now() });
|
|
23097
23241
|
card.done = true;
|
|
23098
23242
|
if (card.callId) done.add(card.callId);
|
|
23099
23243
|
}
|
|
@@ -23153,18 +23297,21 @@ function createAgentJobFeed({
|
|
|
23153
23297
|
function scheduleExecutionPendingResumeKick(body = "") {
|
|
23154
23298
|
queueMicrotask(() => kickExecutionPendingResume(body));
|
|
23155
23299
|
}
|
|
23156
|
-
function
|
|
23300
|
+
function buildAgentJobCardPatch(itemId, text, isError = false) {
|
|
23157
23301
|
const parsed = parseAgentJob(text);
|
|
23158
23302
|
const current = getState().items.find((it) => it.id === itemId);
|
|
23159
23303
|
const rawDisplayText = agentJobResultText(text, parsed) || String(text ?? "").trim();
|
|
23160
23304
|
const displayText = isError ? toolErrorDisplay(rawDisplayText, "agent") : rawDisplayText;
|
|
23161
|
-
|
|
23305
|
+
return {
|
|
23162
23306
|
result: displayText,
|
|
23163
23307
|
text: displayText,
|
|
23164
23308
|
isError,
|
|
23165
23309
|
errorCount: isError ? 1 : 0,
|
|
23166
23310
|
...parsed ? { args: agentArgsWithResultMetadata(current?.args, parsed) } : {}
|
|
23167
|
-
}
|
|
23311
|
+
};
|
|
23312
|
+
}
|
|
23313
|
+
function updateAgentJobCard(itemId, text, isError = false) {
|
|
23314
|
+
patchItem(itemId, buildAgentJobCardPatch(itemId, text, isError));
|
|
23168
23315
|
}
|
|
23169
23316
|
function subscribeRuntimeNotifications() {
|
|
23170
23317
|
if (typeof runtime.onNotification !== "function") return null;
|
|
@@ -23177,7 +23324,7 @@ function createAgentJobFeed({
|
|
|
23177
23324
|
const delivery = resolveTuiRuntimeNotificationDelivery(event, text);
|
|
23178
23325
|
if (delivery.action === "ignore") return;
|
|
23179
23326
|
if (delivery.action === "notice") {
|
|
23180
|
-
pushNotice?.(delivery.displayText, delivery.tone || "info", { transcript: true });
|
|
23327
|
+
pushNotice?.(delivery.displayText, delivery.tone || "info", { transcript: delivery.transcript === true });
|
|
23181
23328
|
return true;
|
|
23182
23329
|
}
|
|
23183
23330
|
if (delivery.action === "status-only") {
|
|
@@ -23193,7 +23340,15 @@ function createAgentJobFeed({
|
|
|
23193
23340
|
if (parsed?.taskId) set(agentStatusState({ force: true }));
|
|
23194
23341
|
const resumeBody = String(delivery.modelContent || "").trim();
|
|
23195
23342
|
if (resumeBody) {
|
|
23196
|
-
|
|
23343
|
+
enqueue(resumeBody, {
|
|
23344
|
+
mode: "task-notification",
|
|
23345
|
+
// Claude Code parity: live execution completions are queued as
|
|
23346
|
+
// task notifications so the active loop can attach them after the
|
|
23347
|
+
// next tool batch; no special pending-resume bypass.
|
|
23348
|
+
priority: "next",
|
|
23349
|
+
key: notificationKey || void 0,
|
|
23350
|
+
displayText: delivery.displayText || text
|
|
23351
|
+
});
|
|
23197
23352
|
}
|
|
23198
23353
|
return true;
|
|
23199
23354
|
}
|
|
@@ -23205,7 +23360,9 @@ function createAgentJobFeed({
|
|
|
23205
23360
|
if (!modelContent && imagePaths.length === 0) return true;
|
|
23206
23361
|
const enqueueOpts = {
|
|
23207
23362
|
mode: "task-notification",
|
|
23208
|
-
|
|
23363
|
+
// Claude Code parity: task/schedule notifications are lower-priority
|
|
23364
|
+
// queue items and drain between turns, behind direct user input.
|
|
23365
|
+
priority: "later",
|
|
23209
23366
|
key: notificationKey || void 0,
|
|
23210
23367
|
displayText: delivery.displayText || text
|
|
23211
23368
|
};
|
|
@@ -23241,6 +23398,7 @@ function createAgentJobFeed({
|
|
|
23241
23398
|
flushDeferredExecutionPendingResumeKick,
|
|
23242
23399
|
scheduleExecutionPendingResumeKick,
|
|
23243
23400
|
updateAgentJobCard,
|
|
23401
|
+
buildAgentJobCardPatch,
|
|
23244
23402
|
subscribeRuntimeNotifications
|
|
23245
23403
|
};
|
|
23246
23404
|
}
|
|
@@ -23461,11 +23619,13 @@ function createContextState({ runtime, getState, getPendingSessionReset }) {
|
|
|
23461
23619
|
});
|
|
23462
23620
|
const routeState = () => {
|
|
23463
23621
|
const state = getState();
|
|
23622
|
+
const base = baseRouteState();
|
|
23623
|
+
const sameContextRoute = state.sessionId === base.sessionId && state.clientHostPid === base.clientHostPid && state.provider === base.provider && state.model === base.model && state.effort === base.effort && state.fast === base.fast && state.contextWindow === base.contextWindow && state.rawContextWindow === base.rawContextWindow;
|
|
23464
23624
|
return {
|
|
23465
|
-
...
|
|
23466
|
-
displayContextWindow: state.displayContextWindow || 0,
|
|
23467
|
-
compactBoundaryTokens: state.compactBoundaryTokens || 0,
|
|
23468
|
-
autoCompactTokenLimit: state.autoCompactTokenLimit || 0
|
|
23625
|
+
...base,
|
|
23626
|
+
displayContextWindow: sameContextRoute ? state.displayContextWindow || 0 : 0,
|
|
23627
|
+
compactBoundaryTokens: sameContextRoute ? state.compactBoundaryTokens || 0 : 0,
|
|
23628
|
+
autoCompactTokenLimit: sameContextRoute ? state.autoCompactTokenLimit || 0 : 0
|
|
23469
23629
|
};
|
|
23470
23630
|
};
|
|
23471
23631
|
function syncContextDisplayFields(ctx = null) {
|
|
@@ -23617,9 +23777,6 @@ function createSessionFlow(bag) {
|
|
|
23617
23777
|
}
|
|
23618
23778
|
function removeQueuedEntries(entries) {
|
|
23619
23779
|
const ids = new Set(entries.map((entry) => entry.id));
|
|
23620
|
-
const keys = entries.map((entry) => entry.key).filter(Boolean);
|
|
23621
|
-
for (const key of keys) pendingNotificationKeys.delete(key);
|
|
23622
|
-
for (const key of keys) displayedExecutionNotificationKeys.delete(key);
|
|
23623
23780
|
const queued = getState().queued.filter((q) => !ids.has(q.id));
|
|
23624
23781
|
if (queued.length !== getState().queued.length) set({ queued });
|
|
23625
23782
|
}
|
|
@@ -23632,8 +23789,9 @@ function createSessionFlow(bag) {
|
|
|
23632
23789
|
displayText: entry.displayText || (entry.mode === "task-notification" ? notificationDisplayText(entry.text) : String(entry.text || ""))
|
|
23633
23790
|
};
|
|
23634
23791
|
if (next.mode === "task-notification" && next.key) {
|
|
23635
|
-
|
|
23636
|
-
|
|
23792
|
+
const duplicateQueued = pending.some((entry2) => entry2?.mode === "task-notification" && entry2?.key === next.key);
|
|
23793
|
+
if (duplicateQueued) continue;
|
|
23794
|
+
if (!pendingNotificationKeys.has(next.key)) pendingNotificationKeys.add(next.key);
|
|
23637
23795
|
}
|
|
23638
23796
|
restored.push(next);
|
|
23639
23797
|
}
|
|
@@ -23674,14 +23832,49 @@ function createSessionFlow(bag) {
|
|
|
23674
23832
|
removeQueuedEntries(batch);
|
|
23675
23833
|
return batch;
|
|
23676
23834
|
}
|
|
23835
|
+
function scheduleBlockedDrainRetry() {
|
|
23836
|
+
if (pending.length === 0) return;
|
|
23837
|
+
if (flags.blockedDrainRetryTimer) return;
|
|
23838
|
+
const timer2 = setTimeout(() => {
|
|
23839
|
+
flags.blockedDrainRetryTimer = null;
|
|
23840
|
+
if (pending.length > 0) void drain();
|
|
23841
|
+
}, 50);
|
|
23842
|
+
if (typeof timer2.unref === "function") timer2.unref();
|
|
23843
|
+
flags.blockedDrainRetryTimer = timer2;
|
|
23844
|
+
}
|
|
23845
|
+
function clearBlockedDrainRetry() {
|
|
23846
|
+
if (!flags.blockedDrainRetryTimer) return;
|
|
23847
|
+
clearTimeout(flags.blockedDrainRetryTimer);
|
|
23848
|
+
flags.blockedDrainRetryTimer = null;
|
|
23849
|
+
}
|
|
23850
|
+
function hasModelDrainablePending() {
|
|
23851
|
+
return pending.some((entry) => !isSlashQueuedEntry(entry));
|
|
23852
|
+
}
|
|
23677
23853
|
async function drain() {
|
|
23678
23854
|
if (flags.draining) return;
|
|
23679
|
-
if (flags.autoClearRunning)
|
|
23855
|
+
if (flags.autoClearRunning || getState().commandBusy) {
|
|
23856
|
+
scheduleBlockedDrainRetry();
|
|
23857
|
+
return;
|
|
23858
|
+
}
|
|
23859
|
+
if (getState().busy) {
|
|
23860
|
+
tuiDebug2(`busy-queue drain deferred while active pending=${pending.length}`);
|
|
23861
|
+
return;
|
|
23862
|
+
}
|
|
23863
|
+
clearBlockedDrainRetry();
|
|
23864
|
+
const drainEpoch = (Number(flags.drainEpoch) || 0) + 1;
|
|
23865
|
+
flags.drainEpoch = drainEpoch;
|
|
23680
23866
|
flags.draining = true;
|
|
23681
23867
|
let firstBatch = true;
|
|
23682
23868
|
try {
|
|
23683
23869
|
while (pending.length > 0) {
|
|
23684
|
-
|
|
23870
|
+
if (flags.drainEpoch !== drainEpoch) return;
|
|
23871
|
+
const batch = dequeueQueueBatch("later", {
|
|
23872
|
+
limit: firstBatch ? 1 : Infinity,
|
|
23873
|
+
// Slash commands must run through the TUI command dispatcher, not be
|
|
23874
|
+
// delivered to the model as plain text. Claude Code's queueProcessor
|
|
23875
|
+
// similarly handles slash entries outside the queued_command drain.
|
|
23876
|
+
predicate: (entry) => !isSlashQueuedEntry(entry)
|
|
23877
|
+
});
|
|
23685
23878
|
firstBatch = false;
|
|
23686
23879
|
if (batch.length === 0) break;
|
|
23687
23880
|
tuiDebug2(`busy-queue drain batch=${batch.length} remaining=${pending.length}`);
|
|
@@ -23703,6 +23896,7 @@ function createSessionFlow(bag) {
|
|
|
23703
23896
|
restorable: nonEditable.length === 0,
|
|
23704
23897
|
requeueOnAbort: nonEditable
|
|
23705
23898
|
});
|
|
23899
|
+
if (flags.drainEpoch !== drainEpoch) return;
|
|
23706
23900
|
flushDeferredClearedSessionUi();
|
|
23707
23901
|
const scheduledReset = runtime.consumePendingSessionReset?.();
|
|
23708
23902
|
if ((scheduledReset === "clear" || scheduledReset === "compact_clear") && turnStatus !== "cancelled" && !getState().commandBusy) {
|
|
@@ -23717,10 +23911,12 @@ function createSessionFlow(bag) {
|
|
|
23717
23911
|
if (turnStatus === "cancelled" && pending.length === 0) break;
|
|
23718
23912
|
}
|
|
23719
23913
|
} finally {
|
|
23720
|
-
flags.
|
|
23721
|
-
|
|
23722
|
-
|
|
23723
|
-
|
|
23914
|
+
if (flags.drainEpoch === drainEpoch) {
|
|
23915
|
+
flags.draining = false;
|
|
23916
|
+
flushDeferredClearedSessionUi();
|
|
23917
|
+
if (hasModelDrainablePending()) void drain();
|
|
23918
|
+
else flushDeferredExecutionPendingResumeKick();
|
|
23919
|
+
}
|
|
23724
23920
|
}
|
|
23725
23921
|
}
|
|
23726
23922
|
function enqueue(text, options = {}) {
|
|
@@ -23738,11 +23934,17 @@ function createSessionFlow(bag) {
|
|
|
23738
23934
|
void drain();
|
|
23739
23935
|
return true;
|
|
23740
23936
|
}
|
|
23741
|
-
function drainPendingSteering() {
|
|
23742
|
-
const
|
|
23937
|
+
function drainPendingSteering(_sessionIdOrOptions = null, maybeOptions = null) {
|
|
23938
|
+
const options = maybeOptions && typeof maybeOptions === "object" ? maybeOptions : _sessionIdOrOptions && typeof _sessionIdOrOptions === "object" ? _sessionIdOrOptions : {};
|
|
23939
|
+
const maxPriority = options.maxPriority || "next";
|
|
23940
|
+
const predicate = (entry) => {
|
|
23941
|
+
if (isSlashQueuedEntry(entry)) return false;
|
|
23942
|
+
const mode = entry?.mode || "prompt";
|
|
23943
|
+
return mode === "prompt" || mode === "task-notification";
|
|
23944
|
+
};
|
|
23743
23945
|
const out = [];
|
|
23744
23946
|
for (; ; ) {
|
|
23745
|
-
const batch = dequeueQueueBatch(
|
|
23947
|
+
const batch = dequeueQueueBatch(maxPriority, { predicate });
|
|
23746
23948
|
if (batch.length === 0) break;
|
|
23747
23949
|
for (const entry of batch) {
|
|
23748
23950
|
const content = entry.content;
|
|
@@ -24019,6 +24221,7 @@ function createRunTurn(bag) {
|
|
|
24019
24221
|
const turnIndex = getState().stats.turns || 0;
|
|
24020
24222
|
const startedAt = Date.now();
|
|
24021
24223
|
const turnEpoch = ++flags.leadTurnEpoch;
|
|
24224
|
+
const isCurrentTurn = () => !flags.disposed && flags.leadTurnEpoch === turnEpoch;
|
|
24022
24225
|
const inputBaseline = getState().stats.inputTokens;
|
|
24023
24226
|
const outputBaseline = getState().stats.outputTokens;
|
|
24024
24227
|
const submittedIds = Array.isArray(options.submittedIds) ? options.submittedIds : [];
|
|
@@ -24042,6 +24245,8 @@ function createRunTurn(bag) {
|
|
|
24042
24245
|
let watchdogTripped = false;
|
|
24043
24246
|
let watchdogTimer = null;
|
|
24044
24247
|
let watchdogGraceTimer = null;
|
|
24248
|
+
let lastProgressAt = startedAt;
|
|
24249
|
+
let lastProgressLabel = "start";
|
|
24045
24250
|
const clearWatchdog = () => {
|
|
24046
24251
|
if (watchdogTimer) {
|
|
24047
24252
|
clearTimeout(watchdogTimer);
|
|
@@ -24052,31 +24257,67 @@ function createRunTurn(bag) {
|
|
|
24052
24257
|
watchdogGraceTimer = null;
|
|
24053
24258
|
}
|
|
24054
24259
|
};
|
|
24055
|
-
|
|
24056
|
-
if (
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
tuiDebug2(`runTurn WATCHDOG TRIP turn=${turnIndex} elapsedMs=${elapsed} \u2014 aborting stuck turn`);
|
|
24060
|
-
pushNotice(`Turn timed out after ${Math.round(elapsed / 1e3)}s \u2014 aborting stuck request. Input is available again.`, "warn", { transcript: true });
|
|
24061
|
-
try {
|
|
24062
|
-
runtime.abort("cli-react-abort-watchdog");
|
|
24063
|
-
} catch {
|
|
24260
|
+
const armWatchdog = () => {
|
|
24261
|
+
if (watchdogTimer) {
|
|
24262
|
+
clearTimeout(watchdogTimer);
|
|
24263
|
+
watchdogTimer = null;
|
|
24064
24264
|
}
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24068
|
-
if (
|
|
24069
|
-
|
|
24070
|
-
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
|
|
24265
|
+
if (watchdogTripped) return;
|
|
24266
|
+
const remaining = Math.max(1, LEAD_TURN_TIMEOUT_MS2 - Math.max(0, Date.now() - lastProgressAt));
|
|
24267
|
+
watchdogTimer = setTimeout(() => {
|
|
24268
|
+
if (!isCurrentTurn()) return;
|
|
24269
|
+
if (watchdogTripped) return;
|
|
24270
|
+
const idleMs = Date.now() - lastProgressAt;
|
|
24271
|
+
if (idleMs < LEAD_TURN_TIMEOUT_MS2) {
|
|
24272
|
+
armWatchdog();
|
|
24273
|
+
return;
|
|
24274
|
+
}
|
|
24275
|
+
watchdogTripped = true;
|
|
24276
|
+
if (_batchTimer !== null) {
|
|
24277
|
+
clearTimeout(_batchTimer);
|
|
24278
|
+
_batchTimer = null;
|
|
24279
|
+
}
|
|
24280
|
+
_pendingTextFlush = false;
|
|
24281
|
+
_pendingThinkFlush = false;
|
|
24282
|
+
_pendingThinkingLastEndedAt = 0;
|
|
24283
|
+
const elapsed = Date.now() - startedAt;
|
|
24284
|
+
tuiDebug2(`runTurn WATCHDOG TRIP turn=${turnIndex} elapsedMs=${elapsed} idleMs=${idleMs} lastProgress=${lastProgressLabel} \u2014 aborting stuck turn`);
|
|
24285
|
+
pushNotice(`Turn timed out after ${Math.round(idleMs / 1e3)}s idle \u2014 aborting stuck request. Input will be released shortly if abort does not unwind.`, "warn", { transcript: true });
|
|
24286
|
+
try {
|
|
24287
|
+
runtime.abort("cli-react-abort-watchdog");
|
|
24288
|
+
} catch {
|
|
24289
|
+
}
|
|
24290
|
+
watchdogGraceTimer = setTimeout(() => {
|
|
24291
|
+
if (flags.disposed) return;
|
|
24292
|
+
if (!getState().busy) return;
|
|
24293
|
+
if (flags.leadTurnEpoch !== turnEpoch) return;
|
|
24294
|
+
tuiDebug2(`runTurn WATCHDOG FORCE-RELEASE turn=${turnIndex} \u2014 abort unwind starved`);
|
|
24295
|
+
denyAllToolApprovals("turn timed out");
|
|
24296
|
+
clearActiveToolSummary();
|
|
24297
|
+
flushToolResults([], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true, cancelled: true });
|
|
24298
|
+
finalizeToolHeaders();
|
|
24299
|
+
clearDeferredTimers();
|
|
24300
|
+
flags.flushDeferredBeforeImmediatePush = null;
|
|
24301
|
+
flags.leadTurnEpoch++;
|
|
24302
|
+
set({ busy: false, spinner: null, thinking: null, lastTurn: null });
|
|
24303
|
+
flags.activePromptRestore = null;
|
|
24304
|
+
if (flags.draining) flags.draining = false;
|
|
24305
|
+
if (pending.length > 0) void drain();
|
|
24306
|
+
flushDeferredExecutionPendingResumeKick();
|
|
24307
|
+
}, 5e3);
|
|
24308
|
+
watchdogGraceTimer.unref?.();
|
|
24309
|
+
}, remaining);
|
|
24310
|
+
watchdogTimer.unref?.();
|
|
24311
|
+
};
|
|
24312
|
+
const markTurnProgress = (label) => {
|
|
24313
|
+
if (!isCurrentTurn()) return false;
|
|
24314
|
+
if (watchdogTripped) return;
|
|
24315
|
+
lastProgressAt = Date.now();
|
|
24316
|
+
lastProgressLabel = String(label || "progress");
|
|
24317
|
+
armWatchdog();
|
|
24318
|
+
return true;
|
|
24319
|
+
};
|
|
24320
|
+
armWatchdog();
|
|
24080
24321
|
let currentAssistantText = "";
|
|
24081
24322
|
let thinkingText = "";
|
|
24082
24323
|
let thinkingStartedAt = 0;
|
|
@@ -24097,6 +24338,7 @@ function createRunTurn(bag) {
|
|
|
24097
24338
|
let deferredSeqCounter = 0;
|
|
24098
24339
|
const deferredEntries = [];
|
|
24099
24340
|
const flushDeferredUpTo = (entry) => {
|
|
24341
|
+
if (!isCurrentTurn()) return;
|
|
24100
24342
|
if (!entry) return;
|
|
24101
24343
|
const specs = collectDeferredUpTo(entry);
|
|
24102
24344
|
if (!specs.length) return;
|
|
@@ -24141,7 +24383,7 @@ function createRunTurn(bag) {
|
|
|
24141
24383
|
deferredEntries.push(entry);
|
|
24142
24384
|
entry.timer = setTimeout(() => {
|
|
24143
24385
|
entry.timer = null;
|
|
24144
|
-
if (
|
|
24386
|
+
if (!isCurrentTurn()) return;
|
|
24145
24387
|
flushDeferredUpTo(entry);
|
|
24146
24388
|
}, TOOL_CARD_PUSH_DELAY_MS);
|
|
24147
24389
|
entry.timer.unref?.();
|
|
@@ -24173,7 +24415,7 @@ function createRunTurn(bag) {
|
|
|
24173
24415
|
deferredEntries.push(entry);
|
|
24174
24416
|
entry.timer = setTimeout(() => {
|
|
24175
24417
|
entry.timer = null;
|
|
24176
|
-
if (
|
|
24418
|
+
if (!isCurrentTurn()) return;
|
|
24177
24419
|
flushDeferredUpTo(entry);
|
|
24178
24420
|
}, TOOL_CARD_PUSH_DELAY_MS);
|
|
24179
24421
|
entry.timer.unref?.();
|
|
@@ -24203,6 +24445,7 @@ function createRunTurn(bag) {
|
|
|
24203
24445
|
return specs;
|
|
24204
24446
|
};
|
|
24205
24447
|
const appendItemsBatch = (newItems, extra = {}) => {
|
|
24448
|
+
if (!isCurrentTurn()) return;
|
|
24206
24449
|
if (!newItems || !newItems.length) {
|
|
24207
24450
|
set(extra);
|
|
24208
24451
|
return;
|
|
@@ -24257,6 +24500,7 @@ function createRunTurn(bag) {
|
|
|
24257
24500
|
if (allCalls.length === 0) continue;
|
|
24258
24501
|
aggregate.ensureVisible?.();
|
|
24259
24502
|
const errors = allCalls.filter((r) => r.isError).length;
|
|
24503
|
+
const callErrors = allCalls.filter((r) => r.isCallError).length;
|
|
24260
24504
|
const completed = allCalls.filter((r) => r.resolved).length;
|
|
24261
24505
|
const succeeded = completed - errors;
|
|
24262
24506
|
const rawResult = aggregateRawResult(allCalls);
|
|
@@ -24266,6 +24510,8 @@ function createRunTurn(bag) {
|
|
|
24266
24510
|
text: displayDetail,
|
|
24267
24511
|
rawResult: rawResult || null,
|
|
24268
24512
|
isError: errors > 0,
|
|
24513
|
+
errorCount: errors,
|
|
24514
|
+
callErrorCount: callErrors,
|
|
24269
24515
|
count: allCalls.length,
|
|
24270
24516
|
completedCount: allCalls.length,
|
|
24271
24517
|
doneCategories: aggregateDoneCategories(allCalls),
|
|
@@ -24389,6 +24635,12 @@ function createRunTurn(bag) {
|
|
|
24389
24635
|
clearTimeout(_batchTimer);
|
|
24390
24636
|
_batchTimer = null;
|
|
24391
24637
|
}
|
|
24638
|
+
if (!isCurrentTurn()) {
|
|
24639
|
+
_pendingTextFlush = false;
|
|
24640
|
+
_pendingThinkFlush = false;
|
|
24641
|
+
_pendingThinkingLastEndedAt = 0;
|
|
24642
|
+
return;
|
|
24643
|
+
}
|
|
24392
24644
|
if (_pendingTextFlush) {
|
|
24393
24645
|
_pendingTextFlush = false;
|
|
24394
24646
|
const textLen = currentAssistantText.length;
|
|
@@ -24467,16 +24719,20 @@ function createRunTurn(bag) {
|
|
|
24467
24719
|
aggregate.ensureVisible?.();
|
|
24468
24720
|
const rawText = toolResultText(message?.content);
|
|
24469
24721
|
const isMemoryCall = classifyToolCategory(callRec.name, callRec.args) === "Memory";
|
|
24470
|
-
const
|
|
24722
|
+
const isCallError = message?.isError === true || message?.toolKind === "error";
|
|
24723
|
+
const isResultError = /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText)) || isMemoryCall && memoryCoreResultErrorText(rawText) != null;
|
|
24724
|
+
const isError = isCallError || isResultError;
|
|
24471
24725
|
const text = isError ? toolErrorDisplay(rawText, callRec.name || "tool") : rawText;
|
|
24472
24726
|
callRec.summary = !isError ? summarizeToolResult(callRec.name, callRec.args, rawText, isError) : null;
|
|
24473
24727
|
assignAggregateSummaryOrder(aggregate, callRec);
|
|
24474
24728
|
callRec.isError = isError;
|
|
24729
|
+
callRec.isCallError = isCallError;
|
|
24475
24730
|
callRec.resultText = text;
|
|
24476
24731
|
callRec.completedEarly = true;
|
|
24477
24732
|
const allCalls = [...aggregate.calls.values()];
|
|
24478
24733
|
const completedCount = allCalls.filter((r) => r.resolved || r.completedEarly).length;
|
|
24479
24734
|
const errors = allCalls.filter((r) => r.isError).length;
|
|
24735
|
+
const callErrors = allCalls.filter((r) => r.isCallError).length;
|
|
24480
24736
|
const succeeded = completedCount - errors;
|
|
24481
24737
|
const rawResult = aggregateRawResult(allCalls);
|
|
24482
24738
|
const displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
|
|
@@ -24490,6 +24746,7 @@ function createRunTurn(bag) {
|
|
|
24490
24746
|
text: displayDetail,
|
|
24491
24747
|
isError: errors > 0,
|
|
24492
24748
|
errorCount: errors,
|
|
24749
|
+
callErrorCount: callErrors,
|
|
24493
24750
|
count: allCalls.length,
|
|
24494
24751
|
completedCount: visualCompleted
|
|
24495
24752
|
};
|
|
@@ -24510,8 +24767,9 @@ function createRunTurn(bag) {
|
|
|
24510
24767
|
};
|
|
24511
24768
|
try {
|
|
24512
24769
|
const { result, session } = await runtime.ask(userText, {
|
|
24513
|
-
drainSteering: () => drainPendingSteering(),
|
|
24770
|
+
drainSteering: (_sessionId, drainOptions) => isCurrentTurn() ? drainPendingSteering(drainOptions) : [],
|
|
24514
24771
|
onSteerMessage: (text) => {
|
|
24772
|
+
if (!markTurnProgress("steer-message")) return;
|
|
24515
24773
|
flushStreamBatch();
|
|
24516
24774
|
if (currentAssistantId) {
|
|
24517
24775
|
patchItem(currentAssistantId, { text: currentAssistantText || assistantText, streaming: false });
|
|
@@ -24525,6 +24783,7 @@ function createRunTurn(bag) {
|
|
|
24525
24783
|
}
|
|
24526
24784
|
},
|
|
24527
24785
|
onToolCall: async (_iter, calls) => {
|
|
24786
|
+
if (!markTurnProgress("tool-call")) return;
|
|
24528
24787
|
markPromptCommitted();
|
|
24529
24788
|
flushStreamBatch();
|
|
24530
24789
|
if (thinkingText && getState().thinking) {
|
|
@@ -24538,7 +24797,8 @@ function createRunTurn(bag) {
|
|
|
24538
24797
|
if (batchCalls.length === 0) return;
|
|
24539
24798
|
const committedAssistantSegment = commitAssistantSegment({ sealToolBlock: true });
|
|
24540
24799
|
if (committedAssistantSegment) {
|
|
24541
|
-
await yieldToRenderer();
|
|
24800
|
+
await yieldToRenderer({ frames: 2 });
|
|
24801
|
+
if (!isCurrentTurn()) return;
|
|
24542
24802
|
}
|
|
24543
24803
|
const touchedAggregates = /* @__PURE__ */ new Set();
|
|
24544
24804
|
let standaloneReserve = null;
|
|
@@ -24590,7 +24850,7 @@ function createRunTurn(bag) {
|
|
|
24590
24850
|
...categoryEntry,
|
|
24591
24851
|
count: Number(prevCategory?.count || 0) + Number(categoryEntry.count || 1)
|
|
24592
24852
|
});
|
|
24593
|
-
aggregateCard.calls.set(callKey, { name, args, category, summary: null, summarySeq: null, isError: false, resultText: null, resolved: false, completedEarly: false });
|
|
24853
|
+
aggregateCard.calls.set(callKey, { name, args, category, summary: null, summarySeq: null, isError: false, isCallError: false, resultText: null, resolved: false, completedEarly: false });
|
|
24594
24854
|
touchedAggregates.add(aggregateCard);
|
|
24595
24855
|
const card = { itemId: aggregateCard.itemId, callId: callKey, done: false, aggregate: aggregateCard };
|
|
24596
24856
|
if (callId) {
|
|
@@ -24614,6 +24874,7 @@ function createRunTurn(bag) {
|
|
|
24614
24874
|
await yieldToRenderer();
|
|
24615
24875
|
},
|
|
24616
24876
|
onToolResult: (message) => {
|
|
24877
|
+
if (!markTurnProgress("tool-result")) return;
|
|
24617
24878
|
const callId = toolResultCallId(message);
|
|
24618
24879
|
if (callId && !cardByCallId.has(callId) && !resultsDone.has(callId)) {
|
|
24619
24880
|
earlyResultBuffer.set(callId, message);
|
|
@@ -24622,12 +24883,16 @@ function createRunTurn(bag) {
|
|
|
24622
24883
|
deliverToolResultMessage(message);
|
|
24623
24884
|
},
|
|
24624
24885
|
onToolApproval: async (request) => {
|
|
24886
|
+
if (!markTurnProgress("tool-approval")) return { approved: false, reason: "turn no longer active" };
|
|
24625
24887
|
markPromptCommitted();
|
|
24626
24888
|
flushStreamBatch();
|
|
24627
24889
|
if (getState().spinner) set({ spinner: { ...getState().spinner, mode: "tool-approval" } });
|
|
24628
|
-
|
|
24890
|
+
const approval = await requestToolApproval(request);
|
|
24891
|
+
if (!isCurrentTurn()) return { approved: false, reason: "turn no longer active" };
|
|
24892
|
+
return approval;
|
|
24629
24893
|
},
|
|
24630
24894
|
onCompactEvent: (event) => {
|
|
24895
|
+
if (!markTurnProgress("compact-event")) return;
|
|
24631
24896
|
flushStreamBatch();
|
|
24632
24897
|
clearAggregateContinuation();
|
|
24633
24898
|
pushItem({
|
|
@@ -24638,6 +24903,7 @@ function createRunTurn(bag) {
|
|
|
24638
24903
|
});
|
|
24639
24904
|
},
|
|
24640
24905
|
onStageChange: async (stage) => {
|
|
24906
|
+
if (!markTurnProgress(`stage:${String(stage || "")}`)) return;
|
|
24641
24907
|
if (!getState().spinner) return;
|
|
24642
24908
|
const value = String(stage || "");
|
|
24643
24909
|
if (value === "compacting") {
|
|
@@ -24667,6 +24933,7 @@ function createRunTurn(bag) {
|
|
|
24667
24933
|
onTextDelta: (chunk) => {
|
|
24668
24934
|
const textChunk = String(chunk ?? "");
|
|
24669
24935
|
if (!textChunk) return;
|
|
24936
|
+
if (!markTurnProgress("text-delta")) return;
|
|
24670
24937
|
markPromptCommitted();
|
|
24671
24938
|
const thinkingLastEndedAt = closeThinkingSegment();
|
|
24672
24939
|
_pendingThinkFlush = false;
|
|
@@ -24683,6 +24950,7 @@ function createRunTurn(bag) {
|
|
|
24683
24950
|
onAssistantText: (text) => {
|
|
24684
24951
|
const full = String(text ?? "");
|
|
24685
24952
|
if (!full.trim()) return;
|
|
24953
|
+
if (!markTurnProgress("assistant-text")) return;
|
|
24686
24954
|
if (currentAssistantText.trim()) return;
|
|
24687
24955
|
markPromptCommitted();
|
|
24688
24956
|
closeThinkingSegment();
|
|
@@ -24697,13 +24965,18 @@ function createRunTurn(bag) {
|
|
|
24697
24965
|
flushStreamBatch();
|
|
24698
24966
|
},
|
|
24699
24967
|
onReasoningDelta: (chunk) => {
|
|
24700
|
-
if (
|
|
24968
|
+
if (!isCurrentTurn() || watchdogTripped) return;
|
|
24969
|
+
if (String(chunk ?? "")) {
|
|
24970
|
+
if (!markTurnProgress("reasoning-delta")) return;
|
|
24971
|
+
markPromptCommitted();
|
|
24972
|
+
}
|
|
24701
24973
|
startThinkingSegment();
|
|
24702
24974
|
thinkingText += String(chunk ?? "");
|
|
24703
24975
|
_pendingThinkFlush = true;
|
|
24704
24976
|
scheduleStreamFlush();
|
|
24705
24977
|
},
|
|
24706
24978
|
onUsageDelta: (delta) => {
|
|
24979
|
+
if (!markTurnProgress("usage-delta")) return;
|
|
24707
24980
|
applyUsageDelta(getState().stats, delta);
|
|
24708
24981
|
syncContextStats({ allowEstimated: true });
|
|
24709
24982
|
const currentTurnInput = Math.max(0, getState().stats.inputTokens - inputBaseline);
|
|
@@ -24715,64 +24988,73 @@ function createRunTurn(bag) {
|
|
|
24715
24988
|
}
|
|
24716
24989
|
}
|
|
24717
24990
|
});
|
|
24718
|
-
|
|
24719
|
-
markPromptCommitted();
|
|
24720
|
-
flushToolResults(session?.messages || [], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true });
|
|
24721
|
-
finalizeToolHeaders();
|
|
24722
|
-
flushStreamBatch();
|
|
24723
|
-
syncContextStats({ allowEstimated: true });
|
|
24724
|
-
const finalText = result?.content != null ? String(result.content) : "";
|
|
24725
|
-
if (finalText.trim()) {
|
|
24726
|
-
const id = currentAssistantId || ensureAssistant(finalText);
|
|
24727
|
-
currentAssistantText = finalText;
|
|
24728
|
-
patchItem(id, { text: finalText, streaming: false });
|
|
24729
|
-
} else if (currentAssistantId && (currentAssistantText.trim() || assistantText.trim())) {
|
|
24730
|
-
const streamedText = currentAssistantText || assistantText;
|
|
24731
|
-
patchItem(currentAssistantId, { text: streamedText, streaming: false });
|
|
24732
|
-
}
|
|
24733
|
-
turnFinishedNormally = true;
|
|
24734
|
-
} catch (error) {
|
|
24735
|
-
flushStreamBatch();
|
|
24736
|
-
if (error?.name === "SessionClosedError") {
|
|
24991
|
+
if (!isCurrentTurn()) {
|
|
24737
24992
|
cancelled = true;
|
|
24738
|
-
if (assistantText.trim() && currentAssistantId) {
|
|
24739
|
-
patchItem(currentAssistantId, { text: currentAssistantText || assistantText, streaming: false });
|
|
24740
|
-
}
|
|
24741
|
-
flushToolResults([], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true, cancelled: true });
|
|
24742
|
-
finalizeToolHeaders();
|
|
24743
24993
|
} else {
|
|
24994
|
+
askResult = result;
|
|
24995
|
+
markPromptCommitted();
|
|
24996
|
+
flushToolResults(session?.messages || [], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true });
|
|
24744
24997
|
finalizeToolHeaders();
|
|
24745
|
-
|
|
24998
|
+
flushStreamBatch();
|
|
24999
|
+
syncContextStats({ allowEstimated: true });
|
|
25000
|
+
const finalText = result?.content != null ? String(result.content) : "";
|
|
25001
|
+
if (finalText.trim()) {
|
|
25002
|
+
const id = currentAssistantId || ensureAssistant(finalText);
|
|
25003
|
+
currentAssistantText = finalText;
|
|
25004
|
+
patchItem(id, { text: finalText, streaming: false });
|
|
25005
|
+
} else if (currentAssistantId && (currentAssistantText.trim() || assistantText.trim())) {
|
|
25006
|
+
const streamedText = currentAssistantText || assistantText;
|
|
25007
|
+
patchItem(currentAssistantId, { text: streamedText, streaming: false });
|
|
25008
|
+
}
|
|
25009
|
+
turnFinishedNormally = true;
|
|
25010
|
+
}
|
|
25011
|
+
} catch (error) {
|
|
25012
|
+
const staleCatch = !isCurrentTurn();
|
|
25013
|
+
if (staleCatch) {
|
|
25014
|
+
cancelled = true;
|
|
25015
|
+
} else {
|
|
25016
|
+
flushStreamBatch();
|
|
25017
|
+
if (error?.name === "SessionClosedError") {
|
|
25018
|
+
cancelled = true;
|
|
25019
|
+
if (assistantText.trim() && currentAssistantId) {
|
|
25020
|
+
patchItem(currentAssistantId, { text: currentAssistantText || assistantText, streaming: false });
|
|
25021
|
+
}
|
|
25022
|
+
flushToolResults([], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true, cancelled: true });
|
|
25023
|
+
finalizeToolHeaders();
|
|
25024
|
+
} else {
|
|
25025
|
+
finalizeToolHeaders();
|
|
25026
|
+
pushNotice(toolErrorDisplay(error, "turn"), "error");
|
|
25027
|
+
}
|
|
24746
25028
|
}
|
|
24747
25029
|
} finally {
|
|
24748
|
-
|
|
25030
|
+
const isStaleUnwind = !isCurrentTurn();
|
|
25031
|
+
if (!isStaleUnwind) denyAllToolApprovals(cancelled ? "turn cancelled" : "turn finished");
|
|
24749
25032
|
clearWatchdog();
|
|
24750
|
-
const isStaleUnwind = flags.leadTurnEpoch !== turnEpoch;
|
|
24751
25033
|
let closingItems = [];
|
|
24752
25034
|
if (deferredEntries.length) {
|
|
24753
|
-
|
|
24754
|
-
|
|
25035
|
+
if (!isStaleUnwind) {
|
|
25036
|
+
const last = deferredEntries[deferredEntries.length - 1];
|
|
25037
|
+
closingItems = collectDeferredUpTo(last);
|
|
25038
|
+
}
|
|
24755
25039
|
clearDeferredTimers();
|
|
24756
25040
|
}
|
|
24757
|
-
flags.flushDeferredBeforeImmediatePush = null;
|
|
24758
|
-
const producedTranscriptItem = getState().items.length + closingItems.length > itemsAtTurnStart;
|
|
24759
|
-
const reclaimed = cancelled && flags.activePromptRestore?.reclaimed === true;
|
|
24760
|
-
if (!isStaleUnwind) flags.activePromptRestore = null;
|
|
25041
|
+
if (!isStaleUnwind) flags.flushDeferredBeforeImmediatePush = null;
|
|
24761
25042
|
closeThinkingSegment();
|
|
24762
|
-
const elapsedMs = Date.now() - startedAt;
|
|
24763
|
-
const thinkingElapsedMs = thinkingStartedAt ? accumulatedThinkingMs : 0;
|
|
24764
|
-
const finalAssistantLen = Math.max(assistantText.length, currentAssistantText.length);
|
|
24765
|
-
const finalResponseLength = finalAssistantLen + thinkingText.length;
|
|
24766
|
-
const finalOutputTokens = Math.max(0, Number(getState().spinner?.outputTokens || 0), Math.round(finalResponseLength / 4));
|
|
24767
|
-
const turnStatus = cancelled ? "cancelled" : "done";
|
|
24768
|
-
const resultContent = askResult?.content != null ? String(askResult.content).trim() : "";
|
|
24769
|
-
const assistantOutput = (currentAssistantText || assistantText || "").trim();
|
|
24770
|
-
const isNoOpTurn = turnFinishedNormally && !cancelled && toolCards.length === 0 && !resultContent && !assistantOutput && !producedTranscriptItem;
|
|
24771
25043
|
if (isStaleUnwind) {
|
|
24772
|
-
|
|
24773
|
-
tuiDebug2(`runTurn STALE UNWIND turn=${turnIndex} \u2014 force-released; skipping shared-getState() writes`);
|
|
24774
|
-
flushDeferredExecutionPendingResumeKick();
|
|
25044
|
+
tuiDebug2(`runTurn STALE UNWIND turn=${turnIndex} \u2014 force-released; skipping shared UI/state writes`);
|
|
24775
25045
|
} else {
|
|
25046
|
+
const producedTranscriptItem = getState().items.length + closingItems.length > itemsAtTurnStart;
|
|
25047
|
+
const reclaimed = cancelled && flags.activePromptRestore?.reclaimed === true;
|
|
25048
|
+
flags.activePromptRestore = null;
|
|
25049
|
+
const elapsedMs = Date.now() - startedAt;
|
|
25050
|
+
const thinkingElapsedMs = thinkingStartedAt ? accumulatedThinkingMs : 0;
|
|
25051
|
+
const finalAssistantLen = Math.max(assistantText.length, currentAssistantText.length);
|
|
25052
|
+
const finalResponseLength = finalAssistantLen + thinkingText.length;
|
|
25053
|
+
const finalOutputTokens = Math.max(0, Number(getState().spinner?.outputTokens || 0), Math.round(finalResponseLength / 4));
|
|
25054
|
+
const turnStatus = cancelled ? "cancelled" : "done";
|
|
25055
|
+
const resultContent = askResult?.content != null ? String(askResult.content).trim() : "";
|
|
25056
|
+
const assistantOutput = (currentAssistantText || assistantText || "").trim();
|
|
25057
|
+
const isNoOpTurn = turnFinishedNormally && !cancelled && toolCards.length === 0 && !resultContent && !assistantOutput && !producedTranscriptItem;
|
|
24776
25058
|
if (!isNoOpTurn) {
|
|
24777
25059
|
getState().stats.turns = (getState().stats.turns || 0) + 1;
|
|
24778
25060
|
}
|
|
@@ -25478,6 +25760,10 @@ async function buildDoctorReport(runtime = {}, getState = () => ({})) {
|
|
|
25478
25760
|
}
|
|
25479
25761
|
|
|
25480
25762
|
// src/tui/engine/session-api.mjs
|
|
25763
|
+
var MANUAL_ABORT_RECOVERY_MS = (() => {
|
|
25764
|
+
const v = Number(process.env.MIXDOG_MANUAL_ABORT_RECOVERY_MS);
|
|
25765
|
+
return Number.isFinite(v) && v > 0 ? v : 4e3;
|
|
25766
|
+
})();
|
|
25481
25767
|
function createEngineApi(bag) {
|
|
25482
25768
|
return { ...createEngineApiA(bag), ...createEngineApiB(bag) };
|
|
25483
25769
|
}
|
|
@@ -25504,7 +25790,9 @@ function createEngineApiA(bag) {
|
|
|
25504
25790
|
enqueue,
|
|
25505
25791
|
autoClearBeforeSubmit,
|
|
25506
25792
|
restoreQueued,
|
|
25507
|
-
resetStatsAndSyncContext
|
|
25793
|
+
resetStatsAndSyncContext,
|
|
25794
|
+
drain,
|
|
25795
|
+
flushDeferredExecutionPendingResumeKick
|
|
25508
25796
|
} = bag;
|
|
25509
25797
|
return {
|
|
25510
25798
|
getState: () => getState(),
|
|
@@ -25528,12 +25816,16 @@ function createEngineApiA(bag) {
|
|
|
25528
25816
|
enqueue(text, queueOptions);
|
|
25529
25817
|
return true;
|
|
25530
25818
|
}
|
|
25531
|
-
if (getState().commandBusy)
|
|
25819
|
+
if (getState().commandBusy) {
|
|
25820
|
+
enqueue(text, queueOptions);
|
|
25821
|
+
return true;
|
|
25822
|
+
}
|
|
25532
25823
|
if (getState().busy) {
|
|
25533
25824
|
enqueue(text, queueOptions);
|
|
25534
25825
|
return true;
|
|
25535
25826
|
}
|
|
25536
|
-
void autoClearBeforeSubmit().
|
|
25827
|
+
void autoClearBeforeSubmit().catch(() => {
|
|
25828
|
+
}).then(() => enqueue(text, queueOptions));
|
|
25537
25829
|
return true;
|
|
25538
25830
|
},
|
|
25539
25831
|
restoreQueued,
|
|
@@ -26013,6 +26305,22 @@ function createEngineApiA(bag) {
|
|
|
26013
26305
|
restoreState.restorable = false;
|
|
26014
26306
|
restoreState.requeueEntries = [];
|
|
26015
26307
|
}
|
|
26308
|
+
const abortEpoch = flags.leadTurnEpoch;
|
|
26309
|
+
const recoveryMs = Number(flags.manualAbortRecoveryMs) > 0 ? Number(flags.manualAbortRecoveryMs) : MANUAL_ABORT_RECOVERY_MS;
|
|
26310
|
+
const recoveryTimer = setTimeout(() => {
|
|
26311
|
+
if (flags.disposed) return;
|
|
26312
|
+
if (!getState().busy) return;
|
|
26313
|
+
if (flags.leadTurnEpoch !== abortEpoch) return;
|
|
26314
|
+
flags.leadTurnEpoch = (Number(flags.leadTurnEpoch) || 0) + 1;
|
|
26315
|
+
set({ busy: false, spinner: null, thinking: null, lastTurn: null });
|
|
26316
|
+
flags.activePromptRestore = null;
|
|
26317
|
+
flags.drainEpoch = (Number(flags.drainEpoch) || 0) + 1;
|
|
26318
|
+
if (flags.draining) flags.draining = false;
|
|
26319
|
+
pushNotice("Interrupt did not settle \u2014 input restored.", "warn", { transcript: true });
|
|
26320
|
+
if (pending.length > 0 && typeof drain === "function") void drain();
|
|
26321
|
+
if (typeof flushDeferredExecutionPendingResumeKick === "function") flushDeferredExecutionPendingResumeKick();
|
|
26322
|
+
}, recoveryMs);
|
|
26323
|
+
recoveryTimer.unref?.();
|
|
26016
26324
|
return { aborted, restoreText, pastedImages: restorePastedImages, discardPastedImages, pastedTexts: restorePastedTexts, discardPastedTexts };
|
|
26017
26325
|
}
|
|
26018
26326
|
};
|
|
@@ -26149,8 +26457,12 @@ async function createEngineSession({
|
|
|
26149
26457
|
}
|
|
26150
26458
|
}
|
|
26151
26459
|
if (!changed) return false;
|
|
26460
|
+
const commandBusyReleased = state.commandBusy === true && Object.prototype.hasOwnProperty.call(patch, "commandBusy") && patch.commandBusy === false;
|
|
26152
26461
|
state = { ...state, ...patch };
|
|
26153
26462
|
emit();
|
|
26463
|
+
if (commandBusyReleased) queueMicrotask(() => {
|
|
26464
|
+
void bag.drain?.();
|
|
26465
|
+
});
|
|
26154
26466
|
return true;
|
|
26155
26467
|
};
|
|
26156
26468
|
const itemIndexById = /* @__PURE__ */ new Map();
|
|
@@ -26342,6 +26654,7 @@ async function createEngineSession({
|
|
|
26342
26654
|
flushDeferredExecutionPendingResumeKick,
|
|
26343
26655
|
scheduleExecutionPendingResumeKick,
|
|
26344
26656
|
updateAgentJobCard,
|
|
26657
|
+
buildAgentJobCardPatch,
|
|
26345
26658
|
subscribeRuntimeNotifications
|
|
26346
26659
|
} = createAgentJobFeed({
|
|
26347
26660
|
runtime,
|
|
@@ -26375,6 +26688,7 @@ async function createEngineSession({
|
|
|
26375
26688
|
patchItem,
|
|
26376
26689
|
markToolCallDone,
|
|
26377
26690
|
updateAgentJobCard,
|
|
26691
|
+
buildAgentJobCardPatch,
|
|
26378
26692
|
agentStatusState
|
|
26379
26693
|
});
|
|
26380
26694
|
Object.assign(bag, {
|
|
@@ -26676,7 +26990,10 @@ function installTuiPerfProbe() {
|
|
|
26676
26990
|
};
|
|
26677
26991
|
}
|
|
26678
26992
|
function makeRenderProfiler() {
|
|
26679
|
-
|
|
26993
|
+
const ackRenderedFrame = () => {
|
|
26994
|
+
scheduleRenderFrameAck();
|
|
26995
|
+
};
|
|
26996
|
+
if (!PERF_ENABLED) return ackRenderedFrame;
|
|
26680
26997
|
let count = 0;
|
|
26681
26998
|
let sum = 0;
|
|
26682
26999
|
let max = 0;
|
|
@@ -26684,6 +27001,7 @@ function makeRenderProfiler() {
|
|
|
26684
27001
|
let maxGap = 0;
|
|
26685
27002
|
let lastFrameAt = 0;
|
|
26686
27003
|
return ({ renderTime } = {}) => {
|
|
27004
|
+
ackRenderedFrame();
|
|
26687
27005
|
const now = performance3.now();
|
|
26688
27006
|
const ms = Number(renderTime) || 0;
|
|
26689
27007
|
const gap = lastFrameAt ? now - lastFrameAt : 0;
|