mixdog 0.9.22 → 0.9.23
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/build-runtime-windows.ps1 +242 -242
- package/scripts/channel-daemon-smoke.mjs +165 -0
- package/scripts/channel-daemon-stub.mjs +69 -0
- package/scripts/recall-usecase-cases.json +1 -1
- package/scripts/smoke-runtime-negative.ps1 +106 -106
- package/scripts/statusline-quota-hysteresis-test.mjs +37 -0
- package/scripts/tool-efficiency-diag.mjs +1 -1
- package/scripts/tool-smoke.mjs +30 -17
- package/src/rules/lead/02-channels.md +3 -3
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +187 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +40 -3
- 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/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/session-lifecycle.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +24 -11
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +6 -5
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +56 -1
- package/src/runtime/channels/lib/owned-runtime.mjs +126 -167
- package/src/runtime/channels/lib/owner-heartbeat.mjs +11 -60
- 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/seat-lock.mjs +196 -0
- package/src/runtime/channels/lib/session-discovery.mjs +2 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +24 -8
- package/src/runtime/channels/lib/worker-main.mjs +42 -11
- 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/shared/atomic-file.mjs +20 -4
- package/src/runtime/shared/tool-primitives.mjs +31 -1
- package/src/runtime/shared/tool-surface.mjs +23 -13
- package/src/session-runtime/config-lifecycle.mjs +48 -7
- package/src/session-runtime/lifecycle-api.mjs +9 -0
- package/src/session-runtime/mcp-glue.mjs +63 -1
- package/src/session-runtime/resource-api.mjs +62 -8
- package/src/session-runtime/runtime-core.mjs +32 -2
- package/src/session-runtime/session-text.mjs +41 -0
- package/src/session-runtime/session-turn-api.mjs +24 -0
- package/src/session-runtime/settings-api.mjs +8 -1
- package/src/session-runtime/tool-catalog.mjs +306 -38
- package/src/session-runtime/tool-defs.mjs +7 -7
- package/src/session-runtime/workflow.mjs +2 -1
- package/src/standalone/channel-daemon-client.mjs +224 -0
- package/src/standalone/channel-daemon-transport.mjs +351 -0
- package/src/standalone/channel-daemon.mjs +139 -0
- package/src/standalone/channel-worker.mjs +213 -4
- package/src/standalone/hook-bus.mjs +71 -3
- package/src/tui/App.jsx +105 -17
- 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/ToolExecution.jsx +2 -2
- package/src/tui/dist/index.mjs +635 -306
- package/src/tui/engine/session-api.mjs +17 -7
- package/src/tui/engine/session-flow.mjs +6 -0
- package/src/tui/engine.mjs +8 -0
- package/src/tui/index.jsx +62 -18
- package/src/tui/paste-attachments.mjs +26 -0
- package/src/ui/statusline.mjs +45 -5
- 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
|
}
|
|
@@ -4943,7 +4954,7 @@ function PromptInput({
|
|
|
4943
4954
|
return;
|
|
4944
4955
|
}
|
|
4945
4956
|
if (!commandPaletteActive && (key.ctrl && inputKey === "v" || key.meta && inputKey === "v")) {
|
|
4946
|
-
handleExternalPaste("", { source: "clipboard-
|
|
4957
|
+
handleExternalPaste("", { source: "clipboard-shortcut" });
|
|
4947
4958
|
return;
|
|
4948
4959
|
}
|
|
4949
4960
|
if (key.return) {
|
|
@@ -5220,13 +5231,17 @@ ${d.value.slice(d.cursor)}`,
|
|
|
5220
5231
|
import React4 from "react";
|
|
5221
5232
|
import { Box as Box4, Text as Text4 } from "../../../vendor/ink/build/index.js";
|
|
5222
5233
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
5223
|
-
function QueuedCommands({ queued, columns }) {
|
|
5234
|
+
function QueuedCommands({ queued, columns, compact = false }) {
|
|
5224
5235
|
if (!queued || queued.length === 0) return null;
|
|
5225
5236
|
const bandColumns = Math.max(1, columns - 1);
|
|
5237
|
+
const contentWidth = Math.max(1, columns - 4);
|
|
5226
5238
|
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: queued.map((item) => {
|
|
5227
|
-
const contentWidth = Math.max(1, columns - 4);
|
|
5228
5239
|
const sourceText = String(item.displayText || item.text || "");
|
|
5229
|
-
|
|
5240
|
+
let displayText = sourceText;
|
|
5241
|
+
if (compact) {
|
|
5242
|
+
const oneLine2 = sourceText.replace(/\r?\n/g, " ");
|
|
5243
|
+
displayText = oneLine2.length > contentWidth ? contentWidth <= 1 ? "\u2026".repeat(contentWidth) : oneLine2.slice(0, Math.max(1, contentWidth - 1)) + "\u2026" : oneLine2;
|
|
5244
|
+
}
|
|
5230
5245
|
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
5246
|
}) });
|
|
5232
5247
|
}
|
|
@@ -6346,6 +6361,12 @@ function wrappedDetailRows(text, width) {
|
|
|
6346
6361
|
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
6347
6362
|
return wrapAnsi(value, w, { trim: false, hard: true }).split("\n").length;
|
|
6348
6363
|
}
|
|
6364
|
+
function queuedBandRows(text, width) {
|
|
6365
|
+
const value = String(text ?? "");
|
|
6366
|
+
if (!value) return 1;
|
|
6367
|
+
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
6368
|
+
return Math.max(1, wrapAnsi(value, w, { trim: false, hard: true }).split("\n").length);
|
|
6369
|
+
}
|
|
6349
6370
|
function visualRowStartOffsets(text, width) {
|
|
6350
6371
|
const value = String(text ?? "");
|
|
6351
6372
|
const w = Math.max(1, Math.floor(Number(width) || 1));
|
|
@@ -6830,6 +6851,7 @@ function TextEntryPanel({
|
|
|
6830
6851
|
|
|
6831
6852
|
// src/tui/paste-attachments.mjs
|
|
6832
6853
|
import { execFile } from "node:child_process";
|
|
6854
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
6833
6855
|
import { randomBytes } from "node:crypto";
|
|
6834
6856
|
import { existsSync, unlinkSync } from "node:fs";
|
|
6835
6857
|
import { readFile as readFileAsync, stat as statAsync } from "node:fs/promises";
|
|
@@ -6983,7 +7005,7 @@ function mimeForPath(path2) {
|
|
|
6983
7005
|
return IMAGE_MIME[extname(String(path2 || "")).toLowerCase()] || null;
|
|
6984
7006
|
}
|
|
6985
7007
|
function execFileBuffer(cmd, args, options = {}) {
|
|
6986
|
-
return new Promise((
|
|
7008
|
+
return new Promise((resolve5) => {
|
|
6987
7009
|
execFile(cmd, args, {
|
|
6988
7010
|
windowsHide: true,
|
|
6989
7011
|
encoding: "buffer",
|
|
@@ -6991,7 +7013,7 @@ function execFileBuffer(cmd, args, options = {}) {
|
|
|
6991
7013
|
timeout: 5e3,
|
|
6992
7014
|
...options
|
|
6993
7015
|
}, (error, stdout, stderr) => {
|
|
6994
|
-
|
|
7016
|
+
resolve5({ ok: !error, code: error?.code ?? 0, stdout, stderr, error });
|
|
6995
7017
|
});
|
|
6996
7018
|
});
|
|
6997
7019
|
}
|
|
@@ -7066,7 +7088,7 @@ function splitPastedImagePathCandidates(text) {
|
|
|
7066
7088
|
return out;
|
|
7067
7089
|
}
|
|
7068
7090
|
async function imageAttachmentFromBuffer(buffer, mimeType, { filename = "Pasted image", sourcePath = "" } = {}) {
|
|
7069
|
-
if (!
|
|
7091
|
+
if (!Buffer2.isBuffer(buffer) || buffer.length === 0) throw new Error("image is empty");
|
|
7070
7092
|
const ext = (mimeType || "image/png").split("/")[1] || "png";
|
|
7071
7093
|
const resized = await resizeImageBuffer(buffer, ext);
|
|
7072
7094
|
if (resized) {
|
|
@@ -7145,6 +7167,30 @@ async function readClipboardImageAttachment() {
|
|
|
7145
7167
|
}
|
|
7146
7168
|
}
|
|
7147
7169
|
}
|
|
7170
|
+
async function readClipboardText() {
|
|
7171
|
+
if (process.platform === "win32") {
|
|
7172
|
+
const ps = "$t=Get-Clipboard -Raw; if ($null -eq $t) { exit 2 }; [Console]::Out.Write([Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($t)))";
|
|
7173
|
+
const r = await execFileBuffer("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", ps], { timeout: 3e3 });
|
|
7174
|
+
if (!r.ok || !r.stdout?.length) return "";
|
|
7175
|
+
try {
|
|
7176
|
+
return Buffer2.from(r.stdout.toString("ascii").trim(), "base64").toString("utf8");
|
|
7177
|
+
} catch {
|
|
7178
|
+
return "";
|
|
7179
|
+
}
|
|
7180
|
+
}
|
|
7181
|
+
if (process.platform === "darwin") {
|
|
7182
|
+
const r = await execFileBuffer("pbpaste", [], { timeout: 3e3 });
|
|
7183
|
+
return r.ok && r.stdout?.length ? r.stdout.toString("utf8") : "";
|
|
7184
|
+
}
|
|
7185
|
+
if (process.platform === "linux") {
|
|
7186
|
+
const wl = await execFileBuffer("wl-paste", ["--no-newline"], { timeout: 3e3 });
|
|
7187
|
+
if (wl.ok && wl.stdout?.length) return wl.stdout.toString("utf8");
|
|
7188
|
+
const xc = await execFileBuffer("xclip", ["-selection", "clipboard", "-o"], { timeout: 3e3 });
|
|
7189
|
+
if (xc.ok && xc.stdout?.length) return xc.stdout.toString("utf8");
|
|
7190
|
+
return "";
|
|
7191
|
+
}
|
|
7192
|
+
return "";
|
|
7193
|
+
}
|
|
7148
7194
|
|
|
7149
7195
|
// src/standalone/projects.mjs
|
|
7150
7196
|
import { homedir } from "node:os";
|
|
@@ -7302,12 +7348,12 @@ import { spawn } from "node:child_process";
|
|
|
7302
7348
|
import path from "node:path";
|
|
7303
7349
|
var DIALOG_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
7304
7350
|
function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
7305
|
-
return new Promise((
|
|
7351
|
+
return new Promise((resolve5) => {
|
|
7306
7352
|
let child;
|
|
7307
7353
|
try {
|
|
7308
7354
|
child = spawn(cmd, args, { stdio: ["ignore", "pipe", "ignore"], windowsHide: true });
|
|
7309
7355
|
} catch (error) {
|
|
7310
|
-
|
|
7356
|
+
resolve5({ ok: false, code: -1, stdout: "", error });
|
|
7311
7357
|
return;
|
|
7312
7358
|
}
|
|
7313
7359
|
let stdout = "";
|
|
@@ -7319,7 +7365,7 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
|
7319
7365
|
child.kill();
|
|
7320
7366
|
} catch {
|
|
7321
7367
|
}
|
|
7322
|
-
|
|
7368
|
+
resolve5({ ok: false, code: -1, stdout: "", error: new Error("dialog timed out") });
|
|
7323
7369
|
}, timeoutMs);
|
|
7324
7370
|
timer.unref?.();
|
|
7325
7371
|
child.stdout.on("data", (chunk) => {
|
|
@@ -7329,13 +7375,13 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
|
|
|
7329
7375
|
if (settled) return;
|
|
7330
7376
|
settled = true;
|
|
7331
7377
|
clearTimeout(timer);
|
|
7332
|
-
|
|
7378
|
+
resolve5({ ok: false, code: -1, stdout: "", error });
|
|
7333
7379
|
});
|
|
7334
7380
|
child.on("close", (code) => {
|
|
7335
7381
|
if (settled) return;
|
|
7336
7382
|
settled = true;
|
|
7337
7383
|
clearTimeout(timer);
|
|
7338
|
-
|
|
7384
|
+
resolve5({ ok: code === 0, code, stdout });
|
|
7339
7385
|
});
|
|
7340
7386
|
});
|
|
7341
7387
|
}
|
|
@@ -7343,10 +7389,10 @@ function spawnFailed(result) {
|
|
|
7343
7389
|
return !!result && result.code === -1;
|
|
7344
7390
|
}
|
|
7345
7391
|
function commandExists(cmd) {
|
|
7346
|
-
return new Promise((
|
|
7392
|
+
return new Promise((resolve5) => {
|
|
7347
7393
|
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) =>
|
|
7394
|
+
probe.on("error", () => resolve5(false));
|
|
7395
|
+
probe.on("close", (code) => resolve5(code === 0));
|
|
7350
7396
|
});
|
|
7351
7397
|
}
|
|
7352
7398
|
function psSingleQuoted(value) {
|
|
@@ -7845,64 +7891,67 @@ function memoryCoreResultErrorText(text) {
|
|
|
7845
7891
|
}
|
|
7846
7892
|
|
|
7847
7893
|
// src/tui/app/clipboard.mjs
|
|
7848
|
-
import { Buffer as
|
|
7894
|
+
import { Buffer as Buffer3 } from "node:buffer";
|
|
7849
7895
|
import { spawn as spawn2 } from "node:child_process";
|
|
7850
7896
|
function osc52ClipboardSequence(text) {
|
|
7851
|
-
const b64 =
|
|
7897
|
+
const b64 = Buffer3.from(String(text ?? ""), "utf8").toString("base64");
|
|
7852
7898
|
const raw = `\x1B]52;c;${b64}\x07`;
|
|
7853
7899
|
if (!process.env.TMUX) return raw;
|
|
7854
7900
|
return `\x1BPtmux;${raw.replaceAll("\x1B", "\x1B\x1B")}\x1B\\`;
|
|
7855
7901
|
}
|
|
7902
|
+
var OSC52_MAX_BYTES = 256 * 1024;
|
|
7856
7903
|
function writeOsc52Clipboard(text) {
|
|
7904
|
+
const value = String(text ?? "");
|
|
7905
|
+
if (Buffer3.byteLength(value, "utf8") > OSC52_MAX_BYTES) return false;
|
|
7857
7906
|
try {
|
|
7858
|
-
process.stdout.write(osc52ClipboardSequence(
|
|
7907
|
+
process.stdout.write(osc52ClipboardSequence(value));
|
|
7859
7908
|
return true;
|
|
7860
7909
|
} catch {
|
|
7861
7910
|
return false;
|
|
7862
7911
|
}
|
|
7863
7912
|
}
|
|
7864
7913
|
function nativeClipboardCommand(text) {
|
|
7914
|
+
const value = String(text ?? "");
|
|
7865
7915
|
if (process.platform === "win32") {
|
|
7866
7916
|
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")
|
|
7917
|
+
cmd: "clip.exe",
|
|
7918
|
+
args: [],
|
|
7919
|
+
input: Buffer3.from(value, "utf16le")
|
|
7876
7920
|
};
|
|
7877
7921
|
}
|
|
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:
|
|
7922
|
+
if (process.platform === "darwin") return { cmd: "pbcopy", args: [], input: value };
|
|
7923
|
+
if (process.env.WAYLAND_DISPLAY) return { cmd: "wl-copy", args: [], input: value };
|
|
7924
|
+
return { cmd: "xclip", args: ["-selection", "clipboard"], input: value };
|
|
7881
7925
|
}
|
|
7882
7926
|
function copyToClipboard(text) {
|
|
7883
7927
|
const value = String(text ?? "");
|
|
7884
7928
|
const wroteOsc52 = writeOsc52Clipboard(value);
|
|
7885
|
-
return new Promise((
|
|
7929
|
+
return new Promise((resolve5, reject) => {
|
|
7886
7930
|
const { cmd, args, input } = nativeClipboardCommand(value);
|
|
7887
7931
|
let child;
|
|
7888
7932
|
try {
|
|
7889
7933
|
child = spawn2(cmd, args, { stdio: ["pipe", "ignore", "ignore"], windowsHide: true });
|
|
7890
7934
|
} catch (e) {
|
|
7891
|
-
if (wroteOsc52)
|
|
7935
|
+
if (wroteOsc52) resolve5();
|
|
7892
7936
|
else reject(e);
|
|
7893
7937
|
return;
|
|
7894
7938
|
}
|
|
7895
7939
|
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}`));
|
|
7940
|
+
if (!wroteOsc52) reject(e);
|
|
7902
7941
|
});
|
|
7942
|
+
if (!wroteOsc52) {
|
|
7943
|
+
child.on("close", (code) => {
|
|
7944
|
+
if (code === 0) resolve5();
|
|
7945
|
+
else reject(new Error(`${cmd} exited with code ${code}`));
|
|
7946
|
+
});
|
|
7947
|
+
}
|
|
7903
7948
|
child.stdin.on("error", () => {
|
|
7904
7949
|
});
|
|
7905
7950
|
child.stdin.end(input);
|
|
7951
|
+
if (wroteOsc52) {
|
|
7952
|
+
child.unref?.();
|
|
7953
|
+
resolve5();
|
|
7954
|
+
}
|
|
7906
7955
|
});
|
|
7907
7956
|
}
|
|
7908
7957
|
|
|
@@ -10382,6 +10431,7 @@ var ALT_SCROLL_ON = "\x1B[?1007h";
|
|
|
10382
10431
|
var MOUSE_MODIFIER_MASK = 4 | 8 | 16;
|
|
10383
10432
|
var MOUSE_CTRL_MASK = 16;
|
|
10384
10433
|
var MOUSE_SHIFT_MASK = 4;
|
|
10434
|
+
var IS_WINDOWS_TERMINAL = Boolean(process.env.WT_SESSION);
|
|
10385
10435
|
function useMouseInput({
|
|
10386
10436
|
inkInput,
|
|
10387
10437
|
isRawModeSupported,
|
|
@@ -10409,7 +10459,8 @@ function useMouseInput({
|
|
|
10409
10459
|
setMeasuredRowsVersion,
|
|
10410
10460
|
clearStitchBuffer
|
|
10411
10461
|
}) {
|
|
10412
|
-
const
|
|
10462
|
+
const zoomTimerRef = useRef6(null);
|
|
10463
|
+
const edgeAutoscrollRef = useRef6({ dir: 0, timer: null, noMove: 0 });
|
|
10413
10464
|
const passthroughCtrlWheelZoom = useCallback2(() => {
|
|
10414
10465
|
if (!stdout?.write) return;
|
|
10415
10466
|
try {
|
|
@@ -10417,19 +10468,19 @@ function useMouseInput({
|
|
|
10417
10468
|
} catch {
|
|
10418
10469
|
return;
|
|
10419
10470
|
}
|
|
10420
|
-
if (
|
|
10421
|
-
|
|
10422
|
-
|
|
10471
|
+
if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
|
|
10472
|
+
zoomTimerRef.current = setTimeout(() => {
|
|
10473
|
+
zoomTimerRef.current = null;
|
|
10423
10474
|
try {
|
|
10424
10475
|
stdout.write(ALT_SCROLL_ON + MOUSE_TRACKING_ON);
|
|
10425
10476
|
} catch {
|
|
10426
10477
|
}
|
|
10427
10478
|
}, 700);
|
|
10428
|
-
|
|
10429
|
-
}, [stdout]);
|
|
10479
|
+
zoomTimerRef.current.unref?.();
|
|
10480
|
+
}, [stdout, zoomTimerRef]);
|
|
10430
10481
|
useEffect6(() => () => {
|
|
10431
|
-
if (
|
|
10432
|
-
}, []);
|
|
10482
|
+
if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
|
|
10483
|
+
}, [zoomTimerRef]);
|
|
10433
10484
|
useEffect6(() => {
|
|
10434
10485
|
if (!inkInput || !isRawModeSupported) return void 0;
|
|
10435
10486
|
const WHEEL_SGR = /\x1b\[<(\d+);/;
|
|
@@ -10494,6 +10545,65 @@ function useMouseInput({
|
|
|
10494
10545
|
promptMouseSelectionRef.current?.clear?.();
|
|
10495
10546
|
applySelectionRect(null);
|
|
10496
10547
|
};
|
|
10548
|
+
const EDGE_AUTOSCROLL_INTERVAL_MS = 50;
|
|
10549
|
+
const stopEdgeAutoscroll = () => {
|
|
10550
|
+
const st = edgeAutoscrollRef.current;
|
|
10551
|
+
if (st.timer) {
|
|
10552
|
+
clearInterval(st.timer);
|
|
10553
|
+
st.timer = null;
|
|
10554
|
+
}
|
|
10555
|
+
st.dir = 0;
|
|
10556
|
+
st.noMove = 0;
|
|
10557
|
+
};
|
|
10558
|
+
const startEdgeAutoscroll = (dir) => {
|
|
10559
|
+
const st = edgeAutoscrollRef.current;
|
|
10560
|
+
if (st.dir === dir && st.timer) return;
|
|
10561
|
+
stopEdgeAutoscroll();
|
|
10562
|
+
st.dir = dir;
|
|
10563
|
+
st.noMove = 0;
|
|
10564
|
+
st.timer = setInterval(() => {
|
|
10565
|
+
const drag = dragRef.current;
|
|
10566
|
+
const st2 = edgeAutoscrollRef.current;
|
|
10567
|
+
if (!drag.active || drag.region !== "transcript" || st2.dir === 0) {
|
|
10568
|
+
stopEdgeAutoscroll();
|
|
10569
|
+
return;
|
|
10570
|
+
}
|
|
10571
|
+
const before = Number(scrollTargetRef.current) || 0;
|
|
10572
|
+
queueScrollCoalesced(st2.dir * 3);
|
|
10573
|
+
if ((Number(scrollTargetRef.current) || 0) !== before) {
|
|
10574
|
+
st2.noMove = 0;
|
|
10575
|
+
} else if (++st2.noMove >= 3) {
|
|
10576
|
+
stopEdgeAutoscroll();
|
|
10577
|
+
}
|
|
10578
|
+
}, EDGE_AUTOSCROLL_INTERVAL_MS);
|
|
10579
|
+
st.timer.unref?.();
|
|
10580
|
+
};
|
|
10581
|
+
const finalizeActiveDrag = (fx, fy) => {
|
|
10582
|
+
const drag = dragRef.current;
|
|
10583
|
+
if (!drag.active) return;
|
|
10584
|
+
stopEdgeAutoscroll();
|
|
10585
|
+
const region = drag.region;
|
|
10586
|
+
if (region === "prompt") {
|
|
10587
|
+
const offset = promptOffsetAt(fx, fy);
|
|
10588
|
+
drag.active = false;
|
|
10589
|
+
promptMouseSelectionRef.current?.extendTo?.(offset, true);
|
|
10590
|
+
return;
|
|
10591
|
+
}
|
|
10592
|
+
const span = drag.anchorSpan;
|
|
10593
|
+
const finalY = region === "status" ? clampToStatusBand(fy) : clampToTranscriptViewport(fy);
|
|
10594
|
+
drag.active = false;
|
|
10595
|
+
if (span) {
|
|
10596
|
+
const rect = buildSpanRect(span, fx, finalY, region, drag.anchorScroll);
|
|
10597
|
+
applySelectionRect(rect);
|
|
10598
|
+
} else {
|
|
10599
|
+
const anchor = region === "status" ? drag.anchor : selectionPointAtCurrentScroll(drag.anchor, drag.anchorScroll);
|
|
10600
|
+
const rect = linearSelection(anchor, { x: fx, y: finalY });
|
|
10601
|
+
const empty = rect.x1 === rect.x2 && rect.y1 === rect.y2;
|
|
10602
|
+
if (empty) applySelectionRect(null);
|
|
10603
|
+
else applySelectionRect(rect);
|
|
10604
|
+
}
|
|
10605
|
+
if (TRANSCRIPT_MEASURED_ROWS) setMeasuredRowsVersion((v) => (v + 1) % 1e6);
|
|
10606
|
+
};
|
|
10497
10607
|
const onMouse = (event) => {
|
|
10498
10608
|
if (!event || typeof event !== "object") return;
|
|
10499
10609
|
let up = 0;
|
|
@@ -10505,15 +10615,20 @@ function useMouseInput({
|
|
|
10505
10615
|
const wm = WHEEL_SGR.exec(seq);
|
|
10506
10616
|
const ctrl = wm ? (Number(wm[1]) & MOUSE_CTRL_MASK) !== 0 : false;
|
|
10507
10617
|
if (ctrl) {
|
|
10618
|
+
if (dragRef.current.active) {
|
|
10619
|
+
const last = dragRef.current.last || {};
|
|
10620
|
+
finalizeActiveDrag(Number(last.x) || 0, Number(last.y) || 0);
|
|
10621
|
+
} else {
|
|
10622
|
+
stopEdgeAutoscroll();
|
|
10623
|
+
}
|
|
10508
10624
|
passthroughCtrlWheelZoom();
|
|
10509
10625
|
return;
|
|
10510
10626
|
}
|
|
10511
10627
|
if (name === "wheelup") up += 1;
|
|
10512
10628
|
else down += 1;
|
|
10513
10629
|
if (up !== 0 || down !== 0) {
|
|
10514
|
-
if (dragRef.current.active) return;
|
|
10515
10630
|
const palette = slashPaletteRef.current;
|
|
10516
|
-
if (palette.open && palette.count > 0) {
|
|
10631
|
+
if (!dragRef.current.active && palette.open && palette.count > 0) {
|
|
10517
10632
|
const step = down - up;
|
|
10518
10633
|
if (step !== 0) {
|
|
10519
10634
|
setSlashIndex((index) => Math.max(0, Math.min(palette.count - 1, index + step)));
|
|
@@ -10535,14 +10650,64 @@ function useMouseInput({
|
|
|
10535
10650
|
const baseButton = button & 3;
|
|
10536
10651
|
const isMotion = (button & 32) !== 0;
|
|
10537
10652
|
const shiftHeld = (button & MOUSE_SHIFT_MASK) !== 0;
|
|
10653
|
+
const ctrlHeld = (button & MOUSE_CTRL_MASK) !== 0;
|
|
10654
|
+
if (IS_WINDOWS_TERMINAL && shiftHeld) return;
|
|
10655
|
+
if (IS_WINDOWS_TERMINAL && press && !isMotion) {
|
|
10656
|
+
store.forceRenderRepaint?.();
|
|
10657
|
+
}
|
|
10658
|
+
const extendHeld = shiftHeld || ctrlHeld;
|
|
10659
|
+
const isRightPress = baseButton === 2 && press && !isMotion;
|
|
10660
|
+
if (isRightPress) {
|
|
10661
|
+
if (isInPromptBox(x, y)) {
|
|
10662
|
+
const ctl = promptMouseSelectionRef.current;
|
|
10663
|
+
if (ctl?.hasSelection?.()) {
|
|
10664
|
+
applySelectionRect(null);
|
|
10665
|
+
const offset = promptOffsetAt(x, y);
|
|
10666
|
+
stopSmoothScroll();
|
|
10667
|
+
dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: false, rect: null, region: "prompt", anchorSpan: null };
|
|
10668
|
+
if (offset != null) ctl.extendTo?.(offset, true);
|
|
10669
|
+
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
10670
|
+
}
|
|
10671
|
+
return;
|
|
10672
|
+
}
|
|
10673
|
+
const inTranscriptR = isInTranscriptViewport(y);
|
|
10674
|
+
const inStatusR = !inTranscriptR && isInStatusBand(y);
|
|
10675
|
+
if (!inTranscriptR && !inStatusR) return;
|
|
10676
|
+
const regionR = inTranscriptR ? "transcript" : "status";
|
|
10677
|
+
const nowR = Date.now();
|
|
10678
|
+
if (dragRef.current.region === regionR && dragRef.current.anchorSpan) {
|
|
10679
|
+
promptMouseSelectionRef.current?.clear?.();
|
|
10680
|
+
const selectionY = regionR === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10681
|
+
const span = dragRef.current.anchorSpan;
|
|
10682
|
+
const rect = buildSpanRect(span, x, selectionY, regionR, dragRef.current.anchorScroll);
|
|
10683
|
+
stopSmoothScroll();
|
|
10684
|
+
dragRef.current = { ...dragRef.current, last: { x, y: selectionY }, active: false, region: regionR };
|
|
10685
|
+
applySelectionRect(rect);
|
|
10686
|
+
lastClickRef.current = { x, y, t: nowR, count: 1 };
|
|
10687
|
+
return;
|
|
10688
|
+
}
|
|
10689
|
+
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)) {
|
|
10690
|
+
promptMouseSelectionRef.current?.clear?.();
|
|
10691
|
+
const selectionY = regionR === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10692
|
+
const anchor = regionR === "status" ? dragRef.current.anchor : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
10693
|
+
const rect = linearSelection(anchor, { x, y: selectionY });
|
|
10694
|
+
stopSmoothScroll();
|
|
10695
|
+
dragRef.current = { ...dragRef.current, last: { x, y: selectionY }, active: false, region: regionR };
|
|
10696
|
+
applySelectionRect(rect);
|
|
10697
|
+
lastClickRef.current = { x, y, t: nowR, count: 1 };
|
|
10698
|
+
return;
|
|
10699
|
+
}
|
|
10700
|
+
return;
|
|
10701
|
+
}
|
|
10538
10702
|
if (baseButton === 0 && press && !isMotion) {
|
|
10703
|
+
stopEdgeAutoscroll();
|
|
10539
10704
|
if (isInPromptBox(x, y)) {
|
|
10540
10705
|
applySelectionRect(null);
|
|
10541
10706
|
const offset = promptOffsetAt(x, y);
|
|
10542
10707
|
stopSmoothScroll();
|
|
10543
10708
|
dragRef.current = { anchor: { x, y }, anchorScroll: 0, last: { x, y }, active: true, rect: null, region: "prompt", anchorSpan: null };
|
|
10544
10709
|
const ctl = promptMouseSelectionRef.current;
|
|
10545
|
-
if (
|
|
10710
|
+
if (extendHeld && ctl?.hasSelection?.()) {
|
|
10546
10711
|
if (offset != null) ctl.extendTo?.(offset, true);
|
|
10547
10712
|
lastClickRef.current = { x: -1, y: -1, t: 0 };
|
|
10548
10713
|
return;
|
|
@@ -10577,7 +10742,22 @@ function useMouseInput({
|
|
|
10577
10742
|
const region = inTranscript ? "transcript" : "status";
|
|
10578
10743
|
promptMouseSelectionRef.current?.clear?.();
|
|
10579
10744
|
const now = Date.now();
|
|
10580
|
-
if (
|
|
10745
|
+
if (extendHeld && dragRef.current.region === region && dragRef.current.anchorSpan) {
|
|
10746
|
+
const selectionY = region === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10747
|
+
const span = dragRef.current.anchorSpan;
|
|
10748
|
+
const rect = buildSpanRect(span, x, selectionY, region, dragRef.current.anchorScroll);
|
|
10749
|
+
stopSmoothScroll();
|
|
10750
|
+
dragRef.current = {
|
|
10751
|
+
...dragRef.current,
|
|
10752
|
+
last: { x, y: selectionY },
|
|
10753
|
+
active: true,
|
|
10754
|
+
region
|
|
10755
|
+
};
|
|
10756
|
+
applySelectionRect(rect);
|
|
10757
|
+
lastClickRef.current = { x, y, t: now, count: 1 };
|
|
10758
|
+
return;
|
|
10759
|
+
}
|
|
10760
|
+
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
10761
|
const selectionY = region === "status" ? clampToStatusBand(y) : clampToTranscriptViewport(y);
|
|
10582
10762
|
const anchor = region === "status" ? dragRef.current.anchor : selectionPointAtCurrentScroll(dragRef.current.anchor, dragRef.current.anchorScroll);
|
|
10583
10763
|
const rect = linearSelection(anchor, { x, y: selectionY });
|
|
@@ -10656,41 +10836,23 @@ function useMouseInput({
|
|
|
10656
10836
|
const { top, bottom } = transcriptViewport();
|
|
10657
10837
|
if (y <= 1 && (y < prevDragY || y < top)) {
|
|
10658
10838
|
queueScrollCoalesced(3);
|
|
10839
|
+
startEdgeAutoscroll(1);
|
|
10659
10840
|
} else if (y >= frameRows - 5 && (y > prevDragY || y > bottom)) {
|
|
10660
10841
|
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);
|
|
10842
|
+
startEdgeAutoscroll(-1);
|
|
10683
10843
|
} else {
|
|
10684
|
-
|
|
10844
|
+
stopEdgeAutoscroll();
|
|
10685
10845
|
}
|
|
10686
10846
|
}
|
|
10687
|
-
|
|
10847
|
+
} else if (!press && baseButton === 0 && dragRef.current.active) {
|
|
10848
|
+
finalizeActiveDrag(x, y);
|
|
10688
10849
|
}
|
|
10689
10850
|
}
|
|
10690
10851
|
};
|
|
10691
10852
|
inkInput.on("mouse", onMouse);
|
|
10692
10853
|
return () => {
|
|
10693
10854
|
inkInput.off("mouse", onMouse);
|
|
10855
|
+
stopEdgeAutoscroll();
|
|
10694
10856
|
};
|
|
10695
10857
|
}, [inkInput, isRawModeSupported, store, passthroughCtrlWheelZoom, rows, scrollTranscriptRows, queueScrollCoalesced, applySelectionRect, applySelectionRectThrottled, selectionPointAtCurrentScroll, buildSpanRect]);
|
|
10696
10858
|
}
|
|
@@ -10742,26 +10904,53 @@ function useTranscriptScroll({
|
|
|
10742
10904
|
const scroll = stitchHarvestScrollRef.current;
|
|
10743
10905
|
for (const row of rows) {
|
|
10744
10906
|
if (!row || typeof row.y !== "number") continue;
|
|
10745
|
-
stitchBufferRef.current.set(row.y - scroll,
|
|
10907
|
+
stitchBufferRef.current.set(row.y - scroll, {
|
|
10908
|
+
text: typeof row.text === "string" ? row.text : "",
|
|
10909
|
+
sw: row.sw === true
|
|
10910
|
+
});
|
|
10746
10911
|
}
|
|
10747
10912
|
}, 0);
|
|
10748
10913
|
}, [store]);
|
|
10914
|
+
const harvestStitchRowsNow = useCallback3((scroll) => {
|
|
10915
|
+
if (dragRef.current.region !== "transcript") return;
|
|
10916
|
+
const rows = store.getRenderSelectionRows?.();
|
|
10917
|
+
if (!Array.isArray(rows)) return;
|
|
10918
|
+
const s = Number(scroll) || 0;
|
|
10919
|
+
for (const row of rows) {
|
|
10920
|
+
if (!row || typeof row.y !== "number") continue;
|
|
10921
|
+
stitchBufferRef.current.set(row.y - s, {
|
|
10922
|
+
text: typeof row.text === "string" ? row.text : "",
|
|
10923
|
+
sw: row.sw === true
|
|
10924
|
+
});
|
|
10925
|
+
}
|
|
10926
|
+
}, [store]);
|
|
10749
10927
|
const getStitchedSelectionText = useCallback3(() => {
|
|
10928
|
+
const empty = { text: "", complete: false };
|
|
10750
10929
|
const buf = stitchBufferRef.current;
|
|
10751
|
-
if (!buf.size) return
|
|
10752
|
-
if (dragRef.current.region !== "transcript") return
|
|
10930
|
+
if (!buf.size) return empty;
|
|
10931
|
+
if (dragRef.current.region !== "transcript") return empty;
|
|
10753
10932
|
const rect = dragRef.current.rect;
|
|
10754
|
-
if (!rect) return
|
|
10933
|
+
if (!rect) return empty;
|
|
10755
10934
|
const y1 = Number(rect.y1);
|
|
10756
10935
|
const y2 = Number(rect.y2);
|
|
10757
|
-
if (!Number.isFinite(y1) || !Number.isFinite(y2)) return
|
|
10936
|
+
if (!Number.isFinite(y1) || !Number.isFinite(y2)) return empty;
|
|
10758
10937
|
const scroll = Number(scrollTargetRef.current) || 0;
|
|
10759
10938
|
const lo = Math.min(y1, y2) - scroll;
|
|
10760
10939
|
const hi = Math.max(y1, y2) - scroll;
|
|
10761
10940
|
const keys = [...buf.keys()].filter((k) => k >= lo && k <= hi).sort((a, b) => a - b);
|
|
10762
|
-
if (!keys.length) return
|
|
10763
|
-
const
|
|
10764
|
-
|
|
10941
|
+
if (!keys.length) return empty;
|
|
10942
|
+
const complete = keys.length === hi - lo + 1;
|
|
10943
|
+
const logical = [];
|
|
10944
|
+
for (const k of keys) {
|
|
10945
|
+
const entry = buf.get(k);
|
|
10946
|
+
if (entry == null) continue;
|
|
10947
|
+
const t = typeof entry === "string" ? entry : entry.text ?? "";
|
|
10948
|
+
const sw = typeof entry === "string" ? false : entry.sw === true;
|
|
10949
|
+
if (sw && logical.length > 0) logical[logical.length - 1] += t;
|
|
10950
|
+
else logical.push(t);
|
|
10951
|
+
}
|
|
10952
|
+
const text = logical.map((l) => l.replace(/\s+$/u, "")).join("\n");
|
|
10953
|
+
return text.trim() ? { text, complete } : empty;
|
|
10765
10954
|
}, []);
|
|
10766
10955
|
const stopSmoothScroll = useCallback3(() => {
|
|
10767
10956
|
if (!scrollAnimationRef.current) return;
|
|
@@ -10856,6 +11045,25 @@ function useTranscriptScroll({
|
|
|
10856
11045
|
if (nextRect) harvestStitchRowsSoon();
|
|
10857
11046
|
return true;
|
|
10858
11047
|
}, [store, rememberSelectionTextSoon, harvestStitchRowsSoon]);
|
|
11048
|
+
const cancelPendingSelectionPaint = useCallback3(() => {
|
|
11049
|
+
const state = selectionPaintRef.current;
|
|
11050
|
+
if (state.timer) {
|
|
11051
|
+
clearTimeout(state.timer);
|
|
11052
|
+
state.timer = null;
|
|
11053
|
+
}
|
|
11054
|
+
state.pending = null;
|
|
11055
|
+
}, []);
|
|
11056
|
+
const flushPendingSelectionPaint = useCallback3(() => {
|
|
11057
|
+
const state = selectionPaintRef.current;
|
|
11058
|
+
if (!state.timer && !state.pending) return;
|
|
11059
|
+
const pending = state.pending;
|
|
11060
|
+
if (state.timer) {
|
|
11061
|
+
clearTimeout(state.timer);
|
|
11062
|
+
state.timer = null;
|
|
11063
|
+
}
|
|
11064
|
+
state.pending = null;
|
|
11065
|
+
if (pending) paintSelectionRect(pending, { rememberText: false, immediate: true });
|
|
11066
|
+
}, [paintSelectionRect]);
|
|
10859
11067
|
const applySelectionRect = useCallback3((rect) => {
|
|
10860
11068
|
const clippedRect = withSelectionClip(rect);
|
|
10861
11069
|
dragRef.current.rect = clippedRect || null;
|
|
@@ -10863,14 +11071,9 @@ function useTranscriptScroll({
|
|
|
10863
11071
|
selectionTextRef.current = "";
|
|
10864
11072
|
clearStitchBuffer();
|
|
10865
11073
|
}
|
|
10866
|
-
|
|
10867
|
-
if (state.timer) {
|
|
10868
|
-
clearTimeout(state.timer);
|
|
10869
|
-
state.timer = null;
|
|
10870
|
-
state.pending = null;
|
|
10871
|
-
}
|
|
11074
|
+
cancelPendingSelectionPaint();
|
|
10872
11075
|
paintSelectionRect(clippedRect, { rememberText: true, immediate: true });
|
|
10873
|
-
}, [paintSelectionRect, withSelectionClip, clearStitchBuffer]);
|
|
11076
|
+
}, [paintSelectionRect, withSelectionClip, clearStitchBuffer, cancelPendingSelectionPaint]);
|
|
10874
11077
|
const applySelectionRectThrottled = useCallback3((rect) => {
|
|
10875
11078
|
const clippedRect = withSelectionClip(rect, { captureText: false });
|
|
10876
11079
|
if (selectionRectsEqual(dragRef.current.rect, clippedRect)) return;
|
|
@@ -10880,11 +11083,7 @@ function useTranscriptScroll({
|
|
|
10880
11083
|
const now = Date.now();
|
|
10881
11084
|
const elapsed = now - state.t;
|
|
10882
11085
|
if (elapsed >= SELECTION_PAINT_INTERVAL_MS) {
|
|
10883
|
-
|
|
10884
|
-
clearTimeout(state.timer);
|
|
10885
|
-
state.timer = null;
|
|
10886
|
-
state.pending = null;
|
|
10887
|
-
}
|
|
11086
|
+
cancelPendingSelectionPaint();
|
|
10888
11087
|
paintSelectionRect(clippedRect, { rememberText: false });
|
|
10889
11088
|
return;
|
|
10890
11089
|
}
|
|
@@ -10899,7 +11098,7 @@ function useTranscriptScroll({
|
|
|
10899
11098
|
}, Math.max(1, SELECTION_PAINT_INTERVAL_MS - elapsed));
|
|
10900
11099
|
state.timer.unref?.();
|
|
10901
11100
|
}
|
|
10902
|
-
}, [paintSelectionRect, withSelectionClip]);
|
|
11101
|
+
}, [paintSelectionRect, withSelectionClip, cancelPendingSelectionPaint]);
|
|
10903
11102
|
const selectionPointAtCurrentScroll = useCallback3((point, pointScroll = 0) => {
|
|
10904
11103
|
if (!point) return null;
|
|
10905
11104
|
return {
|
|
@@ -10929,14 +11128,14 @@ function useTranscriptScroll({
|
|
|
10929
11128
|
mHi = { x: lr.x2, y: lr.y2 };
|
|
10930
11129
|
} else {
|
|
10931
11130
|
mLo = { x: 0, y };
|
|
10932
|
-
mHi = { x, y };
|
|
11131
|
+
mHi = { x: Math.max(0, frameColumns - 1), y };
|
|
10933
11132
|
}
|
|
10934
11133
|
}
|
|
10935
11134
|
const rect = (a, b) => ({ mode: "linear", x1: a.x, y1: a.y, x2: b.x, y2: b.y });
|
|
10936
11135
|
if (comparePoints(mHi, spanLo) < 0) return rect(spanHi, mLo);
|
|
10937
11136
|
if (comparePoints(mLo, spanHi) > 0) return rect(spanLo, mHi);
|
|
10938
11137
|
return rect(spanLo, spanHi);
|
|
10939
|
-
}, [store, selectionPointAtCurrentScroll]);
|
|
11138
|
+
}, [store, frameColumns, selectionPointAtCurrentScroll]);
|
|
10940
11139
|
const transcriptViewportRows = useCallback3(() => {
|
|
10941
11140
|
const top = Math.max(0, Number(transcriptViewportRef.current?.top) || 0);
|
|
10942
11141
|
const bottom = Math.max(top, Number(transcriptViewportRef.current?.bottom) || top);
|
|
@@ -10977,6 +11176,10 @@ function useTranscriptScroll({
|
|
|
10977
11176
|
const maxTarget = Math.max(0, Number(maxScrollRowsRef.current) || 0);
|
|
10978
11177
|
const target = Math.max(0, Math.min(maxTarget, scrollTargetRef.current + deltaRows));
|
|
10979
11178
|
const appliedDelta = target - scrollTargetRef.current;
|
|
11179
|
+
if (appliedDelta !== 0 && dragRef.current.region === "transcript" && dragRef.current.rect) {
|
|
11180
|
+
flushPendingSelectionPaint();
|
|
11181
|
+
harvestStitchRowsNow(Number(scrollTargetRef.current) || 0);
|
|
11182
|
+
}
|
|
10980
11183
|
if (appliedDelta !== 0) cancelTranscriptFollow();
|
|
10981
11184
|
scrollTargetRef.current = target;
|
|
10982
11185
|
if (appliedDelta !== 0) {
|
|
@@ -11022,8 +11225,9 @@ function useTranscriptScroll({
|
|
|
11022
11225
|
} else {
|
|
11023
11226
|
rect = shiftSelectionRectY(dragRef.current.rect, appliedDelta);
|
|
11024
11227
|
}
|
|
11025
|
-
const clippedRect = withSelectionClip(rect);
|
|
11228
|
+
const clippedRect = dragRef.current.active ? withSelectionClip(rect, { captureText: false }) : withSelectionClip(rect);
|
|
11026
11229
|
dragRef.current = { ...dragRef.current, rect: clippedRect };
|
|
11230
|
+
cancelPendingSelectionPaint();
|
|
11027
11231
|
paintSelectionRect(clippedRect, { rememberText: false });
|
|
11028
11232
|
}
|
|
11029
11233
|
if (options.smooth) {
|
|
@@ -11033,7 +11237,7 @@ function useTranscriptScroll({
|
|
|
11033
11237
|
stopSmoothScroll();
|
|
11034
11238
|
scrollPositionRef.current = target;
|
|
11035
11239
|
setScrollOffset(Math.round(target));
|
|
11036
|
-
}, [startSmoothScroll, stopSmoothScroll, paintSelectionRect, selectionPointAtCurrentScroll, withSelectionClip, cancelTranscriptFollow, buildSpanRect]);
|
|
11240
|
+
}, [startSmoothScroll, stopSmoothScroll, paintSelectionRect, selectionPointAtCurrentScroll, withSelectionClip, cancelTranscriptFollow, buildSpanRect, harvestStitchRowsNow, cancelPendingSelectionPaint, flushPendingSelectionPaint]);
|
|
11037
11241
|
const queueScrollCoalesced = useCallback3((deltaRows) => {
|
|
11038
11242
|
const state = scrollCoalesceRef.current;
|
|
11039
11243
|
state.pendingRows += deltaRows;
|
|
@@ -11189,6 +11393,7 @@ function useTranscriptWindow({
|
|
|
11189
11393
|
}) {
|
|
11190
11394
|
const transcriptTotalRowsRef = useRef8(0);
|
|
11191
11395
|
const preservedScrollDeltaRef = useRef8(0);
|
|
11396
|
+
const committedMaxScrollRowsRef = useRef8(0);
|
|
11192
11397
|
const incrementalRowIndexCacheRef = useRef8(null);
|
|
11193
11398
|
const prevViewportGeomRef = useRef8({ contentHeight: 0, floatingPanelRows: 0 });
|
|
11194
11399
|
const transcriptItemElsRef = useRef8(/* @__PURE__ */ new Map());
|
|
@@ -11358,7 +11563,31 @@ function useTranscriptWindow({
|
|
|
11358
11563
|
rowIndex: transcriptRowIndex
|
|
11359
11564
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: sig+scroll/viewport capture the relevant changes
|
|
11360
11565
|
}), [transcriptStructureSig, renderScrollOffset, transcriptContentHeight, transcriptRowIndex]);
|
|
11361
|
-
|
|
11566
|
+
const estimateMaxScrollRows = Math.max(0, Number(transcriptWindow.maxScrollRows) || 0);
|
|
11567
|
+
let holdCommittedMax = false;
|
|
11568
|
+
if (TRANSCRIPT_MEASURED_ROWS && estimateMaxScrollRows > committedMaxScrollRowsRef.current) {
|
|
11569
|
+
const toolExpandedFlag = toolOutputExpanded ? 1 : 0;
|
|
11570
|
+
const mountedSlice = transcriptWindow.items || [];
|
|
11571
|
+
for (let i = 0; i < mountedSlice.length; i++) {
|
|
11572
|
+
const it = mountedSlice[i];
|
|
11573
|
+
if (!it || shouldSuppressFullyFailedToolItem(it)) continue;
|
|
11574
|
+
if (it.kind === "assistant" && it.streaming) {
|
|
11575
|
+
const idPrev = streamingMeasuredRowsById.get(it.id);
|
|
11576
|
+
if (!idPrev || idPrev.columns !== frameColumns || idPrev.toolExpanded !== toolExpandedFlag) {
|
|
11577
|
+
holdCommittedMax = true;
|
|
11578
|
+
break;
|
|
11579
|
+
}
|
|
11580
|
+
} else {
|
|
11581
|
+
const prev = transcriptMeasuredRowsCache.get(it);
|
|
11582
|
+
if (!prev || prev.columns !== frameColumns || prev.toolExpanded !== toolExpandedFlag || prev.variantKey !== transcriptItemVariantKey(it)) {
|
|
11583
|
+
holdCommittedMax = true;
|
|
11584
|
+
break;
|
|
11585
|
+
}
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
}
|
|
11589
|
+
if (!holdCommittedMax) committedMaxScrollRowsRef.current = estimateMaxScrollRows;
|
|
11590
|
+
maxScrollRowsRef.current = committedMaxScrollRowsRef.current;
|
|
11362
11591
|
transcriptGeomRef.current = {
|
|
11363
11592
|
prefixRows: transcriptRowIndex?.prefixRows || null,
|
|
11364
11593
|
totalRows: Math.max(0, Number(transcriptWindow.totalRows) || 0),
|
|
@@ -12576,6 +12805,7 @@ function createExtensionPickers({
|
|
|
12576
12805
|
clean: clean3,
|
|
12577
12806
|
copyToClipboard: copyToClipboard2,
|
|
12578
12807
|
setPicker,
|
|
12808
|
+
getPicker,
|
|
12579
12809
|
setProviderPrompt,
|
|
12580
12810
|
setChannelPrompt,
|
|
12581
12811
|
setHookPrompt,
|
|
@@ -12583,6 +12813,8 @@ function createExtensionPickers({
|
|
|
12583
12813
|
getDisabledSkills,
|
|
12584
12814
|
setDisabledSkills
|
|
12585
12815
|
}) {
|
|
12816
|
+
let mcpEpoch = 0;
|
|
12817
|
+
let mcpActive = false;
|
|
12586
12818
|
const mcpStatus = () => {
|
|
12587
12819
|
let status;
|
|
12588
12820
|
try {
|
|
@@ -12597,6 +12829,7 @@ function createExtensionPickers({
|
|
|
12597
12829
|
const status = mcpStatus();
|
|
12598
12830
|
if (!status) return;
|
|
12599
12831
|
const servers = status.servers || [];
|
|
12832
|
+
const optimistic = options?.optimistic || null;
|
|
12600
12833
|
const items = [];
|
|
12601
12834
|
if (servers.length === 0) {
|
|
12602
12835
|
items.push({
|
|
@@ -12607,13 +12840,14 @@ function createExtensionPickers({
|
|
|
12607
12840
|
});
|
|
12608
12841
|
}
|
|
12609
12842
|
for (const server of servers) {
|
|
12610
|
-
const
|
|
12843
|
+
const pending = optimistic && optimistic.name === server.name;
|
|
12844
|
+
const enabled = pending ? optimistic.enabled : server.enabled !== false;
|
|
12611
12845
|
items.push({
|
|
12612
12846
|
value: `server:${server.name}`,
|
|
12613
12847
|
label: server.name,
|
|
12614
12848
|
marker: enabled ? "\u25CF" : "\u25CB",
|
|
12615
12849
|
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}` : ""}`,
|
|
12850
|
+
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
12851
|
_action: "server",
|
|
12618
12852
|
_server: server,
|
|
12619
12853
|
_enabled: enabled
|
|
@@ -12625,9 +12859,24 @@ function createExtensionPickers({
|
|
|
12625
12859
|
setSettingsPrompt(null);
|
|
12626
12860
|
const toggleServer = (item) => {
|
|
12627
12861
|
if (item._action !== "server" || !item._server?.name) return;
|
|
12628
|
-
|
|
12862
|
+
const name = item._server.name;
|
|
12863
|
+
const target = !item._enabled;
|
|
12864
|
+
const highlightValue = `server:${name}`;
|
|
12865
|
+
const token = ++mcpEpoch;
|
|
12866
|
+
const settle = (fn) => {
|
|
12867
|
+
if (token !== mcpEpoch || !mcpActive || getPicker?.()?._kind !== "mcp-servers") return;
|
|
12868
|
+
fn();
|
|
12869
|
+
};
|
|
12870
|
+
openMcpServersPicker({ highlightValue, optimistic: { name, enabled: target } });
|
|
12871
|
+
Promise.resolve(store.setMcpServerEnabled?.(name, target)).then(() => {
|
|
12872
|
+
settle(() => openMcpServersPicker({ highlightValue }));
|
|
12873
|
+
}).catch((e) => {
|
|
12874
|
+
store.pushNotice(`mcp toggle failed: ${e?.message || e}`, "error");
|
|
12875
|
+
settle(() => openMcpServersPicker({ highlightValue }));
|
|
12876
|
+
});
|
|
12629
12877
|
};
|
|
12630
12878
|
setPicker({
|
|
12879
|
+
_kind: "mcp-servers",
|
|
12631
12880
|
title: "MCP servers",
|
|
12632
12881
|
description: "Enable or disable configured MCP servers.",
|
|
12633
12882
|
initialIndex: Math.max(0, items.findIndex((entry) => entry.value === options?.highlightValue)),
|
|
@@ -12636,9 +12885,12 @@ function createExtensionPickers({
|
|
|
12636
12885
|
onLeft: (item) => toggleServer(item),
|
|
12637
12886
|
onRight: (item) => toggleServer(item),
|
|
12638
12887
|
onCancel: () => {
|
|
12888
|
+
mcpActive = false;
|
|
12889
|
+
mcpEpoch++;
|
|
12639
12890
|
setPicker(null);
|
|
12640
12891
|
}
|
|
12641
12892
|
});
|
|
12893
|
+
mcpActive = true;
|
|
12642
12894
|
};
|
|
12643
12895
|
const openMcpPicker = () => {
|
|
12644
12896
|
openMcpServersPicker();
|
|
@@ -12695,9 +12947,14 @@ function createExtensionPickers({
|
|
|
12695
12947
|
});
|
|
12696
12948
|
};
|
|
12697
12949
|
const openSkillsPicker = (options = {}) => {
|
|
12698
|
-
|
|
12699
|
-
if (
|
|
12700
|
-
|
|
12950
|
+
let skills;
|
|
12951
|
+
if (Array.isArray(options.skills)) {
|
|
12952
|
+
skills = options.skills;
|
|
12953
|
+
} else {
|
|
12954
|
+
const status = skillsStatus();
|
|
12955
|
+
if (!status) return;
|
|
12956
|
+
skills = status.skills || [];
|
|
12957
|
+
}
|
|
12701
12958
|
const disabledSet = options.disabledOverride instanceof Set ? options.disabledOverride : getDisabledSkills();
|
|
12702
12959
|
const items = [];
|
|
12703
12960
|
if (skills.length === 0) {
|
|
@@ -12736,9 +12993,10 @@ function createExtensionPickers({
|
|
|
12736
12993
|
`skill ${item._enabled ? "disabled" : "enabled"}: ${name} (prompt updates next session /clear)`,
|
|
12737
12994
|
"info"
|
|
12738
12995
|
);
|
|
12739
|
-
openSkillsPicker({ highlightValue: name, disabledOverride: next });
|
|
12996
|
+
openSkillsPicker({ highlightValue: name, disabledOverride: next, skills });
|
|
12740
12997
|
};
|
|
12741
12998
|
setPicker({
|
|
12999
|
+
_kind: "skills",
|
|
12742
13000
|
title: "Skills",
|
|
12743
13001
|
description: "Enable or disable project skills.",
|
|
12744
13002
|
initialIndex: Math.max(0, items.findIndex((entry) => entry.value === options.highlightValue)),
|
|
@@ -13021,6 +13279,7 @@ ${p.description}` : ""
|
|
|
13021
13279
|
}
|
|
13022
13280
|
};
|
|
13023
13281
|
setPicker({
|
|
13282
|
+
_kind: "hooks",
|
|
13024
13283
|
title: "Hooks",
|
|
13025
13284
|
description: "Before-tool hook rules; Enter toggles a rule.",
|
|
13026
13285
|
items,
|
|
@@ -13249,8 +13508,6 @@ function createMaintenancePickers({
|
|
|
13249
13508
|
const current = readCurrent();
|
|
13250
13509
|
const enabled = current?.enabled !== false;
|
|
13251
13510
|
const idleMs = Number(current?.idleMs || HOUR_MS);
|
|
13252
|
-
const custom = current?.custom === true;
|
|
13253
|
-
const providerDefault = Number(current?.providerDefault || HOUR_MS);
|
|
13254
13511
|
const cacheTtlLabel = !enabled || idleMs >= HOUR_MS ? "1h" : "5m";
|
|
13255
13512
|
const items = [
|
|
13256
13513
|
{
|
|
@@ -13263,9 +13520,6 @@ function createMaintenancePickers({
|
|
|
13263
13520
|
{
|
|
13264
13521
|
value: "advanced",
|
|
13265
13522
|
label: "Advanced",
|
|
13266
|
-
marker: !custom ? "\u2713" : "",
|
|
13267
|
-
markerColor: theme2.success,
|
|
13268
|
-
meta: `${current?.provider || "current"} \xB7 ${formatDuration2(providerDefault)}`,
|
|
13269
13523
|
description: "Edit provider-paired default idle windows as text.",
|
|
13270
13524
|
_action: "advanced"
|
|
13271
13525
|
}
|
|
@@ -13748,6 +14002,18 @@ function sleepSync(ms) {
|
|
|
13748
14002
|
}
|
|
13749
14003
|
}
|
|
13750
14004
|
}
|
|
14005
|
+
function _describeLockHolder(lockPath) {
|
|
14006
|
+
try {
|
|
14007
|
+
const st = statSync2(lockPath);
|
|
14008
|
+
const owner = _readLockOwner(lockPath);
|
|
14009
|
+
const ageMs = Math.max(0, Math.round(Date.now() - st.mtimeMs));
|
|
14010
|
+
const live = owner.pid === null ? "unknown" : _ownerIsLive(owner) ? "live" : "dead";
|
|
14011
|
+
const token = owner.token === null ? "?" : String(owner.token).slice(0, 8);
|
|
14012
|
+
return `holder pid=${owner.pid ?? "?"} token=${token} age=${ageMs}ms ${live}`;
|
|
14013
|
+
} catch {
|
|
14014
|
+
return "holder unknown (lock file unreadable/absent)";
|
|
14015
|
+
}
|
|
14016
|
+
}
|
|
13751
14017
|
function renameWithRetrySync(src, dst, opts = {}) {
|
|
13752
14018
|
const backoffs = Array.isArray(opts.backoffs) && opts.backoffs.length > 0 ? opts.backoffs : DEFAULT_BACKOFFS_MS;
|
|
13753
14019
|
let lastErr = null;
|
|
@@ -13779,7 +14045,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
13779
14045
|
lastErr = err;
|
|
13780
14046
|
if (!LOCK_WAIT_CODES.has(err?.code)) throw err;
|
|
13781
14047
|
if (timeoutMs <= 0) {
|
|
13782
|
-
const contErr = new Error(`atomic lock contended (try-once): ${lockPath}`);
|
|
14048
|
+
const contErr = new Error(`atomic lock contended (try-once): ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
13783
14049
|
contErr.code = "ELOCKCONTENDED";
|
|
13784
14050
|
contErr.cause = err;
|
|
13785
14051
|
throw contErr;
|
|
@@ -13814,7 +14080,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
13814
14080
|
}
|
|
13815
14081
|
}
|
|
13816
14082
|
}
|
|
13817
|
-
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath}`);
|
|
14083
|
+
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
13818
14084
|
timeoutErr.code = "ELOCKTIMEOUT";
|
|
13819
14085
|
timeoutErr.cause = lastErr;
|
|
13820
14086
|
throw timeoutErr;
|
|
@@ -14006,8 +14272,8 @@ function _releaseReclaimGuard(reclaim) {
|
|
|
14006
14272
|
}
|
|
14007
14273
|
}
|
|
14008
14274
|
function _asyncSleep(ms) {
|
|
14009
|
-
return new Promise((
|
|
14010
|
-
setTimeout(
|
|
14275
|
+
return new Promise((resolve5) => {
|
|
14276
|
+
setTimeout(resolve5, Math.max(1, Number(ms) || 1));
|
|
14011
14277
|
});
|
|
14012
14278
|
}
|
|
14013
14279
|
async function withFileLock(lockPath, fn, opts = {}) {
|
|
@@ -14025,7 +14291,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14025
14291
|
lastErr = err;
|
|
14026
14292
|
if (!LOCK_WAIT_CODES.has(err?.code)) throw err;
|
|
14027
14293
|
if (timeoutMs <= 0) {
|
|
14028
|
-
const contErr = new Error(`atomic lock contended (try-once): ${lockPath}`);
|
|
14294
|
+
const contErr = new Error(`atomic lock contended (try-once): ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
14029
14295
|
contErr.code = "ELOCKCONTENDED";
|
|
14030
14296
|
contErr.cause = err;
|
|
14031
14297
|
throw contErr;
|
|
@@ -14060,7 +14326,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14060
14326
|
}
|
|
14061
14327
|
}
|
|
14062
14328
|
}
|
|
14063
|
-
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath}`);
|
|
14329
|
+
const timeoutErr = new Error(`atomic lock timeout after ${timeoutMs}ms: ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
14064
14330
|
timeoutErr.code = "ELOCKTIMEOUT";
|
|
14065
14331
|
timeoutErr.cause = lastErr;
|
|
14066
14332
|
throw timeoutErr;
|
|
@@ -14210,21 +14476,6 @@ function writeFileAtomicSync(filePath, data, opts = {}) {
|
|
|
14210
14476
|
function writeJsonAtomicSync(filePath, value, opts = {}) {
|
|
14211
14477
|
return writeFileAtomicSync(filePath, JSON.stringify(value, null, opts.compact ? 0 : 2) + "\n", opts);
|
|
14212
14478
|
}
|
|
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
14479
|
async function updateJsonAtomic(filePath, mutator, opts = {}) {
|
|
14229
14480
|
const { lock: _lock, ...writeOpts } = opts;
|
|
14230
14481
|
return withFileLock(`${filePath}.lock`, () => {
|
|
@@ -15174,7 +15425,7 @@ function createModelPicker({
|
|
|
15174
15425
|
items: [],
|
|
15175
15426
|
onCancel: cancelModelPicker
|
|
15176
15427
|
});
|
|
15177
|
-
await new Promise((
|
|
15428
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
15178
15429
|
try {
|
|
15179
15430
|
if (options.refreshModels !== true && options.cacheRef !== "search") {
|
|
15180
15431
|
refreshModelsPromise = Promise.resolve(loadModels({ force: false }));
|
|
@@ -15500,7 +15751,7 @@ function createProviderSetupPicker({
|
|
|
15500
15751
|
}
|
|
15501
15752
|
});
|
|
15502
15753
|
try {
|
|
15503
|
-
await new Promise((
|
|
15754
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
15504
15755
|
setup = await store.getProviderSetup();
|
|
15505
15756
|
} catch (e) {
|
|
15506
15757
|
store.pushNotice(`providers failed: ${e?.message || e}`, "error");
|
|
@@ -16886,10 +17137,6 @@ function createSlashDispatch({
|
|
|
16886
17137
|
return true;
|
|
16887
17138
|
}
|
|
16888
17139
|
case "search":
|
|
16889
|
-
if (state.busy) {
|
|
16890
|
-
store.pushNotice("wait for the current turn to finish before /search", "warn");
|
|
16891
|
-
return false;
|
|
16892
|
-
}
|
|
16893
17140
|
if (arg) store.pushNotice("/search sets the search provider/model; the search tool uses that model when called.", "warn");
|
|
16894
17141
|
openSearchPicker();
|
|
16895
17142
|
return true;
|
|
@@ -17290,57 +17537,64 @@ function usePromptHandlers({
|
|
|
17290
17537
|
const handlePromptPaste = useCallback5((text, meta = {}) => {
|
|
17291
17538
|
const source = String(meta?.source || "paste");
|
|
17292
17539
|
const value = String(text ?? "");
|
|
17293
|
-
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17540
|
+
const processText = (raw, returnRaw = false) => {
|
|
17541
|
+
const chunks = splitPastedImagePathCandidates(raw);
|
|
17542
|
+
const hasImagePath = chunks.some((chunk) => chunk.imagePath);
|
|
17543
|
+
if (!hasImagePath) {
|
|
17544
|
+
if (shouldFoldPastedText(raw)) return registerPastedText(raw);
|
|
17545
|
+
return returnRaw ? raw : void 0;
|
|
17546
|
+
}
|
|
17547
|
+
return Promise.all(chunks.map(async (chunk) => {
|
|
17548
|
+
if (!chunk.imagePath) return chunk.text;
|
|
17549
|
+
try {
|
|
17550
|
+
const image = await readImageAttachmentFromPath(chunk.text, state.cwd || process.cwd());
|
|
17551
|
+
if (!image) return chunk.text;
|
|
17552
|
+
const ref = registerPastedImage(image);
|
|
17553
|
+
showPromptHint(`attached ${image.filename || "image"}`, "plain");
|
|
17554
|
+
return ref;
|
|
17555
|
+
} catch (e) {
|
|
17556
|
+
showPromptHint(`image attach failed: ${e?.message || e}`, "warn");
|
|
17557
|
+
return chunk.text;
|
|
17558
|
+
}
|
|
17559
|
+
})).then((parts) => {
|
|
17560
|
+
let out = "";
|
|
17561
|
+
let run = "";
|
|
17562
|
+
const flushRun = () => {
|
|
17563
|
+
if (!run) return;
|
|
17564
|
+
out += shouldFoldPastedText(run) ? registerPastedText(run) : run;
|
|
17565
|
+
run = "";
|
|
17566
|
+
};
|
|
17567
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
17568
|
+
if (chunks[i].imagePath) {
|
|
17569
|
+
flushRun();
|
|
17570
|
+
out += parts[i];
|
|
17571
|
+
} else {
|
|
17572
|
+
run += parts[i];
|
|
17573
|
+
}
|
|
17298
17574
|
}
|
|
17299
|
-
|
|
17300
|
-
|
|
17301
|
-
|
|
17575
|
+
flushRun();
|
|
17576
|
+
return out;
|
|
17577
|
+
});
|
|
17578
|
+
};
|
|
17579
|
+
if (source === "clipboard-shortcut" && !value) {
|
|
17580
|
+
return readClipboardText().then((clip) => {
|
|
17581
|
+
const normalized = String(clip ?? "").replace(/\r\n?/g, "\n");
|
|
17582
|
+
if (normalized) return processText(normalized, true);
|
|
17583
|
+
return readClipboardImageAttachment().then((image) => {
|
|
17584
|
+
if (!image) {
|
|
17585
|
+
showPromptHint("no text or image found on clipboard", "plain");
|
|
17586
|
+
return false;
|
|
17587
|
+
}
|
|
17588
|
+
const ref = registerPastedImage(image);
|
|
17589
|
+
showPromptHint(`attached ${image.filename || "clipboard image"}`, "plain");
|
|
17590
|
+
return ref;
|
|
17591
|
+
});
|
|
17302
17592
|
}).catch((e) => {
|
|
17303
|
-
showPromptHint(`
|
|
17593
|
+
showPromptHint(`paste failed: ${e?.message || e}`, "warn");
|
|
17304
17594
|
return false;
|
|
17305
17595
|
});
|
|
17306
17596
|
}
|
|
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
|
-
});
|
|
17597
|
+
return processText(value);
|
|
17344
17598
|
}, [registerPastedImage, registerPastedText, showPromptHint, state.cwd]);
|
|
17345
17599
|
const handlePromptHistoryNavigate = useCallback5((direction, currentText = "", meta = {}) => {
|
|
17346
17600
|
const currentValue = String(currentText || "");
|
|
@@ -17689,7 +17943,7 @@ var AssistantMessage = React14.memo(function AssistantMessage2({
|
|
|
17689
17943
|
});
|
|
17690
17944
|
var UserMessage = React14.memo(function UserMessage2({ text, attached = false, columns, themeEpoch = 0 }) {
|
|
17691
17945
|
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.
|
|
17946
|
+
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
17947
|
});
|
|
17694
17948
|
function NoticeMessage({ text, tone, columns = 80 }) {
|
|
17695
17949
|
const accentColor = tone === "error" ? theme.error : tone === "warn" ? theme.warning : theme.inactive;
|
|
@@ -18378,13 +18632,13 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18378
18632
|
else if (isShellSurface) labelText = shellHeader(shellStatus, displayGroupCount);
|
|
18379
18633
|
else labelText = (isAgentTool(normalizedName) ? agentActionTitle(parsedArgs) : "") || statusCopy(name, label, displayGroupCount, doneCount, headerPending, isError, parsedArgs);
|
|
18380
18634
|
labelText = safeInlineText(labelText);
|
|
18381
|
-
const toolSearchSummary = !pending && normalizedName === "
|
|
18635
|
+
const toolSearchSummary = !pending && normalizedName === "load_tool" && hasResult ? toolSearchLoadedSummary(displayedResultText) : "";
|
|
18382
18636
|
const rawSummaryText = safeInlineText(isAgentResponse || isBackgroundResponse ? "" : toolSearchSummary || (isAgentTool(normalizedName) ? agentActionSummary(parsedArgs, summary) : summary));
|
|
18383
18637
|
const summaryIsHeaderCount = rawSummaryText && /^\d+\s+\S+$/.test(rawSummaryText) && labelText.endsWith(rawSummaryText);
|
|
18384
18638
|
const summaryText = summaryIsHeaderCount ? "" : rawSummaryText;
|
|
18385
18639
|
const agentHasExpandableBody = isAgentSurfaceCard && !pending && hasResult && (isAgentResponse || totalLines > 1 || firstResultLineClipped);
|
|
18386
18640
|
const shellHasExpandableBody = isShellSurface && !pending && hasDisplayResult && hasDisplayBody && (totalLines > 1 || firstResultLineClipped || Boolean(shellCollapsedSummary && shellCollapsedSummary !== firstResultLine));
|
|
18387
|
-
const showHeaderExpandHint = (isShellSurface ? shellHasExpandableBody : isAgentSurfaceCard ? agentHasExpandableBody : hasHiddenDetail || backgroundMetadataExpandable) && normalizedName !== "
|
|
18641
|
+
const showHeaderExpandHint = (isShellSurface ? shellHasExpandableBody : isAgentSurfaceCard ? agentHasExpandableBody : hasHiddenDetail || backgroundMetadataExpandable) && normalizedName !== "load_tool";
|
|
18388
18642
|
const expandHintColor = theme.subtle;
|
|
18389
18643
|
const gutter = 2;
|
|
18390
18644
|
const hintLabel = showHeaderExpandHint ? `ctrl+o ${expanded ? "collapse" : "expand"}` : "";
|
|
@@ -18616,7 +18870,9 @@ var PANEL_LAYOUT_SIG = {
|
|
|
18616
18870
|
TEXT: 5,
|
|
18617
18871
|
// Prompt-wrap/meta row counts (trailing churn tokens, see token order note
|
|
18618
18872
|
// below). PROMPT_META is the 2-row live-spinner band slot.
|
|
18619
|
-
PROMPT_META: 9
|
|
18873
|
+
PROMPT_META: 9,
|
|
18874
|
+
// Queued steering band rows (full wrapped height, see queuedBandRows).
|
|
18875
|
+
QUEUED: 10
|
|
18620
18876
|
};
|
|
18621
18877
|
var PROJECT_TEXT_ENTRY_KINDS = /* @__PURE__ */ new Set(["project-new", "project-create-confirm", "project-rename"]);
|
|
18622
18878
|
var CORE_MULTILINE_TEXT_ENTRY_KINDS = /* @__PURE__ */ new Set(["core-add", "core-edit"]);
|
|
@@ -18678,7 +18934,9 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18678
18934
|
const projectPickerRef = useRef9(null);
|
|
18679
18935
|
const buildProjectPickerState = (opts) => projectPickerRef.current.buildProjectPickerState(opts);
|
|
18680
18936
|
const [picker, setPickerState] = useState8(null);
|
|
18937
|
+
const livePickerRef = useRef9(null);
|
|
18681
18938
|
const setPicker = useCallback6((next) => {
|
|
18939
|
+
livePickerRef.current = typeof next === "function" ? next(livePickerRef.current) : next;
|
|
18682
18940
|
setPickerState((prev) => {
|
|
18683
18941
|
const resolved = typeof next === "function" ? next(prev) : next;
|
|
18684
18942
|
if (resolved && typeof resolved === "object" && pickerOpenedFromEnterRef.current) {
|
|
@@ -18689,9 +18947,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18689
18947
|
}
|
|
18690
18948
|
return resolved.indexMode ? resolved : { ...resolved, indexMode: "always" };
|
|
18691
18949
|
}
|
|
18950
|
+
if (resolved && typeof resolved === "object" && !resolved.indexMode && prev && typeof prev === "object" && prev.indexMode && prev._kind && prev._kind === resolved._kind) {
|
|
18951
|
+
return { ...resolved, indexMode: prev.indexMode };
|
|
18952
|
+
}
|
|
18692
18953
|
return resolved;
|
|
18693
18954
|
});
|
|
18694
18955
|
}, []);
|
|
18956
|
+
livePickerRef.current = picker;
|
|
18695
18957
|
const [contextPanel, setContextPanel] = useState8(null);
|
|
18696
18958
|
const [usagePanel, setUsagePanel] = useState8(null);
|
|
18697
18959
|
const usageRequestRef = useRef9(0);
|
|
@@ -18733,7 +18995,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18733
18995
|
} catch {
|
|
18734
18996
|
}
|
|
18735
18997
|
if (!onboardingOwnsScreen && state.items.length === 0) {
|
|
18736
|
-
|
|
18998
|
+
setPicker(projectPicker.buildProjectPickerState({ initialEntry: true }));
|
|
18737
18999
|
}
|
|
18738
19000
|
}
|
|
18739
19001
|
const {
|
|
@@ -18804,6 +19066,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18804
19066
|
clean,
|
|
18805
19067
|
copyToClipboard,
|
|
18806
19068
|
setPicker,
|
|
19069
|
+
getPicker: () => livePickerRef.current,
|
|
18807
19070
|
setProviderPrompt,
|
|
18808
19071
|
setChannelPrompt,
|
|
18809
19072
|
setHookPrompt,
|
|
@@ -19306,13 +19569,16 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19306
19569
|
const renderText = store.getRenderSelectionText?.();
|
|
19307
19570
|
const remembered = selectionTextRef.current || "";
|
|
19308
19571
|
let text = renderText == null ? remembered : remembered.length > renderText.length ? remembered : renderText;
|
|
19309
|
-
const stitched = getStitchedSelectionText?.() || "";
|
|
19310
|
-
if (stitched.length > text.length) text = stitched;
|
|
19572
|
+
const stitched = getStitchedSelectionText?.() || { text: "", complete: false };
|
|
19573
|
+
if (stitched.complete && stitched.text.length > text.length) text = stitched.text;
|
|
19311
19574
|
if ((!text || !text.trim()) && attempt < 4) {
|
|
19312
19575
|
setTimeout(() => copySelection(attempt + 1), attempt === 0 ? 0 : 24);
|
|
19313
19576
|
return;
|
|
19314
19577
|
}
|
|
19315
|
-
if (!text || !text.trim())
|
|
19578
|
+
if (!text || !text.trim()) {
|
|
19579
|
+
showSelectionCopyHint("nothing to copy \xB7 select text first", "error");
|
|
19580
|
+
return;
|
|
19581
|
+
}
|
|
19316
19582
|
selectionTextRef.current = text;
|
|
19317
19583
|
copyToClipboard(text).then(() => {
|
|
19318
19584
|
const lines = text.split("\n").length;
|
|
@@ -19388,8 +19654,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19388
19654
|
let timer = null;
|
|
19389
19655
|
Promise.race([
|
|
19390
19656
|
Promise.resolve(store.dispose?.("cli-react-exit", { detach: true })),
|
|
19391
|
-
new Promise((
|
|
19392
|
-
timer = setTimeout(
|
|
19657
|
+
new Promise((resolve5) => {
|
|
19658
|
+
timer = setTimeout(resolve5, 350);
|
|
19393
19659
|
})
|
|
19394
19660
|
]).finally(() => {
|
|
19395
19661
|
if (timer) clearTimeout(timer);
|
|
@@ -20495,7 +20761,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20495
20761
|
const promptMetaRows = promptMetaVisible ? 2 : 0;
|
|
20496
20762
|
const overlayHintRequested = !inputBoxHidden && !hasFloatingPanel && !liveSpinner && !!inputHint && !queuedVisible;
|
|
20497
20763
|
const overlayHintRows = 0;
|
|
20498
|
-
const
|
|
20764
|
+
const queuedFullRows = queuedVisible ? state.queued.reduce(
|
|
20765
|
+
(sum, item) => sum + queuedBandRows(String(item.displayText || item.text || ""), Math.max(1, frameColumns - 4)),
|
|
20766
|
+
0
|
|
20767
|
+
) : 0;
|
|
20768
|
+
const queuedRowBudget = Math.max(3, Math.floor(resizeState.rows / 3));
|
|
20769
|
+
const queuedCompact = queuedFullRows > queuedRowBudget;
|
|
20770
|
+
const queuedRows = queuedVisible ? queuedCompact ? state.queued.length : queuedFullRows : 0;
|
|
20499
20771
|
const INPUT_BOX_ROWS = promptBoxRows + promptMetaRows + overlayHintRows;
|
|
20500
20772
|
const baseReserve = WELCOME_ROWS + SCROLL_HINT_ROWS + LIVE_STATUS_ROWS + INPUT_BOX_ROWS + STATUSLINE_ROWS + queuedRows;
|
|
20501
20773
|
const maxFloatingPanelRows = Math.max(0, resizeState.rows - baseReserve - 1);
|
|
@@ -20530,10 +20802,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20530
20802
|
const nextMetaRows = Number(String(panelLayoutSignature).split("|")[PANEL_LAYOUT_SIG.PROMPT_META]) || 0;
|
|
20531
20803
|
const doneTailAppendedThisCommit = latestDoneAtTail && (latestTranscriptItem?.id ?? null) !== panelTransition.tailId;
|
|
20532
20804
|
const spinnerMetaCollapseRows = doneTailAppendedThisCommit ? Math.max(0, prevMetaRows - nextMetaRows) : 0;
|
|
20805
|
+
const prevQueuedSigRows = Number(String(panelTransition.signature).split("|")[PANEL_LAYOUT_SIG.QUEUED]) || 0;
|
|
20806
|
+
const nextQueuedSigRows = Number(String(panelLayoutSignature).split("|")[PANEL_LAYOUT_SIG.QUEUED]) || 0;
|
|
20807
|
+
const userTailAppendedThisCommit = latestTranscriptItem?.kind === "user" && (latestTranscriptItem?.id ?? null) !== panelTransition.tailId;
|
|
20808
|
+
const queuedPromoteCollapseRows = userTailAppendedThisCommit ? Math.max(0, prevQueuedSigRows - nextQueuedSigRows) : 0;
|
|
20533
20809
|
const instantPanelClose = panelShrinkRows > 0 && (promptRowsOnlyChange || isInstantPanelCloseTransition(panelTransition.signature, panelLayoutSignature, initialProjectEntryClose));
|
|
20534
20810
|
const slashOpenOnEmptyTranscript = initialProjectEntryClose && !panelSignatureFlags(panelTransition.signature).slash && panelSignatureFlags(panelLayoutSignature).slash;
|
|
20535
20811
|
if (instantPanelClose) {
|
|
20536
|
-
panelCloseInkMaskRowsRef.current = Math.max(0, panelShrinkRows - spinnerMetaCollapseRows);
|
|
20812
|
+
panelCloseInkMaskRowsRef.current = Math.max(0, panelShrinkRows - spinnerMetaCollapseRows - queuedPromoteCollapseRows);
|
|
20537
20813
|
panelTransition.clearRows = 0;
|
|
20538
20814
|
panelTransition.guardRows = 0;
|
|
20539
20815
|
panelTransition.epoch = panelTransitionEpoch;
|
|
@@ -20562,8 +20838,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20562
20838
|
const viewportHeight = Math.max(1, resizeState.rows - bottomReserve);
|
|
20563
20839
|
const guardCapacityRows = Math.max(0, viewportHeight - 1);
|
|
20564
20840
|
const baseGuardRows = guardCapacityRows > 0 ? 1 : 0;
|
|
20565
|
-
const
|
|
20566
|
-
const scrollGuardRows = !scrollGuardNearBottom && scrollOffset > 0 && guardCapacityRows > baseGuardRows ? 1 : 0;
|
|
20841
|
+
const scrollGuardRows = 0;
|
|
20567
20842
|
const transcriptGuardRows = Math.min(guardCapacityRows, baseGuardRows + panelTransitionGuardRows + scrollGuardRows);
|
|
20568
20843
|
const welcomePromptHintText = conditionalWelcomePromptHint || welcomePromptHintRef.current || "";
|
|
20569
20844
|
const welcomePromptHintVisible = Boolean(
|
|
@@ -20571,13 +20846,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20571
20846
|
);
|
|
20572
20847
|
const welcomePromptHintRows = welcomePromptHintVisible && viewportHeight - transcriptGuardRows >= 2 ? 1 : 0;
|
|
20573
20848
|
welcomePromptHintVisibleRef.current = welcomePromptHintRows > 0;
|
|
20849
|
+
const overlayHintBandRows = overlayHintRequested && state.items.length === 0 && viewportHeight - transcriptGuardRows - welcomePromptHintRows >= 2 ? 1 : 0;
|
|
20574
20850
|
const panelCloseMaskRows = Math.min(
|
|
20575
20851
|
panelCloseInkMaskRows,
|
|
20576
|
-
Math.max(0, viewportHeight - transcriptGuardRows - welcomePromptHintRows - 1)
|
|
20852
|
+
Math.max(0, viewportHeight - transcriptGuardRows - welcomePromptHintRows - overlayHintBandRows - 1)
|
|
20577
20853
|
);
|
|
20578
20854
|
const transcriptContentHeight = Math.max(
|
|
20579
20855
|
1,
|
|
20580
|
-
viewportHeight - transcriptGuardRows - panelCloseMaskRows - welcomePromptHintRows
|
|
20856
|
+
viewportHeight - transcriptGuardRows - panelCloseMaskRows - welcomePromptHintRows - overlayHintBandRows
|
|
20581
20857
|
);
|
|
20582
20858
|
const transcriptBottomSlackRows = Math.max(0, baseGuardRows);
|
|
20583
20859
|
transcriptBottomSlackRowsRef.current = transcriptBottomSlackRows;
|
|
@@ -20799,9 +21075,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20799
21075
|
backgroundColor: surfaceBackground()
|
|
20800
21076
|
}
|
|
20801
21077
|
) : null,
|
|
21078
|
+
overlayHintBandRows > 0 ? /* @__PURE__ */ jsxs16(Box18, { height: 1, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
|
|
21079
|
+
/* @__PURE__ */ jsx20(Box18, { flexGrow: 1, flexShrink: 1, overflow: "hidden" }),
|
|
21080
|
+
/* @__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 }) })
|
|
21081
|
+
] }) : null,
|
|
20802
21082
|
transcriptGuardRows > 0 ? /* @__PURE__ */ jsxs16(Box18, { height: transcriptGuardRows, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
|
|
20803
21083
|
/* @__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
|
|
21084
|
+
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
21085
|
] }) : null
|
|
20806
21086
|
]
|
|
20807
21087
|
}
|
|
@@ -21000,7 +21280,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
21000
21280
|
),
|
|
21001
21281
|
/* @__PURE__ */ jsx20(Box18, { height: 1, width: "100%", backgroundColor: surfaceBackground() })
|
|
21002
21282
|
] }) : null,
|
|
21003
|
-
queuedVisible ? /* @__PURE__ */ jsx20(QueuedCommands, { queued: state.queued, columns: frameColumns }) : null,
|
|
21283
|
+
queuedVisible ? /* @__PURE__ */ jsx20(QueuedCommands, { queued: state.queued, columns: frameColumns, compact: queuedCompact }) : null,
|
|
21004
21284
|
/* @__PURE__ */ jsx20(
|
|
21005
21285
|
Box18,
|
|
21006
21286
|
{
|
|
@@ -21188,6 +21468,33 @@ function modelVisibleToolCompletionMessage(text, meta = {}) {
|
|
|
21188
21468
|
].join("\n");
|
|
21189
21469
|
}
|
|
21190
21470
|
|
|
21471
|
+
// src/session-runtime/session-text.mjs
|
|
21472
|
+
var LATE_TOOL_ANNOUNCEMENT_SENTINEL = "connected after this session started";
|
|
21473
|
+
function isLateToolAnnouncement(text) {
|
|
21474
|
+
const value = String(text || "");
|
|
21475
|
+
return value.includes(LATE_TOOL_ANNOUNCEMENT_SENTINEL) && /<available-deferred-tools>/i.test(value);
|
|
21476
|
+
}
|
|
21477
|
+
function summarizeLateToolAnnouncement(text) {
|
|
21478
|
+
const value = String(text || "");
|
|
21479
|
+
if (!isLateToolAnnouncement(value)) return "";
|
|
21480
|
+
const block = value.match(/<available-deferred-tools>([\s\S]*?)<\/available-deferred-tools>/i);
|
|
21481
|
+
const body = block ? block[1] : value;
|
|
21482
|
+
const names = [];
|
|
21483
|
+
const lineRe = /^\s*-\s+([A-Za-z0-9_.:-]+)/gm;
|
|
21484
|
+
let m;
|
|
21485
|
+
while (m = lineRe.exec(body)) names.push(m[1]);
|
|
21486
|
+
const servers = /* @__PURE__ */ new Set();
|
|
21487
|
+
for (const name of names) {
|
|
21488
|
+
const seg = name.startsWith("mcp__") ? name.slice(5) : name;
|
|
21489
|
+
const server = seg.split("__")[0];
|
|
21490
|
+
if (server) servers.add(server);
|
|
21491
|
+
}
|
|
21492
|
+
const count = names.length;
|
|
21493
|
+
const label = servers.size === 1 ? [...servers][0] : servers.size ? `${servers.size} MCP servers` : "MCP";
|
|
21494
|
+
if (!count) return `MCP tools available: ${label}`;
|
|
21495
|
+
return `MCP tools available: ${label} (${count} ${count === 1 ? "tool" : "tools"})`;
|
|
21496
|
+
}
|
|
21497
|
+
|
|
21191
21498
|
// src/tui/engine/boot-profile.mjs
|
|
21192
21499
|
import { performance } from "node:perf_hooks";
|
|
21193
21500
|
var BOOT_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ""));
|
|
@@ -21973,8 +22280,8 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
21973
22280
|
|
|
21974
22281
|
// src/tui/engine/render-timing.mjs
|
|
21975
22282
|
var RENDER_THROTTLE_FLUSH_MS = 12;
|
|
21976
|
-
var yieldToRenderer = () => new Promise((
|
|
21977
|
-
setTimeout(
|
|
22283
|
+
var yieldToRenderer = () => new Promise((resolve5) => {
|
|
22284
|
+
setTimeout(resolve5, RENDER_THROTTLE_FLUSH_MS);
|
|
21978
22285
|
});
|
|
21979
22286
|
|
|
21980
22287
|
// src/tui/engine/tool-result-status.mjs
|
|
@@ -22152,9 +22459,9 @@ function createToolApproval({ getState, set, nextId: nextId2, getDisposed, timeo
|
|
|
22152
22459
|
}
|
|
22153
22460
|
function requestToolApproval(input = {}) {
|
|
22154
22461
|
if (getDisposed()) return Promise.resolve({ approved: false, reason: "runtime disposed" });
|
|
22155
|
-
return new Promise((
|
|
22462
|
+
return new Promise((resolve5) => {
|
|
22156
22463
|
const id = nextId2();
|
|
22157
|
-
toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve:
|
|
22464
|
+
toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve: resolve5, timer: null });
|
|
22158
22465
|
presentNextToolApproval();
|
|
22159
22466
|
});
|
|
22160
22467
|
}
|
|
@@ -23016,7 +23323,7 @@ function createSessionFlow(bag) {
|
|
|
23016
23323
|
const startedAt = Date.now();
|
|
23017
23324
|
set({ commandBusy: true, commandStatus: { active: true, verb, startedAt, mode: "auto-clear" } });
|
|
23018
23325
|
try {
|
|
23019
|
-
await new Promise((
|
|
23326
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
23020
23327
|
let compactType = null;
|
|
23021
23328
|
if (useCompaction) {
|
|
23022
23329
|
const compaction = runtime.getCompactionSettings();
|
|
@@ -24529,6 +24836,26 @@ import { readFileSync as readFileSync6 } from "node:fs";
|
|
|
24529
24836
|
import { dirname as dirname8, join as join9 } from "node:path";
|
|
24530
24837
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
24531
24838
|
var GLYPH = { ok: "\u2713", warn: "\u26A0", fail: "\u2717" };
|
|
24839
|
+
var CKPT_WRITE_WARN_S = 10;
|
|
24840
|
+
var CKPT_WRITE_MEDIAN_S = 5;
|
|
24841
|
+
function parseCheckpointWriteSeconds(logText) {
|
|
24842
|
+
const lines = String(logText).split(/\r?\n/).slice(-200);
|
|
24843
|
+
const out = [];
|
|
24844
|
+
for (const line of lines) {
|
|
24845
|
+
const m = /checkpoint complete:.*?\bwrite=([\d.]+)\s*s/i.exec(line);
|
|
24846
|
+
if (m) {
|
|
24847
|
+
const v = Number(m[1]);
|
|
24848
|
+
if (Number.isFinite(v)) out.push(v);
|
|
24849
|
+
}
|
|
24850
|
+
}
|
|
24851
|
+
return out;
|
|
24852
|
+
}
|
|
24853
|
+
function median(nums) {
|
|
24854
|
+
if (!nums.length) return 0;
|
|
24855
|
+
const s = [...nums].sort((a, b) => a - b);
|
|
24856
|
+
const mid = s.length >> 1;
|
|
24857
|
+
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
|
|
24858
|
+
}
|
|
24532
24859
|
function readPackageJson() {
|
|
24533
24860
|
try {
|
|
24534
24861
|
const dir = dirname8(fileURLToPath3(import.meta.url));
|
|
@@ -24662,6 +24989,31 @@ async function buildDoctorReport(runtime = {}, getState = () => ({})) {
|
|
|
24662
24989
|
const enabled = h.enabled === true;
|
|
24663
24990
|
row("ok", `${enabled ? "enabled" : "disabled"} \xB7 ${events.length} event${events.length === 1 ? "" : "s"}`);
|
|
24664
24991
|
});
|
|
24992
|
+
if (process.platform === "win32") {
|
|
24993
|
+
await check("defender", async (row) => {
|
|
24994
|
+
const dataDir = resolvePluginData();
|
|
24995
|
+
const pgdata = join9(dataDir, "pgdata");
|
|
24996
|
+
let writes;
|
|
24997
|
+
try {
|
|
24998
|
+
writes = parseCheckpointWriteSeconds(readFileSync6(join9(dataDir, "pg.log"), "utf8"));
|
|
24999
|
+
} catch {
|
|
25000
|
+
row("ok", "pg.log unavailable \xB7 check skipped");
|
|
25001
|
+
return;
|
|
25002
|
+
}
|
|
25003
|
+
if (!writes.length) {
|
|
25004
|
+
row("ok", "no checkpoint timings yet");
|
|
25005
|
+
return;
|
|
25006
|
+
}
|
|
25007
|
+
const max = Math.max(...writes);
|
|
25008
|
+
const med = median(writes);
|
|
25009
|
+
if (max <= CKPT_WRITE_WARN_S && med <= CKPT_WRITE_MEDIAN_S) {
|
|
25010
|
+
row("ok", `checkpoint write median ${med.toFixed(1)}s \xB7 max ${max.toFixed(1)}s`);
|
|
25011
|
+
return;
|
|
25012
|
+
}
|
|
25013
|
+
const fix = `Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile','-Command',"Add-MpPreference -ExclusionPath '${pgdata}'"`;
|
|
25014
|
+
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}`);
|
|
25015
|
+
});
|
|
25016
|
+
}
|
|
24665
25017
|
return ["mixdog doctor \u2014 installation health", ...rows].join("\n");
|
|
24666
25018
|
}
|
|
24667
25019
|
|
|
@@ -24948,17 +25300,18 @@ function createEngineApiA(bag) {
|
|
|
24948
25300
|
}
|
|
24949
25301
|
},
|
|
24950
25302
|
setMcpServerEnabled: async (name, enabled) => {
|
|
24951
|
-
|
|
24952
|
-
|
|
24953
|
-
try {
|
|
24954
|
-
const status = await runtime.setMcpServerEnabled?.(name, enabled);
|
|
25303
|
+
const status = await runtime.setMcpServerEnabled?.(name, enabled);
|
|
25304
|
+
setImmediate(() => {
|
|
24955
25305
|
resetStatsAndSyncContext();
|
|
24956
25306
|
set({ ...routeState(), stats: { ...getState().stats } });
|
|
25307
|
+
});
|
|
25308
|
+
const row = status?.servers?.find((s) => s.name === name);
|
|
25309
|
+
if (enabled && row && (row.status === "failed" || row.error)) {
|
|
25310
|
+
pushNotice(`mcp enable failed: ${name}${row.error ? ` \u2014 ${row.error}` : ""}`, "error");
|
|
25311
|
+
} else {
|
|
24957
25312
|
pushNotice(`mcp ${enabled ? "enabled" : "disabled"}: ${name}`, "info");
|
|
24958
|
-
return status;
|
|
24959
|
-
} finally {
|
|
24960
|
-
set({ commandBusy: false });
|
|
24961
25313
|
}
|
|
25314
|
+
return status;
|
|
24962
25315
|
},
|
|
24963
25316
|
getDisabledSkills: () => runtime.getDisabledSkills?.() || { disabled: [] },
|
|
24964
25317
|
setDisabledSkills: (disabled) => runtime.setDisabledSkills?.(disabled) || { disabled: [] },
|
|
@@ -25112,7 +25465,7 @@ function createEngineApiA(bag) {
|
|
|
25112
25465
|
const startedAt = Date.now();
|
|
25113
25466
|
set({ commandBusy: true, commandStatus: { active: true, verb: "Running diagnostics", startedAt, mode: "doctor" } });
|
|
25114
25467
|
try {
|
|
25115
|
-
await new Promise((
|
|
25468
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
25116
25469
|
const report = await buildDoctorReport(runtime, getState);
|
|
25117
25470
|
pushNotice(report, "info");
|
|
25118
25471
|
return report;
|
|
@@ -25132,7 +25485,7 @@ function createEngineApiA(bag) {
|
|
|
25132
25485
|
const startedAt = Date.now();
|
|
25133
25486
|
set({ commandBusy: true, commandStatus: { active: true, verb: "Compacting conversation", startedAt, mode: "compacting" } });
|
|
25134
25487
|
try {
|
|
25135
|
-
await new Promise((
|
|
25488
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
25136
25489
|
const result = await runtime.compact({ recoverAgent: true });
|
|
25137
25490
|
syncContextStats({ allowEstimated: true });
|
|
25138
25491
|
set({ ...routeState(), stats: { ...getState().stats } });
|
|
@@ -25426,6 +25779,10 @@ async function createEngineSession({
|
|
|
25426
25779
|
const pushUserOrSyntheticItem = (text, id = nextId(), origin = "user") => {
|
|
25427
25780
|
if (origin === "injected" && isLikelyToolCompletionWrapper(text)) return;
|
|
25428
25781
|
if (isModelVisibleToolCompletionWrapper(text)) return;
|
|
25782
|
+
if (isLateToolAnnouncement(text)) {
|
|
25783
|
+
pushItem({ kind: "notice", id, text: summarizeLateToolAnnouncement(text), tone: "info" });
|
|
25784
|
+
return;
|
|
25785
|
+
}
|
|
25429
25786
|
if (upsertSyntheticToolItem(text, id)) return;
|
|
25430
25787
|
pushItem({ kind: "user", id, text });
|
|
25431
25788
|
};
|
|
@@ -25624,7 +25981,7 @@ function signalExitCode(signal, fallback = 1) {
|
|
|
25624
25981
|
}
|
|
25625
25982
|
function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
25626
25983
|
const ms = Math.max(1, Math.floor(Number(timeoutMs) || 1));
|
|
25627
|
-
return new Promise((
|
|
25984
|
+
return new Promise((resolve5, reject) => {
|
|
25628
25985
|
let settled = false;
|
|
25629
25986
|
const timer = setTimeout(() => {
|
|
25630
25987
|
if (settled) return;
|
|
@@ -25636,7 +25993,7 @@ function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
|
25636
25993
|
if (settled) return;
|
|
25637
25994
|
settled = true;
|
|
25638
25995
|
clearTimeout(timer);
|
|
25639
|
-
|
|
25996
|
+
resolve5(value);
|
|
25640
25997
|
}).catch((error) => {
|
|
25641
25998
|
if (settled) return;
|
|
25642
25999
|
settled = true;
|
|
@@ -25750,54 +26107,14 @@ function installProcessSignalCleanup({
|
|
|
25750
26107
|
};
|
|
25751
26108
|
}
|
|
25752
26109
|
|
|
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
26110
|
// src/tui/index.jsx
|
|
25795
26111
|
var import_mixdog_debug = __toESM(require_mixdog_debug(), 1);
|
|
25796
26112
|
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";
|
|
26113
|
+
var TERMINAL_MODE_RESET = "\x1B[?1006l\x1B[?1005l\x1B[?1015l\x1B[?1003l\x1B[?1002l\x1B[?1000l\x1B[?2004l\x1B[>0s\x1B[?25h";
|
|
25798
26114
|
var TERMINAL_OSC_RESET_BG = "\x1B]111\x07";
|
|
25799
26115
|
var TERMINAL_MODE_RESET_HIDDEN_CURSOR = TERMINAL_MODE_RESET.replace("\x1B[?25h", "\x1B[?25l");
|
|
25800
|
-
var
|
|
26116
|
+
var XTSHIFTESCAPE_ON = process.env.WT_SESSION ? "" : "\x1B[>1s";
|
|
26117
|
+
var MOUSE_TRACKING_ON2 = `\x1B[?1000h\x1B[?1002h\x1B[?1006h${XTSHIFTESCAPE_ON}`;
|
|
25801
26118
|
var BOOT_PROFILE_ENABLED2 = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ""));
|
|
25802
26119
|
var BOOT_PROFILE_START2 = globalThis.__mixdogBootProfileStart || (globalThis.__mixdogBootProfileStart = performance3.now());
|
|
25803
26120
|
var EXIT_WAIT_TIMEOUT_MS = positiveIntEnv2("MIXDOG_TUI_EXIT_WAIT_MS", 2500);
|
|
@@ -25986,8 +26303,8 @@ function waitWithTimeout2(promise, ms) {
|
|
|
25986
26303
|
let timer = null;
|
|
25987
26304
|
return Promise.race([
|
|
25988
26305
|
Promise.resolve(promise).then(() => true),
|
|
25989
|
-
new Promise((
|
|
25990
|
-
timer = setTimeout(() =>
|
|
26306
|
+
new Promise((resolve5) => {
|
|
26307
|
+
timer = setTimeout(() => resolve5(false), ms);
|
|
25991
26308
|
})
|
|
25992
26309
|
]).finally(() => {
|
|
25993
26310
|
if (timer) clearTimeout(timer);
|
|
@@ -26006,7 +26323,7 @@ function scheduleHardExit(code = 0) {
|
|
|
26006
26323
|
timer.unref?.();
|
|
26007
26324
|
}
|
|
26008
26325
|
function resolveTuiStderrLogPath() {
|
|
26009
|
-
return process.env.MIXDOG_TUI_STDERR_LOG ||
|
|
26326
|
+
return process.env.MIXDOG_TUI_STDERR_LOG || join10(process.env.MIXDOG_RUNTIME_ROOT || join10(tmpdir2(), "mixdog"), "mixdog-tui.stderr.log");
|
|
26010
26327
|
}
|
|
26011
26328
|
function ansiFg(rgb) {
|
|
26012
26329
|
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ""));
|
|
@@ -26088,7 +26405,7 @@ function installTuiStderrGuard() {
|
|
|
26088
26405
|
const originalWrite = process.stderr.write.bind(process.stderr);
|
|
26089
26406
|
const logPath = resolveTuiStderrLogPath();
|
|
26090
26407
|
try {
|
|
26091
|
-
|
|
26408
|
+
mkdirSync6(dirname9(logPath), { recursive: true });
|
|
26092
26409
|
} catch {
|
|
26093
26410
|
}
|
|
26094
26411
|
(0, import_mixdog_debug.rotateBoundedLog)(logPath, import_mixdog_debug.PLUGIN_LOG_MAX_BYTES, import_mixdog_debug.PLUGIN_LOG_KEEP_BYTES);
|
|
@@ -26133,6 +26450,23 @@ function installTuiStderrGuard() {
|
|
|
26133
26450
|
}
|
|
26134
26451
|
};
|
|
26135
26452
|
}
|
|
26453
|
+
function installTuiConsoleGuard() {
|
|
26454
|
+
const methods = ["log", "info", "warn", "error", "debug", "trace"];
|
|
26455
|
+
const original = /* @__PURE__ */ new Map();
|
|
26456
|
+
for (const m of methods) {
|
|
26457
|
+
original.set(m, console[m]);
|
|
26458
|
+
console[m] = (...args) => {
|
|
26459
|
+
try {
|
|
26460
|
+
process.stderr.write(`[console.${m}] ${format(...args)}
|
|
26461
|
+
`);
|
|
26462
|
+
} catch {
|
|
26463
|
+
}
|
|
26464
|
+
};
|
|
26465
|
+
}
|
|
26466
|
+
return () => {
|
|
26467
|
+
for (const [m, fn] of original) console[m] = fn;
|
|
26468
|
+
};
|
|
26469
|
+
}
|
|
26136
26470
|
async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {}) {
|
|
26137
26471
|
const startedAt = performance3.now();
|
|
26138
26472
|
bootProfile2("run:start", { provider, model, toolMode, remote });
|
|
@@ -26143,6 +26477,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26143
26477
|
return 1;
|
|
26144
26478
|
}
|
|
26145
26479
|
const restoreStderr = installTuiStderrGuard();
|
|
26480
|
+
const restoreConsole = installTuiConsoleGuard();
|
|
26146
26481
|
const stopPerfProbe = installTuiPerfProbe();
|
|
26147
26482
|
const stopLoopProbe = installTuiLoopProbe();
|
|
26148
26483
|
const restorePrimedInput = () => drainStdin(process.stdin);
|
|
@@ -26183,6 +26518,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26183
26518
|
process.off("exit", restoreTerminal);
|
|
26184
26519
|
} catch {
|
|
26185
26520
|
}
|
|
26521
|
+
restoreConsole();
|
|
26186
26522
|
restoreStderr();
|
|
26187
26523
|
process.stderr.write(`mixdog: ${error?.message || error}
|
|
26188
26524
|
`);
|
|
@@ -26208,20 +26544,10 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26208
26544
|
afterCleanup: (reason) => {
|
|
26209
26545
|
restoreTerminal();
|
|
26210
26546
|
dumpActiveHandles(`after-${reason}`);
|
|
26547
|
+
restoreConsole();
|
|
26211
26548
|
restoreStderr();
|
|
26212
26549
|
}
|
|
26213
26550
|
});
|
|
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
26551
|
const STDIO_DEATH_FATAL_CODES = /* @__PURE__ */ new Set(["EPIPE", "ERR_STREAM_DESTROYED", "EIO"]);
|
|
26226
26552
|
const stdioDeathListeners = [];
|
|
26227
26553
|
const registerStdioDeath = (stream, event, { requireCode = false } = {}) => {
|
|
@@ -26245,7 +26571,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26245
26571
|
registerStdioDeath(process.stderr, "error", { requireCode: true });
|
|
26246
26572
|
registerStdioDeath(process.stderr, "close");
|
|
26247
26573
|
try {
|
|
26248
|
-
const instance = render(/* @__PURE__ */ jsx21(App, { store, forceOnboarding: forceOnboarding === true }), { exitOnCtrlC: false, maxFps: 60, incrementalRendering: true, onRender: makeRenderProfiler() });
|
|
26574
|
+
const instance = render(/* @__PURE__ */ jsx21(App, { store, forceOnboarding: forceOnboarding === true }), { exitOnCtrlC: false, maxFps: 60, incrementalRendering: true, patchConsole: false, onRender: makeRenderProfiler() });
|
|
26249
26575
|
bootProfile2("render:mounted", { ms: (performance3.now() - startedAt).toFixed(1) });
|
|
26250
26576
|
const { waitUntilExit } = instance;
|
|
26251
26577
|
if (mouseTracking && typeof instance.setSelection === "function") {
|
|
@@ -26263,11 +26589,13 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26263
26589
|
if (mouseTracking && typeof instance.getSelectionRows === "function") {
|
|
26264
26590
|
store.getRenderSelectionRows = instance.getSelectionRows;
|
|
26265
26591
|
}
|
|
26592
|
+
if (mouseTracking && typeof instance.forceFullRepaint === "function") {
|
|
26593
|
+
store.forceRenderRepaint = instance.forceFullRepaint;
|
|
26594
|
+
}
|
|
26266
26595
|
await waitUntilExit();
|
|
26267
26596
|
} finally {
|
|
26268
26597
|
stopPerfProbe();
|
|
26269
26598
|
stopLoopProbe();
|
|
26270
|
-
clearInterval(uiHeartbeatTimer);
|
|
26271
26599
|
for (const [stream, event, handler] of stdioDeathListeners.splice(0)) {
|
|
26272
26600
|
try {
|
|
26273
26601
|
stream.off(event, handler);
|
|
@@ -26282,6 +26610,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26282
26610
|
}
|
|
26283
26611
|
restoreTerminal();
|
|
26284
26612
|
dumpActiveHandles("after-restore");
|
|
26613
|
+
restoreConsole();
|
|
26285
26614
|
restoreStderr();
|
|
26286
26615
|
}
|
|
26287
26616
|
scheduleHardExit(0);
|