mixdog 0.9.22 → 0.9.24
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/README.md +1 -4
- package/package.json +1 -1
- package/scripts/boot-smoke.mjs +1 -1
- package/scripts/channel-daemon-smoke.mjs +483 -0
- package/scripts/channel-daemon-stub.mjs +80 -0
- package/scripts/debounced-skills-async-save-test.mjs +57 -0
- package/scripts/explore-bench-tmp.mjs +17 -0
- package/scripts/find-fuzzy-hidden-test.mjs +145 -0
- package/scripts/mcp-grace-deferred-test.mjs +149 -0
- package/scripts/statusline-quota-hysteresis-test.mjs +37 -0
- package/scripts/tool-smoke.mjs +68 -47
- package/src/rules/agent/30-explorer.md +6 -0
- package/src/rules/shared/01-tool.md +11 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +76 -1
- package/src/runtime/agent/orchestrator/config.mjs +33 -7
- package/src/runtime/agent/orchestrator/context/collect.mjs +43 -8
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +226 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +184 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +38 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +39 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +24 -7
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +40 -6
- package/src/runtime/agent/orchestrator/session/store.mjs +30 -14
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +13 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +62 -24
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -6
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +20 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +34 -3
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +220 -27
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +61 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +67 -27
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +6 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +54 -5
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +97 -54
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +49 -31
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +35 -2
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +126 -22
- package/src/runtime/channels/lib/crash-log.mjs +4 -2
- package/src/runtime/channels/lib/output-forwarder.mjs +2 -1
- package/src/runtime/channels/lib/owned-runtime.mjs +67 -223
- package/src/runtime/channels/lib/owner-heartbeat.mjs +9 -62
- package/src/runtime/channels/lib/parent-bridge.mjs +16 -1
- package/src/runtime/channels/lib/runtime-paths.mjs +32 -113
- package/src/runtime/channels/lib/session-discovery.mjs +2 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +22 -14
- package/src/runtime/channels/lib/tool-format.mjs +7 -2
- package/src/runtime/channels/lib/worker-main.mjs +42 -30
- package/src/runtime/memory/index.mjs +54 -7
- package/src/runtime/memory/lib/embedding-warmup.mjs +7 -0
- package/src/runtime/memory/lib/pg/process.mjs +85 -40
- package/src/runtime/memory/lib/pg/supervisor.mjs +82 -17
- package/src/runtime/memory/lib/query-handlers.mjs +4 -1
- package/src/runtime/memory/lib/recall-format.mjs +7 -3
- package/src/runtime/memory/tool-defs.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +150 -6
- package/src/runtime/shared/background-tasks.mjs +1 -1
- package/src/runtime/shared/config.mjs +53 -1
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-primitives.mjs +31 -1
- package/src/runtime/shared/tool-surface.mjs +42 -13
- package/src/runtime/shared/update-checker.mjs +3 -0
- package/src/runtime/shared/user-data-guard.mjs +66 -0
- package/src/session-runtime/config-lifecycle.mjs +221 -20
- package/src/session-runtime/lifecycle-api.mjs +9 -0
- package/src/session-runtime/mcp-glue.mjs +93 -1
- package/src/session-runtime/resource-api.mjs +62 -8
- package/src/session-runtime/runtime-core.mjs +118 -4
- package/src/session-runtime/session-text.mjs +41 -0
- package/src/session-runtime/session-turn-api.mjs +50 -0
- package/src/session-runtime/settings-api.mjs +8 -1
- package/src/session-runtime/tool-catalog.mjs +350 -38
- package/src/session-runtime/tool-defs.mjs +7 -7
- package/src/session-runtime/workflow.mjs +2 -1
- package/src/standalone/channel-admin.mjs +32 -3
- package/src/standalone/channel-daemon-client.mjs +226 -0
- package/src/standalone/channel-daemon-transport.mjs +545 -0
- package/src/standalone/channel-daemon.mjs +176 -0
- package/src/standalone/channel-worker.mjs +224 -4
- package/src/standalone/explore-tool.mjs +87 -15
- package/src/standalone/hook-bus.mjs +71 -3
- package/src/tui/App.jsx +107 -19
- package/src/tui/app/clipboard.mjs +39 -19
- package/src/tui/app/doctor.mjs +57 -0
- package/src/tui/app/extension-pickers.mjs +53 -9
- package/src/tui/app/maintenance-pickers.mjs +0 -5
- package/src/tui/app/slash-dispatch.mjs +4 -4
- package/src/tui/app/text-layout.mjs +11 -0
- package/src/tui/app/use-mouse-input.mjs +235 -51
- package/src/tui/app/use-prompt-handlers.mjs +49 -30
- package/src/tui/app/use-transcript-scroll.mjs +124 -27
- package/src/tui/app/use-transcript-window.mjs +55 -1
- package/src/tui/components/Message.jsx +1 -1
- package/src/tui/components/PromptInput.jsx +3 -1
- package/src/tui/components/QueuedCommands.jsx +21 -10
- package/src/tui/components/StatusLine.jsx +3 -3
- package/src/tui/components/ToolExecution.jsx +16 -4
- package/src/tui/components/TranscriptItem.jsx +1 -1
- package/src/tui/dist/index.mjs +820 -326
- package/src/tui/engine/agent-job-feed.mjs +5 -0
- package/src/tui/engine/notification-plan.mjs +5 -0
- package/src/tui/engine/session-api.mjs +23 -8
- package/src/tui/engine/session-flow.mjs +6 -0
- package/src/tui/engine/tool-card-results.mjs +14 -5
- package/src/tui/engine/turn.mjs +9 -2
- package/src/tui/engine.mjs +32 -5
- package/src/tui/index.jsx +62 -18
- package/src/tui/paste-attachments.mjs +26 -0
- package/src/ui/statusline-agents.mjs +36 -0
- package/src/ui/statusline.mjs +60 -10
- package/src/ui/tool-card.mjs +8 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +23 -7
- package/src/workflows/bench/WORKFLOW.md +46 -0
- package/src/workflows/default/WORKFLOW.md +6 -0
- package/src/workflows/solo/WORKFLOW.md +5 -0
- package/vendor/ink/build/ink.js +23 -1
- package/vendor/ink/build/output.js +154 -71
- package/vendor/ink/build/render-node-to-output.js +44 -2
- package/vendor/ink/build/render.js +4 -0
- package/vendor/ink/build/renderer.js +4 -1
package/src/tui/dist/index.mjs
CHANGED
|
@@ -156,10 +156,11 @@ var require_mixdog_debug = __commonJS({
|
|
|
156
156
|
// src/tui/index.jsx
|
|
157
157
|
import React21 from "react";
|
|
158
158
|
import { render } from "../../../vendor/ink/build/index.js";
|
|
159
|
-
import { closeSync as closeSync2, constants as fsConstants, createWriteStream, mkdirSync as
|
|
160
|
-
import { tmpdir as
|
|
161
|
-
import { dirname as dirname9, join as
|
|
159
|
+
import { closeSync as closeSync2, constants as fsConstants, createWriteStream, mkdirSync as mkdirSync6, openSync as openSync2, readSync } from "node:fs";
|
|
160
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
161
|
+
import { dirname as dirname9, join as join10 } from "node:path";
|
|
162
162
|
import { performance as performance3 } from "node:perf_hooks";
|
|
163
|
+
import { format } from "node:util";
|
|
163
164
|
|
|
164
165
|
// src/tui/App.jsx
|
|
165
166
|
import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef9 } from "react";
|
|
@@ -1788,8 +1789,21 @@ function parseMcpToolName(name) {
|
|
|
1788
1789
|
function isMcpToolName(name) {
|
|
1789
1790
|
return Boolean(parseMcpToolName(name));
|
|
1790
1791
|
}
|
|
1792
|
+
var SELF_MCP_SERVER = "plugin_mixdog_mixdog";
|
|
1793
|
+
function isSelfMcpToolName(name) {
|
|
1794
|
+
const mcp = parseMcpToolName(name);
|
|
1795
|
+
return Boolean(mcp && mcp.server === SELF_MCP_SERVER);
|
|
1796
|
+
}
|
|
1797
|
+
function isExternalMcpToolName(name) {
|
|
1798
|
+
return isMcpToolName(name) && !isSelfMcpToolName(name);
|
|
1799
|
+
}
|
|
1800
|
+
function titleCaseMcpServer(server) {
|
|
1801
|
+
return String(server || "").split(/[_\s-]+/).filter(Boolean).map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1).toLowerCase()}`).join(" ");
|
|
1802
|
+
}
|
|
1803
|
+
var TOOL_NAME_ALIASES = { tool_search: "load_tool" };
|
|
1791
1804
|
function normalizeToolName(name) {
|
|
1792
|
-
|
|
1805
|
+
const base = stripToolPrefix(name).replace(/-/g, "_").toLowerCase();
|
|
1806
|
+
return TOOL_NAME_ALIASES[base] || base;
|
|
1793
1807
|
}
|
|
1794
1808
|
function truncateToolText(value, max = DEFAULT_SUMMARY_MAX) {
|
|
1795
1809
|
const text = String(value ?? "").trim();
|
|
@@ -1829,16 +1843,6 @@ function displayToolPath(path2) {
|
|
|
1829
1843
|
function compactParts(parts) {
|
|
1830
1844
|
return parts.filter((part) => part != null && String(part).trim()).map((part) => String(part).trim()).join(STATUS_SEPARATOR);
|
|
1831
1845
|
}
|
|
1832
|
-
function compactSlash(left, right) {
|
|
1833
|
-
const a = String(left ?? "").trim();
|
|
1834
|
-
const b = String(right ?? "").trim();
|
|
1835
|
-
return a && b ? `${a}/${b}` : a || b;
|
|
1836
|
-
}
|
|
1837
|
-
function mcpToolTarget(name, max = DEFAULT_SUMMARY_MAX) {
|
|
1838
|
-
const mcp = parseMcpToolName(name);
|
|
1839
|
-
if (!mcp) return "";
|
|
1840
|
-
return truncateToolText(compactSlash(mcp.server, mcp.tool), max);
|
|
1841
|
-
}
|
|
1842
1846
|
function quoted(value, max) {
|
|
1843
1847
|
const text = truncateToolText(value || "", max);
|
|
1844
1848
|
return text ? `"${text}"` : "";
|
|
@@ -2435,7 +2439,10 @@ function summarizeAgentSurfaceBrief(name, args, resultText, { isError = false, i
|
|
|
2435
2439
|
|
|
2436
2440
|
// src/runtime/shared/tool-surface.mjs
|
|
2437
2441
|
function displayToolName(name, args = {}) {
|
|
2438
|
-
if (
|
|
2442
|
+
if (isExternalMcpToolName(name)) {
|
|
2443
|
+
const mcp = parseMcpToolName(name);
|
|
2444
|
+
return `MCP ${titleCaseMcpServer(mcp.server)}`;
|
|
2445
|
+
}
|
|
2439
2446
|
const normalized = normalizeToolName(name);
|
|
2440
2447
|
switch (normalized) {
|
|
2441
2448
|
case "read":
|
|
@@ -2461,7 +2468,7 @@ function displayToolName(name, args = {}) {
|
|
|
2461
2468
|
case "list":
|
|
2462
2469
|
case "ls":
|
|
2463
2470
|
return "Search";
|
|
2464
|
-
case "
|
|
2471
|
+
case "load_tool":
|
|
2465
2472
|
return toolSearchDisplayLabel(parseToolArgs(args));
|
|
2466
2473
|
case "search":
|
|
2467
2474
|
case "search_query":
|
|
@@ -2511,10 +2518,10 @@ function summarizeToolArgs(name, args, { max = DEFAULT_SUMMARY_MAX } = {}) {
|
|
|
2511
2518
|
const a = parseToolArgs(args);
|
|
2512
2519
|
if (!a || typeof a !== "object") return "";
|
|
2513
2520
|
const normalized = normalizeToolName(name);
|
|
2514
|
-
|
|
2515
|
-
|
|
2521
|
+
if (isExternalMcpToolName(name)) {
|
|
2522
|
+
const mcp = parseMcpToolName(name);
|
|
2516
2523
|
return compactParts([
|
|
2517
|
-
|
|
2524
|
+
truncateToolText(mcp.tool, max),
|
|
2518
2525
|
truncateToolText(firstText(a.query, a.q, a.text, a.prompt, a.path, a.uri, a.name, a.id, a.action), Math.min(max, 80))
|
|
2519
2526
|
]);
|
|
2520
2527
|
}
|
|
@@ -2591,8 +2598,8 @@ function summarizeToolArgs(name, args, { max = DEFAULT_SUMMARY_MAX } = {}) {
|
|
|
2591
2598
|
return formatCountedUnit(collectionCount(a.query, a.prompt, a.task, a.goal), "query", "queries");
|
|
2592
2599
|
}
|
|
2593
2600
|
return truncateSingleLine(firstText(a.query, a.prompt, a.task, a.goal, a.path), Math.min(max, 80));
|
|
2594
|
-
case "
|
|
2595
|
-
const selected = splitToolSearchSelection(a.select);
|
|
2601
|
+
case "load_tool": {
|
|
2602
|
+
const selected = [...splitToolSearchSelection(a.names), ...splitToolSearchSelection(a.select)];
|
|
2596
2603
|
if (selected.length) return truncateToolText(selected.map(displayToolSearchTarget).join(", "), max);
|
|
2597
2604
|
return quoted(firstText(a.query, a.q, a.text), max);
|
|
2598
2605
|
}
|
|
@@ -2695,7 +2702,7 @@ var TOOL_CATEGORY = /* @__PURE__ */ new Map([
|
|
|
2695
2702
|
["glob", "Search"],
|
|
2696
2703
|
["list", "Search"],
|
|
2697
2704
|
["ls", "Search"],
|
|
2698
|
-
["
|
|
2705
|
+
["load_tool", "Load"],
|
|
2699
2706
|
["search", "Web Research"],
|
|
2700
2707
|
["web_search", "Web Research"],
|
|
2701
2708
|
["search_query", "Web Research"],
|
|
@@ -2732,7 +2739,7 @@ var TOOL_CATEGORY = /* @__PURE__ */ new Map([
|
|
|
2732
2739
|
["use_skill", "Skill"]
|
|
2733
2740
|
]);
|
|
2734
2741
|
function classifyToolCategory(name, args = {}) {
|
|
2735
|
-
if (
|
|
2742
|
+
if (isExternalMcpToolName(name)) return "MCP";
|
|
2736
2743
|
const normalized = normalizeToolName(name);
|
|
2737
2744
|
if (normalized === "code_graph") {
|
|
2738
2745
|
const mode = String(args.mode || args.action || "").toLowerCase();
|
|
@@ -2780,8 +2787,12 @@ function toolWorkUnit(name, args = {}, category = "") {
|
|
|
2780
2787
|
const a = parseToolArgs(args);
|
|
2781
2788
|
const normalized = normalizeToolName(name);
|
|
2782
2789
|
const cat = category || classifyToolCategory(name, a);
|
|
2783
|
-
if (
|
|
2784
|
-
|
|
2790
|
+
if (isExternalMcpToolName(name)) {
|
|
2791
|
+
const mcp = parseMcpToolName(name);
|
|
2792
|
+
return unitDescriptor("MCP", {
|
|
2793
|
+
count: queryCount(a, "query", "q", "text", "prompt", "path", "uri", "name", "id", "action") || 1,
|
|
2794
|
+
noun: `${titleCaseMcpServer(mcp.server)} tool`
|
|
2795
|
+
});
|
|
2785
2796
|
}
|
|
2786
2797
|
switch (normalized) {
|
|
2787
2798
|
case "read":
|
|
@@ -2816,8 +2827,8 @@ function toolWorkUnit(name, args = {}, category = "") {
|
|
|
2816
2827
|
case "list":
|
|
2817
2828
|
case "ls":
|
|
2818
2829
|
return unitDescriptor("Search", { count: queryCount(a, "path", "paths", "dir", "dirs", "cwd") || 1, active: "Listing", done: "Listed", noun: "directory", pluralNoun: "directories" });
|
|
2819
|
-
case "
|
|
2820
|
-
const selected = splitToolSearchSelection(a.select);
|
|
2830
|
+
case "load_tool": {
|
|
2831
|
+
const selected = [...splitToolSearchSelection(a.names), ...splitToolSearchSelection(a.select)];
|
|
2821
2832
|
if (selected.length) return unitDescriptor("Load", { count: selected.length, noun: "tool" });
|
|
2822
2833
|
return unitDescriptor("Load", { count: queryCount(a, "query", "q", "text") || 1, noun: "query", pluralNoun: "queries" });
|
|
2823
2834
|
}
|
|
@@ -2913,6 +2924,16 @@ function aggregateToolCategoryEntry(name, args = {}, category = "") {
|
|
|
2913
2924
|
count: Math.max(1, Number(unit.count || 1))
|
|
2914
2925
|
};
|
|
2915
2926
|
}
|
|
2927
|
+
function aggregateDoneCategories(calls = []) {
|
|
2928
|
+
const map = /* @__PURE__ */ new Map();
|
|
2929
|
+
for (const rec of calls || []) {
|
|
2930
|
+
if (!rec || rec.isError) continue;
|
|
2931
|
+
const entry = aggregateToolCategoryEntry(rec.name, rec.args, rec.category);
|
|
2932
|
+
const prev = map.get(entry.key);
|
|
2933
|
+
map.set(entry.key, { ...entry, count: Number(prev?.count || 0) + Number(entry.count || 1) });
|
|
2934
|
+
}
|
|
2935
|
+
return Object.fromEntries(map);
|
|
2936
|
+
}
|
|
2916
2937
|
function aggregateCount(value) {
|
|
2917
2938
|
if (value && typeof value === "object") return Math.max(0, Number(value.count || 0));
|
|
2918
2939
|
return Math.max(0, Number(value || 0));
|
|
@@ -3889,7 +3910,7 @@ function localStatusLineL2({
|
|
|
3889
3910
|
}
|
|
3890
3911
|
if (searchInfo && localNum(searchInfo.count) > 0) {
|
|
3891
3912
|
const elapsed = localNum(searchInfo.startedAt) > 0 ? localFormatElapsed(now - localNum(searchInfo.startedAt)) : "";
|
|
3892
|
-
l2Parts.push(`${spin} ${STATUS}Searching${RESET2}${elapsedSuffix(elapsed)}`);
|
|
3913
|
+
l2Parts.push(`${spin} ${STATUS}Web Searching${RESET2}${elapsedSuffix(elapsed)}`);
|
|
3893
3914
|
}
|
|
3894
3915
|
return l2Parts.length ? l2Parts.join(segSep) : "";
|
|
3895
3916
|
}
|
|
@@ -4943,7 +4964,7 @@ function PromptInput({
|
|
|
4943
4964
|
return;
|
|
4944
4965
|
}
|
|
4945
4966
|
if (!commandPaletteActive && (key.ctrl && inputKey === "v" || key.meta && inputKey === "v")) {
|
|
4946
|
-
handleExternalPaste("", { source: "clipboard-
|
|
4967
|
+
handleExternalPaste("", { source: "clipboard-shortcut" });
|
|
4947
4968
|
return;
|
|
4948
4969
|
}
|
|
4949
4970
|
if (key.return) {
|
|
@@ -5220,13 +5241,17 @@ ${d.value.slice(d.cursor)}`,
|
|
|
5220
5241
|
import React4 from "react";
|
|
5221
5242
|
import { Box as Box4, Text as Text4 } from "../../../vendor/ink/build/index.js";
|
|
5222
5243
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
5223
|
-
function QueuedCommands({ queued, columns }) {
|
|
5244
|
+
function QueuedCommands({ queued, columns, compact = false }) {
|
|
5224
5245
|
if (!queued || queued.length === 0) return null;
|
|
5225
5246
|
const bandColumns = Math.max(1, columns - 1);
|
|
5247
|
+
const contentWidth = Math.max(1, columns - 4);
|
|
5226
5248
|
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: queued.map((item) => {
|
|
5227
|
-
const contentWidth = Math.max(1, columns - 4);
|
|
5228
5249
|
const sourceText = String(item.displayText || item.text || "");
|
|
5229
|
-
|
|
5250
|
+
let displayText = sourceText;
|
|
5251
|
+
if (compact) {
|
|
5252
|
+
const oneLine2 = sourceText.replace(/\r?\n/g, " ");
|
|
5253
|
+
displayText = oneLine2.length > contentWidth ? contentWidth <= 1 ? "\u2026".repeat(contentWidth) : oneLine2.slice(0, Math.max(1, contentWidth - 1)) + "\u2026" : oneLine2;
|
|
5254
|
+
}
|
|
5230
5255
|
return /* @__PURE__ */ jsx4(Box4, { width: bandColumns, backgroundColor: theme.userMessageBackground, paddingLeft: 2, paddingRight: 1, children: /* @__PURE__ */ jsx4(Text4, { wrap: "wrap", children: /* @__PURE__ */ jsx4(Text4, { color: theme.mixdogIvory, children: displayText }) }) }, item.id);
|
|
5231
5256
|
}) });
|
|
5232
5257
|
}
|
|
@@ -6346,6 +6371,12 @@ function wrappedDetailRows(text, width) {
|
|
|
6346
6371
|
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
6347
6372
|
return wrapAnsi(value, w, { trim: false, hard: true }).split("\n").length;
|
|
6348
6373
|
}
|
|
6374
|
+
function queuedBandRows(text, width) {
|
|
6375
|
+
const value = String(text ?? "");
|
|
6376
|
+
if (!value) return 1;
|
|
6377
|
+
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
6378
|
+
return Math.max(1, wrapAnsi(value, w, { trim: false, hard: true }).split("\n").length);
|
|
6379
|
+
}
|
|
6349
6380
|
function visualRowStartOffsets(text, width) {
|
|
6350
6381
|
const value = String(text ?? "");
|
|
6351
6382
|
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
@@ -6830,6 +6861,7 @@ function TextEntryPanel({
|
|
|
6830
6861
|
|
|
6831
6862
|
// src/tui/paste-attachments.mjs
|
|
6832
6863
|
import { execFile } from "node:child_process";
|
|
6864
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
6833
6865
|
import { randomBytes } from "node:crypto";
|
|
6834
6866
|
import { existsSync, unlinkSync } from "node:fs";
|
|
6835
6867
|
import { readFile as readFileAsync, stat as statAsync } from "node:fs/promises";
|
|
@@ -6983,7 +7015,7 @@ function mimeForPath(path2) {
|
|
|
6983
7015
|
return IMAGE_MIME[extname(String(path2 || "")).toLowerCase()] || null;
|
|
6984
7016
|
}
|
|
6985
7017
|
function execFileBuffer(cmd, args, options = {}) {
|
|
6986
|
-
return new Promise((
|
|
7018
|
+
return new Promise((resolve5) => {
|
|
6987
7019
|
execFile(cmd, args, {
|
|
6988
7020
|
windowsHide: true,
|
|
6989
7021
|
encoding: "buffer",
|
|
@@ -6991,7 +7023,7 @@ function execFileBuffer(cmd, args, options = {}) {
|
|
|
6991
7023
|
timeout: 5e3,
|
|
6992
7024
|
...options
|
|
6993
7025
|
}, (error, stdout, stderr) => {
|
|
6994
|
-
|
|
7026
|
+
resolve5({ ok: !error, code: error?.code ?? 0, stdout, stderr, error });
|
|
6995
7027
|
});
|
|
6996
7028
|
});
|
|
6997
7029
|
}
|
|
@@ -7066,7 +7098,7 @@ function splitPastedImagePathCandidates(text) {
|
|
|
7066
7098
|
return out;
|
|
7067
7099
|
}
|
|
7068
7100
|
async function imageAttachmentFromBuffer(buffer, mimeType, { filename = "Pasted image", sourcePath = "" } = {}) {
|
|
7069
|
-
if (!
|
|
7101
|
+
if (!Buffer2.isBuffer(buffer) || buffer.length === 0) throw new Error("image is empty");
|
|
7070
7102
|
const ext = (mimeType || "image/png").split("/")[1] || "png";
|
|
7071
7103
|
const resized = await resizeImageBuffer(buffer, ext);
|
|
7072
7104
|
if (resized) {
|
|
@@ -7145,6 +7177,30 @@ async function readClipboardImageAttachment() {
|
|
|
7145
7177
|
}
|
|
7146
7178
|
}
|
|
7147
7179
|
}
|
|
7180
|
+
async function readClipboardText() {
|
|
7181
|
+
if (process.platform === "win32") {
|
|
7182
|
+
const ps = "$t=Get-Clipboard -Raw; if ($null -eq $t) { exit 2 }; [Console]::Out.Write([Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($t)))";
|
|
7183
|
+
const r = await execFileBuffer("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", ps], { timeout: 3e3 });
|
|
7184
|
+
if (!r.ok || !r.stdout?.length) return "";
|
|
7185
|
+
try {
|
|
7186
|
+
return Buffer2.from(r.stdout.toString("ascii").trim(), "base64").toString("utf8");
|
|
7187
|
+
} catch {
|
|
7188
|
+
return "";
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
if (process.platform === "darwin") {
|
|
7192
|
+
const r = await execFileBuffer("pbpaste", [], { timeout: 3e3 });
|
|
7193
|
+
return r.ok && r.stdout?.length ? r.stdout.toString("utf8") : "";
|
|
7194
|
+
}
|
|
7195
|
+
if (process.platform === "linux") {
|
|
7196
|
+
const wl = await execFileBuffer("wl-paste", ["--no-newline"], { timeout: 3e3 });
|
|
7197
|
+
if (wl.ok && wl.stdout?.length) return wl.stdout.toString("utf8");
|
|
7198
|
+
const xc = await execFileBuffer("xclip", ["-selection", "clipboard", "-o"], { timeout: 3e3 });
|
|
7199
|
+
if (xc.ok && xc.stdout?.length) return xc.stdout.toString("utf8");
|
|
7200
|
+
return "";
|
|
7201
|
+
}
|
|
7202
|
+
return "";
|
|
7203
|
+
}
|
|
7148
7204
|
|
|
7149
7205
|
// src/standalone/projects.mjs
|
|
7150
7206
|
import { homedir } from "node:os";
|
|
@@ -7302,12 +7358,12 @@ import { spawn } from "node:child_process";
|
|
|
7302
7358
|
import path from "node:path";
|
|
7303
7359
|
var DIALOG_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
7304
7360
|
function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
7305
|
-
return new Promise((
|
|
7361
|
+
return new Promise((resolve5) => {
|
|
7306
7362
|
let child;
|
|
7307
7363
|
try {
|
|
7308
7364
|
child = spawn(cmd, args, { stdio: ["ignore", "pipe", "ignore"], windowsHide: true });
|
|
7309
7365
|
} catch (error) {
|
|
7310
|
-
|
|
7366
|
+
resolve5({ ok: false, code: -1, stdout: "", error });
|
|
7311
7367
|
return;
|
|
7312
7368
|
}
|
|
7313
7369
|
let stdout = "";
|
|
@@ -7319,7 +7375,7 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
|
7319
7375
|
child.kill();
|
|
7320
7376
|
} catch {
|
|
7321
7377
|
}
|
|
7322
|
-
|
|
7378
|
+
resolve5({ ok: false, code: -1, stdout: "", error: new Error("dialog timed out") });
|
|
7323
7379
|
}, timeoutMs);
|
|
7324
7380
|
timer.unref?.();
|
|
7325
7381
|
child.stdout.on("data", (chunk) => {
|
|
@@ -7329,13 +7385,13 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
|
7329
7385
|
if (settled) return;
|
|
7330
7386
|
settled = true;
|
|
7331
7387
|
clearTimeout(timer);
|
|
7332
|
-
|
|
7388
|
+
resolve5({ ok: false, code: -1, stdout: "", error });
|
|
7333
7389
|
});
|
|
7334
7390
|
child.on("close", (code) => {
|
|
7335
7391
|
if (settled) return;
|
|
7336
7392
|
settled = true;
|
|
7337
7393
|
clearTimeout(timer);
|
|
7338
|
-
|
|
7394
|
+
resolve5({ ok: code === 0, code, stdout });
|
|
7339
7395
|
});
|
|
7340
7396
|
});
|
|
7341
7397
|
}
|
|
@@ -7343,10 +7399,10 @@ function spawnFailed(result) {
|
|
|
7343
7399
|
return !!result && result.code === -1;
|
|
7344
7400
|
}
|
|
7345
7401
|
function commandExists(cmd) {
|
|
7346
|
-
return new Promise((
|
|
7402
|
+
return new Promise((resolve5) => {
|
|
7347
7403
|
const probe = process.platform === "win32" ? spawn("where", [cmd], { stdio: "ignore", windowsHide: true }) : spawn("which", [cmd], { stdio: "ignore" });
|
|
7348
|
-
probe.on("error", () =>
|
|
7349
|
-
probe.on("close", (code) =>
|
|
7404
|
+
probe.on("error", () => resolve5(false));
|
|
7405
|
+
probe.on("close", (code) => resolve5(code === 0));
|
|
7350
7406
|
});
|
|
7351
7407
|
}
|
|
7352
7408
|
function psSingleQuoted(value) {
|
|
@@ -7845,64 +7901,67 @@ function memoryCoreResultErrorText(text) {
|
|
|
7845
7901
|
}
|
|
7846
7902
|
|
|
7847
7903
|
// src/tui/app/clipboard.mjs
|
|
7848
|
-
import { Buffer as
|
|
7904
|
+
import { Buffer as Buffer3 } from "node:buffer";
|
|
7849
7905
|
import { spawn as spawn2 } from "node:child_process";
|
|
7850
7906
|
function osc52ClipboardSequence(text) {
|
|
7851
|
-
const b64 =
|
|
7907
|
+
const b64 = Buffer3.from(String(text ?? ""), "utf8").toString("base64");
|
|
7852
7908
|
const raw = `\x1B]52;c;${b64}\x07`;
|
|
7853
7909
|
if (!process.env.TMUX) return raw;
|
|
7854
7910
|
return `\x1BPtmux;${raw.replaceAll("\x1B", "\x1B\x1B")}\x1B\\`;
|
|
7855
7911
|
}
|
|
7912
|
+
var OSC52_MAX_BYTES = 256 * 1024;
|
|
7856
7913
|
function writeOsc52Clipboard(text) {
|
|
7914
|
+
const value = String(text ?? "");
|
|
7915
|
+
if (Buffer3.byteLength(value, "utf8") > OSC52_MAX_BYTES) return false;
|
|
7857
7916
|
try {
|
|
7858
|
-
process.stdout.write(osc52ClipboardSequence(
|
|
7917
|
+
process.stdout.write(osc52ClipboardSequence(value));
|
|
7859
7918
|
return true;
|
|
7860
7919
|
} catch {
|
|
7861
7920
|
return false;
|
|
7862
7921
|
}
|
|
7863
7922
|
}
|
|
7864
7923
|
function nativeClipboardCommand(text) {
|
|
7924
|
+
const value = String(text ?? "");
|
|
7865
7925
|
if (process.platform === "win32") {
|
|
7866
7926
|
return {
|
|
7867
|
-
cmd: "
|
|
7868
|
-
args: [
|
|
7869
|
-
|
|
7870
|
-
"-NoProfile",
|
|
7871
|
-
"-NonInteractive",
|
|
7872
|
-
"-Command",
|
|
7873
|
-
"$b=[Console]::In.ReadToEnd();$t=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($b));Set-Clipboard -Value $t"
|
|
7874
|
-
],
|
|
7875
|
-
input: Buffer2.from(String(text ?? ""), "utf8").toString("base64")
|
|
7927
|
+
cmd: "clip.exe",
|
|
7928
|
+
args: [],
|
|
7929
|
+
input: Buffer3.from(value, "utf16le")
|
|
7876
7930
|
};
|
|
7877
7931
|
}
|
|
7878
|
-
if (process.platform === "darwin") return { cmd: "pbcopy", args: [], input:
|
|
7879
|
-
if (process.env.WAYLAND_DISPLAY) return { cmd: "wl-copy", args: [], input:
|
|
7880
|
-
return { cmd: "xclip", args: ["-selection", "clipboard"], input:
|
|
7932
|
+
if (process.platform === "darwin") return { cmd: "pbcopy", args: [], input: value };
|
|
7933
|
+
if (process.env.WAYLAND_DISPLAY) return { cmd: "wl-copy", args: [], input: value };
|
|
7934
|
+
return { cmd: "xclip", args: ["-selection", "clipboard"], input: value };
|
|
7881
7935
|
}
|
|
7882
7936
|
function copyToClipboard(text) {
|
|
7883
7937
|
const value = String(text ?? "");
|
|
7884
7938
|
const wroteOsc52 = writeOsc52Clipboard(value);
|
|
7885
|
-
return new Promise((
|
|
7939
|
+
return new Promise((resolve5, reject) => {
|
|
7886
7940
|
const { cmd, args, input } = nativeClipboardCommand(value);
|
|
7887
7941
|
let child;
|
|
7888
7942
|
try {
|
|
7889
7943
|
child = spawn2(cmd, args, { stdio: ["pipe", "ignore", "ignore"], windowsHide: true });
|
|
7890
7944
|
} catch (e) {
|
|
7891
|
-
if (wroteOsc52)
|
|
7945
|
+
if (wroteOsc52) resolve5();
|
|
7892
7946
|
else reject(e);
|
|
7893
7947
|
return;
|
|
7894
7948
|
}
|
|
7895
7949
|
child.on("error", (e) => {
|
|
7896
|
-
if (wroteOsc52)
|
|
7897
|
-
else reject(e);
|
|
7898
|
-
});
|
|
7899
|
-
child.on("close", (code) => {
|
|
7900
|
-
if (code === 0 || wroteOsc52) resolve6();
|
|
7901
|
-
else reject(new Error(`${cmd} exited with code ${code}`));
|
|
7950
|
+
if (!wroteOsc52) reject(e);
|
|
7902
7951
|
});
|
|
7952
|
+
if (!wroteOsc52) {
|
|
7953
|
+
child.on("close", (code) => {
|
|
7954
|
+
if (code === 0) resolve5();
|
|
7955
|
+
else reject(new Error(`${cmd} exited with code ${code}`));
|
|
7956
|
+
});
|
|
7957
|
+
}
|
|
7903
7958
|
child.stdin.on("error", () => {
|
|
7904
7959
|
});
|
|
7905
7960
|
child.stdin.end(input);
|
|
7961
|
+
if (wroteOsc52) {
|
|
7962
|
+
child.unref?.();
|
|
7963
|
+
resolve5();
|
|
7964
|
+
}
|
|
7906
7965
|
});
|
|
7907
7966
|
}
|
|
7908
7967
|
|
|
@@ -10382,6 +10441,7 @@ var ALT_SCROLL_ON = "\x1B[?1007h";
|
|
|
10382
10441
|
var MOUSE_MODIFIER_MASK = 4 | 8 | 16;
|
|
10383
10442
|
var MOUSE_CTRL_MASK = 16;
|
|
10384
10443
|
var MOUSE_SHIFT_MASK = 4;
|
|
10444
|
+
var IS_WINDOWS_TERMINAL = Boolean(process.env.WT_SESSION);
|
|
10385
10445
|
function useMouseInput({
|
|
10386
10446
|
inkInput,
|
|
10387
10447
|
isRawModeSupported,
|
|
@@ -10409,7 +10469,8 @@ function useMouseInput({
|
|
|
10409
10469
|
setMeasuredRowsVersion,
|
|
10410
10470
|
clearStitchBuffer
|
|
10411
10471
|
}) {
|
|
10412
|
-
const
|
|
10472
|
+
const zoomTimerRef = useRef6(null);
|
|
10473
|
+
const edgeAutoscrollRef = useRef6({ dir: 0, timer: null, noMove: 0 });
|
|
10413
10474
|
const passthroughCtrlWheelZoom = useCallback2(() => {
|
|
10414
10475
|
if (!stdout?.write) return;
|
|
10415
10476
|
try {
|
|
@@ -10417,19 +10478,19 @@ function useMouseInput({
|
|
|
10417
10478
|
} catch {
|
|
10418
10479
|
return;
|
|
10419
10480
|
}
|
|
10420
|
-
if (
|
|
10421
|
-
|
|
10422
|
-
|
|
10481
|
+
if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
|
|
10482
|
+
zoomTimerRef.current = setTimeout(() => {
|
|
10483
|
+
zoomTimerRef.current = null;
|
|
10423
10484
|
try {
|
|
10424
10485
|
stdout.write(ALT_SCROLL_ON + MOUSE_TRACKING_ON);
|
|
10425
10486
|
} catch {
|
|
10426
10487
|
}
|
|
10427
10488
|
}, 700);
|
|
10428
|
-
|
|
10429
|
-
}, [stdout]);
|
|
10489
|
+
zoomTimerRef.current.unref?.();
|
|
10490
|
+
}, [stdout, zoomTimerRef]);
|
|
10430
10491
|
useEffect6(() => () => {
|
|
10431
|
-
if (
|
|
10432
|
-
}, []);
|
|
10492
|
+
if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
|
|
10493
|
+
}, [zoomTimerRef]);
|
|
10433
10494
|
useEffect6(() => {
|
|
10434
10495
|
if (!inkInput || !isRawModeSupported) return void 0;
|
|
10435
10496
|
const WHEEL_SGR = /\x1b\[<(\d+);/;
|
|
@@ -10494,6 +10555,65 @@ function useMouseInput({
|
|
|
10494
10555
|
promptMouseSelectionRef.current?.clear?.();
|
|
10495
10556
|
applySelectionRect(null);
|
|
10496
10557
|
};
|
|
10558
|
+
const EDGE_AUTOSCROLL_INTERVAL_MS = 50;
|
|
10559
|
+
const stopEdgeAutoscroll = () => {
|
|
10560
|
+
const st = edgeAutoscrollRef.current;
|
|
10561
|
+
if (st.timer) {
|
|
10562
|
+
clearInterval(st.timer);
|
|
10563
|
+
st.timer = null;
|
|
10564
|
+
}
|
|
10565
|
+
st.dir = 0;
|
|
10566
|
+
st.noMove = 0;
|
|
10567
|
+
};
|
|
10568
|
+
const startEdgeAutoscroll = (dir) => {
|
|
10569
|
+
const st = edgeAutoscrollRef.current;
|
|
10570
|
+
if (st.dir === dir && st.timer) return;
|
|
10571
|
+
stopEdgeAutoscroll();
|
|
10572
|
+
st.dir = dir;
|
|
10573
|
+
st.noMove = 0;
|
|
10574
|
+
st.timer = setInterval(() => {
|
|
10575
|
+
const drag = dragRef.current;
|
|
10576
|
+
const st2 = edgeAutoscrollRef.current;
|
|
10577
|
+
if (!drag.active || drag.region !== "transcript" || st2.dir === 0) {
|
|
10578
|
+
stopEdgeAutoscroll();
|
|
10579
|
+
return;
|
|
10580
|
+
}
|
|
10581
|
+
const before = Number(scrollTargetRef.current) || 0;
|
|
10582
|
+
queueScrollCoalesced(st2.dir * 3);
|
|
10583
|
+
if ((Number(scrollTargetRef.current) || 0) !== before) {
|
|
10584
|
+
st2.noMove = 0;
|
|
10585
|
+
} else if (++st2.noMove >= 3) {
|
|
10586
|
+
stopEdgeAutoscroll();
|
|
10587
|
+
}
|
|
10588
|
+
}, EDGE_AUTOSCROLL_INTERVAL_MS);
|
|
10589
|
+
st.timer.unref?.();
|
|
10590
|
+
};
|
|
10591
|
+
const finalizeActiveDrag = (fx, fy) => {
|
|
10592
|
+
const drag = dragRef.current;
|
|
10593
|
+
if (!drag.active) return;
|
|
10594
|
+
stopEdgeAutoscroll();
|
|
10595
|
+
const region = drag.region;
|
|
10596
|
+
if (region === "prompt") {
|
|
10597
|
+
const offset = promptOffsetAt(fx, fy);
|
|
10598
|
+
drag.active = false;
|
|
10599
|
+
promptMouseSelectionRef.current?.extendTo?.(offset, true);
|
|
10600
|
+
return;
|
|
10601
|
+
}
|
|
10602
|
+
const span = drag.anchorSpan;
|
|
10603
|
+
const finalY = region === "status" ? clampToStatusBand(fy) : clampToTranscriptViewport(fy);
|
|
10604
|
+
drag.active = false;
|
|
10605
|
+
if (span) {
|
|
10606
|
+
const rect = buildSpanRect(span, fx, finalY, region, drag.anchorScroll);
|
|
10607
|
+
applySelectionRect(rect);
|
|
10608
|
+
} else {
|
|
10609
|
+
const anchor = region === "status" ? drag.anchor : selectionPointAtCurrentScroll(drag.anchor, drag.anchorScroll);
|
|
10610
|
+
const rect = linearSelection(anchor, { x: fx, y: finalY });
|
|
10611
|
+
const empty = rect.x1 === rect.x2 && rect.y1 === rect.y2;
|
|
10612
|
+
if (empty) applySelectionRect(null);
|
|
10613
|
+
else applySelectionRect(rect);
|
|
10614
|
+
}
|
|
10615
|
+
if (TRANSCRIPT_MEASURED_ROWS) setMeasuredRowsVersion((v) => (v + 1) % 1e6);
|
|
10616
|
+
};
|
|
10497
10617
|
const onMouse = (event) => {
|
|
10498
10618
|
if (!event || typeof event !== "object") return;
|
|
10499
10619
|
let up = 0;
|
|
@@ -10505,15 +10625,20 @@ function useMouseInput({
|
|
|
10505
10625
|
const wm = WHEEL_SGR.exec(seq);
|
|
10506
10626
|
const ctrl = wm ? (Number(wm[1]) & MOUSE_CTRL_MASK) !== 0 : false;
|
|
10507
10627
|
if (ctrl) {
|
|
10628
|
+
if (dragRef.current.active) {
|
|
10629
|
+
const last = dragRef.current.last || {};
|
|
10630
|
+
finalizeActiveDrag(Number(last.x) || 0, Number(last.y) || 0);
|
|
10631
|
+
} else {
|
|
10632
|
+
stopEdgeAutoscroll();
|
|
10633
|
+
}
|
|
10508
10634
|
passthroughCtrlWheelZoom();
|
|
10509
10635
|
return;
|
|
10510
10636
|
}
|
|
10511
10637
|
if (name === "wheelup") up += 1;
|
|
10512
10638
|
else down += 1;
|
|
10513
10639
|
if (up !== 0 || down !== 0) {
|
|
10514
|
-
if (dragRef.current.active) return;
|
|
10515
10640
|
const palette = slashPaletteRef.current;
|
|
10516
|
-
if (palette.open && palette.count > 0) {
|
|
10641
|
+
if (!dragRef.current.active && palette.open && palette.count > 0) {
|
|
10517
10642
|
const step = down - up;
|
|
10518
10643
|
if (step !== 0) {
|
|
10519
10644
|
setSlashIndex((index) => Math.max(0, Math.min(palette.count - 1, index + step)));
|
|
@@ -10535,14 +10660,64 @@ function useMouseInput({
|
|
|
10535
10660
|
const baseButton = button & 3;
|
|
10536
10661
|
const isMotion = (button & 32) !== 0;
|
|
10537
10662
|
const shiftHeld = (button & MOUSE_SHIFT_MASK) !== 0;
|
|
10663
|
+
const ctrlHeld = (button & MOUSE_CTRL_MASK) !== 0;
|
|
10664
|
+
if (IS_WINDOWS_TERMINAL && shiftHeld) return;
|
|
10665
|
+
if (IS_WINDOWS_TERMINAL && press && !isMotion) {
|
|
10666
|
+
store.forceRenderRepaint?.();
|
|
10667
|
+
}
|
|
10668
|
+
const extendHeld = shiftHeld || ctrlHeld;
|
|
10669
|
+
const isRightPress = baseButton === 2 && press && !isMotion;
|
|
10670
|
+
if (isRightPress) {
|
|
10671
|
+
if (isInPromptBox(x, y)) {
|
|
10672
|
+
const ctl = promptMouseSelectionRef.current;
|
|
10673
|
+
if (ctl?.hasSelection?.()) {
|
|
10674
|
+
applySelectionRect(null);
|
|
10675
|
+
const offset = promptOffsetAt(x, y);
|
|
10676
|
+
stopSmoothScroll();
|
|
10677
|
+
dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: false, rect: null, region: "prompt", anchorSpan: null };
|
|
10678
|
+
if (offset != null) ctl.extendTo?.(offset, true);
|
|
10679
|
+
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
10680
|
+
}
|
|
10681
|
+
return;
|
|
10682
|
+
}
|
|
10683
|
+
const inTranscriptR = isInTranscriptViewport(y);
|
|
10684
|
+
const inStatusR = !inTranscriptR && isInStatusBand(y);
|
|
10685
|
+
if (!inTranscriptR && !inStatusR) return;
|
|
10686
|
+
const regionR = inTranscriptR ? "transcript" : "status";
|
|
10687
|
+
const nowR = Date.now();
|
|
10688
|
+
if (dragRef.current.region === regionR && dragRef.current.anchorSpan) {
|
|
10689
|
+
promptMouseSelectionRef.current?.clear?.();
|
|
10690
|
+
const selectionY = regionR === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10691
|
+
const span = dragRef.current.anchorSpan;
|
|
10692
|
+
const rect = buildSpanRect(span, x, selectionY, regionR, dragRef.current.anchorScroll);
|
|
10693
|
+
stopSmoothScroll();
|
|
10694
|
+
dragRef.current = { ...dragRef.current, last: { x, y: selectionY }, active: false, region: regionR };
|
|
10695
|
+
applySelectionRect(rect);
|
|
10696
|
+
lastClickRef.current = { x, y, t: nowR, count: 1 };
|
|
10697
|
+
return;
|
|
10698
|
+
}
|
|
10699
|
+
if (dragRef.current.region === regionR && !dragRef.current.anchorSpan && dragRef.current.anchor && dragRef.current.rect && !(dragRef.current.rect.x1 === dragRef.current.rect.x2 && dragRef.current.rect.y1 === dragRef.current.rect.y2)) {
|
|
10700
|
+
promptMouseSelectionRef.current?.clear?.();
|
|
10701
|
+
const selectionY = regionR === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10702
|
+
const anchor = regionR === "status" ? dragRef.current.anchor : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
10703
|
+
const rect = linearSelection(anchor, { x, y: selectionY });
|
|
10704
|
+
stopSmoothScroll();
|
|
10705
|
+
dragRef.current = { ...dragRef.current, last: { x, y: selectionY }, active: false, region: regionR };
|
|
10706
|
+
applySelectionRect(rect);
|
|
10707
|
+
lastClickRef.current = { x, y, t: nowR, count: 1 };
|
|
10708
|
+
return;
|
|
10709
|
+
}
|
|
10710
|
+
return;
|
|
10711
|
+
}
|
|
10538
10712
|
if (baseButton === 0 && press && !isMotion) {
|
|
10713
|
+
stopEdgeAutoscroll();
|
|
10539
10714
|
if (isInPromptBox(x, y)) {
|
|
10540
10715
|
applySelectionRect(null);
|
|
10541
10716
|
const offset = promptOffsetAt(x, y);
|
|
10542
10717
|
stopSmoothScroll();
|
|
10543
10718
|
dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: true, rect: null, region: "prompt", anchorSpan: null };
|
|
10544
10719
|
const ctl = promptMouseSelectionRef.current;
|
|
10545
|
-
if (
|
|
10720
|
+
if (extendHeld && ctl?.hasSelection?.()) {
|
|
10546
10721
|
if (offset != null) ctl.extendTo?.(offset, true);
|
|
10547
10722
|
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
10548
10723
|
return;
|
|
@@ -10577,7 +10752,22 @@ function useMouseInput({
|
|
|
10577
10752
|
const region = inTranscript ? "transcript" : "status";
|
|
10578
10753
|
promptMouseSelectionRef.current?.clear?.();
|
|
10579
10754
|
const now = Date.now();
|
|
10580
|
-
if (
|
|
10755
|
+
if (extendHeld && dragRef.current.region === region && dragRef.current.anchorSpan) {
|
|
10756
|
+
const selectionY = region === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10757
|
+
const span = dragRef.current.anchorSpan;
|
|
10758
|
+
const rect = buildSpanRect(span, x, selectionY, region, dragRef.current.anchorScroll);
|
|
10759
|
+
stopSmoothScroll();
|
|
10760
|
+
dragRef.current = {
|
|
10761
|
+
...dragRef.current,
|
|
10762
|
+
last: { x, y: selectionY },
|
|
10763
|
+
active: true,
|
|
10764
|
+
region
|
|
10765
|
+
};
|
|
10766
|
+
applySelectionRect(rect);
|
|
10767
|
+
lastClickRef.current = { x, y, t: now, count: 1 };
|
|
10768
|
+
return;
|
|
10769
|
+
}
|
|
10770
|
+
if (extendHeld && dragRef.current.region === region && !dragRef.current.anchorSpan && dragRef.current.anchor && dragRef.current.rect && !(dragRef.current.rect.x1 === dragRef.current.rect.x2 && dragRef.current.rect.y1 === dragRef.current.rect.y2)) {
|
|
10581
10771
|
const selectionY = region === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10582
10772
|
const anchor = region === "status" ? dragRef.current.anchor : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
10583
10773
|
const rect = linearSelection(anchor, { x, y: selectionY });
|
|
@@ -10656,41 +10846,23 @@ function useMouseInput({
|
|
|
10656
10846
|
const { top, bottom } = transcriptViewport();
|
|
10657
10847
|
if (y <= 1 && (y < prevDragY || y < top)) {
|
|
10658
10848
|
queueScrollCoalesced(3);
|
|
10849
|
+
startEdgeAutoscroll(1);
|
|
10659
10850
|
} else if (y >= frameRows - 5 && (y > prevDragY || y > bottom)) {
|
|
10660
10851
|
queueScrollCoalesced(-3);
|
|
10661
|
-
|
|
10662
|
-
}
|
|
10663
|
-
} else if (!press && dragRef.current.active) {
|
|
10664
|
-
const region = dragRef.current.region;
|
|
10665
|
-
if (region === "prompt") {
|
|
10666
|
-
const offset = promptOffsetAt(x, y);
|
|
10667
|
-
dragRef.current.active = false;
|
|
10668
|
-
promptMouseSelectionRef.current?.extendTo?.(offset, true);
|
|
10669
|
-
return;
|
|
10670
|
-
}
|
|
10671
|
-
const span = dragRef.current.anchorSpan;
|
|
10672
|
-
const releaseY = region === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10673
|
-
dragRef.current.active = false;
|
|
10674
|
-
if (span) {
|
|
10675
|
-
const rect = buildSpanRect(span, x, releaseY, region, dragRef.current.anchorScroll);
|
|
10676
|
-
applySelectionRect(rect);
|
|
10677
|
-
} else {
|
|
10678
|
-
const anchor = region === "status" ? dragRef.current.anchor : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
10679
|
-
const rect = linearSelection(anchor, { x, y: releaseY });
|
|
10680
|
-
const empty = rect.x1 === rect.x2 && rect.y1 === rect.y2;
|
|
10681
|
-
if (empty) {
|
|
10682
|
-
applySelectionRect(null);
|
|
10852
|
+
startEdgeAutoscroll(-1);
|
|
10683
10853
|
} else {
|
|
10684
|
-
|
|
10854
|
+
stopEdgeAutoscroll();
|
|
10685
10855
|
}
|
|
10686
10856
|
}
|
|
10687
|
-
|
|
10857
|
+
} else if (!press && baseButton === 0 && dragRef.current.active) {
|
|
10858
|
+
finalizeActiveDrag(x, y);
|
|
10688
10859
|
}
|
|
10689
10860
|
}
|
|
10690
10861
|
};
|
|
10691
10862
|
inkInput.on("mouse", onMouse);
|
|
10692
10863
|
return () => {
|
|
10693
10864
|
inkInput.off("mouse", onMouse);
|
|
10865
|
+
stopEdgeAutoscroll();
|
|
10694
10866
|
};
|
|
10695
10867
|
}, [inkInput, isRawModeSupported, store, passthroughCtrlWheelZoom, rows, scrollTranscriptRows, queueScrollCoalesced, applySelectionRect, applySelectionRectThrottled, selectionPointAtCurrentScroll, buildSpanRect]);
|
|
10696
10868
|
}
|
|
@@ -10742,26 +10914,53 @@ function useTranscriptScroll({
|
|
|
10742
10914
|
const scroll = stitchHarvestScrollRef.current;
|
|
10743
10915
|
for (const row of rows) {
|
|
10744
10916
|
if (!row || typeof row.y !== "number") continue;
|
|
10745
|
-
stitchBufferRef.current.set(row.y - scroll,
|
|
10917
|
+
stitchBufferRef.current.set(row.y - scroll, {
|
|
10918
|
+
text: typeof row.text === "string" ? row.text : "",
|
|
10919
|
+
sw: row.sw === true
|
|
10920
|
+
});
|
|
10746
10921
|
}
|
|
10747
10922
|
}, 0);
|
|
10748
10923
|
}, [store]);
|
|
10924
|
+
const harvestStitchRowsNow = useCallback3((scroll) => {
|
|
10925
|
+
if (dragRef.current.region !== "transcript") return;
|
|
10926
|
+
const rows = store.getRenderSelectionRows?.();
|
|
10927
|
+
if (!Array.isArray(rows)) return;
|
|
10928
|
+
const s = Number(scroll) || 0;
|
|
10929
|
+
for (const row of rows) {
|
|
10930
|
+
if (!row || typeof row.y !== "number") continue;
|
|
10931
|
+
stitchBufferRef.current.set(row.y - s, {
|
|
10932
|
+
text: typeof row.text === "string" ? row.text : "",
|
|
10933
|
+
sw: row.sw === true
|
|
10934
|
+
});
|
|
10935
|
+
}
|
|
10936
|
+
}, [store]);
|
|
10749
10937
|
const getStitchedSelectionText = useCallback3(() => {
|
|
10938
|
+
const empty = { text: "", complete: false };
|
|
10750
10939
|
const buf = stitchBufferRef.current;
|
|
10751
|
-
if (!buf.size) return
|
|
10752
|
-
if (dragRef.current.region !== "transcript") return
|
|
10940
|
+
if (!buf.size) return empty;
|
|
10941
|
+
if (dragRef.current.region !== "transcript") return empty;
|
|
10753
10942
|
const rect = dragRef.current.rect;
|
|
10754
|
-
if (!rect) return
|
|
10943
|
+
if (!rect) return empty;
|
|
10755
10944
|
const y1 = Number(rect.y1);
|
|
10756
10945
|
const y2 = Number(rect.y2);
|
|
10757
|
-
if (!Number.isFinite(y1) || !Number.isFinite(y2)) return
|
|
10946
|
+
if (!Number.isFinite(y1) || !Number.isFinite(y2)) return empty;
|
|
10758
10947
|
const scroll = Number(scrollTargetRef.current) || 0;
|
|
10759
10948
|
const lo = Math.min(y1, y2) - scroll;
|
|
10760
10949
|
const hi = Math.max(y1, y2) - scroll;
|
|
10761
10950
|
const keys = [...buf.keys()].filter((k) => k >= lo && k <= hi).sort((a, b) => a - b);
|
|
10762
|
-
if (!keys.length) return
|
|
10763
|
-
const
|
|
10764
|
-
|
|
10951
|
+
if (!keys.length) return empty;
|
|
10952
|
+
const complete = keys.length === hi - lo + 1;
|
|
10953
|
+
const logical = [];
|
|
10954
|
+
for (const k of keys) {
|
|
10955
|
+
const entry = buf.get(k);
|
|
10956
|
+
if (entry == null) continue;
|
|
10957
|
+
const t = typeof entry === "string" ? entry : entry.text ?? "";
|
|
10958
|
+
const sw = typeof entry === "string" ? false : entry.sw === true;
|
|
10959
|
+
if (sw && logical.length > 0) logical[logical.length - 1] += t;
|
|
10960
|
+
else logical.push(t);
|
|
10961
|
+
}
|
|
10962
|
+
const text = logical.map((l) => l.replace(/\s+$/u, "")).join("\n");
|
|
10963
|
+
return text.trim() ? { text, complete } : empty;
|
|
10765
10964
|
}, []);
|
|
10766
10965
|
const stopSmoothScroll = useCallback3(() => {
|
|
10767
10966
|
if (!scrollAnimationRef.current) return;
|
|
@@ -10856,6 +11055,25 @@ function useTranscriptScroll({
|
|
|
10856
11055
|
if (nextRect) harvestStitchRowsSoon();
|
|
10857
11056
|
return true;
|
|
10858
11057
|
}, [store, rememberSelectionTextSoon, harvestStitchRowsSoon]);
|
|
11058
|
+
const cancelPendingSelectionPaint = useCallback3(() => {
|
|
11059
|
+
const state = selectionPaintRef.current;
|
|
11060
|
+
if (state.timer) {
|
|
11061
|
+
clearTimeout(state.timer);
|
|
11062
|
+
state.timer = null;
|
|
11063
|
+
}
|
|
11064
|
+
state.pending = null;
|
|
11065
|
+
}, []);
|
|
11066
|
+
const flushPendingSelectionPaint = useCallback3(() => {
|
|
11067
|
+
const state = selectionPaintRef.current;
|
|
11068
|
+
if (!state.timer && !state.pending) return;
|
|
11069
|
+
const pending = state.pending;
|
|
11070
|
+
if (state.timer) {
|
|
11071
|
+
clearTimeout(state.timer);
|
|
11072
|
+
state.timer = null;
|
|
11073
|
+
}
|
|
11074
|
+
state.pending = null;
|
|
11075
|
+
if (pending) paintSelectionRect(pending, { rememberText: false, immediate: true });
|
|
11076
|
+
}, [paintSelectionRect]);
|
|
10859
11077
|
const applySelectionRect = useCallback3((rect) => {
|
|
10860
11078
|
const clippedRect = withSelectionClip(rect);
|
|
10861
11079
|
dragRef.current.rect = clippedRect || null;
|
|
@@ -10863,14 +11081,9 @@ function useTranscriptScroll({
|
|
|
10863
11081
|
selectionTextRef.current = "";
|
|
10864
11082
|
clearStitchBuffer();
|
|
10865
11083
|
}
|
|
10866
|
-
|
|
10867
|
-
if (state.timer) {
|
|
10868
|
-
clearTimeout(state.timer);
|
|
10869
|
-
state.timer = null;
|
|
10870
|
-
state.pending = null;
|
|
10871
|
-
}
|
|
11084
|
+
cancelPendingSelectionPaint();
|
|
10872
11085
|
paintSelectionRect(clippedRect, { rememberText: true, immediate: true });
|
|
10873
|
-
}, [paintSelectionRect, withSelectionClip, clearStitchBuffer]);
|
|
11086
|
+
}, [paintSelectionRect, withSelectionClip, clearStitchBuffer, cancelPendingSelectionPaint]);
|
|
10874
11087
|
const applySelectionRectThrottled = useCallback3((rect) => {
|
|
10875
11088
|
const clippedRect = withSelectionClip(rect, { captureText: false });
|
|
10876
11089
|
if (selectionRectsEqual(dragRef.current.rect, clippedRect)) return;
|
|
@@ -10880,11 +11093,7 @@ function useTranscriptScroll({
|
|
|
10880
11093
|
const now = Date.now();
|
|
10881
11094
|
const elapsed = now - state.t;
|
|
10882
11095
|
if (elapsed >= SELECTION_PAINT_INTERVAL_MS) {
|
|
10883
|
-
|
|
10884
|
-
clearTimeout(state.timer);
|
|
10885
|
-
state.timer = null;
|
|
10886
|
-
state.pending = null;
|
|
10887
|
-
}
|
|
11096
|
+
cancelPendingSelectionPaint();
|
|
10888
11097
|
paintSelectionRect(clippedRect, { rememberText: false });
|
|
10889
11098
|
return;
|
|
10890
11099
|
}
|
|
@@ -10899,7 +11108,7 @@ function useTranscriptScroll({
|
|
|
10899
11108
|
}, Math.max(1, SELECTION_PAINT_INTERVAL_MS - elapsed));
|
|
10900
11109
|
state.timer.unref?.();
|
|
10901
11110
|
}
|
|
10902
|
-
}, [paintSelectionRect, withSelectionClip]);
|
|
11111
|
+
}, [paintSelectionRect, withSelectionClip, cancelPendingSelectionPaint]);
|
|
10903
11112
|
const selectionPointAtCurrentScroll = useCallback3((point, pointScroll = 0) => {
|
|
10904
11113
|
if (!point) return null;
|
|
10905
11114
|
return {
|
|
@@ -10929,14 +11138,14 @@ function useTranscriptScroll({
|
|
|
10929
11138
|
mHi = { x: lr.x2, y: lr.y2 };
|
|
10930
11139
|
} else {
|
|
10931
11140
|
mLo = { x: 0, y };
|
|
10932
|
-
mHi = { x, y };
|
|
11141
|
+
mHi = { x: Math.max(0, frameColumns - 1), y };
|
|
10933
11142
|
}
|
|
10934
11143
|
}
|
|
10935
11144
|
const rect = (a, b) => ({ mode: "linear", x1: a.x, y1: a.y, x2: b.x, y2: b.y });
|
|
10936
11145
|
if (comparePoints(mHi, spanLo) < 0) return rect(spanHi, mLo);
|
|
10937
11146
|
if (comparePoints(mLo, spanHi) > 0) return rect(spanLo, mHi);
|
|
10938
11147
|
return rect(spanLo, spanHi);
|
|
10939
|
-
}, [store, selectionPointAtCurrentScroll]);
|
|
11148
|
+
}, [store, frameColumns, selectionPointAtCurrentScroll]);
|
|
10940
11149
|
const transcriptViewportRows = useCallback3(() => {
|
|
10941
11150
|
const top = Math.max(0, Number(transcriptViewportRef.current?.top) || 0);
|
|
10942
11151
|
const bottom = Math.max(top, Number(transcriptViewportRef.current?.bottom) || top);
|
|
@@ -10977,6 +11186,10 @@ function useTranscriptScroll({
|
|
|
10977
11186
|
const maxTarget = Math.max(0, Number(maxScrollRowsRef.current) || 0);
|
|
10978
11187
|
const target = Math.max(0, Math.min(maxTarget, scrollTargetRef.current + deltaRows));
|
|
10979
11188
|
const appliedDelta = target - scrollTargetRef.current;
|
|
11189
|
+
if (appliedDelta !== 0 && dragRef.current.region === "transcript" && dragRef.current.rect) {
|
|
11190
|
+
flushPendingSelectionPaint();
|
|
11191
|
+
harvestStitchRowsNow(Number(scrollTargetRef.current) || 0);
|
|
11192
|
+
}
|
|
10980
11193
|
if (appliedDelta !== 0) cancelTranscriptFollow();
|
|
10981
11194
|
scrollTargetRef.current = target;
|
|
10982
11195
|
if (appliedDelta !== 0) {
|
|
@@ -11022,8 +11235,9 @@ function useTranscriptScroll({
|
|
|
11022
11235
|
} else {
|
|
11023
11236
|
rect = shiftSelectionRectY(dragRef.current.rect, appliedDelta);
|
|
11024
11237
|
}
|
|
11025
|
-
const clippedRect = withSelectionClip(rect);
|
|
11238
|
+
const clippedRect = dragRef.current.active ? withSelectionClip(rect, { captureText: false }) : withSelectionClip(rect);
|
|
11026
11239
|
dragRef.current = { ...dragRef.current, rect: clippedRect };
|
|
11240
|
+
cancelPendingSelectionPaint();
|
|
11027
11241
|
paintSelectionRect(clippedRect, { rememberText: false });
|
|
11028
11242
|
}
|
|
11029
11243
|
if (options.smooth) {
|
|
@@ -11033,7 +11247,7 @@ function useTranscriptScroll({
|
|
|
11033
11247
|
stopSmoothScroll();
|
|
11034
11248
|
scrollPositionRef.current = target;
|
|
11035
11249
|
setScrollOffset(Math.round(target));
|
|
11036
|
-
}, [startSmoothScroll, stopSmoothScroll, paintSelectionRect, selectionPointAtCurrentScroll, withSelectionClip, cancelTranscriptFollow, buildSpanRect]);
|
|
11250
|
+
}, [startSmoothScroll, stopSmoothScroll, paintSelectionRect, selectionPointAtCurrentScroll, withSelectionClip, cancelTranscriptFollow, buildSpanRect, harvestStitchRowsNow, cancelPendingSelectionPaint, flushPendingSelectionPaint]);
|
|
11037
11251
|
const queueScrollCoalesced = useCallback3((deltaRows) => {
|
|
11038
11252
|
const state = scrollCoalesceRef.current;
|
|
11039
11253
|
state.pendingRows += deltaRows;
|
|
@@ -11189,6 +11403,7 @@ function useTranscriptWindow({
|
|
|
11189
11403
|
}) {
|
|
11190
11404
|
const transcriptTotalRowsRef = useRef8(0);
|
|
11191
11405
|
const preservedScrollDeltaRef = useRef8(0);
|
|
11406
|
+
const committedMaxScrollRowsRef = useRef8(0);
|
|
11192
11407
|
const incrementalRowIndexCacheRef = useRef8(null);
|
|
11193
11408
|
const prevViewportGeomRef = useRef8({ contentHeight: 0, floatingPanelRows: 0 });
|
|
11194
11409
|
const transcriptItemElsRef = useRef8(/* @__PURE__ */ new Map());
|
|
@@ -11358,7 +11573,31 @@ function useTranscriptWindow({
|
|
|
11358
11573
|
rowIndex: transcriptRowIndex
|
|
11359
11574
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: sig+scroll/viewport capture the relevant changes
|
|
11360
11575
|
}), [transcriptStructureSig, renderScrollOffset, transcriptContentHeight, transcriptRowIndex]);
|
|
11361
|
-
|
|
11576
|
+
const estimateMaxScrollRows = Math.max(0, Number(transcriptWindow.maxScrollRows) || 0);
|
|
11577
|
+
let holdCommittedMax = false;
|
|
11578
|
+
if (TRANSCRIPT_MEASURED_ROWS && estimateMaxScrollRows > committedMaxScrollRowsRef.current) {
|
|
11579
|
+
const toolExpandedFlag = toolOutputExpanded ? 1 : 0;
|
|
11580
|
+
const mountedSlice = transcriptWindow.items || [];
|
|
11581
|
+
for (let i = 0; i < mountedSlice.length; i++) {
|
|
11582
|
+
const it = mountedSlice[i];
|
|
11583
|
+
if (!it || shouldSuppressFullyFailedToolItem(it)) continue;
|
|
11584
|
+
if (it.kind === "assistant" && it.streaming) {
|
|
11585
|
+
const idPrev = streamingMeasuredRowsById.get(it.id);
|
|
11586
|
+
if (!idPrev || idPrev.columns !== frameColumns || idPrev.toolExpanded !== toolExpandedFlag) {
|
|
11587
|
+
holdCommittedMax = true;
|
|
11588
|
+
break;
|
|
11589
|
+
}
|
|
11590
|
+
} else {
|
|
11591
|
+
const prev = transcriptMeasuredRowsCache.get(it);
|
|
11592
|
+
if (!prev || prev.columns !== frameColumns || prev.toolExpanded !== toolExpandedFlag || prev.variantKey !== transcriptItemVariantKey(it)) {
|
|
11593
|
+
holdCommittedMax = true;
|
|
11594
|
+
break;
|
|
11595
|
+
}
|
|
11596
|
+
}
|
|
11597
|
+
}
|
|
11598
|
+
}
|
|
11599
|
+
if (!holdCommittedMax) committedMaxScrollRowsRef.current = estimateMaxScrollRows;
|
|
11600
|
+
maxScrollRowsRef.current = committedMaxScrollRowsRef.current;
|
|
11362
11601
|
transcriptGeomRef.current = {
|
|
11363
11602
|
prefixRows: transcriptRowIndex?.prefixRows || null,
|
|
11364
11603
|
totalRows: Math.max(0, Number(transcriptWindow.totalRows) || 0),
|
|
@@ -12576,6 +12815,7 @@ function createExtensionPickers({
|
|
|
12576
12815
|
clean: clean3,
|
|
12577
12816
|
copyToClipboard: copyToClipboard2,
|
|
12578
12817
|
setPicker,
|
|
12818
|
+
getPicker,
|
|
12579
12819
|
setProviderPrompt,
|
|
12580
12820
|
setChannelPrompt,
|
|
12581
12821
|
setHookPrompt,
|
|
@@ -12583,6 +12823,8 @@ function createExtensionPickers({
|
|
|
12583
12823
|
getDisabledSkills,
|
|
12584
12824
|
setDisabledSkills
|
|
12585
12825
|
}) {
|
|
12826
|
+
let mcpEpoch = 0;
|
|
12827
|
+
let mcpActive = false;
|
|
12586
12828
|
const mcpStatus = () => {
|
|
12587
12829
|
let status;
|
|
12588
12830
|
try {
|
|
@@ -12597,6 +12839,7 @@ function createExtensionPickers({
|
|
|
12597
12839
|
const status = mcpStatus();
|
|
12598
12840
|
if (!status) return;
|
|
12599
12841
|
const servers = status.servers || [];
|
|
12842
|
+
const optimistic = options?.optimistic || null;
|
|
12600
12843
|
const items = [];
|
|
12601
12844
|
if (servers.length === 0) {
|
|
12602
12845
|
items.push({
|
|
@@ -12607,13 +12850,14 @@ function createExtensionPickers({
|
|
|
12607
12850
|
});
|
|
12608
12851
|
}
|
|
12609
12852
|
for (const server of servers) {
|
|
12610
|
-
const
|
|
12853
|
+
const pending = optimistic && optimistic.name === server.name;
|
|
12854
|
+
const enabled = pending ? optimistic.enabled : server.enabled !== false;
|
|
12611
12855
|
items.push({
|
|
12612
12856
|
value: `server:${server.name}`,
|
|
12613
12857
|
label: server.name,
|
|
12614
12858
|
marker: enabled ? "\u25CF" : "\u25CB",
|
|
12615
12859
|
markerColor: enabled ? theme2.success : theme2.inactive,
|
|
12616
|
-
description: `${server.status || "unknown"} \xB7 ${server.transport || "unknown"} \xB7 ${server.toolCount || 0} tools${server.error ? ` \xB7 ${server.error}` : ""}`,
|
|
12860
|
+
description: pending ? `${optimistic.enabled ? "enabling" : "disabling"}\u2026 \xB7 ${server.transport || "unknown"}` : `${server.status || "unknown"} \xB7 ${server.transport || "unknown"} \xB7 ${server.toolCount || 0} tools${server.error ? ` \xB7 ${server.error}` : ""}`,
|
|
12617
12861
|
_action: "server",
|
|
12618
12862
|
_server: server,
|
|
12619
12863
|
_enabled: enabled
|
|
@@ -12625,9 +12869,24 @@ function createExtensionPickers({
|
|
|
12625
12869
|
setSettingsPrompt(null);
|
|
12626
12870
|
const toggleServer = (item) => {
|
|
12627
12871
|
if (item._action !== "server" || !item._server?.name) return;
|
|
12628
|
-
|
|
12872
|
+
const name = item._server.name;
|
|
12873
|
+
const target = !item._enabled;
|
|
12874
|
+
const highlightValue = `server:${name}`;
|
|
12875
|
+
const token = ++mcpEpoch;
|
|
12876
|
+
const settle = (fn) => {
|
|
12877
|
+
if (token !== mcpEpoch || !mcpActive || getPicker?.()?._kind !== "mcp-servers") return;
|
|
12878
|
+
fn();
|
|
12879
|
+
};
|
|
12880
|
+
openMcpServersPicker({ highlightValue, optimistic: { name, enabled: target } });
|
|
12881
|
+
Promise.resolve(store.setMcpServerEnabled?.(name, target)).then(() => {
|
|
12882
|
+
settle(() => openMcpServersPicker({ highlightValue }));
|
|
12883
|
+
}).catch((e) => {
|
|
12884
|
+
store.pushNotice(`mcp toggle failed: ${e?.message || e}`, "error");
|
|
12885
|
+
settle(() => openMcpServersPicker({ highlightValue }));
|
|
12886
|
+
});
|
|
12629
12887
|
};
|
|
12630
12888
|
setPicker({
|
|
12889
|
+
_kind: "mcp-servers",
|
|
12631
12890
|
title: "MCP servers",
|
|
12632
12891
|
description: "Enable or disable configured MCP servers.",
|
|
12633
12892
|
initialIndex: Math.max(0, items.findIndex((entry) => entry.value === options?.highlightValue)),
|
|
@@ -12636,9 +12895,12 @@ function createExtensionPickers({
|
|
|
12636
12895
|
onLeft: (item) => toggleServer(item),
|
|
12637
12896
|
onRight: (item) => toggleServer(item),
|
|
12638
12897
|
onCancel: () => {
|
|
12898
|
+
mcpActive = false;
|
|
12899
|
+
mcpEpoch++;
|
|
12639
12900
|
setPicker(null);
|
|
12640
12901
|
}
|
|
12641
12902
|
});
|
|
12903
|
+
mcpActive = true;
|
|
12642
12904
|
};
|
|
12643
12905
|
const openMcpPicker = () => {
|
|
12644
12906
|
openMcpServersPicker();
|
|
@@ -12695,9 +12957,14 @@ function createExtensionPickers({
|
|
|
12695
12957
|
});
|
|
12696
12958
|
};
|
|
12697
12959
|
const openSkillsPicker = (options = {}) => {
|
|
12698
|
-
|
|
12699
|
-
if (
|
|
12700
|
-
|
|
12960
|
+
let skills;
|
|
12961
|
+
if (Array.isArray(options.skills)) {
|
|
12962
|
+
skills = options.skills;
|
|
12963
|
+
} else {
|
|
12964
|
+
const status = skillsStatus();
|
|
12965
|
+
if (!status) return;
|
|
12966
|
+
skills = status.skills || [];
|
|
12967
|
+
}
|
|
12701
12968
|
const disabledSet = options.disabledOverride instanceof Set ? options.disabledOverride : getDisabledSkills();
|
|
12702
12969
|
const items = [];
|
|
12703
12970
|
if (skills.length === 0) {
|
|
@@ -12736,9 +13003,10 @@ function createExtensionPickers({
|
|
|
12736
13003
|
`skill ${item._enabled ? "disabled" : "enabled"}: ${name} (prompt updates next session /clear)`,
|
|
12737
13004
|
"info"
|
|
12738
13005
|
);
|
|
12739
|
-
openSkillsPicker({ highlightValue: name, disabledOverride: next });
|
|
13006
|
+
openSkillsPicker({ highlightValue: name, disabledOverride: next, skills });
|
|
12740
13007
|
};
|
|
12741
13008
|
setPicker({
|
|
13009
|
+
_kind: "skills",
|
|
12742
13010
|
title: "Skills",
|
|
12743
13011
|
description: "Enable or disable project skills.",
|
|
12744
13012
|
initialIndex: Math.max(0, items.findIndex((entry) => entry.value === options.highlightValue)),
|
|
@@ -13021,6 +13289,7 @@ ${p.description}` : ""
|
|
|
13021
13289
|
}
|
|
13022
13290
|
};
|
|
13023
13291
|
setPicker({
|
|
13292
|
+
_kind: "hooks",
|
|
13024
13293
|
title: "Hooks",
|
|
13025
13294
|
description: "Before-tool hook rules; Enter toggles a rule.",
|
|
13026
13295
|
items,
|
|
@@ -13249,8 +13518,6 @@ function createMaintenancePickers({
|
|
|
13249
13518
|
const current = readCurrent();
|
|
13250
13519
|
const enabled = current?.enabled !== false;
|
|
13251
13520
|
const idleMs = Number(current?.idleMs || HOUR_MS);
|
|
13252
|
-
const custom = current?.custom === true;
|
|
13253
|
-
const providerDefault = Number(current?.providerDefault || HOUR_MS);
|
|
13254
13521
|
const cacheTtlLabel = !enabled || idleMs >= HOUR_MS ? "1h" : "5m";
|
|
13255
13522
|
const items = [
|
|
13256
13523
|
{
|
|
@@ -13263,9 +13530,6 @@ function createMaintenancePickers({
|
|
|
13263
13530
|
{
|
|
13264
13531
|
value: "advanced",
|
|
13265
13532
|
label: "Advanced",
|
|
13266
|
-
marker: !custom ? "\u2713" : "",
|
|
13267
|
-
markerColor: theme2.success,
|
|
13268
|
-
meta: `${current?.provider || "current"} \xB7 ${formatDuration2(providerDefault)}`,
|
|
13269
13533
|
description: "Edit provider-paired default idle windows as text.",
|
|
13270
13534
|
_action: "advanced"
|
|
13271
13535
|
}
|
|
@@ -13732,7 +13996,9 @@ import {
|
|
|
13732
13996
|
} from "fs";
|
|
13733
13997
|
import { dirname as dirname3, basename as basename3, join as join3 } from "path";
|
|
13734
13998
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
13735
|
-
import { execFileSync } from "child_process";
|
|
13999
|
+
import { execFile as execFile2, execFileSync } from "child_process";
|
|
14000
|
+
import { promisify } from "util";
|
|
14001
|
+
var _execFileAsync = promisify(execFile2);
|
|
13736
14002
|
var RETRY_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY", "EEXIST"]);
|
|
13737
14003
|
var LOCK_WAIT_CODES = /* @__PURE__ */ new Set(["EEXIST", "EPERM", "EACCES", "EBUSY"]);
|
|
13738
14004
|
var DEFAULT_BACKOFFS_MS = Object.freeze([25, 50, 100, 200, 400, 800, 1200, 1600]);
|
|
@@ -13748,6 +14014,18 @@ function sleepSync(ms) {
|
|
|
13748
14014
|
}
|
|
13749
14015
|
}
|
|
13750
14016
|
}
|
|
14017
|
+
function _describeLockHolder(lockPath) {
|
|
14018
|
+
try {
|
|
14019
|
+
const st = statSync2(lockPath);
|
|
14020
|
+
const owner = _readLockOwner(lockPath);
|
|
14021
|
+
const ageMs = Math.max(0, Math.round(Date.now() - st.mtimeMs));
|
|
14022
|
+
const live = owner.pid === null ? "unknown" : _ownerIsLive(owner) ? "live" : "dead";
|
|
14023
|
+
const token = owner.token === null ? "?" : String(owner.token).slice(0, 8);
|
|
14024
|
+
return `holder pid=${owner.pid ?? "?"} token=${token} age=${ageMs}ms ${live}`;
|
|
14025
|
+
} catch {
|
|
14026
|
+
return "holder unknown (lock file unreadable/absent)";
|
|
14027
|
+
}
|
|
14028
|
+
}
|
|
13751
14029
|
function renameWithRetrySync(src, dst, opts = {}) {
|
|
13752
14030
|
const backoffs = Array.isArray(opts.backoffs) && opts.backoffs.length > 0 ? opts.backoffs : DEFAULT_BACKOFFS_MS;
|
|
13753
14031
|
let lastErr = null;
|
|
@@ -13779,7 +14057,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
13779
14057
|
lastErr = err;
|
|
13780
14058
|
if (!LOCK_WAIT_CODES.has(err?.code)) throw err;
|
|
13781
14059
|
if (timeoutMs <= 0) {
|
|
13782
|
-
const contErr = new Error(`atomic lock contended (try-once): ${lockPath}`);
|
|
14060
|
+
const contErr = new Error(`atomic lock contended (try-once): ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
13783
14061
|
contErr.code = "ELOCKCONTENDED";
|
|
13784
14062
|
contErr.cause = err;
|
|
13785
14063
|
throw contErr;
|
|
@@ -13814,7 +14092,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
13814
14092
|
}
|
|
13815
14093
|
}
|
|
13816
14094
|
}
|
|
13817
|
-
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath}`);
|
|
14095
|
+
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
13818
14096
|
timeoutErr.code = "ELOCKTIMEOUT";
|
|
13819
14097
|
timeoutErr.cause = lastErr;
|
|
13820
14098
|
throw timeoutErr;
|
|
@@ -14006,8 +14284,8 @@ function _releaseReclaimGuard(reclaim) {
|
|
|
14006
14284
|
}
|
|
14007
14285
|
}
|
|
14008
14286
|
function _asyncSleep(ms) {
|
|
14009
|
-
return new Promise((
|
|
14010
|
-
setTimeout(
|
|
14287
|
+
return new Promise((resolve5) => {
|
|
14288
|
+
setTimeout(resolve5, Math.max(1, Number(ms) || 1));
|
|
14011
14289
|
});
|
|
14012
14290
|
}
|
|
14013
14291
|
async function withFileLock(lockPath, fn, opts = {}) {
|
|
@@ -14025,7 +14303,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14025
14303
|
lastErr = err;
|
|
14026
14304
|
if (!LOCK_WAIT_CODES.has(err?.code)) throw err;
|
|
14027
14305
|
if (timeoutMs <= 0) {
|
|
14028
|
-
const contErr = new Error(`atomic lock contended (try-once): ${lockPath}`);
|
|
14306
|
+
const contErr = new Error(`atomic lock contended (try-once): ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
14029
14307
|
contErr.code = "ELOCKCONTENDED";
|
|
14030
14308
|
contErr.cause = err;
|
|
14031
14309
|
throw contErr;
|
|
@@ -14047,7 +14325,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14047
14325
|
} catch {
|
|
14048
14326
|
}
|
|
14049
14327
|
try {
|
|
14050
|
-
if (opts.secret === true)
|
|
14328
|
+
if (opts.secret === true) await _enforceOwnerOnlyAclWin32Async(lockPath);
|
|
14051
14329
|
return await fn();
|
|
14052
14330
|
} finally {
|
|
14053
14331
|
try {
|
|
@@ -14060,7 +14338,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14060
14338
|
}
|
|
14061
14339
|
}
|
|
14062
14340
|
}
|
|
14063
|
-
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath}`);
|
|
14341
|
+
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
14064
14342
|
timeoutErr.code = "ELOCKTIMEOUT";
|
|
14065
14343
|
timeoutErr.cause = lastErr;
|
|
14066
14344
|
throw timeoutErr;
|
|
@@ -14122,6 +14400,52 @@ function _enforceOwnerOnlyAclWin32(targetPath) {
|
|
|
14122
14400
|
throw e;
|
|
14123
14401
|
}
|
|
14124
14402
|
}
|
|
14403
|
+
async function _resolveCurrentUserPrincipalAsync() {
|
|
14404
|
+
const systemRoot = process.env.SystemRoot || process.env.windir;
|
|
14405
|
+
if (systemRoot) {
|
|
14406
|
+
const whoami = join3(systemRoot, "System32", "whoami.exe");
|
|
14407
|
+
if (existsSync3(whoami)) {
|
|
14408
|
+
try {
|
|
14409
|
+
const { stdout } = await _execFileAsync(whoami, ["/user", "/fo", "csv", "/nh"], {
|
|
14410
|
+
encoding: "utf8",
|
|
14411
|
+
windowsHide: true
|
|
14412
|
+
});
|
|
14413
|
+
const m = String(stdout).match(/S-1-5-[0-9-]+/);
|
|
14414
|
+
if (m) return m[0];
|
|
14415
|
+
} catch {
|
|
14416
|
+
}
|
|
14417
|
+
}
|
|
14418
|
+
}
|
|
14419
|
+
const err = new Error("cannot resolve current Windows user for owner-only ACL enforcement");
|
|
14420
|
+
err.code = "EACLNOUSER";
|
|
14421
|
+
throw err;
|
|
14422
|
+
}
|
|
14423
|
+
async function _enforceOwnerOnlyAclWin32Async(targetPath) {
|
|
14424
|
+
if (process.platform !== "win32") return;
|
|
14425
|
+
const systemRoot = process.env.SystemRoot || process.env.windir;
|
|
14426
|
+
if (!systemRoot) {
|
|
14427
|
+
const err = new Error("SystemRoot not set; cannot locate icacls.exe for owner-only ACL enforcement");
|
|
14428
|
+
err.code = "EACLNOROOT";
|
|
14429
|
+
throw err;
|
|
14430
|
+
}
|
|
14431
|
+
const icacls = join3(systemRoot, "System32", "icacls.exe");
|
|
14432
|
+
if (!existsSync3(icacls)) {
|
|
14433
|
+
const err = new Error(`icacls.exe not found at ${icacls}; refusing to leave secret world-readable`);
|
|
14434
|
+
err.code = "EACLNOICACLS";
|
|
14435
|
+
throw err;
|
|
14436
|
+
}
|
|
14437
|
+
if (_cachedUserSid === null) _cachedUserSid = await _resolveCurrentUserPrincipalAsync();
|
|
14438
|
+
const principal = _icaclsPrincipal(_cachedUserSid);
|
|
14439
|
+
try {
|
|
14440
|
+
await _execFileAsync(icacls, [targetPath, "/reset"], { windowsHide: true });
|
|
14441
|
+
await _execFileAsync(icacls, [targetPath, "/inheritance:r", "/grant:r", `${principal}:(F)`], { windowsHide: true });
|
|
14442
|
+
} catch (err) {
|
|
14443
|
+
const e = new Error(`icacls failed to apply owner-only ACL to ${targetPath}: ${err?.message || err}`);
|
|
14444
|
+
e.code = "EACLFAIL";
|
|
14445
|
+
e.cause = err;
|
|
14446
|
+
throw e;
|
|
14447
|
+
}
|
|
14448
|
+
}
|
|
14125
14449
|
function writeFileAtomicSync(filePath, data, opts = {}) {
|
|
14126
14450
|
const run = () => {
|
|
14127
14451
|
const dir = dirname3(filePath);
|
|
@@ -14210,21 +14534,6 @@ function writeFileAtomicSync(filePath, data, opts = {}) {
|
|
|
14210
14534
|
function writeJsonAtomicSync(filePath, value, opts = {}) {
|
|
14211
14535
|
return writeFileAtomicSync(filePath, JSON.stringify(value, null, opts.compact ? 0 : 2) + "\n", opts);
|
|
14212
14536
|
}
|
|
14213
|
-
function updateJsonAtomicSync(filePath, mutator, opts = {}) {
|
|
14214
|
-
const { lock: _lock, ...writeOpts } = opts;
|
|
14215
|
-
return withFileLockSync(`${filePath}.lock`, () => {
|
|
14216
|
-
let cur = null;
|
|
14217
|
-
try {
|
|
14218
|
-
cur = JSON.parse(readFileSync2(filePath, "utf8"));
|
|
14219
|
-
} catch {
|
|
14220
|
-
cur = null;
|
|
14221
|
-
}
|
|
14222
|
-
const next = mutator(cur);
|
|
14223
|
-
if (next === void 0) return cur;
|
|
14224
|
-
writeJsonAtomicSync(filePath, next, { ...writeOpts, lock: false });
|
|
14225
|
-
return next;
|
|
14226
|
-
}, opts);
|
|
14227
|
-
}
|
|
14228
14537
|
async function updateJsonAtomic(filePath, mutator, opts = {}) {
|
|
14229
14538
|
const { lock: _lock, ...writeOpts } = opts;
|
|
14230
14539
|
return withFileLock(`${filePath}.lock`, () => {
|
|
@@ -15174,7 +15483,7 @@ function createModelPicker({
|
|
|
15174
15483
|
items: [],
|
|
15175
15484
|
onCancel: cancelModelPicker
|
|
15176
15485
|
});
|
|
15177
|
-
await new Promise((
|
|
15486
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
15178
15487
|
try {
|
|
15179
15488
|
if (options.refreshModels !== true && options.cacheRef !== "search") {
|
|
15180
15489
|
refreshModelsPromise = Promise.resolve(loadModels({ force: false }));
|
|
@@ -15500,7 +15809,7 @@ function createProviderSetupPicker({
|
|
|
15500
15809
|
}
|
|
15501
15810
|
});
|
|
15502
15811
|
try {
|
|
15503
|
-
await new Promise((
|
|
15812
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
15504
15813
|
setup = await store.getProviderSetup();
|
|
15505
15814
|
} catch (e) {
|
|
15506
15815
|
store.pushNotice(`providers failed: ${e?.message || e}`, "error");
|
|
@@ -16886,10 +17195,6 @@ function createSlashDispatch({
|
|
|
16886
17195
|
return true;
|
|
16887
17196
|
}
|
|
16888
17197
|
case "search":
|
|
16889
|
-
if (state.busy) {
|
|
16890
|
-
store.pushNotice("wait for the current turn to finish before /search", "warn");
|
|
16891
|
-
return false;
|
|
16892
|
-
}
|
|
16893
17198
|
if (arg) store.pushNotice("/search sets the search provider/model; the search tool uses that model when called.", "warn");
|
|
16894
17199
|
openSearchPicker();
|
|
16895
17200
|
return true;
|
|
@@ -17189,12 +17494,26 @@ function createSlashDispatch({
|
|
|
17189
17494
|
import { useCallback as useCallback5 } from "react";
|
|
17190
17495
|
|
|
17191
17496
|
// src/tui/prompt-history-store.mjs
|
|
17497
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
17192
17498
|
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
17499
|
+
import { chmod, mkdir, rename, writeFile } from "node:fs/promises";
|
|
17193
17500
|
import { dirname as dirname6, join as join6, resolve as resolve4 } from "node:path";
|
|
17194
17501
|
var PROMPT_HISTORY_LIMIT2 = 50;
|
|
17195
17502
|
function promptHistoryKey2(value) {
|
|
17196
17503
|
return String(value || "").trim().replace(/\s+/g, " ");
|
|
17197
17504
|
}
|
|
17505
|
+
function promptHistoryCwdKey(rawPath) {
|
|
17506
|
+
const text = String(rawPath || "").trim();
|
|
17507
|
+
if (!text) return "";
|
|
17508
|
+
const abs = resolve4(text);
|
|
17509
|
+
return process.platform === "win32" ? abs.replace(/[\\/]+$/, "").toLowerCase() : abs.replace(/\/+$/, "");
|
|
17510
|
+
}
|
|
17511
|
+
function historyFilePath(cwd) {
|
|
17512
|
+
const key = promptHistoryCwdKey(cwd);
|
|
17513
|
+
if (!key) return "";
|
|
17514
|
+
const digest = createHash2("sha256").update(key, "utf8").digest("hex").slice(0, 32);
|
|
17515
|
+
return join6(resolvePluginData(), "tui-prompt-history", `${digest}.json`);
|
|
17516
|
+
}
|
|
17198
17517
|
function readEntries(filePath) {
|
|
17199
17518
|
if (!filePath || !existsSync6(filePath)) return [];
|
|
17200
17519
|
try {
|
|
@@ -17225,9 +17544,33 @@ function writeEntriesSync(filePath, entries) {
|
|
|
17225
17544
|
return false;
|
|
17226
17545
|
}
|
|
17227
17546
|
}
|
|
17547
|
+
async function writeEntriesAsync(filePath, entries) {
|
|
17548
|
+
if (!filePath) return false;
|
|
17549
|
+
try {
|
|
17550
|
+
await mkdir(dirname6(filePath), { recursive: true, mode: 448 });
|
|
17551
|
+
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
17552
|
+
await writeFile(tmp, buildPayload(entries), { encoding: "utf8", mode: 384 });
|
|
17553
|
+
await rename(tmp, filePath);
|
|
17554
|
+
try {
|
|
17555
|
+
await chmod(filePath, 384);
|
|
17556
|
+
} catch {
|
|
17557
|
+
}
|
|
17558
|
+
return true;
|
|
17559
|
+
} catch {
|
|
17560
|
+
return false;
|
|
17561
|
+
}
|
|
17562
|
+
}
|
|
17563
|
+
var WRITE_BEHIND_MS = 250;
|
|
17564
|
+
var FLUSH_CAP = 5;
|
|
17228
17565
|
var memCache = /* @__PURE__ */ new Map();
|
|
17229
17566
|
var pendingAppends = /* @__PURE__ */ new Map();
|
|
17230
17567
|
var pendingTimers = /* @__PURE__ */ new Map();
|
|
17568
|
+
function cachedEntries(filePath) {
|
|
17569
|
+
if (memCache.has(filePath)) return memCache.get(filePath);
|
|
17570
|
+
const entries = readEntries(filePath);
|
|
17571
|
+
memCache.set(filePath, entries);
|
|
17572
|
+
return entries;
|
|
17573
|
+
}
|
|
17231
17574
|
function applyAppend(base, value) {
|
|
17232
17575
|
const key = promptHistoryKey2(value);
|
|
17233
17576
|
const next = base.filter((entry) => promptHistoryKey2(entry) !== key);
|
|
@@ -17239,6 +17582,33 @@ function reconcileWithDisk(filePath, pend) {
|
|
|
17239
17582
|
for (const value of pend) out = applyAppend(out, value);
|
|
17240
17583
|
return out;
|
|
17241
17584
|
}
|
|
17585
|
+
function scheduleWriteBehind(filePath) {
|
|
17586
|
+
if (!filePath) return;
|
|
17587
|
+
if (pendingTimers.has(filePath)) clearTimeout(pendingTimers.get(filePath));
|
|
17588
|
+
const timer = setTimeout(() => {
|
|
17589
|
+
void writeBehindFlush(filePath);
|
|
17590
|
+
}, WRITE_BEHIND_MS);
|
|
17591
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
17592
|
+
pendingTimers.set(filePath, timer);
|
|
17593
|
+
}
|
|
17594
|
+
async function writeBehindFlush(filePath) {
|
|
17595
|
+
const timer = pendingTimers.get(filePath);
|
|
17596
|
+
if (timer) {
|
|
17597
|
+
clearTimeout(timer);
|
|
17598
|
+
pendingTimers.delete(filePath);
|
|
17599
|
+
}
|
|
17600
|
+
const pend = pendingAppends.get(filePath);
|
|
17601
|
+
if (!pend || !pend.length) return;
|
|
17602
|
+
pendingAppends.set(filePath, []);
|
|
17603
|
+
const merged = reconcileWithDisk(filePath, pend);
|
|
17604
|
+
memCache.set(filePath, merged);
|
|
17605
|
+
const ok = await writeEntriesAsync(filePath, merged);
|
|
17606
|
+
if (!ok) {
|
|
17607
|
+
const cur = pendingAppends.get(filePath) || [];
|
|
17608
|
+
pendingAppends.set(filePath, pend.concat(cur));
|
|
17609
|
+
scheduleWriteBehind(filePath);
|
|
17610
|
+
}
|
|
17611
|
+
}
|
|
17242
17612
|
function flushPromptHistory() {
|
|
17243
17613
|
for (const timer of pendingTimers.values()) clearTimeout(timer);
|
|
17244
17614
|
pendingTimers.clear();
|
|
@@ -17251,6 +17621,43 @@ function flushPromptHistory() {
|
|
|
17251
17621
|
pendingAppends.clear();
|
|
17252
17622
|
}
|
|
17253
17623
|
process.once("exit", flushPromptHistory);
|
|
17624
|
+
function buildMergedPromptHistory(sessionTexts, persistedTexts, limit = PROMPT_HISTORY_LIMIT2) {
|
|
17625
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17626
|
+
const history = [];
|
|
17627
|
+
const push = (raw) => {
|
|
17628
|
+
if (history.length >= limit) return;
|
|
17629
|
+
const text = String(raw || "").trim();
|
|
17630
|
+
const key = promptHistoryKey2(text);
|
|
17631
|
+
if (!key || seen.has(key)) return;
|
|
17632
|
+
seen.add(key);
|
|
17633
|
+
history.push(text);
|
|
17634
|
+
};
|
|
17635
|
+
for (const text of sessionTexts) push(text);
|
|
17636
|
+
const persisted = Array.isArray(persistedTexts) ? persistedTexts : [];
|
|
17637
|
+
for (let i = persisted.length - 1; i >= 0; i -= 1) {
|
|
17638
|
+
push(persisted[i]);
|
|
17639
|
+
}
|
|
17640
|
+
return history;
|
|
17641
|
+
}
|
|
17642
|
+
function loadPromptHistory(cwd) {
|
|
17643
|
+
const filePath = historyFilePath(cwd);
|
|
17644
|
+
const entries = cachedEntries(filePath);
|
|
17645
|
+
return entries.length <= PROMPT_HISTORY_LIMIT2 ? entries.slice() : entries.slice(-PROMPT_HISTORY_LIMIT2);
|
|
17646
|
+
}
|
|
17647
|
+
function appendPromptHistory(cwd, value) {
|
|
17648
|
+
const key = promptHistoryKey2(value);
|
|
17649
|
+
if (!key) return null;
|
|
17650
|
+
const filePath = historyFilePath(cwd);
|
|
17651
|
+
if (!filePath) return null;
|
|
17652
|
+
const trimmed = applyAppend(cachedEntries(filePath), value);
|
|
17653
|
+
memCache.set(filePath, trimmed);
|
|
17654
|
+
const pend = pendingAppends.get(filePath) || [];
|
|
17655
|
+
pend.push(String(value).trim());
|
|
17656
|
+
pendingAppends.set(filePath, pend);
|
|
17657
|
+
if (pend.length >= FLUSH_CAP) void writeBehindFlush(filePath);
|
|
17658
|
+
else scheduleWriteBehind(filePath);
|
|
17659
|
+
return trimmed;
|
|
17660
|
+
}
|
|
17254
17661
|
|
|
17255
17662
|
// src/tui/app/use-prompt-handlers.mjs
|
|
17256
17663
|
function usePromptHandlers({
|
|
@@ -17290,57 +17697,64 @@ function usePromptHandlers({
|
|
|
17290
17697
|
const handlePromptPaste = useCallback5((text, meta = {}) => {
|
|
17291
17698
|
const source = String(meta?.source || "paste");
|
|
17292
17699
|
const value = String(text ?? "");
|
|
17293
|
-
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17700
|
+
const processText = (raw, returnRaw = false) => {
|
|
17701
|
+
const chunks = splitPastedImagePathCandidates(raw);
|
|
17702
|
+
const hasImagePath = chunks.some((chunk) => chunk.imagePath);
|
|
17703
|
+
if (!hasImagePath) {
|
|
17704
|
+
if (shouldFoldPastedText(raw)) return registerPastedText(raw);
|
|
17705
|
+
return returnRaw ? raw : void 0;
|
|
17706
|
+
}
|
|
17707
|
+
return Promise.all(chunks.map(async (chunk) => {
|
|
17708
|
+
if (!chunk.imagePath) return chunk.text;
|
|
17709
|
+
try {
|
|
17710
|
+
const image = await readImageAttachmentFromPath(chunk.text, state.cwd || process.cwd());
|
|
17711
|
+
if (!image) return chunk.text;
|
|
17712
|
+
const ref = registerPastedImage(image);
|
|
17713
|
+
showPromptHint(`attached ${image.filename || "image"}`, "plain");
|
|
17714
|
+
return ref;
|
|
17715
|
+
} catch (e) {
|
|
17716
|
+
showPromptHint(`image attach failed: ${e?.message || e}`, "warn");
|
|
17717
|
+
return chunk.text;
|
|
17718
|
+
}
|
|
17719
|
+
})).then((parts) => {
|
|
17720
|
+
let out = "";
|
|
17721
|
+
let run = "";
|
|
17722
|
+
const flushRun = () => {
|
|
17723
|
+
if (!run) return;
|
|
17724
|
+
out += shouldFoldPastedText(run) ? registerPastedText(run) : run;
|
|
17725
|
+
run = "";
|
|
17726
|
+
};
|
|
17727
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
17728
|
+
if (chunks[i].imagePath) {
|
|
17729
|
+
flushRun();
|
|
17730
|
+
out += parts[i];
|
|
17731
|
+
} else {
|
|
17732
|
+
run += parts[i];
|
|
17733
|
+
}
|
|
17298
17734
|
}
|
|
17299
|
-
|
|
17300
|
-
|
|
17301
|
-
|
|
17735
|
+
flushRun();
|
|
17736
|
+
return out;
|
|
17737
|
+
});
|
|
17738
|
+
};
|
|
17739
|
+
if (source === "clipboard-shortcut" && !value) {
|
|
17740
|
+
return readClipboardText().then((clip) => {
|
|
17741
|
+
const normalized = String(clip ?? "").replace(/\r\n?/g, "\n");
|
|
17742
|
+
if (normalized) return processText(normalized, true);
|
|
17743
|
+
return readClipboardImageAttachment().then((image) => {
|
|
17744
|
+
if (!image) {
|
|
17745
|
+
showPromptHint("no text or image found on clipboard", "plain");
|
|
17746
|
+
return false;
|
|
17747
|
+
}
|
|
17748
|
+
const ref = registerPastedImage(image);
|
|
17749
|
+
showPromptHint(`attached ${image.filename || "clipboard image"}`, "plain");
|
|
17750
|
+
return ref;
|
|
17751
|
+
});
|
|
17302
17752
|
}).catch((e) => {
|
|
17303
|
-
showPromptHint(`
|
|
17753
|
+
showPromptHint(`paste failed: ${e?.message || e}`, "warn");
|
|
17304
17754
|
return false;
|
|
17305
17755
|
});
|
|
17306
17756
|
}
|
|
17307
|
-
|
|
17308
|
-
const hasImagePath = chunks.some((chunk) => chunk.imagePath);
|
|
17309
|
-
if (!hasImagePath) {
|
|
17310
|
-
if (shouldFoldPastedText(value)) return registerPastedText(value);
|
|
17311
|
-
return void 0;
|
|
17312
|
-
}
|
|
17313
|
-
return Promise.all(chunks.map(async (chunk) => {
|
|
17314
|
-
if (!chunk.imagePath) return chunk.text;
|
|
17315
|
-
try {
|
|
17316
|
-
const image = await readImageAttachmentFromPath(chunk.text, state.cwd || process.cwd());
|
|
17317
|
-
if (!image) return chunk.text;
|
|
17318
|
-
const ref = registerPastedImage(image);
|
|
17319
|
-
showPromptHint(`attached ${image.filename || "image"}`, "plain");
|
|
17320
|
-
return ref;
|
|
17321
|
-
} catch (e) {
|
|
17322
|
-
showPromptHint(`image attach failed: ${e?.message || e}`, "warn");
|
|
17323
|
-
return chunk.text;
|
|
17324
|
-
}
|
|
17325
|
-
})).then((parts) => {
|
|
17326
|
-
let out = "";
|
|
17327
|
-
let run = "";
|
|
17328
|
-
const flushRun = () => {
|
|
17329
|
-
if (!run) return;
|
|
17330
|
-
out += shouldFoldPastedText(run) ? registerPastedText(run) : run;
|
|
17331
|
-
run = "";
|
|
17332
|
-
};
|
|
17333
|
-
for (let i = 0; i < chunks.length; i += 1) {
|
|
17334
|
-
if (chunks[i].imagePath) {
|
|
17335
|
-
flushRun();
|
|
17336
|
-
out += parts[i];
|
|
17337
|
-
} else {
|
|
17338
|
-
run += parts[i];
|
|
17339
|
-
}
|
|
17340
|
-
}
|
|
17341
|
-
flushRun();
|
|
17342
|
-
return out;
|
|
17343
|
-
});
|
|
17757
|
+
return processText(value);
|
|
17344
17758
|
}, [registerPastedImage, registerPastedText, showPromptHint, state.cwd]);
|
|
17345
17759
|
const handlePromptHistoryNavigate = useCallback5((direction, currentText = "", meta = {}) => {
|
|
17346
17760
|
const currentValue = String(currentText || "");
|
|
@@ -17689,7 +18103,7 @@ var AssistantMessage = React14.memo(function AssistantMessage2({
|
|
|
17689
18103
|
});
|
|
17690
18104
|
var UserMessage = React14.memo(function UserMessage2({ text, attached = false, columns, themeEpoch = 0 }) {
|
|
17691
18105
|
const bandColumns = Math.max(1, columns - 1);
|
|
17692
|
-
return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", width: bandColumns, marginTop: attached ? 0 : 1, backgroundColor: theme.userMessageBackground, paddingLeft: 2, paddingRight: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme.
|
|
18106
|
+
return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", width: bandColumns, marginTop: attached ? 0 : 1, backgroundColor: theme.userMessageBackground, paddingLeft: 2, paddingRight: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme.mixdogIvory, wrap: "wrap", children: text }) });
|
|
17693
18107
|
});
|
|
17694
18108
|
function NoticeMessage({ text, tone, columns = 80 }) {
|
|
17695
18109
|
const accentColor = tone === "error" ? theme.error : tone === "warn" ? theme.warning : theme.inactive;
|
|
@@ -18180,7 +18594,7 @@ var TOOL_PENDING_SHOW_DELAY_MS = 1e3;
|
|
|
18180
18594
|
function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
|
|
18181
18595
|
return formatToolActionHeader(name, args, { pending, count });
|
|
18182
18596
|
}
|
|
18183
|
-
function ToolExecution({ name, args, result, rawResult, isError, errorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, headerFinalized = true, deferredDisplayReady = false }) {
|
|
18597
|
+
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 }) {
|
|
18184
18598
|
const rowWidth = Math.max(1, Number(columns || 80));
|
|
18185
18599
|
const [blinkOn, setBlinkOn] = useState7(true);
|
|
18186
18600
|
const [blinkExpired, setBlinkExpired] = useState7(false);
|
|
@@ -18204,6 +18618,11 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18204
18618
|
const failedCount = clampFailureCount(errorCount, groupCount, isError);
|
|
18205
18619
|
const displayGroupCount = groupCount;
|
|
18206
18620
|
const displayCategories = normalizeCountMap(categories || {});
|
|
18621
|
+
const normalizedDoneCategories = doneCategories ? normalizeCountMap(doneCategories) : displayCategories;
|
|
18622
|
+
const hasDoneCounts = Object.values(normalizedDoneCategories || {}).some(
|
|
18623
|
+
(v) => (v && typeof v === "object" ? Number(v.count || 0) : Number(v || 0)) > 0
|
|
18624
|
+
);
|
|
18625
|
+
const displayDoneCategories = hasDoneCounts ? normalizedDoneCategories : displayCategories;
|
|
18207
18626
|
useEffect9(() => {
|
|
18208
18627
|
if (!pending) {
|
|
18209
18628
|
setPendingDelayElapsed(false);
|
|
@@ -18261,7 +18680,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18261
18680
|
}
|
|
18262
18681
|
if (aggregate) {
|
|
18263
18682
|
const headerOrder = Array.isArray(args?.categoryOrder) ? args.categoryOrder : null;
|
|
18264
|
-
const headerText = safeInlineText(formatAggregateHeader(displayCategories || {}, { pending: headerPending, order: headerOrder }));
|
|
18683
|
+
const headerText = safeInlineText(formatAggregateHeader((headerPending ? displayCategories : displayDoneCategories) || {}, { pending: headerPending, order: headerOrder }));
|
|
18265
18684
|
let detailText;
|
|
18266
18685
|
if (hasResult) {
|
|
18267
18686
|
detailText = safeInlineText(rt);
|
|
@@ -18378,13 +18797,13 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18378
18797
|
else if (isShellSurface) labelText = shellHeader(shellStatus, displayGroupCount);
|
|
18379
18798
|
else labelText = (isAgentTool(normalizedName) ? agentActionTitle(parsedArgs) : "") || statusCopy(name, label, displayGroupCount, doneCount, headerPending, isError, parsedArgs);
|
|
18380
18799
|
labelText = safeInlineText(labelText);
|
|
18381
|
-
const toolSearchSummary = !pending && normalizedName === "
|
|
18800
|
+
const toolSearchSummary = !pending && normalizedName === "load_tool" && hasResult ? toolSearchLoadedSummary(displayedResultText) : "";
|
|
18382
18801
|
const rawSummaryText = safeInlineText(isAgentResponse || isBackgroundResponse ? "" : toolSearchSummary || (isAgentTool(normalizedName) ? agentActionSummary(parsedArgs, summary) : summary));
|
|
18383
18802
|
const summaryIsHeaderCount = rawSummaryText && /^\d+\s+\S+$/.test(rawSummaryText) && labelText.endsWith(rawSummaryText);
|
|
18384
18803
|
const summaryText = summaryIsHeaderCount ? "" : rawSummaryText;
|
|
18385
18804
|
const agentHasExpandableBody = isAgentSurfaceCard && !pending && hasResult && (isAgentResponse || totalLines > 1 || firstResultLineClipped);
|
|
18386
18805
|
const shellHasExpandableBody = isShellSurface && !pending && hasDisplayResult && hasDisplayBody && (totalLines > 1 || firstResultLineClipped || Boolean(shellCollapsedSummary && shellCollapsedSummary !== firstResultLine));
|
|
18387
|
-
const showHeaderExpandHint = (isShellSurface ? shellHasExpandableBody : isAgentSurfaceCard ? agentHasExpandableBody : hasHiddenDetail || backgroundMetadataExpandable) && normalizedName !== "
|
|
18806
|
+
const showHeaderExpandHint = (isShellSurface ? shellHasExpandableBody : isAgentSurfaceCard ? agentHasExpandableBody : hasHiddenDetail || backgroundMetadataExpandable) && normalizedName !== "load_tool";
|
|
18388
18807
|
const expandHintColor = theme.subtle;
|
|
18389
18808
|
const gutter = 2;
|
|
18390
18809
|
const hintLabel = showHeaderExpandHint ? `ctrl+o ${expanded ? "collapse" : "expand"}` : "";
|
|
@@ -18581,7 +19000,7 @@ var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpa
|
|
|
18581
19000
|
node = /* @__PURE__ */ jsx19(ToolHookDenialCard, { item, columns });
|
|
18582
19001
|
break;
|
|
18583
19002
|
}
|
|
18584
|
-
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, headerFinalized: item.headerFinalized, deferredDisplayReady: item.deferredDisplayReady, themeEpoch });
|
|
19003
|
+
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, themeEpoch });
|
|
18585
19004
|
break;
|
|
18586
19005
|
}
|
|
18587
19006
|
case "notice":
|
|
@@ -18616,7 +19035,9 @@ var PANEL_LAYOUT_SIG = {
|
|
|
18616
19035
|
TEXT: 5,
|
|
18617
19036
|
// Prompt-wrap/meta row counts (trailing churn tokens, see token order note
|
|
18618
19037
|
// below). PROMPT_META is the 2-row live-spinner band slot.
|
|
18619
|
-
PROMPT_META: 9
|
|
19038
|
+
PROMPT_META: 9,
|
|
19039
|
+
// Queued steering band rows (full wrapped height, see queuedBandRows).
|
|
19040
|
+
QUEUED: 10
|
|
18620
19041
|
};
|
|
18621
19042
|
var PROJECT_TEXT_ENTRY_KINDS = /* @__PURE__ */ new Set(["project-new", "project-create-confirm", "project-rename"]);
|
|
18622
19043
|
var CORE_MULTILINE_TEXT_ENTRY_KINDS = /* @__PURE__ */ new Set(["core-add", "core-edit"]);
|
|
@@ -18678,7 +19099,9 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18678
19099
|
const projectPickerRef = useRef9(null);
|
|
18679
19100
|
const buildProjectPickerState = (opts) => projectPickerRef.current.buildProjectPickerState(opts);
|
|
18680
19101
|
const [picker, setPickerState] = useState8(null);
|
|
19102
|
+
const livePickerRef = useRef9(null);
|
|
18681
19103
|
const setPicker = useCallback6((next) => {
|
|
19104
|
+
livePickerRef.current = typeof next === "function" ? next(livePickerRef.current) : next;
|
|
18682
19105
|
setPickerState((prev) => {
|
|
18683
19106
|
const resolved = typeof next === "function" ? next(prev) : next;
|
|
18684
19107
|
if (resolved && typeof resolved === "object" && pickerOpenedFromEnterRef.current) {
|
|
@@ -18689,9 +19112,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18689
19112
|
}
|
|
18690
19113
|
return resolved.indexMode ? resolved : { ...resolved, indexMode: "always" };
|
|
18691
19114
|
}
|
|
19115
|
+
if (resolved && typeof resolved === "object" && !resolved.indexMode && prev && typeof prev === "object" && prev.indexMode && prev._kind && prev._kind === resolved._kind) {
|
|
19116
|
+
return { ...resolved, indexMode: prev.indexMode };
|
|
19117
|
+
}
|
|
18692
19118
|
return resolved;
|
|
18693
19119
|
});
|
|
18694
19120
|
}, []);
|
|
19121
|
+
livePickerRef.current = picker;
|
|
18695
19122
|
const [contextPanel, setContextPanel] = useState8(null);
|
|
18696
19123
|
const [usagePanel, setUsagePanel] = useState8(null);
|
|
18697
19124
|
const usageRequestRef = useRef9(0);
|
|
@@ -18733,7 +19160,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18733
19160
|
} catch {
|
|
18734
19161
|
}
|
|
18735
19162
|
if (!onboardingOwnsScreen && state.items.length === 0) {
|
|
18736
|
-
|
|
19163
|
+
setPicker(projectPicker.buildProjectPickerState({ initialEntry: true }));
|
|
18737
19164
|
}
|
|
18738
19165
|
}
|
|
18739
19166
|
const {
|
|
@@ -18804,6 +19231,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18804
19231
|
clean,
|
|
18805
19232
|
copyToClipboard,
|
|
18806
19233
|
setPicker,
|
|
19234
|
+
getPicker: () => livePickerRef.current,
|
|
18807
19235
|
setProviderPrompt,
|
|
18808
19236
|
setChannelPrompt,
|
|
18809
19237
|
setHookPrompt,
|
|
@@ -19306,13 +19734,16 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19306
19734
|
const renderText = store.getRenderSelectionText?.();
|
|
19307
19735
|
const remembered = selectionTextRef.current || "";
|
|
19308
19736
|
let text = renderText == null ? remembered : remembered.length > renderText.length ? remembered : renderText;
|
|
19309
|
-
const stitched = getStitchedSelectionText?.() || "";
|
|
19310
|
-
if (stitched.length > text.length) text = stitched;
|
|
19737
|
+
const stitched = getStitchedSelectionText?.() || { text: "", complete: false };
|
|
19738
|
+
if (stitched.complete && stitched.text.length > text.length) text = stitched.text;
|
|
19311
19739
|
if ((!text || !text.trim()) && attempt < 4) {
|
|
19312
19740
|
setTimeout(() => copySelection(attempt + 1), attempt === 0 ? 0 : 24);
|
|
19313
19741
|
return;
|
|
19314
19742
|
}
|
|
19315
|
-
if (!text || !text.trim())
|
|
19743
|
+
if (!text || !text.trim()) {
|
|
19744
|
+
showSelectionCopyHint("nothing to copy \xB7 select text first", "error");
|
|
19745
|
+
return;
|
|
19746
|
+
}
|
|
19316
19747
|
selectionTextRef.current = text;
|
|
19317
19748
|
copyToClipboard(text).then(() => {
|
|
19318
19749
|
const lines = text.split("\n").length;
|
|
@@ -19388,8 +19819,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19388
19819
|
let timer = null;
|
|
19389
19820
|
Promise.race([
|
|
19390
19821
|
Promise.resolve(store.dispose?.("cli-react-exit", { detach: true })),
|
|
19391
|
-
new Promise((
|
|
19392
|
-
timer = setTimeout(
|
|
19822
|
+
new Promise((resolve5) => {
|
|
19823
|
+
timer = setTimeout(resolve5, 350);
|
|
19393
19824
|
})
|
|
19394
19825
|
]).finally(() => {
|
|
19395
19826
|
if (timer) clearTimeout(timer);
|
|
@@ -20391,12 +20822,12 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20391
20822
|
const cat = v && typeof v === "object" ? v.category : null;
|
|
20392
20823
|
const c = Math.max(1, Number(v && typeof v === "object" ? v.count : 1) || 1);
|
|
20393
20824
|
if (cat === "Explore") exploreHits += c;
|
|
20394
|
-
else if (cat === "
|
|
20825
|
+
else if (cat === "Web Research") searchHits += c;
|
|
20395
20826
|
}
|
|
20396
20827
|
} else if (it.name) {
|
|
20397
20828
|
const cat = classifyToolCategory(it.name, it.args || {});
|
|
20398
20829
|
if (cat === "Explore") exploreHits = count;
|
|
20399
|
-
else if (cat === "
|
|
20830
|
+
else if (cat === "Web Research") searchHits = count;
|
|
20400
20831
|
}
|
|
20401
20832
|
if (exploreHits > 0) {
|
|
20402
20833
|
exploreCount += exploreHits;
|
|
@@ -20495,7 +20926,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20495
20926
|
const promptMetaRows = promptMetaVisible ? 2 : 0;
|
|
20496
20927
|
const overlayHintRequested = !inputBoxHidden && !hasFloatingPanel && !liveSpinner && !!inputHint && !queuedVisible;
|
|
20497
20928
|
const overlayHintRows = 0;
|
|
20498
|
-
const
|
|
20929
|
+
const queuedFullRows = queuedVisible ? state.queued.reduce(
|
|
20930
|
+
(sum, item) => sum + queuedBandRows(String(item.displayText || item.text || ""), Math.max(1, frameColumns - 4)),
|
|
20931
|
+
0
|
|
20932
|
+
) : 0;
|
|
20933
|
+
const queuedRowBudget = Math.max(3, Math.floor(resizeState.rows / 3));
|
|
20934
|
+
const queuedCompact = queuedFullRows > queuedRowBudget;
|
|
20935
|
+
const queuedRows = queuedVisible ? queuedCompact ? state.queued.length : queuedFullRows : 0;
|
|
20499
20936
|
const INPUT_BOX_ROWS = promptBoxRows + promptMetaRows + overlayHintRows;
|
|
20500
20937
|
const baseReserve = WELCOME_ROWS + SCROLL_HINT_ROWS + LIVE_STATUS_ROWS + INPUT_BOX_ROWS + STATUSLINE_ROWS + queuedRows;
|
|
20501
20938
|
const maxFloatingPanelRows = Math.max(0, resizeState.rows - baseReserve - 1);
|
|
@@ -20530,10 +20967,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20530
20967
|
const nextMetaRows = Number(String(panelLayoutSignature).split("|")[PANEL_LAYOUT_SIG.PROMPT_META]) || 0;
|
|
20531
20968
|
const doneTailAppendedThisCommit = latestDoneAtTail && (latestTranscriptItem?.id ?? null) !== panelTransition.tailId;
|
|
20532
20969
|
const spinnerMetaCollapseRows = doneTailAppendedThisCommit ? Math.max(0, prevMetaRows - nextMetaRows) : 0;
|
|
20970
|
+
const prevQueuedSigRows = Number(String(panelTransition.signature).split("|")[PANEL_LAYOUT_SIG.QUEUED]) || 0;
|
|
20971
|
+
const nextQueuedSigRows = Number(String(panelLayoutSignature).split("|")[PANEL_LAYOUT_SIG.QUEUED]) || 0;
|
|
20972
|
+
const userTailAppendedThisCommit = latestTranscriptItem?.kind === "user" && (latestTranscriptItem?.id ?? null) !== panelTransition.tailId;
|
|
20973
|
+
const queuedPromoteCollapseRows = userTailAppendedThisCommit ? Math.max(0, prevQueuedSigRows - nextQueuedSigRows) : 0;
|
|
20533
20974
|
const instantPanelClose = panelShrinkRows > 0 && (promptRowsOnlyChange || isInstantPanelCloseTransition(panelTransition.signature, panelLayoutSignature, initialProjectEntryClose));
|
|
20534
20975
|
const slashOpenOnEmptyTranscript = initialProjectEntryClose && !panelSignatureFlags(panelTransition.signature).slash && panelSignatureFlags(panelLayoutSignature).slash;
|
|
20535
20976
|
if (instantPanelClose) {
|
|
20536
|
-
panelCloseInkMaskRowsRef.current = Math.max(0, panelShrinkRows - spinnerMetaCollapseRows);
|
|
20977
|
+
panelCloseInkMaskRowsRef.current = Math.max(0, panelShrinkRows - spinnerMetaCollapseRows - queuedPromoteCollapseRows);
|
|
20537
20978
|
panelTransition.clearRows = 0;
|
|
20538
20979
|
panelTransition.guardRows = 0;
|
|
20539
20980
|
panelTransition.epoch = panelTransitionEpoch;
|
|
@@ -20562,8 +21003,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20562
21003
|
const viewportHeight = Math.max(1, resizeState.rows - bottomReserve);
|
|
20563
21004
|
const guardCapacityRows = Math.max(0, viewportHeight - 1);
|
|
20564
21005
|
const baseGuardRows = guardCapacityRows > 0 ? 1 : 0;
|
|
20565
|
-
const
|
|
20566
|
-
const scrollGuardRows = !scrollGuardNearBottom && scrollOffset > 0 && guardCapacityRows > baseGuardRows ? 1 : 0;
|
|
21006
|
+
const scrollGuardRows = 0;
|
|
20567
21007
|
const transcriptGuardRows = Math.min(guardCapacityRows, baseGuardRows + panelTransitionGuardRows + scrollGuardRows);
|
|
20568
21008
|
const welcomePromptHintText = conditionalWelcomePromptHint || welcomePromptHintRef.current || "";
|
|
20569
21009
|
const welcomePromptHintVisible = Boolean(
|
|
@@ -20571,13 +21011,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20571
21011
|
);
|
|
20572
21012
|
const welcomePromptHintRows = welcomePromptHintVisible && viewportHeight - transcriptGuardRows >= 2 ? 1 : 0;
|
|
20573
21013
|
welcomePromptHintVisibleRef.current = welcomePromptHintRows > 0;
|
|
21014
|
+
const overlayHintBandRows = overlayHintRequested && state.items.length === 0 && viewportHeight - transcriptGuardRows - welcomePromptHintRows >= 2 ? 1 : 0;
|
|
20574
21015
|
const panelCloseMaskRows = Math.min(
|
|
20575
21016
|
panelCloseInkMaskRows,
|
|
20576
|
-
Math.max(0, viewportHeight - transcriptGuardRows - welcomePromptHintRows - 1)
|
|
21017
|
+
Math.max(0, viewportHeight - transcriptGuardRows - welcomePromptHintRows - overlayHintBandRows - 1)
|
|
20577
21018
|
);
|
|
20578
21019
|
const transcriptContentHeight = Math.max(
|
|
20579
21020
|
1,
|
|
20580
|
-
viewportHeight - transcriptGuardRows - panelCloseMaskRows - welcomePromptHintRows
|
|
21021
|
+
viewportHeight - transcriptGuardRows - panelCloseMaskRows - welcomePromptHintRows - overlayHintBandRows
|
|
20581
21022
|
);
|
|
20582
21023
|
const transcriptBottomSlackRows = Math.max(0, baseGuardRows);
|
|
20583
21024
|
transcriptBottomSlackRowsRef.current = transcriptBottomSlackRows;
|
|
@@ -20799,9 +21240,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20799
21240
|
backgroundColor: surfaceBackground()
|
|
20800
21241
|
}
|
|
20801
21242
|
) : null,
|
|
21243
|
+
overlayHintBandRows > 0 ? /* @__PURE__ */ jsxs16(Box18, { height: 1, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
|
|
21244
|
+
/* @__PURE__ */ jsx20(Box18, { flexGrow: 1, flexShrink: 1, overflow: "hidden" }),
|
|
21245
|
+
/* @__PURE__ */ jsx20(Box18, { flexShrink: 0, width: guardHintWidth || 1, marginLeft: 1, marginRight: 1, justifyContent: "flex-end", overflow: "hidden", children: /* @__PURE__ */ jsx20(Text20, { color: promptStatusColor(inputHintTone), wrap: "truncate", children: inputHint }) })
|
|
21246
|
+
] }) : null,
|
|
20802
21247
|
transcriptGuardRows > 0 ? /* @__PURE__ */ jsxs16(Box18, { height: transcriptGuardRows, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
|
|
20803
21248
|
/* @__PURE__ */ jsx20(Box18, { flexGrow: 1, flexShrink: 1, overflow: "hidden" }),
|
|
20804
|
-
overlayHintFallbackRow ? /* @__PURE__ */ jsx20(Box18, { flexShrink: 0, width: guardHintWidth || 1, marginLeft: 1, marginRight: 1, justifyContent: "flex-end", overflow: "hidden", children: /* @__PURE__ */ jsx20(Text20, { color: promptStatusColor(inputHintTone), wrap: "truncate", children: inputHint }) }) : null
|
|
21249
|
+
overlayHintFallbackRow && overlayHintBandRows === 0 ? /* @__PURE__ */ jsx20(Box18, { flexShrink: 0, width: guardHintWidth || 1, marginLeft: 1, marginRight: 1, justifyContent: "flex-end", overflow: "hidden", children: /* @__PURE__ */ jsx20(Text20, { color: promptStatusColor(inputHintTone), wrap: "truncate", children: inputHint }) }) : null
|
|
20805
21250
|
] }) : null
|
|
20806
21251
|
]
|
|
20807
21252
|
}
|
|
@@ -21000,7 +21445,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
21000
21445
|
),
|
|
21001
21446
|
/* @__PURE__ */ jsx20(Box18, { height: 1, width: "100%", backgroundColor: surfaceBackground() })
|
|
21002
21447
|
] }) : null,
|
|
21003
|
-
queuedVisible ? /* @__PURE__ */ jsx20(QueuedCommands, { queued: state.queued, columns: frameColumns }) : null,
|
|
21448
|
+
queuedVisible ? /* @__PURE__ */ jsx20(QueuedCommands, { queued: state.queued, columns: frameColumns, compact: queuedCompact }) : null,
|
|
21004
21449
|
/* @__PURE__ */ jsx20(
|
|
21005
21450
|
Box18,
|
|
21006
21451
|
{
|
|
@@ -21188,6 +21633,13 @@ function modelVisibleToolCompletionMessage(text, meta = {}) {
|
|
|
21188
21633
|
].join("\n");
|
|
21189
21634
|
}
|
|
21190
21635
|
|
|
21636
|
+
// src/session-runtime/session-text.mjs
|
|
21637
|
+
var LATE_TOOL_ANNOUNCEMENT_SENTINEL = "connected after this session started";
|
|
21638
|
+
function isLateToolAnnouncement(text) {
|
|
21639
|
+
const value = String(text || "");
|
|
21640
|
+
return value.includes(LATE_TOOL_ANNOUNCEMENT_SENTINEL) && /<available-deferred-tools>/i.test(value);
|
|
21641
|
+
}
|
|
21642
|
+
|
|
21191
21643
|
// src/tui/engine/boot-profile.mjs
|
|
21192
21644
|
import { performance } from "node:perf_hooks";
|
|
21193
21645
|
var BOOT_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ""));
|
|
@@ -21957,6 +22409,9 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
21957
22409
|
if (!trimmed) return { action: "ignore" };
|
|
21958
22410
|
const parsed = parseAgentJob(trimmed);
|
|
21959
22411
|
const meta = event?.meta && typeof event.meta === "object" ? event.meta : {};
|
|
22412
|
+
if (meta.kind === "update-notice") {
|
|
22413
|
+
return { action: "notice", displayText: trimmed, tone: meta.tone === "warn" ? "warn" : "info" };
|
|
22414
|
+
}
|
|
21960
22415
|
if (!isExecutionNotification(event, trimmed, parsed)) {
|
|
21961
22416
|
return { action: "enqueue", displayText: trimmed, modelContent: trimmed };
|
|
21962
22417
|
}
|
|
@@ -21973,8 +22428,8 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
21973
22428
|
|
|
21974
22429
|
// src/tui/engine/render-timing.mjs
|
|
21975
22430
|
var RENDER_THROTTLE_FLUSH_MS = 12;
|
|
21976
|
-
var yieldToRenderer = () => new Promise((
|
|
21977
|
-
setTimeout(
|
|
22431
|
+
var yieldToRenderer = () => new Promise((resolve5) => {
|
|
22432
|
+
setTimeout(resolve5, RENDER_THROTTLE_FLUSH_MS);
|
|
21978
22433
|
});
|
|
21979
22434
|
|
|
21980
22435
|
// src/tui/engine/tool-result-status.mjs
|
|
@@ -22152,9 +22607,9 @@ function createToolApproval({ getState, set, nextId: nextId2, getDisposed, timeo
|
|
|
22152
22607
|
}
|
|
22153
22608
|
function requestToolApproval(input = {}) {
|
|
22154
22609
|
if (getDisposed()) return Promise.resolve({ approved: false, reason: "runtime disposed" });
|
|
22155
|
-
return new Promise((
|
|
22610
|
+
return new Promise((resolve5) => {
|
|
22156
22611
|
const id = nextId2();
|
|
22157
|
-
toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve:
|
|
22612
|
+
toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve: resolve5, timer: null });
|
|
22158
22613
|
presentNextToolApproval();
|
|
22159
22614
|
});
|
|
22160
22615
|
}
|
|
@@ -22190,11 +22645,12 @@ function createToolCardResults({
|
|
|
22190
22645
|
markToolCallDone(card.callId);
|
|
22191
22646
|
(card.aggregate?.ensureVisible || card.ensureVisible)?.();
|
|
22192
22647
|
const rawText = toolResultText(message?.content);
|
|
22193
|
-
const isError = message?.isError === true || message?.toolKind === "error" || /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText));
|
|
22194
|
-
const text = isError ? toolErrorDisplay(rawText, card?.name || "tool") : rawText;
|
|
22195
22648
|
const aggregate = card.aggregate;
|
|
22649
|
+
const callRec = aggregate && callId ? aggregate.calls.get(callId) : null;
|
|
22650
|
+
const isMemoryCall = classifyToolCategory(callRec?.name || card?.name || "", callRec?.args || {}) === "Memory";
|
|
22651
|
+
const isError = message?.isError === true || message?.toolKind === "error" || /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText)) || isMemoryCall && memoryCoreResultErrorText(rawText) != null;
|
|
22652
|
+
const text = isError ? toolErrorDisplay(rawText, card?.name || "tool") : rawText;
|
|
22196
22653
|
if (aggregate && card.itemId === aggregate.itemId) {
|
|
22197
|
-
const callRec = callId ? aggregate.calls.get(callId) : null;
|
|
22198
22654
|
if (!callRec) return false;
|
|
22199
22655
|
if (callRec.resolved) {
|
|
22200
22656
|
card.done = true;
|
|
@@ -22224,6 +22680,7 @@ function createToolCardResults({
|
|
|
22224
22680
|
errorCount: errors,
|
|
22225
22681
|
count: allCalls.length,
|
|
22226
22682
|
completedCount: visualCompleted,
|
|
22683
|
+
doneCategories: aggregateDoneCategories(allCalls),
|
|
22227
22684
|
completedAt: Number(currentItem?.completedAt) || Date.now()
|
|
22228
22685
|
});
|
|
22229
22686
|
card.done = true;
|
|
@@ -22326,6 +22783,7 @@ function createToolCardResults({
|
|
|
22326
22783
|
errorCount: errors,
|
|
22327
22784
|
count: allCalls.length,
|
|
22328
22785
|
completedCount: totalCompleted,
|
|
22786
|
+
doneCategories: aggregateDoneCategories(allCalls),
|
|
22329
22787
|
completedAt: Date.now()
|
|
22330
22788
|
});
|
|
22331
22789
|
for (const sibling of toolCards || []) {
|
|
@@ -22365,7 +22823,8 @@ function createAgentJobFeed({
|
|
|
22365
22823
|
makeQueueEntry,
|
|
22366
22824
|
getPending,
|
|
22367
22825
|
agentStatusState,
|
|
22368
|
-
displayedExecutionNotificationKeys
|
|
22826
|
+
displayedExecutionNotificationKeys,
|
|
22827
|
+
pushNotice
|
|
22369
22828
|
}) {
|
|
22370
22829
|
let executionResumeKickDeferred = false;
|
|
22371
22830
|
function kickExecutionPendingResume() {
|
|
@@ -22413,6 +22872,10 @@ function createAgentJobFeed({
|
|
|
22413
22872
|
const notificationKey = notificationQueueKey(event, text, parsed);
|
|
22414
22873
|
const delivery = resolveTuiRuntimeNotificationDelivery(event, text);
|
|
22415
22874
|
if (delivery.action === "ignore") return;
|
|
22875
|
+
if (delivery.action === "notice") {
|
|
22876
|
+
pushNotice?.(delivery.displayText, delivery.tone || "info", { transcript: true });
|
|
22877
|
+
return true;
|
|
22878
|
+
}
|
|
22416
22879
|
if (delivery.action === "status-only") {
|
|
22417
22880
|
if (parsed?.taskId) set(agentStatusState({ force: true }));
|
|
22418
22881
|
return true;
|
|
@@ -23016,7 +23479,7 @@ function createSessionFlow(bag) {
|
|
|
23016
23479
|
const startedAt = Date.now();
|
|
23017
23480
|
set({ commandBusy: true, commandStatus: { active: true, verb, startedAt, mode: "auto-clear" } });
|
|
23018
23481
|
try {
|
|
23019
|
-
await new Promise((
|
|
23482
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
23020
23483
|
let compactType = null;
|
|
23021
23484
|
if (useCompaction) {
|
|
23022
23485
|
const compaction = runtime.getCompactionSettings();
|
|
@@ -23473,6 +23936,7 @@ function createRunTurn(bag) {
|
|
|
23473
23936
|
isError: errors > 0,
|
|
23474
23937
|
count: allCalls.length,
|
|
23475
23938
|
completedCount: allCalls.length,
|
|
23939
|
+
doneCategories: aggregateDoneCategories(allCalls),
|
|
23476
23940
|
completedAt: Date.now()
|
|
23477
23941
|
});
|
|
23478
23942
|
}
|
|
@@ -23670,7 +24134,8 @@ function createRunTurn(bag) {
|
|
|
23670
24134
|
if (!callRec || callRec.resolved || callRec.completedEarly) return;
|
|
23671
24135
|
aggregate.ensureVisible?.();
|
|
23672
24136
|
const rawText = toolResultText(message?.content);
|
|
23673
|
-
const
|
|
24137
|
+
const isMemoryCall = classifyToolCategory(callRec.name, callRec.args) === "Memory";
|
|
24138
|
+
const isError = message?.isError === true || message?.toolKind === "error" || /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText)) || isMemoryCall && memoryCoreResultErrorText(rawText) != null;
|
|
23674
24139
|
const text = isError ? toolErrorDisplay(rawText, callRec.name || "tool") : rawText;
|
|
23675
24140
|
callRec.summary = !isError ? summarizeToolResult(callRec.name, callRec.args, rawText, isError) : null;
|
|
23676
24141
|
assignAggregateSummaryOrder(aggregate, callRec);
|
|
@@ -24529,6 +24994,26 @@ import { readFileSync as readFileSync6 } from "node:fs";
|
|
|
24529
24994
|
import { dirname as dirname8, join as join9 } from "node:path";
|
|
24530
24995
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
24531
24996
|
var GLYPH = { ok: "\u2713", warn: "\u26A0", fail: "\u2717" };
|
|
24997
|
+
var CKPT_WRITE_WARN_S = 10;
|
|
24998
|
+
var CKPT_WRITE_MEDIAN_S = 5;
|
|
24999
|
+
function parseCheckpointWriteSeconds(logText) {
|
|
25000
|
+
const lines = String(logText).split(/\r?\n/).slice(-200);
|
|
25001
|
+
const out = [];
|
|
25002
|
+
for (const line of lines) {
|
|
25003
|
+
const m = /checkpoint complete:.*?\bwrite=([\d.]+)\s*s/i.exec(line);
|
|
25004
|
+
if (m) {
|
|
25005
|
+
const v = Number(m[1]);
|
|
25006
|
+
if (Number.isFinite(v)) out.push(v);
|
|
25007
|
+
}
|
|
25008
|
+
}
|
|
25009
|
+
return out;
|
|
25010
|
+
}
|
|
25011
|
+
function median(nums) {
|
|
25012
|
+
if (!nums.length) return 0;
|
|
25013
|
+
const s = [...nums].sort((a, b) => a - b);
|
|
25014
|
+
const mid = s.length >> 1;
|
|
25015
|
+
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
|
|
25016
|
+
}
|
|
24532
25017
|
function readPackageJson() {
|
|
24533
25018
|
try {
|
|
24534
25019
|
const dir = dirname8(fileURLToPath3(import.meta.url));
|
|
@@ -24662,6 +25147,31 @@ async function buildDoctorReport(runtime = {}, getState = () => ({})) {
|
|
|
24662
25147
|
const enabled = h.enabled === true;
|
|
24663
25148
|
row("ok", `${enabled ? "enabled" : "disabled"} \xB7 ${events.length} event${events.length === 1 ? "" : "s"}`);
|
|
24664
25149
|
});
|
|
25150
|
+
if (process.platform === "win32") {
|
|
25151
|
+
await check("defender", async (row) => {
|
|
25152
|
+
const dataDir = resolvePluginData();
|
|
25153
|
+
const pgdata = join9(dataDir, "pgdata");
|
|
25154
|
+
let writes;
|
|
25155
|
+
try {
|
|
25156
|
+
writes = parseCheckpointWriteSeconds(readFileSync6(join9(dataDir, "pg.log"), "utf8"));
|
|
25157
|
+
} catch {
|
|
25158
|
+
row("ok", "pg.log unavailable \xB7 check skipped");
|
|
25159
|
+
return;
|
|
25160
|
+
}
|
|
25161
|
+
if (!writes.length) {
|
|
25162
|
+
row("ok", "no checkpoint timings yet");
|
|
25163
|
+
return;
|
|
25164
|
+
}
|
|
25165
|
+
const max = Math.max(...writes);
|
|
25166
|
+
const med = median(writes);
|
|
25167
|
+
if (max <= CKPT_WRITE_WARN_S && med <= CKPT_WRITE_MEDIAN_S) {
|
|
25168
|
+
row("ok", `checkpoint write median ${med.toFixed(1)}s \xB7 max ${max.toFixed(1)}s`);
|
|
25169
|
+
return;
|
|
25170
|
+
}
|
|
25171
|
+
const fix = `Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile','-Command',"Add-MpPreference -ExclusionPath '${pgdata}'"`;
|
|
25172
|
+
row("warn", `slow checkpoints (median ${med.toFixed(1)}s \xB7 max ${max.toFixed(1)}s) suggest Defender real-time scan of ${pgdata}. Fix (run in PowerShell): ${fix}`);
|
|
25173
|
+
});
|
|
25174
|
+
}
|
|
24665
25175
|
return ["mixdog doctor \u2014 installation health", ...rows].join("\n");
|
|
24666
25176
|
}
|
|
24667
25177
|
|
|
@@ -24887,7 +25397,8 @@ function createEngineApiA(bag) {
|
|
|
24887
25397
|
},
|
|
24888
25398
|
setCwd: (path2, options = {}) => {
|
|
24889
25399
|
const next = runtime.setCwd(path2);
|
|
24890
|
-
|
|
25400
|
+
const sessionList = recomputePromptHistory(getState().items);
|
|
25401
|
+
set({ cwd: next, promptHistoryList: buildMergedPromptHistory(sessionList, loadPromptHistory(next)) });
|
|
24891
25402
|
if (options?.notice !== false) {
|
|
24892
25403
|
pushNotice(options?.message || `Project set: ${projectNameFromPath2(next)}`, "info");
|
|
24893
25404
|
}
|
|
@@ -24948,17 +25459,18 @@ function createEngineApiA(bag) {
|
|
|
24948
25459
|
}
|
|
24949
25460
|
},
|
|
24950
25461
|
setMcpServerEnabled: async (name, enabled) => {
|
|
24951
|
-
|
|
24952
|
-
|
|
24953
|
-
try {
|
|
24954
|
-
const status = await runtime.setMcpServerEnabled?.(name, enabled);
|
|
25462
|
+
const status = await runtime.setMcpServerEnabled?.(name, enabled);
|
|
25463
|
+
setImmediate(() => {
|
|
24955
25464
|
resetStatsAndSyncContext();
|
|
24956
25465
|
set({ ...routeState(), stats: { ...getState().stats } });
|
|
25466
|
+
});
|
|
25467
|
+
const row = status?.servers?.find((s) => s.name === name);
|
|
25468
|
+
if (enabled && row && (row.status === "failed" || row.error)) {
|
|
25469
|
+
pushNotice(`mcp enable failed: ${name}${row.error ? ` \u2014 ${row.error}` : ""}`, "error");
|
|
25470
|
+
} else {
|
|
24957
25471
|
pushNotice(`mcp ${enabled ? "enabled" : "disabled"}: ${name}`, "info");
|
|
24958
|
-
return status;
|
|
24959
|
-
} finally {
|
|
24960
|
-
set({ commandBusy: false });
|
|
24961
25472
|
}
|
|
25473
|
+
return status;
|
|
24962
25474
|
},
|
|
24963
25475
|
getDisabledSkills: () => runtime.getDisabledSkills?.() || { disabled: [] },
|
|
24964
25476
|
setDisabledSkills: (disabled) => runtime.setDisabledSkills?.(disabled) || { disabled: [] },
|
|
@@ -25112,7 +25624,7 @@ function createEngineApiA(bag) {
|
|
|
25112
25624
|
const startedAt = Date.now();
|
|
25113
25625
|
set({ commandBusy: true, commandStatus: { active: true, verb: "Running diagnostics", startedAt, mode: "doctor" } });
|
|
25114
25626
|
try {
|
|
25115
|
-
await new Promise((
|
|
25627
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
25116
25628
|
const report = await buildDoctorReport(runtime, getState);
|
|
25117
25629
|
pushNotice(report, "info");
|
|
25118
25630
|
return report;
|
|
@@ -25132,7 +25644,7 @@ function createEngineApiA(bag) {
|
|
|
25132
25644
|
const startedAt = Date.now();
|
|
25133
25645
|
set({ commandBusy: true, commandStatus: { active: true, verb: "Compacting conversation", startedAt, mode: "compacting" } });
|
|
25134
25646
|
try {
|
|
25135
|
-
await new Promise((
|
|
25647
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
25136
25648
|
const result = await runtime.compact({ recoverAgent: true });
|
|
25137
25649
|
syncContextStats({ allowEstimated: true });
|
|
25138
25650
|
set({ ...routeState(), stats: { ...getState().stats } });
|
|
@@ -25297,7 +25809,9 @@ async function createEngineSession({
|
|
|
25297
25809
|
// - promptHistoryList: newest-first deduped user-prompt history, rebuilt
|
|
25298
25810
|
// only when a user item is appended (replaces the per-change rescan).
|
|
25299
25811
|
activeToolSummary: null,
|
|
25300
|
-
|
|
25812
|
+
// Seed from the persisted cwd-scoped store so up-arrow history is available
|
|
25813
|
+
// on a fresh start, before any bulk swap / first submit republishes it.
|
|
25814
|
+
promptHistoryList: buildMergedPromptHistory([], loadPromptHistory(cwd)),
|
|
25301
25815
|
...baseRouteState(),
|
|
25302
25816
|
displayContextWindow: 0,
|
|
25303
25817
|
compactBoundaryTokens: 0,
|
|
@@ -25346,7 +25860,12 @@ async function createEngineSession({
|
|
|
25346
25860
|
if (id != null) itemIndexById.set(id, i);
|
|
25347
25861
|
}
|
|
25348
25862
|
activeToolCalls.clear();
|
|
25349
|
-
state = {
|
|
25863
|
+
state = {
|
|
25864
|
+
...state,
|
|
25865
|
+
items: nextItems,
|
|
25866
|
+
promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
|
|
25867
|
+
activeToolSummary: null
|
|
25868
|
+
};
|
|
25350
25869
|
return nextItems;
|
|
25351
25870
|
};
|
|
25352
25871
|
const activeToolCalls = /* @__PURE__ */ new Map();
|
|
@@ -25359,7 +25878,7 @@ async function createEngineSession({
|
|
|
25359
25878
|
if (rec.category === "Explore") {
|
|
25360
25879
|
exploreCount += c;
|
|
25361
25880
|
if (started > 0 && (exploreStart === 0 || started < exploreStart)) exploreStart = started;
|
|
25362
|
-
} else if (rec.category === "
|
|
25881
|
+
} else if (rec.category === "Web Research") {
|
|
25363
25882
|
searchCount += c;
|
|
25364
25883
|
if (started > 0 && (searchStart === 0 || started < searchStart)) searchStart = started;
|
|
25365
25884
|
}
|
|
@@ -25369,7 +25888,7 @@ async function createEngineSession({
|
|
|
25369
25888
|
if (next !== prev) set({ activeToolSummary: next || null });
|
|
25370
25889
|
};
|
|
25371
25890
|
const markToolCallActive = (callKey, category, count, startedAt2) => {
|
|
25372
|
-
if (!callKey || category !== "Explore" && category !== "
|
|
25891
|
+
if (!callKey || category !== "Explore" && category !== "Web Research") return;
|
|
25373
25892
|
activeToolCalls.set(callKey, { category, count: Math.max(1, Number(count || 1)), startedAt: Number(startedAt2 || Date.now()) });
|
|
25374
25893
|
recomputeActiveToolSummary();
|
|
25375
25894
|
};
|
|
@@ -25391,7 +25910,7 @@ async function createEngineSession({
|
|
|
25391
25910
|
const items = [...state.items, item];
|
|
25392
25911
|
if (item?.id != null) itemIndexById.set(item.id, index);
|
|
25393
25912
|
if (item?.kind === "user") {
|
|
25394
|
-
const promptHistoryList = recomputePromptHistory(items);
|
|
25913
|
+
const promptHistoryList = buildMergedPromptHistory(recomputePromptHistory(items), loadPromptHistory(state.cwd));
|
|
25395
25914
|
set({ items, promptHistoryList });
|
|
25396
25915
|
} else {
|
|
25397
25916
|
set({ items });
|
|
@@ -25426,7 +25945,9 @@ async function createEngineSession({
|
|
|
25426
25945
|
const pushUserOrSyntheticItem = (text, id = nextId(), origin = "user") => {
|
|
25427
25946
|
if (origin === "injected" && isLikelyToolCompletionWrapper(text)) return;
|
|
25428
25947
|
if (isModelVisibleToolCompletionWrapper(text)) return;
|
|
25948
|
+
if (isLateToolAnnouncement(text)) return;
|
|
25429
25949
|
if (upsertSyntheticToolItem(text, id)) return;
|
|
25950
|
+
if (origin === "user") appendPromptHistory(state.cwd, text);
|
|
25430
25951
|
pushItem({ kind: "user", id, text });
|
|
25431
25952
|
};
|
|
25432
25953
|
const pushToast = (text, tone = "info", ttlMs = 3e3) => {
|
|
@@ -25533,7 +26054,8 @@ async function createEngineSession({
|
|
|
25533
26054
|
makeQueueEntry: (...args) => bag.makeQueueEntry(...args),
|
|
25534
26055
|
getPending: () => pending,
|
|
25535
26056
|
agentStatusState,
|
|
25536
|
-
displayedExecutionNotificationKeys
|
|
26057
|
+
displayedExecutionNotificationKeys,
|
|
26058
|
+
pushNotice
|
|
25537
26059
|
});
|
|
25538
26060
|
lifecycle.unsubscribeRuntimeNotifications = subscribeRuntimeNotifications();
|
|
25539
26061
|
if (typeof runtime.onRemoteStateChange === "function") {
|
|
@@ -25624,7 +26146,7 @@ function signalExitCode(signal, fallback = 1) {
|
|
|
25624
26146
|
}
|
|
25625
26147
|
function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
25626
26148
|
const ms = Math.max(1, Math.floor(Number(timeoutMs) || 1));
|
|
25627
|
-
return new Promise((
|
|
26149
|
+
return new Promise((resolve5, reject) => {
|
|
25628
26150
|
let settled = false;
|
|
25629
26151
|
const timer = setTimeout(() => {
|
|
25630
26152
|
if (settled) return;
|
|
@@ -25636,7 +26158,7 @@ function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
|
25636
26158
|
if (settled) return;
|
|
25637
26159
|
settled = true;
|
|
25638
26160
|
clearTimeout(timer);
|
|
25639
|
-
|
|
26161
|
+
resolve5(value);
|
|
25640
26162
|
}).catch((error) => {
|
|
25641
26163
|
if (settled) return;
|
|
25642
26164
|
settled = true;
|
|
@@ -25750,54 +26272,14 @@ function installProcessSignalCleanup({
|
|
|
25750
26272
|
};
|
|
25751
26273
|
}
|
|
25752
26274
|
|
|
25753
|
-
// src/runtime/channels/lib/runtime-paths.mjs
|
|
25754
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
25755
|
-
import { basename as basename4, join as join10, resolve as resolve5 } from "path";
|
|
25756
|
-
|
|
25757
|
-
// src/runtime/channels/lib/state-file.mjs
|
|
25758
|
-
import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
25759
|
-
function ensureDir2(dirPath) {
|
|
25760
|
-
mkdirSync6(dirPath, { recursive: true });
|
|
25761
|
-
}
|
|
25762
|
-
|
|
25763
|
-
// src/runtime/channels/lib/runtime-paths.mjs
|
|
25764
|
-
var RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT ? resolve5(process.env.MIXDOG_RUNTIME_ROOT) : join10(tmpdir2(), "mixdog");
|
|
25765
|
-
var OWNER_DIR = join10(RUNTIME_ROOT, "owners");
|
|
25766
|
-
var ACTIVE_INSTANCE_FILE = join10(RUNTIME_ROOT, "active-instance.json");
|
|
25767
|
-
var RUNTIME_STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
25768
|
-
var STATUS_FILE_TTL = 6 * 60 * 60 * 1e3;
|
|
25769
|
-
var TMP_FILE_TTL = 60 * 60 * 1e3;
|
|
25770
|
-
function sanitize(value) {
|
|
25771
|
-
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
25772
|
-
}
|
|
25773
|
-
function ensureRuntimeDirs() {
|
|
25774
|
-
ensureDir2(RUNTIME_ROOT);
|
|
25775
|
-
ensureDir2(OWNER_DIR);
|
|
25776
|
-
}
|
|
25777
|
-
var UI_HEARTBEAT_STALE_MS = 5 * 60 * 1e3;
|
|
25778
|
-
function touchUiHeartbeat(instanceId) {
|
|
25779
|
-
ensureRuntimeDirs();
|
|
25780
|
-
try {
|
|
25781
|
-
updateJsonAtomicSync(ACTIVE_INSTANCE_FILE, (curRaw) => {
|
|
25782
|
-
if (!curRaw) return void 0;
|
|
25783
|
-
if (instanceId && curRaw.instanceId !== instanceId) return void 0;
|
|
25784
|
-
return { ...curRaw, ui_heartbeat_at: Date.now(), updatedAt: Date.now() };
|
|
25785
|
-
}, { compact: true, fsync: false, fsyncDir: false, renameFallback: "truncate", timeoutMs: 0 });
|
|
25786
|
-
} catch {
|
|
25787
|
-
}
|
|
25788
|
-
}
|
|
25789
|
-
var SERVER_PID_FILE = join10(
|
|
25790
|
-
RUNTIME_ROOT,
|
|
25791
|
-
`server-${sanitize(resolvePluginData() ?? "default")}.pid`
|
|
25792
|
-
);
|
|
25793
|
-
|
|
25794
26275
|
// src/tui/index.jsx
|
|
25795
26276
|
var import_mixdog_debug = __toESM(require_mixdog_debug(), 1);
|
|
25796
26277
|
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
25797
|
-
var TERMINAL_MODE_RESET = "\x1B[?1006l\x1B[?1005l\x1B[?1015l\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?2004l\x1B[?25h";
|
|
26278
|
+
var TERMINAL_MODE_RESET = "\x1B[?1006l\x1B[?1005l\x1B[?1015l\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?2004l\x1B[>0s\x1B[?25h";
|
|
25798
26279
|
var TERMINAL_OSC_RESET_BG = "\x1B]111\x07";
|
|
25799
26280
|
var TERMINAL_MODE_RESET_HIDDEN_CURSOR = TERMINAL_MODE_RESET.replace("\x1B[?25h", "\x1B[?25l");
|
|
25800
|
-
var
|
|
26281
|
+
var XTSHIFTESCAPE_ON = process.env.WT_SESSION ? "" : "\x1B[>1s";
|
|
26282
|
+
var MOUSE_TRACKING_ON2 = `\x1B[?1000h\x1B[?1002h\x1B[?1006h${XTSHIFTESCAPE_ON}`;
|
|
25801
26283
|
var BOOT_PROFILE_ENABLED2 = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ""));
|
|
25802
26284
|
var BOOT_PROFILE_START2 = globalThis.__mixdogBootProfileStart || (globalThis.__mixdogBootProfileStart = performance3.now());
|
|
25803
26285
|
var EXIT_WAIT_TIMEOUT_MS = positiveIntEnv2("MIXDOG_TUI_EXIT_WAIT_MS", 2500);
|
|
@@ -25986,8 +26468,8 @@ function waitWithTimeout2(promise, ms) {
|
|
|
25986
26468
|
let timer = null;
|
|
25987
26469
|
return Promise.race([
|
|
25988
26470
|
Promise.resolve(promise).then(() => true),
|
|
25989
|
-
new Promise((
|
|
25990
|
-
timer = setTimeout(() =>
|
|
26471
|
+
new Promise((resolve5) => {
|
|
26472
|
+
timer = setTimeout(() => resolve5(false), ms);
|
|
25991
26473
|
})
|
|
25992
26474
|
]).finally(() => {
|
|
25993
26475
|
if (timer) clearTimeout(timer);
|
|
@@ -26006,7 +26488,7 @@ function scheduleHardExit(code = 0) {
|
|
|
26006
26488
|
timer.unref?.();
|
|
26007
26489
|
}
|
|
26008
26490
|
function resolveTuiStderrLogPath() {
|
|
26009
|
-
return process.env.MIXDOG_TUI_STDERR_LOG ||
|
|
26491
|
+
return process.env.MIXDOG_TUI_STDERR_LOG || join10(process.env.MIXDOG_RUNTIME_ROOT || join10(tmpdir2(), "mixdog"), "mixdog-tui.stderr.log");
|
|
26010
26492
|
}
|
|
26011
26493
|
function ansiFg(rgb) {
|
|
26012
26494
|
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ""));
|
|
@@ -26088,7 +26570,7 @@ function installTuiStderrGuard() {
|
|
|
26088
26570
|
const originalWrite = process.stderr.write.bind(process.stderr);
|
|
26089
26571
|
const logPath = resolveTuiStderrLogPath();
|
|
26090
26572
|
try {
|
|
26091
|
-
|
|
26573
|
+
mkdirSync6(dirname9(logPath), { recursive: true });
|
|
26092
26574
|
} catch {
|
|
26093
26575
|
}
|
|
26094
26576
|
(0, import_mixdog_debug.rotateBoundedLog)(logPath, import_mixdog_debug.PLUGIN_LOG_MAX_BYTES, import_mixdog_debug.PLUGIN_LOG_KEEP_BYTES);
|
|
@@ -26133,6 +26615,23 @@ function installTuiStderrGuard() {
|
|
|
26133
26615
|
}
|
|
26134
26616
|
};
|
|
26135
26617
|
}
|
|
26618
|
+
function installTuiConsoleGuard() {
|
|
26619
|
+
const methods = ["log", "info", "warn", "error", "debug", "trace"];
|
|
26620
|
+
const original = /* @__PURE__ */ new Map();
|
|
26621
|
+
for (const m of methods) {
|
|
26622
|
+
original.set(m, console[m]);
|
|
26623
|
+
console[m] = (...args) => {
|
|
26624
|
+
try {
|
|
26625
|
+
process.stderr.write(`[console.${m}] ${format(...args)}
|
|
26626
|
+
`);
|
|
26627
|
+
} catch {
|
|
26628
|
+
}
|
|
26629
|
+
};
|
|
26630
|
+
}
|
|
26631
|
+
return () => {
|
|
26632
|
+
for (const [m, fn] of original) console[m] = fn;
|
|
26633
|
+
};
|
|
26634
|
+
}
|
|
26136
26635
|
async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {}) {
|
|
26137
26636
|
const startedAt = performance3.now();
|
|
26138
26637
|
bootProfile2("run:start", { provider, model, toolMode, remote });
|
|
@@ -26143,6 +26642,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26143
26642
|
return 1;
|
|
26144
26643
|
}
|
|
26145
26644
|
const restoreStderr = installTuiStderrGuard();
|
|
26645
|
+
const restoreConsole = installTuiConsoleGuard();
|
|
26146
26646
|
const stopPerfProbe = installTuiPerfProbe();
|
|
26147
26647
|
const stopLoopProbe = installTuiLoopProbe();
|
|
26148
26648
|
const restorePrimedInput = () => drainStdin(process.stdin);
|
|
@@ -26183,6 +26683,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26183
26683
|
process.off("exit", restoreTerminal);
|
|
26184
26684
|
} catch {
|
|
26185
26685
|
}
|
|
26686
|
+
restoreConsole();
|
|
26186
26687
|
restoreStderr();
|
|
26187
26688
|
process.stderr.write(`mixdog: ${error?.message || error}
|
|
26188
26689
|
`);
|
|
@@ -26208,20 +26709,10 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26208
26709
|
afterCleanup: (reason) => {
|
|
26209
26710
|
restoreTerminal();
|
|
26210
26711
|
dumpActiveHandles(`after-${reason}`);
|
|
26712
|
+
restoreConsole();
|
|
26211
26713
|
restoreStderr();
|
|
26212
26714
|
}
|
|
26213
26715
|
});
|
|
26214
|
-
const uiHeartbeatTimer = setInterval(() => {
|
|
26215
|
-
try {
|
|
26216
|
-
touchUiHeartbeat();
|
|
26217
|
-
} catch {
|
|
26218
|
-
}
|
|
26219
|
-
}, 3e4);
|
|
26220
|
-
if (typeof uiHeartbeatTimer.unref === "function") uiHeartbeatTimer.unref();
|
|
26221
|
-
try {
|
|
26222
|
-
touchUiHeartbeat();
|
|
26223
|
-
} catch {
|
|
26224
|
-
}
|
|
26225
26716
|
const STDIO_DEATH_FATAL_CODES = /* @__PURE__ */ new Set(["EPIPE", "ERR_STREAM_DESTROYED", "EIO"]);
|
|
26226
26717
|
const stdioDeathListeners = [];
|
|
26227
26718
|
const registerStdioDeath = (stream, event, { requireCode = false } = {}) => {
|
|
@@ -26245,7 +26736,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26245
26736
|
registerStdioDeath(process.stderr, "error", { requireCode: true });
|
|
26246
26737
|
registerStdioDeath(process.stderr, "close");
|
|
26247
26738
|
try {
|
|
26248
|
-
const instance = render(/* @__PURE__ */ jsx21(App, { store, forceOnboarding: forceOnboarding === true }), { exitOnCtrlC: false, maxFps: 60, incrementalRendering: true, onRender: makeRenderProfiler() });
|
|
26739
|
+
const instance = render(/* @__PURE__ */ jsx21(App, { store, forceOnboarding: forceOnboarding === true }), { exitOnCtrlC: false, maxFps: 60, incrementalRendering: true, patchConsole: false, onRender: makeRenderProfiler() });
|
|
26249
26740
|
bootProfile2("render:mounted", { ms: (performance3.now() - startedAt).toFixed(1) });
|
|
26250
26741
|
const { waitUntilExit } = instance;
|
|
26251
26742
|
if (mouseTracking && typeof instance.setSelection === "function") {
|
|
@@ -26263,11 +26754,13 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26263
26754
|
if (mouseTracking && typeof instance.getSelectionRows === "function") {
|
|
26264
26755
|
store.getRenderSelectionRows = instance.getSelectionRows;
|
|
26265
26756
|
}
|
|
26757
|
+
if (mouseTracking && typeof instance.forceFullRepaint === "function") {
|
|
26758
|
+
store.forceRenderRepaint = instance.forceFullRepaint;
|
|
26759
|
+
}
|
|
26266
26760
|
await waitUntilExit();
|
|
26267
26761
|
} finally {
|
|
26268
26762
|
stopPerfProbe();
|
|
26269
26763
|
stopLoopProbe();
|
|
26270
|
-
clearInterval(uiHeartbeatTimer);
|
|
26271
26764
|
for (const [stream, event, handler] of stdioDeathListeners.splice(0)) {
|
|
26272
26765
|
try {
|
|
26273
26766
|
stream.off(event, handler);
|
|
@@ -26282,6 +26775,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26282
26775
|
}
|
|
26283
26776
|
restoreTerminal();
|
|
26284
26777
|
dumpActiveHandles("after-restore");
|
|
26778
|
+
restoreConsole();
|
|
26285
26779
|
restoreStderr();
|
|
26286
26780
|
}
|
|
26287
26781
|
scheduleHardExit(0);
|