mixdog 0.9.21 → 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/channel-daemon-smoke.mjs +165 -0
- package/scripts/channel-daemon-stub.mjs +69 -0
- package/scripts/statusline-quota-hysteresis-test.mjs +37 -0
- package/scripts/tool-smoke.mjs +30 -17
- 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/bash-session.mjs +10 -10
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +8 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +14 -1
- 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/patch/orchestrator.mjs +23 -13
- 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 +44 -10
- 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 +653 -310
- 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;
|
|
@@ -13927,8 +14193,21 @@ function _reclaimGuardMatchesToken(guardPath, token) {
|
|
|
13927
14193
|
return false;
|
|
13928
14194
|
}
|
|
13929
14195
|
}
|
|
13930
|
-
function
|
|
13931
|
-
|
|
14196
|
+
function _readGuardContent(guardPath) {
|
|
14197
|
+
try {
|
|
14198
|
+
return readFileSync2(guardPath, "utf8");
|
|
14199
|
+
} catch {
|
|
14200
|
+
return null;
|
|
14201
|
+
}
|
|
14202
|
+
}
|
|
14203
|
+
function _parseStampPid(raw) {
|
|
14204
|
+
if (raw === null) return null;
|
|
14205
|
+
const tok = String(raw).trim().split(/\s+/)[0];
|
|
14206
|
+
const pid = Number.parseInt(tok, 10);
|
|
14207
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
14208
|
+
}
|
|
14209
|
+
function _unlinkReclaimGuardIfContentMatches(guardPath, content) {
|
|
14210
|
+
if (_readGuardContent(guardPath) !== content) return false;
|
|
13932
14211
|
try {
|
|
13933
14212
|
unlinkSync2(guardPath);
|
|
13934
14213
|
return true;
|
|
@@ -13946,7 +14225,8 @@ function _unlinkUnreadableStaleReclaimGuard(guardPath, staleMs) {
|
|
|
13946
14225
|
}
|
|
13947
14226
|
}
|
|
13948
14227
|
function _tryClearStaleReclaimGuard(guardPath, staleMs) {
|
|
13949
|
-
const
|
|
14228
|
+
const guardContent = _readGuardContent(guardPath);
|
|
14229
|
+
const guardPid = _parseStampPid(guardContent);
|
|
13950
14230
|
if (guardPid !== null) {
|
|
13951
14231
|
let guardStale = false;
|
|
13952
14232
|
try {
|
|
@@ -13954,7 +14234,7 @@ function _tryClearStaleReclaimGuard(guardPath, staleMs) {
|
|
|
13954
14234
|
} catch {
|
|
13955
14235
|
}
|
|
13956
14236
|
if (_pidIsDead(guardPid) || guardStale) {
|
|
13957
|
-
|
|
14237
|
+
_unlinkReclaimGuardIfContentMatches(guardPath, guardContent);
|
|
13958
14238
|
}
|
|
13959
14239
|
return;
|
|
13960
14240
|
}
|
|
@@ -13992,8 +14272,8 @@ function _releaseReclaimGuard(reclaim) {
|
|
|
13992
14272
|
}
|
|
13993
14273
|
}
|
|
13994
14274
|
function _asyncSleep(ms) {
|
|
13995
|
-
return new Promise((
|
|
13996
|
-
setTimeout(
|
|
14275
|
+
return new Promise((resolve5) => {
|
|
14276
|
+
setTimeout(resolve5, Math.max(1, Number(ms) || 1));
|
|
13997
14277
|
});
|
|
13998
14278
|
}
|
|
13999
14279
|
async function withFileLock(lockPath, fn, opts = {}) {
|
|
@@ -14011,7 +14291,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14011
14291
|
lastErr = err;
|
|
14012
14292
|
if (!LOCK_WAIT_CODES.has(err?.code)) throw err;
|
|
14013
14293
|
if (timeoutMs <= 0) {
|
|
14014
|
-
const contErr = new Error(`atomic lock contended (try-once): ${lockPath}`);
|
|
14294
|
+
const contErr = new Error(`atomic lock contended (try-once): ${lockPath} [${_describeLockHolder(lockPath)}]`);
|
|
14015
14295
|
contErr.code = "ELOCKCONTENDED";
|
|
14016
14296
|
contErr.cause = err;
|
|
14017
14297
|
throw contErr;
|
|
@@ -14046,7 +14326,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
|
|
|
14046
14326
|
}
|
|
14047
14327
|
}
|
|
14048
14328
|
}
|
|
14049
|
-
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)}]`);
|
|
14050
14330
|
timeoutErr.code = "ELOCKTIMEOUT";
|
|
14051
14331
|
timeoutErr.cause = lastErr;
|
|
14052
14332
|
throw timeoutErr;
|
|
@@ -14196,21 +14476,6 @@ function writeFileAtomicSync(filePath, data, opts = {}) {
|
|
|
14196
14476
|
function writeJsonAtomicSync(filePath, value, opts = {}) {
|
|
14197
14477
|
return writeFileAtomicSync(filePath, JSON.stringify(value, null, opts.compact ? 0 : 2) + "\n", opts);
|
|
14198
14478
|
}
|
|
14199
|
-
function updateJsonAtomicSync(filePath, mutator, opts = {}) {
|
|
14200
|
-
const { lock: _lock, ...writeOpts } = opts;
|
|
14201
|
-
return withFileLockSync(`${filePath}.lock`, () => {
|
|
14202
|
-
let cur = null;
|
|
14203
|
-
try {
|
|
14204
|
-
cur = JSON.parse(readFileSync2(filePath, "utf8"));
|
|
14205
|
-
} catch {
|
|
14206
|
-
cur = null;
|
|
14207
|
-
}
|
|
14208
|
-
const next = mutator(cur);
|
|
14209
|
-
if (next === void 0) return cur;
|
|
14210
|
-
writeJsonAtomicSync(filePath, next, { ...writeOpts, lock: false });
|
|
14211
|
-
return next;
|
|
14212
|
-
}, opts);
|
|
14213
|
-
}
|
|
14214
14479
|
async function updateJsonAtomic(filePath, mutator, opts = {}) {
|
|
14215
14480
|
const { lock: _lock, ...writeOpts } = opts;
|
|
14216
14481
|
return withFileLock(`${filePath}.lock`, () => {
|
|
@@ -15160,7 +15425,7 @@ function createModelPicker({
|
|
|
15160
15425
|
items: [],
|
|
15161
15426
|
onCancel: cancelModelPicker
|
|
15162
15427
|
});
|
|
15163
|
-
await new Promise((
|
|
15428
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
15164
15429
|
try {
|
|
15165
15430
|
if (options.refreshModels !== true && options.cacheRef !== "search") {
|
|
15166
15431
|
refreshModelsPromise = Promise.resolve(loadModels({ force: false }));
|
|
@@ -15486,7 +15751,7 @@ function createProviderSetupPicker({
|
|
|
15486
15751
|
}
|
|
15487
15752
|
});
|
|
15488
15753
|
try {
|
|
15489
|
-
await new Promise((
|
|
15754
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
15490
15755
|
setup = await store.getProviderSetup();
|
|
15491
15756
|
} catch (e) {
|
|
15492
15757
|
store.pushNotice(`providers failed: ${e?.message || e}`, "error");
|
|
@@ -16872,10 +17137,6 @@ function createSlashDispatch({
|
|
|
16872
17137
|
return true;
|
|
16873
17138
|
}
|
|
16874
17139
|
case "search":
|
|
16875
|
-
if (state.busy) {
|
|
16876
|
-
store.pushNotice("wait for the current turn to finish before /search", "warn");
|
|
16877
|
-
return false;
|
|
16878
|
-
}
|
|
16879
17140
|
if (arg) store.pushNotice("/search sets the search provider/model; the search tool uses that model when called.", "warn");
|
|
16880
17141
|
openSearchPicker();
|
|
16881
17142
|
return true;
|
|
@@ -17276,57 +17537,64 @@ function usePromptHandlers({
|
|
|
17276
17537
|
const handlePromptPaste = useCallback5((text, meta = {}) => {
|
|
17277
17538
|
const source = String(meta?.source || "paste");
|
|
17278
17539
|
const value = String(text ?? "");
|
|
17279
|
-
|
|
17280
|
-
|
|
17281
|
-
|
|
17282
|
-
|
|
17283
|
-
|
|
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;
|
|
17284
17558
|
}
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
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
|
+
}
|
|
17574
|
+
}
|
|
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
|
+
});
|
|
17288
17592
|
}).catch((e) => {
|
|
17289
|
-
showPromptHint(`
|
|
17593
|
+
showPromptHint(`paste failed: ${e?.message || e}`, "warn");
|
|
17290
17594
|
return false;
|
|
17291
17595
|
});
|
|
17292
17596
|
}
|
|
17293
|
-
|
|
17294
|
-
const hasImagePath = chunks.some((chunk) => chunk.imagePath);
|
|
17295
|
-
if (!hasImagePath) {
|
|
17296
|
-
if (shouldFoldPastedText(value)) return registerPastedText(value);
|
|
17297
|
-
return void 0;
|
|
17298
|
-
}
|
|
17299
|
-
return Promise.all(chunks.map(async (chunk) => {
|
|
17300
|
-
if (!chunk.imagePath) return chunk.text;
|
|
17301
|
-
try {
|
|
17302
|
-
const image = await readImageAttachmentFromPath(chunk.text, state.cwd || process.cwd());
|
|
17303
|
-
if (!image) return chunk.text;
|
|
17304
|
-
const ref = registerPastedImage(image);
|
|
17305
|
-
showPromptHint(`attached ${image.filename || "image"}`, "plain");
|
|
17306
|
-
return ref;
|
|
17307
|
-
} catch (e) {
|
|
17308
|
-
showPromptHint(`image attach failed: ${e?.message || e}`, "warn");
|
|
17309
|
-
return chunk.text;
|
|
17310
|
-
}
|
|
17311
|
-
})).then((parts) => {
|
|
17312
|
-
let out = "";
|
|
17313
|
-
let run = "";
|
|
17314
|
-
const flushRun = () => {
|
|
17315
|
-
if (!run) return;
|
|
17316
|
-
out += shouldFoldPastedText(run) ? registerPastedText(run) : run;
|
|
17317
|
-
run = "";
|
|
17318
|
-
};
|
|
17319
|
-
for (let i = 0; i < chunks.length; i += 1) {
|
|
17320
|
-
if (chunks[i].imagePath) {
|
|
17321
|
-
flushRun();
|
|
17322
|
-
out += parts[i];
|
|
17323
|
-
} else {
|
|
17324
|
-
run += parts[i];
|
|
17325
|
-
}
|
|
17326
|
-
}
|
|
17327
|
-
flushRun();
|
|
17328
|
-
return out;
|
|
17329
|
-
});
|
|
17597
|
+
return processText(value);
|
|
17330
17598
|
}, [registerPastedImage, registerPastedText, showPromptHint, state.cwd]);
|
|
17331
17599
|
const handlePromptHistoryNavigate = useCallback5((direction, currentText = "", meta = {}) => {
|
|
17332
17600
|
const currentValue = String(currentText || "");
|
|
@@ -17675,7 +17943,7 @@ var AssistantMessage = React14.memo(function AssistantMessage2({
|
|
|
17675
17943
|
});
|
|
17676
17944
|
var UserMessage = React14.memo(function UserMessage2({ text, attached = false, columns, themeEpoch = 0 }) {
|
|
17677
17945
|
const bandColumns = Math.max(1, columns - 1);
|
|
17678
|
-
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 }) });
|
|
17679
17947
|
});
|
|
17680
17948
|
function NoticeMessage({ text, tone, columns = 80 }) {
|
|
17681
17949
|
const accentColor = tone === "error" ? theme.error : tone === "warn" ? theme.warning : theme.inactive;
|
|
@@ -18364,13 +18632,13 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
|
|
|
18364
18632
|
else if (isShellSurface) labelText = shellHeader(shellStatus, displayGroupCount);
|
|
18365
18633
|
else labelText = (isAgentTool(normalizedName) ? agentActionTitle(parsedArgs) : "") || statusCopy(name, label, displayGroupCount, doneCount, headerPending, isError, parsedArgs);
|
|
18366
18634
|
labelText = safeInlineText(labelText);
|
|
18367
|
-
const toolSearchSummary = !pending && normalizedName === "
|
|
18635
|
+
const toolSearchSummary = !pending && normalizedName === "load_tool" && hasResult ? toolSearchLoadedSummary(displayedResultText) : "";
|
|
18368
18636
|
const rawSummaryText = safeInlineText(isAgentResponse || isBackgroundResponse ? "" : toolSearchSummary || (isAgentTool(normalizedName) ? agentActionSummary(parsedArgs, summary) : summary));
|
|
18369
18637
|
const summaryIsHeaderCount = rawSummaryText && /^\d+\s+\S+$/.test(rawSummaryText) && labelText.endsWith(rawSummaryText);
|
|
18370
18638
|
const summaryText = summaryIsHeaderCount ? "" : rawSummaryText;
|
|
18371
18639
|
const agentHasExpandableBody = isAgentSurfaceCard && !pending && hasResult && (isAgentResponse || totalLines > 1 || firstResultLineClipped);
|
|
18372
18640
|
const shellHasExpandableBody = isShellSurface && !pending && hasDisplayResult && hasDisplayBody && (totalLines > 1 || firstResultLineClipped || Boolean(shellCollapsedSummary && shellCollapsedSummary !== firstResultLine));
|
|
18373
|
-
const showHeaderExpandHint = (isShellSurface ? shellHasExpandableBody : isAgentSurfaceCard ? agentHasExpandableBody : hasHiddenDetail || backgroundMetadataExpandable) && normalizedName !== "
|
|
18641
|
+
const showHeaderExpandHint = (isShellSurface ? shellHasExpandableBody : isAgentSurfaceCard ? agentHasExpandableBody : hasHiddenDetail || backgroundMetadataExpandable) && normalizedName !== "load_tool";
|
|
18374
18642
|
const expandHintColor = theme.subtle;
|
|
18375
18643
|
const gutter = 2;
|
|
18376
18644
|
const hintLabel = showHeaderExpandHint ? `ctrl+o ${expanded ? "collapse" : "expand"}` : "";
|
|
@@ -18602,7 +18870,9 @@ var PANEL_LAYOUT_SIG = {
|
|
|
18602
18870
|
TEXT: 5,
|
|
18603
18871
|
// Prompt-wrap/meta row counts (trailing churn tokens, see token order note
|
|
18604
18872
|
// below). PROMPT_META is the 2-row live-spinner band slot.
|
|
18605
|
-
PROMPT_META: 9
|
|
18873
|
+
PROMPT_META: 9,
|
|
18874
|
+
// Queued steering band rows (full wrapped height, see queuedBandRows).
|
|
18875
|
+
QUEUED: 10
|
|
18606
18876
|
};
|
|
18607
18877
|
var PROJECT_TEXT_ENTRY_KINDS = /* @__PURE__ */ new Set(["project-new", "project-create-confirm", "project-rename"]);
|
|
18608
18878
|
var CORE_MULTILINE_TEXT_ENTRY_KINDS = /* @__PURE__ */ new Set(["core-add", "core-edit"]);
|
|
@@ -18664,7 +18934,9 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18664
18934
|
const projectPickerRef = useRef9(null);
|
|
18665
18935
|
const buildProjectPickerState = (opts) => projectPickerRef.current.buildProjectPickerState(opts);
|
|
18666
18936
|
const [picker, setPickerState] = useState8(null);
|
|
18937
|
+
const livePickerRef = useRef9(null);
|
|
18667
18938
|
const setPicker = useCallback6((next) => {
|
|
18939
|
+
livePickerRef.current = typeof next === "function" ? next(livePickerRef.current) : next;
|
|
18668
18940
|
setPickerState((prev) => {
|
|
18669
18941
|
const resolved = typeof next === "function" ? next(prev) : next;
|
|
18670
18942
|
if (resolved && typeof resolved === "object" && pickerOpenedFromEnterRef.current) {
|
|
@@ -18675,9 +18947,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18675
18947
|
}
|
|
18676
18948
|
return resolved.indexMode ? resolved : { ...resolved, indexMode: "always" };
|
|
18677
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
|
+
}
|
|
18678
18953
|
return resolved;
|
|
18679
18954
|
});
|
|
18680
18955
|
}, []);
|
|
18956
|
+
livePickerRef.current = picker;
|
|
18681
18957
|
const [contextPanel, setContextPanel] = useState8(null);
|
|
18682
18958
|
const [usagePanel, setUsagePanel] = useState8(null);
|
|
18683
18959
|
const usageRequestRef = useRef9(0);
|
|
@@ -18719,7 +18995,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18719
18995
|
} catch {
|
|
18720
18996
|
}
|
|
18721
18997
|
if (!onboardingOwnsScreen && state.items.length === 0) {
|
|
18722
|
-
|
|
18998
|
+
setPicker(projectPicker.buildProjectPickerState({ initialEntry: true }));
|
|
18723
18999
|
}
|
|
18724
19000
|
}
|
|
18725
19001
|
const {
|
|
@@ -18790,6 +19066,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
18790
19066
|
clean,
|
|
18791
19067
|
copyToClipboard,
|
|
18792
19068
|
setPicker,
|
|
19069
|
+
getPicker: () => livePickerRef.current,
|
|
18793
19070
|
setProviderPrompt,
|
|
18794
19071
|
setChannelPrompt,
|
|
18795
19072
|
setHookPrompt,
|
|
@@ -19292,13 +19569,16 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19292
19569
|
const renderText = store.getRenderSelectionText?.();
|
|
19293
19570
|
const remembered = selectionTextRef.current || "";
|
|
19294
19571
|
let text = renderText == null ? remembered : remembered.length > renderText.length ? remembered : renderText;
|
|
19295
|
-
const stitched = getStitchedSelectionText?.() || "";
|
|
19296
|
-
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;
|
|
19297
19574
|
if ((!text || !text.trim()) && attempt < 4) {
|
|
19298
19575
|
setTimeout(() => copySelection(attempt + 1), attempt === 0 ? 0 : 24);
|
|
19299
19576
|
return;
|
|
19300
19577
|
}
|
|
19301
|
-
if (!text || !text.trim())
|
|
19578
|
+
if (!text || !text.trim()) {
|
|
19579
|
+
showSelectionCopyHint("nothing to copy \xB7 select text first", "error");
|
|
19580
|
+
return;
|
|
19581
|
+
}
|
|
19302
19582
|
selectionTextRef.current = text;
|
|
19303
19583
|
copyToClipboard(text).then(() => {
|
|
19304
19584
|
const lines = text.split("\n").length;
|
|
@@ -19374,8 +19654,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
19374
19654
|
let timer = null;
|
|
19375
19655
|
Promise.race([
|
|
19376
19656
|
Promise.resolve(store.dispose?.("cli-react-exit", { detach: true })),
|
|
19377
|
-
new Promise((
|
|
19378
|
-
timer = setTimeout(
|
|
19657
|
+
new Promise((resolve5) => {
|
|
19658
|
+
timer = setTimeout(resolve5, 350);
|
|
19379
19659
|
})
|
|
19380
19660
|
]).finally(() => {
|
|
19381
19661
|
if (timer) clearTimeout(timer);
|
|
@@ -20481,7 +20761,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20481
20761
|
const promptMetaRows = promptMetaVisible ? 2 : 0;
|
|
20482
20762
|
const overlayHintRequested = !inputBoxHidden && !hasFloatingPanel && !liveSpinner && !!inputHint && !queuedVisible;
|
|
20483
20763
|
const overlayHintRows = 0;
|
|
20484
|
-
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;
|
|
20485
20771
|
const INPUT_BOX_ROWS = promptBoxRows + promptMetaRows + overlayHintRows;
|
|
20486
20772
|
const baseReserve = WELCOME_ROWS + SCROLL_HINT_ROWS + LIVE_STATUS_ROWS + INPUT_BOX_ROWS + STATUSLINE_ROWS + queuedRows;
|
|
20487
20773
|
const maxFloatingPanelRows = Math.max(0, resizeState.rows - baseReserve - 1);
|
|
@@ -20516,10 +20802,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20516
20802
|
const nextMetaRows = Number(String(panelLayoutSignature).split("|")[PANEL_LAYOUT_SIG.PROMPT_META]) || 0;
|
|
20517
20803
|
const doneTailAppendedThisCommit = latestDoneAtTail && (latestTranscriptItem?.id ?? null) !== panelTransition.tailId;
|
|
20518
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;
|
|
20519
20809
|
const instantPanelClose = panelShrinkRows > 0 && (promptRowsOnlyChange || isInstantPanelCloseTransition(panelTransition.signature, panelLayoutSignature, initialProjectEntryClose));
|
|
20520
20810
|
const slashOpenOnEmptyTranscript = initialProjectEntryClose && !panelSignatureFlags(panelTransition.signature).slash && panelSignatureFlags(panelLayoutSignature).slash;
|
|
20521
20811
|
if (instantPanelClose) {
|
|
20522
|
-
panelCloseInkMaskRowsRef.current = Math.max(0, panelShrinkRows - spinnerMetaCollapseRows);
|
|
20812
|
+
panelCloseInkMaskRowsRef.current = Math.max(0, panelShrinkRows - spinnerMetaCollapseRows - queuedPromoteCollapseRows);
|
|
20523
20813
|
panelTransition.clearRows = 0;
|
|
20524
20814
|
panelTransition.guardRows = 0;
|
|
20525
20815
|
panelTransition.epoch = panelTransitionEpoch;
|
|
@@ -20548,8 +20838,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20548
20838
|
const viewportHeight = Math.max(1, resizeState.rows - bottomReserve);
|
|
20549
20839
|
const guardCapacityRows = Math.max(0, viewportHeight - 1);
|
|
20550
20840
|
const baseGuardRows = guardCapacityRows > 0 ? 1 : 0;
|
|
20551
|
-
const
|
|
20552
|
-
const scrollGuardRows = !scrollGuardNearBottom && scrollOffset > 0 && guardCapacityRows > baseGuardRows ? 1 : 0;
|
|
20841
|
+
const scrollGuardRows = 0;
|
|
20553
20842
|
const transcriptGuardRows = Math.min(guardCapacityRows, baseGuardRows + panelTransitionGuardRows + scrollGuardRows);
|
|
20554
20843
|
const welcomePromptHintText = conditionalWelcomePromptHint || welcomePromptHintRef.current || "";
|
|
20555
20844
|
const welcomePromptHintVisible = Boolean(
|
|
@@ -20557,13 +20846,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20557
20846
|
);
|
|
20558
20847
|
const welcomePromptHintRows = welcomePromptHintVisible && viewportHeight - transcriptGuardRows >= 2 ? 1 : 0;
|
|
20559
20848
|
welcomePromptHintVisibleRef.current = welcomePromptHintRows > 0;
|
|
20849
|
+
const overlayHintBandRows = overlayHintRequested && state.items.length === 0 && viewportHeight - transcriptGuardRows - welcomePromptHintRows >= 2 ? 1 : 0;
|
|
20560
20850
|
const panelCloseMaskRows = Math.min(
|
|
20561
20851
|
panelCloseInkMaskRows,
|
|
20562
|
-
Math.max(0, viewportHeight - transcriptGuardRows - welcomePromptHintRows - 1)
|
|
20852
|
+
Math.max(0, viewportHeight - transcriptGuardRows - welcomePromptHintRows - overlayHintBandRows - 1)
|
|
20563
20853
|
);
|
|
20564
20854
|
const transcriptContentHeight = Math.max(
|
|
20565
20855
|
1,
|
|
20566
|
-
viewportHeight - transcriptGuardRows - panelCloseMaskRows - welcomePromptHintRows
|
|
20856
|
+
viewportHeight - transcriptGuardRows - panelCloseMaskRows - welcomePromptHintRows - overlayHintBandRows
|
|
20567
20857
|
);
|
|
20568
20858
|
const transcriptBottomSlackRows = Math.max(0, baseGuardRows);
|
|
20569
20859
|
transcriptBottomSlackRowsRef.current = transcriptBottomSlackRows;
|
|
@@ -20785,9 +21075,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20785
21075
|
backgroundColor: surfaceBackground()
|
|
20786
21076
|
}
|
|
20787
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,
|
|
20788
21082
|
transcriptGuardRows > 0 ? /* @__PURE__ */ jsxs16(Box18, { height: transcriptGuardRows, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
|
|
20789
21083
|
/* @__PURE__ */ jsx20(Box18, { flexGrow: 1, flexShrink: 1, overflow: "hidden" }),
|
|
20790
|
-
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
|
|
20791
21085
|
] }) : null
|
|
20792
21086
|
]
|
|
20793
21087
|
}
|
|
@@ -20986,7 +21280,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
20986
21280
|
),
|
|
20987
21281
|
/* @__PURE__ */ jsx20(Box18, { height: 1, width: "100%", backgroundColor: surfaceBackground() })
|
|
20988
21282
|
] }) : null,
|
|
20989
|
-
queuedVisible ? /* @__PURE__ */ jsx20(QueuedCommands, { queued: state.queued, columns: frameColumns }) : null,
|
|
21283
|
+
queuedVisible ? /* @__PURE__ */ jsx20(QueuedCommands, { queued: state.queued, columns: frameColumns, compact: queuedCompact }) : null,
|
|
20990
21284
|
/* @__PURE__ */ jsx20(
|
|
20991
21285
|
Box18,
|
|
20992
21286
|
{
|
|
@@ -21174,6 +21468,33 @@ function modelVisibleToolCompletionMessage(text, meta = {}) {
|
|
|
21174
21468
|
].join("\n");
|
|
21175
21469
|
}
|
|
21176
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
|
+
|
|
21177
21498
|
// src/tui/engine/boot-profile.mjs
|
|
21178
21499
|
import { performance } from "node:perf_hooks";
|
|
21179
21500
|
var BOOT_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ""));
|
|
@@ -21959,8 +22280,8 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
21959
22280
|
|
|
21960
22281
|
// src/tui/engine/render-timing.mjs
|
|
21961
22282
|
var RENDER_THROTTLE_FLUSH_MS = 12;
|
|
21962
|
-
var yieldToRenderer = () => new Promise((
|
|
21963
|
-
setTimeout(
|
|
22283
|
+
var yieldToRenderer = () => new Promise((resolve5) => {
|
|
22284
|
+
setTimeout(resolve5, RENDER_THROTTLE_FLUSH_MS);
|
|
21964
22285
|
});
|
|
21965
22286
|
|
|
21966
22287
|
// src/tui/engine/tool-result-status.mjs
|
|
@@ -22138,9 +22459,9 @@ function createToolApproval({ getState, set, nextId: nextId2, getDisposed, timeo
|
|
|
22138
22459
|
}
|
|
22139
22460
|
function requestToolApproval(input = {}) {
|
|
22140
22461
|
if (getDisposed()) return Promise.resolve({ approved: false, reason: "runtime disposed" });
|
|
22141
|
-
return new Promise((
|
|
22462
|
+
return new Promise((resolve5) => {
|
|
22142
22463
|
const id = nextId2();
|
|
22143
|
-
toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve:
|
|
22464
|
+
toolApprovalQueue.push({ id, request: normalizeToolApprovalRequest(input, id), resolve: resolve5, timer: null });
|
|
22144
22465
|
presentNextToolApproval();
|
|
22145
22466
|
});
|
|
22146
22467
|
}
|
|
@@ -23002,7 +23323,7 @@ function createSessionFlow(bag) {
|
|
|
23002
23323
|
const startedAt = Date.now();
|
|
23003
23324
|
set({ commandBusy: true, commandStatus: { active: true, verb, startedAt, mode: "auto-clear" } });
|
|
23004
23325
|
try {
|
|
23005
|
-
await new Promise((
|
|
23326
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
23006
23327
|
let compactType = null;
|
|
23007
23328
|
if (useCompaction) {
|
|
23008
23329
|
const compaction = runtime.getCompactionSettings();
|
|
@@ -24515,6 +24836,26 @@ import { readFileSync as readFileSync6 } from "node:fs";
|
|
|
24515
24836
|
import { dirname as dirname8, join as join9 } from "node:path";
|
|
24516
24837
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
24517
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
|
+
}
|
|
24518
24859
|
function readPackageJson() {
|
|
24519
24860
|
try {
|
|
24520
24861
|
const dir = dirname8(fileURLToPath3(import.meta.url));
|
|
@@ -24648,6 +24989,31 @@ async function buildDoctorReport(runtime = {}, getState = () => ({})) {
|
|
|
24648
24989
|
const enabled = h.enabled === true;
|
|
24649
24990
|
row("ok", `${enabled ? "enabled" : "disabled"} \xB7 ${events.length} event${events.length === 1 ? "" : "s"}`);
|
|
24650
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
|
+
}
|
|
24651
25017
|
return ["mixdog doctor \u2014 installation health", ...rows].join("\n");
|
|
24652
25018
|
}
|
|
24653
25019
|
|
|
@@ -24934,17 +25300,18 @@ function createEngineApiA(bag) {
|
|
|
24934
25300
|
}
|
|
24935
25301
|
},
|
|
24936
25302
|
setMcpServerEnabled: async (name, enabled) => {
|
|
24937
|
-
|
|
24938
|
-
|
|
24939
|
-
try {
|
|
24940
|
-
const status = await runtime.setMcpServerEnabled?.(name, enabled);
|
|
25303
|
+
const status = await runtime.setMcpServerEnabled?.(name, enabled);
|
|
25304
|
+
setImmediate(() => {
|
|
24941
25305
|
resetStatsAndSyncContext();
|
|
24942
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 {
|
|
24943
25312
|
pushNotice(`mcp ${enabled ? "enabled" : "disabled"}: ${name}`, "info");
|
|
24944
|
-
return status;
|
|
24945
|
-
} finally {
|
|
24946
|
-
set({ commandBusy: false });
|
|
24947
25313
|
}
|
|
25314
|
+
return status;
|
|
24948
25315
|
},
|
|
24949
25316
|
getDisabledSkills: () => runtime.getDisabledSkills?.() || { disabled: [] },
|
|
24950
25317
|
setDisabledSkills: (disabled) => runtime.setDisabledSkills?.(disabled) || { disabled: [] },
|
|
@@ -25098,7 +25465,7 @@ function createEngineApiA(bag) {
|
|
|
25098
25465
|
const startedAt = Date.now();
|
|
25099
25466
|
set({ commandBusy: true, commandStatus: { active: true, verb: "Running diagnostics", startedAt, mode: "doctor" } });
|
|
25100
25467
|
try {
|
|
25101
|
-
await new Promise((
|
|
25468
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
25102
25469
|
const report = await buildDoctorReport(runtime, getState);
|
|
25103
25470
|
pushNotice(report, "info");
|
|
25104
25471
|
return report;
|
|
@@ -25118,7 +25485,7 @@ function createEngineApiA(bag) {
|
|
|
25118
25485
|
const startedAt = Date.now();
|
|
25119
25486
|
set({ commandBusy: true, commandStatus: { active: true, verb: "Compacting conversation", startedAt, mode: "compacting" } });
|
|
25120
25487
|
try {
|
|
25121
|
-
await new Promise((
|
|
25488
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
25122
25489
|
const result = await runtime.compact({ recoverAgent: true });
|
|
25123
25490
|
syncContextStats({ allowEstimated: true });
|
|
25124
25491
|
set({ ...routeState(), stats: { ...getState().stats } });
|
|
@@ -25412,6 +25779,10 @@ async function createEngineSession({
|
|
|
25412
25779
|
const pushUserOrSyntheticItem = (text, id = nextId(), origin = "user") => {
|
|
25413
25780
|
if (origin === "injected" && isLikelyToolCompletionWrapper(text)) return;
|
|
25414
25781
|
if (isModelVisibleToolCompletionWrapper(text)) return;
|
|
25782
|
+
if (isLateToolAnnouncement(text)) {
|
|
25783
|
+
pushItem({ kind: "notice", id, text: summarizeLateToolAnnouncement(text), tone: "info" });
|
|
25784
|
+
return;
|
|
25785
|
+
}
|
|
25415
25786
|
if (upsertSyntheticToolItem(text, id)) return;
|
|
25416
25787
|
pushItem({ kind: "user", id, text });
|
|
25417
25788
|
};
|
|
@@ -25610,7 +25981,7 @@ function signalExitCode(signal, fallback = 1) {
|
|
|
25610
25981
|
}
|
|
25611
25982
|
function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
25612
25983
|
const ms = Math.max(1, Math.floor(Number(timeoutMs) || 1));
|
|
25613
|
-
return new Promise((
|
|
25984
|
+
return new Promise((resolve5, reject) => {
|
|
25614
25985
|
let settled = false;
|
|
25615
25986
|
const timer = setTimeout(() => {
|
|
25616
25987
|
if (settled) return;
|
|
@@ -25622,7 +25993,7 @@ function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
|
|
|
25622
25993
|
if (settled) return;
|
|
25623
25994
|
settled = true;
|
|
25624
25995
|
clearTimeout(timer);
|
|
25625
|
-
|
|
25996
|
+
resolve5(value);
|
|
25626
25997
|
}).catch((error) => {
|
|
25627
25998
|
if (settled) return;
|
|
25628
25999
|
settled = true;
|
|
@@ -25736,54 +26107,14 @@ function installProcessSignalCleanup({
|
|
|
25736
26107
|
};
|
|
25737
26108
|
}
|
|
25738
26109
|
|
|
25739
|
-
// src/runtime/channels/lib/runtime-paths.mjs
|
|
25740
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
25741
|
-
import { basename as basename4, join as join10, resolve as resolve5 } from "path";
|
|
25742
|
-
|
|
25743
|
-
// src/runtime/channels/lib/state-file.mjs
|
|
25744
|
-
import { mkdirSync as mkdirSync6, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
25745
|
-
function ensureDir2(dirPath) {
|
|
25746
|
-
mkdirSync6(dirPath, { recursive: true });
|
|
25747
|
-
}
|
|
25748
|
-
|
|
25749
|
-
// src/runtime/channels/lib/runtime-paths.mjs
|
|
25750
|
-
var RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT ? resolve5(process.env.MIXDOG_RUNTIME_ROOT) : join10(tmpdir2(), "mixdog");
|
|
25751
|
-
var OWNER_DIR = join10(RUNTIME_ROOT, "owners");
|
|
25752
|
-
var ACTIVE_INSTANCE_FILE = join10(RUNTIME_ROOT, "active-instance.json");
|
|
25753
|
-
var RUNTIME_STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
25754
|
-
var STATUS_FILE_TTL = 6 * 60 * 60 * 1e3;
|
|
25755
|
-
var TMP_FILE_TTL = 60 * 60 * 1e3;
|
|
25756
|
-
function sanitize(value) {
|
|
25757
|
-
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
25758
|
-
}
|
|
25759
|
-
function ensureRuntimeDirs() {
|
|
25760
|
-
ensureDir2(RUNTIME_ROOT);
|
|
25761
|
-
ensureDir2(OWNER_DIR);
|
|
25762
|
-
}
|
|
25763
|
-
var UI_HEARTBEAT_STALE_MS = 5 * 60 * 1e3;
|
|
25764
|
-
function touchUiHeartbeat(instanceId) {
|
|
25765
|
-
ensureRuntimeDirs();
|
|
25766
|
-
try {
|
|
25767
|
-
updateJsonAtomicSync(ACTIVE_INSTANCE_FILE, (curRaw) => {
|
|
25768
|
-
if (!curRaw) return void 0;
|
|
25769
|
-
if (instanceId && curRaw.instanceId !== instanceId) return void 0;
|
|
25770
|
-
return { ...curRaw, ui_heartbeat_at: Date.now(), updatedAt: Date.now() };
|
|
25771
|
-
}, { compact: true, fsync: false, fsyncDir: false, renameFallback: "truncate", timeoutMs: 0 });
|
|
25772
|
-
} catch {
|
|
25773
|
-
}
|
|
25774
|
-
}
|
|
25775
|
-
var SERVER_PID_FILE = join10(
|
|
25776
|
-
RUNTIME_ROOT,
|
|
25777
|
-
`server-${sanitize(resolvePluginData() ?? "default")}.pid`
|
|
25778
|
-
);
|
|
25779
|
-
|
|
25780
26110
|
// src/tui/index.jsx
|
|
25781
26111
|
var import_mixdog_debug = __toESM(require_mixdog_debug(), 1);
|
|
25782
26112
|
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
25783
|
-
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";
|
|
25784
26114
|
var TERMINAL_OSC_RESET_BG = "\x1B]111\x07";
|
|
25785
26115
|
var TERMINAL_MODE_RESET_HIDDEN_CURSOR = TERMINAL_MODE_RESET.replace("\x1B[?25h", "\x1B[?25l");
|
|
25786
|
-
var
|
|
26116
|
+
var XTSHIFTESCAPE_ON = process.env.WT_SESSION ? "" : "\x1B[>1s";
|
|
26117
|
+
var MOUSE_TRACKING_ON2 = `\x1B[?1000h\x1B[?1002h\x1B[?1006h${XTSHIFTESCAPE_ON}`;
|
|
25787
26118
|
var BOOT_PROFILE_ENABLED2 = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_BOOT_PROFILE || ""));
|
|
25788
26119
|
var BOOT_PROFILE_START2 = globalThis.__mixdogBootProfileStart || (globalThis.__mixdogBootProfileStart = performance3.now());
|
|
25789
26120
|
var EXIT_WAIT_TIMEOUT_MS = positiveIntEnv2("MIXDOG_TUI_EXIT_WAIT_MS", 2500);
|
|
@@ -25972,8 +26303,8 @@ function waitWithTimeout2(promise, ms) {
|
|
|
25972
26303
|
let timer = null;
|
|
25973
26304
|
return Promise.race([
|
|
25974
26305
|
Promise.resolve(promise).then(() => true),
|
|
25975
|
-
new Promise((
|
|
25976
|
-
timer = setTimeout(() =>
|
|
26306
|
+
new Promise((resolve5) => {
|
|
26307
|
+
timer = setTimeout(() => resolve5(false), ms);
|
|
25977
26308
|
})
|
|
25978
26309
|
]).finally(() => {
|
|
25979
26310
|
if (timer) clearTimeout(timer);
|
|
@@ -25992,7 +26323,7 @@ function scheduleHardExit(code = 0) {
|
|
|
25992
26323
|
timer.unref?.();
|
|
25993
26324
|
}
|
|
25994
26325
|
function resolveTuiStderrLogPath() {
|
|
25995
|
-
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");
|
|
25996
26327
|
}
|
|
25997
26328
|
function ansiFg(rgb) {
|
|
25998
26329
|
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ""));
|
|
@@ -26074,7 +26405,7 @@ function installTuiStderrGuard() {
|
|
|
26074
26405
|
const originalWrite = process.stderr.write.bind(process.stderr);
|
|
26075
26406
|
const logPath = resolveTuiStderrLogPath();
|
|
26076
26407
|
try {
|
|
26077
|
-
|
|
26408
|
+
mkdirSync6(dirname9(logPath), { recursive: true });
|
|
26078
26409
|
} catch {
|
|
26079
26410
|
}
|
|
26080
26411
|
(0, import_mixdog_debug.rotateBoundedLog)(logPath, import_mixdog_debug.PLUGIN_LOG_MAX_BYTES, import_mixdog_debug.PLUGIN_LOG_KEEP_BYTES);
|
|
@@ -26119,6 +26450,23 @@ function installTuiStderrGuard() {
|
|
|
26119
26450
|
}
|
|
26120
26451
|
};
|
|
26121
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
|
+
}
|
|
26122
26470
|
async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {}) {
|
|
26123
26471
|
const startedAt = performance3.now();
|
|
26124
26472
|
bootProfile2("run:start", { provider, model, toolMode, remote });
|
|
@@ -26129,6 +26477,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26129
26477
|
return 1;
|
|
26130
26478
|
}
|
|
26131
26479
|
const restoreStderr = installTuiStderrGuard();
|
|
26480
|
+
const restoreConsole = installTuiConsoleGuard();
|
|
26132
26481
|
const stopPerfProbe = installTuiPerfProbe();
|
|
26133
26482
|
const stopLoopProbe = installTuiLoopProbe();
|
|
26134
26483
|
const restorePrimedInput = () => drainStdin(process.stdin);
|
|
@@ -26169,6 +26518,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26169
26518
|
process.off("exit", restoreTerminal);
|
|
26170
26519
|
} catch {
|
|
26171
26520
|
}
|
|
26521
|
+
restoreConsole();
|
|
26172
26522
|
restoreStderr();
|
|
26173
26523
|
process.stderr.write(`mixdog: ${error?.message || error}
|
|
26174
26524
|
`);
|
|
@@ -26194,20 +26544,10 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26194
26544
|
afterCleanup: (reason) => {
|
|
26195
26545
|
restoreTerminal();
|
|
26196
26546
|
dumpActiveHandles(`after-${reason}`);
|
|
26547
|
+
restoreConsole();
|
|
26197
26548
|
restoreStderr();
|
|
26198
26549
|
}
|
|
26199
26550
|
});
|
|
26200
|
-
const uiHeartbeatTimer = setInterval(() => {
|
|
26201
|
-
try {
|
|
26202
|
-
touchUiHeartbeat();
|
|
26203
|
-
} catch {
|
|
26204
|
-
}
|
|
26205
|
-
}, 3e4);
|
|
26206
|
-
if (typeof uiHeartbeatTimer.unref === "function") uiHeartbeatTimer.unref();
|
|
26207
|
-
try {
|
|
26208
|
-
touchUiHeartbeat();
|
|
26209
|
-
} catch {
|
|
26210
|
-
}
|
|
26211
26551
|
const STDIO_DEATH_FATAL_CODES = /* @__PURE__ */ new Set(["EPIPE", "ERR_STREAM_DESTROYED", "EIO"]);
|
|
26212
26552
|
const stdioDeathListeners = [];
|
|
26213
26553
|
const registerStdioDeath = (stream, event, { requireCode = false } = {}) => {
|
|
@@ -26231,7 +26571,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26231
26571
|
registerStdioDeath(process.stderr, "error", { requireCode: true });
|
|
26232
26572
|
registerStdioDeath(process.stderr, "close");
|
|
26233
26573
|
try {
|
|
26234
|
-
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() });
|
|
26235
26575
|
bootProfile2("render:mounted", { ms: (performance3.now() - startedAt).toFixed(1) });
|
|
26236
26576
|
const { waitUntilExit } = instance;
|
|
26237
26577
|
if (mouseTracking && typeof instance.setSelection === "function") {
|
|
@@ -26249,11 +26589,13 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26249
26589
|
if (mouseTracking && typeof instance.getSelectionRows === "function") {
|
|
26250
26590
|
store.getRenderSelectionRows = instance.getSelectionRows;
|
|
26251
26591
|
}
|
|
26592
|
+
if (mouseTracking && typeof instance.forceFullRepaint === "function") {
|
|
26593
|
+
store.forceRenderRepaint = instance.forceFullRepaint;
|
|
26594
|
+
}
|
|
26252
26595
|
await waitUntilExit();
|
|
26253
26596
|
} finally {
|
|
26254
26597
|
stopPerfProbe();
|
|
26255
26598
|
stopLoopProbe();
|
|
26256
|
-
clearInterval(uiHeartbeatTimer);
|
|
26257
26599
|
for (const [stream, event, handler] of stdioDeathListeners.splice(0)) {
|
|
26258
26600
|
try {
|
|
26259
26601
|
stream.off(event, handler);
|
|
@@ -26268,6 +26610,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
|
|
|
26268
26610
|
}
|
|
26269
26611
|
restoreTerminal();
|
|
26270
26612
|
dumpActiveHandles("after-restore");
|
|
26613
|
+
restoreConsole();
|
|
26271
26614
|
restoreStderr();
|
|
26272
26615
|
}
|
|
26273
26616
|
scheduleHardExit(0);
|