@xingular/harness 1.3.1-unstable.2 → 1.3.1-unstable.3
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/CHANGELOG.md +49 -0
- package/README.md +1 -1
- package/dist/cli.js +1217 -701
- package/dist/sdk/computer/screenshot.d.ts +32 -0
- package/dist/sdk/computer/screenshot.js +35 -0
- package/dist/sdk/context/attribution.d.ts +18 -0
- package/dist/sdk/context/attribution.js +295 -0
- package/dist/sdk/context/types.d.ts +94 -0
- package/dist/sdk/context/types.js +10 -0
- package/dist/sdk/index.d.ts +2 -1
- package/dist/sdk/index.js +2 -1
- package/dist/sdk/plugins/builtin.js +1 -0
- package/dist/sdk/prompt/compile.d.ts +42 -2
- package/dist/sdk/prompt/compile.js +87 -15
- package/dist/sdk/server/methods.js +3 -0
- package/dist/sdk/sessions/contracts.d.ts +4 -0
- package/dist/sdk/sessions/contracts.js +2 -0
- package/dist/sdk/sessions/runner.d.ts +10 -0
- package/dist/sdk/sessions/runner.js +17 -0
- package/dist/sdk/sessions/subagentService.d.ts +11 -1
- package/dist/sdk/sessions/subagentService.js +120 -38
- package/dist/sdk/sessions/subagents.d.ts +31 -0
- package/dist/sdk/sessions/subagents.js +45 -0
- package/dist/sdk/terminal/client.d.ts +1 -1
- package/dist/sdk/terminal/client.js +13 -6
- package/dist/sdk/terminal/control.d.ts +1 -1
- package/dist/sdk/terminal/control.js +2 -2
- package/dist/sdk/terminal/controller.d.ts +9 -0
- package/dist/sdk/terminal/controller.js +48 -11
- package/dist/sdk/terminal/piExtension.js +35 -1
- package/dist/sdk/terminal/terminals.d.ts +2 -0
- package/dist/sdk/terminal/terminals.js +11 -0
- package/dist/sdk/terminal/tui/commands.d.ts +1 -2
- package/dist/sdk/terminal/tui/commands.js +1 -4
- package/dist/sdk/terminal/tui/dialogs.d.ts +3 -2
- package/dist/sdk/terminal/tui/dialogs.js +4 -3
- package/dist/sdk/terminal/tui/keybindings.d.ts +1 -1
- package/dist/sdk/terminal/tui/keybindings.js +1 -1
- package/dist/sdk/terminal/tui/modeChoices.d.ts +3 -2
- package/dist/sdk/terminal/tui/modeChoices.js +4 -4
- package/dist/sdk/terminal/tui/mouseReport.d.ts +30 -0
- package/dist/sdk/terminal/tui/mouseReport.js +80 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +3 -2
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +1 -1
- package/dist/sdk/terminal/tui/pages/contextPage.d.ts +54 -0
- package/dist/sdk/terminal/tui/pages/contextPage.js +393 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +5 -4
- package/dist/sdk/terminal/tui/runtime.js +8 -2
- package/dist/sdk/terminal/tui/search.js +3 -2
- package/dist/sdk/terminal/tui/skillInvocation.d.ts +21 -2
- package/dist/sdk/terminal/tui/skillInvocation.js +31 -11
- package/dist/sdk/tools/defaultSkills.d.ts +19 -0
- package/dist/sdk/tools/defaultSkills.js +27 -1
- package/dist/sdk/tools/dispatch.js +8 -9
- package/dist/sdk/tools/spawnSubagent.d.ts +1 -1
- package/dist/sdk/tools/spawnSubagent.js +4 -2
- package/dist/terminal-opentui.js +570 -240
- package/dist/terminal-pi.js +142 -28
- package/docs/README.md +1 -0
- package/docs/architecture/02-sessions-and-runs.md +9 -0
- package/docs/architecture/04-prompt.md +13 -4
- package/docs/architecture/07-tools-and-gate.md +1 -1
- package/docs/architecture/11-terminal.md +12 -2
- package/docs/architecture/13-world-tools.md +17 -3
- package/docs/architecture/14-surfaces-and-parity.md +3 -3
- package/docs/architecture/README.md +1 -0
- package/docs/architecture/proposals/PROP-0049-context-attribution.md +382 -0
- package/docs/architecture/proposals/README.md +1 -0
- package/docs/decisions/ADR-0048-required-skills-from-a-message.md +42 -0
- package/docs/decisions/ADR-0050-approved-window-screenshots.md +19 -0
- package/docs/decisions/ADR-0051-terminal-decision-wait.md +10 -0
- package/docs/decisions/ADR-0052-per-profile-subagent-budgets.md +25 -0
- package/docs/parity.md +6 -6
- package/docs/releases/1.3.1-unstable.3.md +54 -0
- package/docs/terminal-tui.md +28 -15
- package/package.json +1 -1
package/dist/terminal-opentui.js
CHANGED
|
@@ -70,18 +70,18 @@ function putIdle(path, stream) {
|
|
|
70
70
|
entries.add(entry);
|
|
71
71
|
idle.set(path, entries);
|
|
72
72
|
}
|
|
73
|
-
function request(connection, body, what, timeoutMs) {
|
|
73
|
+
function request(connection, body, what, timeoutMs, signal) {
|
|
74
74
|
const reused = takeIdle(connection.socket);
|
|
75
|
-
return attempt(connection, body, what, timeoutMs, reused).catch((error) => {
|
|
75
|
+
return attempt(connection, body, what, timeoutMs, reused, signal).catch((error) => {
|
|
76
76
|
if (reused && error instanceof StaleConnection)
|
|
77
|
-
return attempt(connection, body, what, timeoutMs, undefined);
|
|
77
|
+
return attempt(connection, body, what, timeoutMs, undefined, signal);
|
|
78
78
|
throw error;
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
class StaleConnection extends Error {
|
|
83
83
|
}
|
|
84
|
-
function attempt(connection, body, what, timeoutMs, reused) {
|
|
84
|
+
function attempt(connection, body, what, timeoutMs, reused, signal) {
|
|
85
85
|
return new Promise((resolve, reject) => {
|
|
86
86
|
const stream = reused ?? createConnection(connection.socket);
|
|
87
87
|
let buffer = "";
|
|
@@ -115,6 +115,7 @@ function attempt(connection, body, what, timeoutMs, reused) {
|
|
|
115
115
|
return;
|
|
116
116
|
settled = true;
|
|
117
117
|
clearTimeout(timer);
|
|
118
|
+
signal?.removeEventListener("abort", onAbort);
|
|
118
119
|
stream.off("connect", send);
|
|
119
120
|
stream.off("data", onData);
|
|
120
121
|
stream.off("error", onError);
|
|
@@ -131,6 +132,15 @@ function attempt(connection, body, what, timeoutMs, reused) {
|
|
|
131
132
|
stream.destroy();
|
|
132
133
|
finish(() => reject(new SocketError("timeout", `The harness did not answer ${what}.`)));
|
|
133
134
|
}, timeoutMs);
|
|
135
|
+
const onAbort = () => {
|
|
136
|
+
stream.destroy();
|
|
137
|
+
finish(() => reject(new SocketError("aborted", `${what} was cancelled.`)));
|
|
138
|
+
};
|
|
139
|
+
if (signal?.aborted) {
|
|
140
|
+
onAbort();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
134
144
|
stream.on("data", onData);
|
|
135
145
|
stream.on("error", onError);
|
|
136
146
|
stream.on("close", onClose);
|
|
@@ -140,8 +150,8 @@ function attempt(connection, body, what, timeoutMs, reused) {
|
|
|
140
150
|
stream.on("connect", send);
|
|
141
151
|
});
|
|
142
152
|
}
|
|
143
|
-
function socketCall(connection, call, params = {}, timeoutMs = 120000, control) {
|
|
144
|
-
return request(connection, { call, params, ...control ? { control } : {} }, call, timeoutMs);
|
|
153
|
+
function socketCall(connection, call, params = {}, timeoutMs = 120000, control, signal) {
|
|
154
|
+
return request(connection, { call, params, ...control ? { control } : {} }, call, timeoutMs, signal);
|
|
145
155
|
}
|
|
146
156
|
function terminalCall(event, data = {}, timeoutMs = 600000) {
|
|
147
157
|
return request(connectionFromEnv(), { event, data }, event, timeoutMs);
|
|
@@ -165,10 +175,10 @@ function claimTerminalControl(connection = connectionFromEnv()) {
|
|
|
165
175
|
}
|
|
166
176
|
return claim;
|
|
167
177
|
}
|
|
168
|
-
async function controlCall(call, params = {}, timeoutMs = 120000) {
|
|
178
|
+
async function controlCall(call, params = {}, timeoutMs = 120000, signal) {
|
|
169
179
|
const connection = connectionFromEnv();
|
|
170
180
|
const control = await claimTerminalControl(connection);
|
|
171
|
-
return socketCall(connection, call, params, timeoutMs, control);
|
|
181
|
+
return signal ? socketCall(connection, call, params, timeoutMs, control, signal) : socketCall(connection, call, params, timeoutMs, control);
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
// src/terminal/tui/chrome.ts
|
|
@@ -584,19 +594,10 @@ function lines(text) {
|
|
|
584
594
|
}
|
|
585
595
|
var compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
586
596
|
|
|
587
|
-
// src/
|
|
588
|
-
import { existsSync as existsSync2,
|
|
589
|
-
import { dirname as dirname2, join as join3
|
|
590
|
-
|
|
591
|
-
// src/core/changes.ts
|
|
592
|
-
var listeners = new Set;
|
|
593
|
-
function noteChange(change) {
|
|
594
|
-
for (const listener of listeners) {
|
|
595
|
-
try {
|
|
596
|
-
listener(change);
|
|
597
|
-
} catch {}
|
|
598
|
-
}
|
|
599
|
-
}
|
|
597
|
+
// src/tools/defaultSkills.ts
|
|
598
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
599
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
600
|
+
import { fileURLToPath } from "url";
|
|
600
601
|
|
|
601
602
|
// src/core/frontmatter.ts
|
|
602
603
|
import { parse as parseYaml, stringify } from "yaml";
|
|
@@ -625,6 +626,17 @@ function parse(text) {
|
|
|
625
626
|
return null;
|
|
626
627
|
}
|
|
627
628
|
}
|
|
629
|
+
function bodyOf(text) {
|
|
630
|
+
const parts = split(text);
|
|
631
|
+
if (!parts)
|
|
632
|
+
return text;
|
|
633
|
+
try {
|
|
634
|
+
parseYaml(parts.yaml);
|
|
635
|
+
return parts.body;
|
|
636
|
+
} catch {
|
|
637
|
+
return text;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
628
640
|
function serialize(front, body) {
|
|
629
641
|
let yaml;
|
|
630
642
|
try {
|
|
@@ -639,6 +651,57 @@ ${body.trimEnd()}
|
|
|
639
651
|
`;
|
|
640
652
|
}
|
|
641
653
|
|
|
654
|
+
// src/tools/defaultSkills.ts
|
|
655
|
+
var SKILL_NAME = /^[a-z0-9][a-z0-9_-]*$/;
|
|
656
|
+
function defaultSkillPath() {
|
|
657
|
+
let dir = dirname2(fileURLToPath(import.meta.url));
|
|
658
|
+
for (;; ) {
|
|
659
|
+
const path = join3(dir, "builtin", "skills", "plugin-authoring", "SKILL.md");
|
|
660
|
+
if (existsSync2(path))
|
|
661
|
+
return path;
|
|
662
|
+
const parent = dirname2(dir);
|
|
663
|
+
if (parent === dir)
|
|
664
|
+
throw new Error("The bundled plugin-authoring skill is missing.");
|
|
665
|
+
dir = parent;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function defaultSkillPaths(variantDir) {
|
|
669
|
+
return variantDir && existsSync2(join3(variantDir, "skills", "plugin-authoring", "SKILL.md")) ? [] : [defaultSkillPath()];
|
|
670
|
+
}
|
|
671
|
+
function readSkillFile(variantDir, name) {
|
|
672
|
+
const wanted = name.trim().replace(/^skills\//, "").replace(/`/g, "");
|
|
673
|
+
if (!SKILL_NAME.test(wanted))
|
|
674
|
+
return null;
|
|
675
|
+
const variant = variantDir ? join3(variantDir, "skills", wanted, "SKILL.md") : null;
|
|
676
|
+
const builtin = wanted === "plugin-authoring" && defaultSkillPaths(variantDir).length ? defaultSkillPath() : null;
|
|
677
|
+
const path = variant && existsSync2(variant) && statSync2(variant).isFile() ? variant : builtin;
|
|
678
|
+
if (!path)
|
|
679
|
+
return null;
|
|
680
|
+
const baseDir = dirname2(path);
|
|
681
|
+
const scripts = join3(baseDir, "scripts");
|
|
682
|
+
return {
|
|
683
|
+
name: wanted,
|
|
684
|
+
path,
|
|
685
|
+
baseDir,
|
|
686
|
+
body: bodyOf(readFileSync2(path, "utf8")),
|
|
687
|
+
helpers: existsSync2(scripts) ? readdirSync(scripts) : []
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// src/wiki/notes.ts
|
|
692
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, statSync as statSync3 } from "fs";
|
|
693
|
+
import { dirname as dirname3, join as join4, relative, sep as sep2 } from "path";
|
|
694
|
+
|
|
695
|
+
// src/core/changes.ts
|
|
696
|
+
var listeners = new Set;
|
|
697
|
+
function noteChange(change) {
|
|
698
|
+
for (const listener of listeners) {
|
|
699
|
+
try {
|
|
700
|
+
listener(change);
|
|
701
|
+
} catch {}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
642
705
|
// src/core/secrets.ts
|
|
643
706
|
var SECRET_MARKERS = ["-----BEGIN", "sk-ant-", "sk-proj-", "ghp_", "github_pat_", "AKIA", "xoxb-"];
|
|
644
707
|
function looksSecret(text) {
|
|
@@ -649,10 +712,10 @@ function looksSecret(text) {
|
|
|
649
712
|
var LEGACY_FOLDERS = ["blocks", "facts", "raw", "revisions", "inbox", "operations", "trash"];
|
|
650
713
|
var MAX_NOTE = 400000;
|
|
651
714
|
function wikiDir(root) {
|
|
652
|
-
return
|
|
715
|
+
return join4(root, "wiki");
|
|
653
716
|
}
|
|
654
717
|
function historyDir(root) {
|
|
655
|
-
return
|
|
718
|
+
return join4(root, "wiki-history");
|
|
656
719
|
}
|
|
657
720
|
function cleanPath(path) {
|
|
658
721
|
const clean = String(path ?? "").trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").replace(/\.md$/i, "");
|
|
@@ -670,7 +733,7 @@ function cleanPath(path) {
|
|
|
670
733
|
return clean;
|
|
671
734
|
}
|
|
672
735
|
function fileOf(root, path) {
|
|
673
|
-
return
|
|
736
|
+
return join4(wikiDir(root), `${cleanPath(path)}.md`);
|
|
674
737
|
}
|
|
675
738
|
function projectOfPath(path) {
|
|
676
739
|
const parts = path.split("/");
|
|
@@ -695,17 +758,17 @@ function safeParse(text) {
|
|
|
695
758
|
function walk(dir, into) {
|
|
696
759
|
let names;
|
|
697
760
|
try {
|
|
698
|
-
names =
|
|
761
|
+
names = readdirSync2(dir);
|
|
699
762
|
} catch {
|
|
700
763
|
return;
|
|
701
764
|
}
|
|
702
765
|
for (const name of names.sort()) {
|
|
703
766
|
if (name.startsWith("."))
|
|
704
767
|
continue;
|
|
705
|
-
const path =
|
|
768
|
+
const path = join4(dir, name);
|
|
706
769
|
let isDir = false;
|
|
707
770
|
try {
|
|
708
|
-
isDir =
|
|
771
|
+
isDir = statSync3(path).isDirectory();
|
|
709
772
|
} catch {
|
|
710
773
|
continue;
|
|
711
774
|
}
|
|
@@ -718,16 +781,16 @@ function walk(dir, into) {
|
|
|
718
781
|
function paths(root) {
|
|
719
782
|
const wiki = wikiDir(root);
|
|
720
783
|
const files = [];
|
|
721
|
-
if (
|
|
722
|
-
files.push(
|
|
723
|
-
walk(
|
|
724
|
-
walk(
|
|
784
|
+
if (existsSync3(join4(wiki, "home.md")))
|
|
785
|
+
files.push(join4(wiki, "home.md"));
|
|
786
|
+
walk(join4(wiki, "library"), files);
|
|
787
|
+
walk(join4(wiki, "projects"), files);
|
|
725
788
|
return files.map((file) => relative(wiki, file).split(sep2).join("/").replace(/\.md$/, ""));
|
|
726
789
|
}
|
|
727
790
|
function summary(root, path) {
|
|
728
791
|
const file = fileOf(root, path);
|
|
729
|
-
const text =
|
|
730
|
-
return { path, title: titleOf(path, text), project: projectOfPath(path), updatedAt:
|
|
792
|
+
const text = readFileSync3(file, "utf8");
|
|
793
|
+
return { path, title: titleOf(path, text), project: projectOfPath(path), updatedAt: statSync3(file).mtime.toISOString(), size: text.length };
|
|
731
794
|
}
|
|
732
795
|
function list(root, options = {}) {
|
|
733
796
|
return paths(root).filter((path) => options.project === undefined || projectOfPath(path) === options.project).map((path) => summary(root, path));
|
|
@@ -779,7 +842,7 @@ function graph(root) {
|
|
|
779
842
|
const titles = new Map(nodes.map((node) => [node.title.toLowerCase(), node.path]));
|
|
780
843
|
const edges = [];
|
|
781
844
|
for (const node of nodes) {
|
|
782
|
-
const body =
|
|
845
|
+
const body = readFileSync3(fileOf(root, node.path), "utf8");
|
|
783
846
|
for (const to of linksIn(body, node.path, known, titles))
|
|
784
847
|
edges.push({ from: node.path, to });
|
|
785
848
|
}
|
|
@@ -788,9 +851,9 @@ function graph(root) {
|
|
|
788
851
|
function read(root, path) {
|
|
789
852
|
const clean = cleanPath(path);
|
|
790
853
|
const file = fileOf(root, clean);
|
|
791
|
-
if (!
|
|
854
|
+
if (!existsSync3(file))
|
|
792
855
|
throw notFound(`The note ${clean}`);
|
|
793
|
-
const text =
|
|
856
|
+
const text = readFileSync3(file, "utf8");
|
|
794
857
|
const { nodes, edges } = graph(root);
|
|
795
858
|
const known = new Set(nodes.map((node) => node.path));
|
|
796
859
|
const titles = new Map(nodes.map((node) => [node.title.toLowerCase(), node.path]));
|
|
@@ -813,16 +876,16 @@ function write(root, path, body, options = {}) {
|
|
|
813
876
|
if (project && !projectsOrNone(root).some((known) => known.slug === project))
|
|
814
877
|
throw invalid(`There is no project with the slug ${project}.`);
|
|
815
878
|
const file = fileOf(root, clean);
|
|
816
|
-
if (
|
|
817
|
-
const current =
|
|
879
|
+
if (existsSync3(file)) {
|
|
880
|
+
const current = statSync3(file).mtime.toISOString();
|
|
818
881
|
if (options.expectedUpdatedAt && options.expectedUpdatedAt !== current)
|
|
819
882
|
throw new HarnessError("conflict", "The note changed since it was opened. Reload it and try again.");
|
|
820
|
-
const previous =
|
|
883
|
+
const previous = readFileSync3(file, "utf8");
|
|
821
884
|
if (previous === body)
|
|
822
885
|
return read(root, clean);
|
|
823
|
-
atomicWrite(
|
|
886
|
+
atomicWrite(join4(historyDir(root), clean, `${now().replace(/[:.]/g, "-")}.md`), previous);
|
|
824
887
|
}
|
|
825
|
-
mkdirSync2(
|
|
888
|
+
mkdirSync2(dirname3(file), { recursive: true });
|
|
826
889
|
atomicWrite(file, body);
|
|
827
890
|
noteChange({ wiki: wikiDir(root), id: clean, path: file, text: body, actor: options.actor ?? "user", reason: options.reason ?? "Saved" });
|
|
828
891
|
return read(root, clean);
|
|
@@ -830,15 +893,15 @@ function write(root, path, body, options = {}) {
|
|
|
830
893
|
function remove(root, path) {
|
|
831
894
|
const clean = cleanPath(path);
|
|
832
895
|
const file = fileOf(root, clean);
|
|
833
|
-
if (!
|
|
896
|
+
if (!existsSync3(file))
|
|
834
897
|
throw notFound(`The note ${clean}`);
|
|
835
|
-
atomicWrite(
|
|
898
|
+
atomicWrite(join4(historyDir(root), clean, `${now().replace(/[:.]/g, "-")}.md`), readFileSync3(file, "utf8"));
|
|
836
899
|
rmSync2(file);
|
|
837
900
|
}
|
|
838
901
|
function versions(root, path) {
|
|
839
|
-
const dir =
|
|
902
|
+
const dir = join4(historyDir(root), cleanPath(path));
|
|
840
903
|
try {
|
|
841
|
-
return
|
|
904
|
+
return readdirSync2(dir).filter((name) => name.endsWith(".md")).sort().reverse().map((name) => ({ id: name.replace(/\.md$/, ""), savedAt: statSync3(join4(dir, name)).mtime.toISOString() }));
|
|
842
905
|
} catch {
|
|
843
906
|
return [];
|
|
844
907
|
}
|
|
@@ -846,10 +909,10 @@ function versions(root, path) {
|
|
|
846
909
|
function version(root, path, id) {
|
|
847
910
|
if (!/^[0-9A-Za-z-]+$/.test(id))
|
|
848
911
|
throw invalid("Invalid version.");
|
|
849
|
-
const file =
|
|
850
|
-
if (!
|
|
912
|
+
const file = join4(historyDir(root), cleanPath(path), `${id}.md`);
|
|
913
|
+
if (!existsSync3(file))
|
|
851
914
|
throw notFound("That version");
|
|
852
|
-
return
|
|
915
|
+
return readFileSync3(file, "utf8");
|
|
853
916
|
}
|
|
854
917
|
function search(root, query, options = {}) {
|
|
855
918
|
const words = [...new Set(String(query ?? "").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((word) => word.length > 1))].slice(0, 16);
|
|
@@ -857,7 +920,7 @@ function search(root, query, options = {}) {
|
|
|
857
920
|
return [];
|
|
858
921
|
const hits = [];
|
|
859
922
|
for (const path of paths(root)) {
|
|
860
|
-
const text =
|
|
923
|
+
const text = readFileSync3(fileOf(root, path), "utf8");
|
|
861
924
|
const lower = text.toLowerCase();
|
|
862
925
|
const title = titleOf(path, text);
|
|
863
926
|
let score = 0;
|
|
@@ -887,15 +950,15 @@ function search(root, query, options = {}) {
|
|
|
887
950
|
}
|
|
888
951
|
function ensureLayout(root, productName) {
|
|
889
952
|
const wiki = wikiDir(root);
|
|
890
|
-
mkdirSync2(
|
|
891
|
-
mkdirSync2(
|
|
892
|
-
if (!
|
|
893
|
-
atomicWrite(
|
|
953
|
+
mkdirSync2(join4(wiki, "library"), { recursive: true });
|
|
954
|
+
mkdirSync2(join4(wiki, "projects"), { recursive: true });
|
|
955
|
+
if (!existsSync3(join4(wiki, "home.md")))
|
|
956
|
+
atomicWrite(join4(wiki, "home.md"), `# Home
|
|
894
957
|
|
|
895
958
|
Notes about your projects for ${productName}. Link notes with [[library/name]] or [[projects/<slug>/name]].
|
|
896
959
|
`);
|
|
897
960
|
for (const project of projectsOrNone(root))
|
|
898
|
-
mkdirSync2(
|
|
961
|
+
mkdirSync2(join4(wiki, "projects", project.slug), { recursive: true });
|
|
899
962
|
}
|
|
900
963
|
|
|
901
964
|
// src/world/untrusted.ts
|
|
@@ -953,6 +1016,7 @@ var contextSnapshotPayload = z.object({
|
|
|
953
1016
|
systemPrompt: z.string().max(1e5),
|
|
954
1017
|
contextUpdate: z.string().max(1e5).optional(),
|
|
955
1018
|
tools: z.array(z.object({ name: z.string(), description: z.string().max(2000), parameters: z.unknown() })).max(120),
|
|
1019
|
+
compaction: z.object({ enabled: z.boolean(), reserveTokens: z.number().nonnegative() }).optional(),
|
|
956
1020
|
systemPromptTruncated: z.boolean().optional(),
|
|
957
1021
|
contextUpdateTruncated: z.boolean().optional(),
|
|
958
1022
|
toolsTruncated: z.boolean().optional()
|
|
@@ -1491,9 +1555,28 @@ function sessionNameTool(current) {
|
|
|
1491
1555
|
|
|
1492
1556
|
// src/prompt/compile.ts
|
|
1493
1557
|
var SESSION_STATE_BUDGET = 1000;
|
|
1494
|
-
var
|
|
1558
|
+
var REQUIRED_SKILLS_BUDGET = 12000;
|
|
1559
|
+
var REQUIRED_SKILLS_POINTER = "Some required skill text was omitted to fit. Load the rest with skill_load.";
|
|
1560
|
+
var SECTION_ORDER = { harnessIdentity: 5, system: 25, sessionState: 30, guardrails: 40, memory: 45, references: 50, requiredSkills: 52, subagents: 64, scripts: 66, wiki: 80, tools: 90, reporting: 95 };
|
|
1495
1561
|
var NATIVE_GROUP = ["read", "grep", "find", "ls", "edit", "write", "bash"];
|
|
1496
1562
|
var CONNECTION_TOOLS = ["mcp_list_tools", "mcp_call_tool"];
|
|
1563
|
+
function fit(text, budget, pointer) {
|
|
1564
|
+
const trimmed = text.trim();
|
|
1565
|
+
if (charCount(trimmed) <= budget)
|
|
1566
|
+
return { body: trimmed, cut: false };
|
|
1567
|
+
const room = Math.max(0, budget - (charCount(pointer) + 2));
|
|
1568
|
+
const head = takeChars(trimmed, room);
|
|
1569
|
+
let index = head.lastIndexOf(`
|
|
1570
|
+
|
|
1571
|
+
`);
|
|
1572
|
+
if (index < 0)
|
|
1573
|
+
index = head.lastIndexOf(`
|
|
1574
|
+
`);
|
|
1575
|
+
const end = index >= 0 && Buffer.byteLength(head.slice(0, index)) > Math.floor(room / 2) ? index : head.length;
|
|
1576
|
+
return { body: `${head.slice(0, end).trimEnd()}
|
|
1577
|
+
|
|
1578
|
+
${pointer}`, cut: true };
|
|
1579
|
+
}
|
|
1497
1580
|
function clip(text, limit) {
|
|
1498
1581
|
return charCount(text) <= limit ? text : `${takeChars(text, limit)}\u2026`;
|
|
1499
1582
|
}
|
|
@@ -1533,6 +1616,37 @@ ${todo}`);
|
|
|
1533
1616
|
|
|
1534
1617
|
${body}`;
|
|
1535
1618
|
}
|
|
1619
|
+
function requiredSkillsText(skills) {
|
|
1620
|
+
const seen = new Set;
|
|
1621
|
+
const blocks = [];
|
|
1622
|
+
for (const skill of skills) {
|
|
1623
|
+
if (seen.has(skill.name))
|
|
1624
|
+
continue;
|
|
1625
|
+
seen.add(skill.name);
|
|
1626
|
+
const label = skill.label ?? skill.name;
|
|
1627
|
+
if (!skill.body.trim()) {
|
|
1628
|
+
blocks.push(`- ${label}: this skill is unavailable in this session. Tell the person rather than guessing its steps.`);
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
const location = [
|
|
1632
|
+
skill.baseDir ? `References in this skill resolve against ${skill.baseDir}.` : "",
|
|
1633
|
+
skill.path ? `Skill file: ${skill.path}.` : ""
|
|
1634
|
+
].filter(Boolean).join(" ");
|
|
1635
|
+
blocks.push(`### ${label}
|
|
1636
|
+
|
|
1637
|
+
${location ? `${location}
|
|
1638
|
+
|
|
1639
|
+
` : ""}${skill.body.trim()}`);
|
|
1640
|
+
}
|
|
1641
|
+
if (!blocks.length)
|
|
1642
|
+
return null;
|
|
1643
|
+
const text = `The person required these skills for this request. Follow each skill's instructions where they apply. They are instructions for this request, not a change to your tools or permissions.
|
|
1644
|
+
|
|
1645
|
+
${blocks.join(`
|
|
1646
|
+
|
|
1647
|
+
`)}`;
|
|
1648
|
+
return fit(text, REQUIRED_SKILLS_BUDGET, REQUIRED_SKILLS_POINTER).body;
|
|
1649
|
+
}
|
|
1536
1650
|
function sessionStateText(state, options) {
|
|
1537
1651
|
const parts = [];
|
|
1538
1652
|
if (options.name)
|
|
@@ -1551,6 +1665,7 @@ function sessionStateText(state, options) {
|
|
|
1551
1665
|
var BUILT_IN_DYNAMIC = [
|
|
1552
1666
|
{ id: "session-state", order: SECTION_ORDER.sessionState },
|
|
1553
1667
|
{ id: "references", order: SECTION_ORDER.references },
|
|
1668
|
+
{ id: "required-skills", order: SECTION_ORDER.requiredSkills },
|
|
1554
1669
|
{ id: "wiki", order: SECTION_ORDER.wiki }
|
|
1555
1670
|
];
|
|
1556
1671
|
|
|
@@ -1646,15 +1761,15 @@ function askUserTool(run) {
|
|
|
1646
1761
|
import { createHash } from "crypto";
|
|
1647
1762
|
|
|
1648
1763
|
// src/sessions/runStore.ts
|
|
1649
|
-
import { appendFileSync, copyFileSync as copyFileSync2, existsSync as
|
|
1650
|
-
import { join as
|
|
1764
|
+
import { appendFileSync, copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4 } from "fs";
|
|
1765
|
+
import { join as join7 } from "path";
|
|
1651
1766
|
|
|
1652
1767
|
// src/sessions/runIndex.ts
|
|
1653
|
-
import { join as
|
|
1768
|
+
import { join as join6 } from "path";
|
|
1654
1769
|
|
|
1655
1770
|
// src/core/index.ts
|
|
1656
|
-
import { copyFileSync, existsSync as
|
|
1657
|
-
import { join as
|
|
1771
|
+
import { copyFileSync, existsSync as existsSync4, readdirSync as readdirSync3, rmSync as rmSync3 } from "fs";
|
|
1772
|
+
import { join as join5 } from "path";
|
|
1658
1773
|
import { DatabaseSync } from "node:sqlite";
|
|
1659
1774
|
var RECALL_SCHEMA = `CREATE TABLE recall_runs (run_id TEXT PRIMARY KEY, passages INTEGER NOT NULL, status TEXT NOT NULL);
|
|
1660
1775
|
CREATE VIRTUAL TABLE recall_fts USING fts5(run_id UNINDEXED, seq UNINDEXED, role UNINDEXED, at UNINDEXED,
|
|
@@ -1714,7 +1829,7 @@ var MIGRATIONS = [
|
|
|
1714
1829
|
];
|
|
1715
1830
|
var connections = new Map;
|
|
1716
1831
|
function dbPath(root) {
|
|
1717
|
-
return
|
|
1832
|
+
return join5(root, "index.sqlite");
|
|
1718
1833
|
}
|
|
1719
1834
|
function sqlError(error) {
|
|
1720
1835
|
return new HarnessError("index_error", error instanceof Error ? error.message : String(error), "Settings \u2192 Projects and Storage \u2192 Repair index.");
|
|
@@ -1750,7 +1865,7 @@ function migrate(path, migrations) {
|
|
|
1750
1865
|
if (current >= migrations.length)
|
|
1751
1866
|
return database;
|
|
1752
1867
|
const backup = `${path.replace(/\.sqlite$/, "")}.sqlite.bak-v${current}`;
|
|
1753
|
-
const hadFile =
|
|
1868
|
+
const hadFile = existsSync4(path) && current > 0;
|
|
1754
1869
|
if (hadFile) {
|
|
1755
1870
|
try {
|
|
1756
1871
|
database.exec("PRAGMA wal_checkpoint(TRUNCATE);");
|
|
@@ -1798,7 +1913,7 @@ function all(database, sql, ...params) {
|
|
|
1798
1913
|
}
|
|
1799
1914
|
function runFolders(root) {
|
|
1800
1915
|
try {
|
|
1801
|
-
return
|
|
1916
|
+
return readdirSync3(join5(root, "runs"));
|
|
1802
1917
|
} catch {
|
|
1803
1918
|
return [];
|
|
1804
1919
|
}
|
|
@@ -1809,7 +1924,7 @@ function readAllRuns(root) {
|
|
|
1809
1924
|
const records = [];
|
|
1810
1925
|
for (const name of runFolders(root)) {
|
|
1811
1926
|
try {
|
|
1812
|
-
const parsed = runRecordSchema.safeParse(upgradeRunRecord(readJson(
|
|
1927
|
+
const parsed = runRecordSchema.safeParse(upgradeRunRecord(readJson(join6(root, "runs", name, "run.json"))));
|
|
1813
1928
|
if (parsed.success)
|
|
1814
1929
|
records.push(parsed.data);
|
|
1815
1930
|
} catch {}
|
|
@@ -1819,9 +1934,9 @@ function readAllRuns(root) {
|
|
|
1819
1934
|
|
|
1820
1935
|
// src/sessions/runStore.ts
|
|
1821
1936
|
function runDir(root, runId) {
|
|
1822
|
-
return
|
|
1937
|
+
return join7(root, "runs", safeId(runId));
|
|
1823
1938
|
}
|
|
1824
|
-
var recordPath = (root, runId) =>
|
|
1939
|
+
var recordPath = (root, runId) => join7(runDir(root, runId), "run.json");
|
|
1825
1940
|
function readRecord(path) {
|
|
1826
1941
|
const parsed = runRecordSchema.safeParse(upgradeRunRecord(readJson(path)));
|
|
1827
1942
|
return parsed.success ? parsed.data : null;
|
|
@@ -1861,7 +1976,7 @@ function storedEvent(line) {
|
|
|
1861
1976
|
function readEvents(root, runId) {
|
|
1862
1977
|
let text;
|
|
1863
1978
|
try {
|
|
1864
|
-
text =
|
|
1979
|
+
text = readFileSync4(join7(runDir(root, runId), "events.jsonl"), "utf8");
|
|
1865
1980
|
} catch (error) {
|
|
1866
1981
|
if (error.code === "ENOENT")
|
|
1867
1982
|
return [];
|
|
@@ -2197,6 +2312,8 @@ function reported(parentRunId, results, spawned) {
|
|
|
2197
2312
|
return { text: JSON.stringify({ results: included, ...results.length > included.length ? { remaining: results.slice(included.length).map((result) => result.runId) } : {} }), details: { [SUBAGENT_RECEIPT]: receipt } };
|
|
2198
2313
|
}
|
|
2199
2314
|
function subagentTools(service, parentRunId) {
|
|
2315
|
+
const quota = service.quota(parentRunId);
|
|
2316
|
+
const budget = `This run allows at most ${quota.children} children: ${Object.entries(quota.profiles).map(([name, value]) => `${value} ${name}`).join(", ")}, ${quota.other} of any other profile and ${quota.unprofiled} unprofiled; a refused spawn means do the work yourself.`;
|
|
2200
2317
|
const execute = (fn) => async (input) => {
|
|
2201
2318
|
try {
|
|
2202
2319
|
return await fn(input);
|
|
@@ -2209,7 +2326,7 @@ function subagentTools(service, parentRunId) {
|
|
|
2209
2326
|
return [{
|
|
2210
2327
|
name: SPAWN_SUBAGENT,
|
|
2211
2328
|
label: "Start a subagent",
|
|
2212
|
-
description: "Start a fresh child run for independent work. Delegate substantial, bounded tasks that benefit from a separate context, not simple lookups, tiny edits or tightly coupled steps. Prefer a named specialist profile; omit agent only when no profile fits. Plan mode permits only eligible read-only research profiles. Give a short title and a self-contained brief: the child has no conversation history and no more capabilities than you. The optional tools field only narrows the profile and parent intersection; in plan mode, omit it unless you need a stricter subset. Returns its run ID immediately by default so you can continue other useful work. Results arrive automatically before you finish. Use wait_subagents when you need results before proceeding, or wait: true for an inline result. Avoid overlapping write assignments. Stopping you stops your children. To resume a child that stopped before finishing (interrupted or failed), pass continue with its run ID and no title, agent or tools: it reopens that child's own conversation with the same profile and tools, and brief becomes an optional extra instruction.",
|
|
2329
|
+
description: "Start a fresh child run for independent work. Delegate substantial, bounded tasks that benefit from a separate context, not simple lookups, tiny edits or tightly coupled steps. Prefer a named specialist profile; omit agent only when no profile fits. Plan mode permits only eligible read-only research profiles. Give a short title and a self-contained brief: the child has no conversation history and no more capabilities than you. The optional tools field only narrows the profile and parent intersection; in plan mode, omit it unless you need a stricter subset. Returns its run ID immediately by default so you can continue other useful work. Results arrive automatically before you finish. Use wait_subagents when you need results before proceeding, or wait: true for an inline result. Avoid overlapping write assignments. Stopping you stops your children. To resume a child that stopped before finishing (interrupted or failed), pass continue with its run ID and no title, agent or tools: it reopens that child's own conversation with the same profile and tools, and brief becomes an optional extra instruction. " + budget,
|
|
2213
2330
|
planModeSafe: (input) => service.planSpawnRefusal(parentRunId, input) === null,
|
|
2214
2331
|
parameters: (Type) => Type.Object({
|
|
2215
2332
|
title: Type.Optional(Type.String({ description: `Required for a new child: a short imperative title for the roster, at most ${MAX_SUBAGENT_TITLE_WORDS} words, such as "Map the auth flow"` })),
|
|
@@ -3244,8 +3361,8 @@ function browserPluginPart(engine) {
|
|
|
3244
3361
|
}
|
|
3245
3362
|
|
|
3246
3363
|
// src/annotations/store.ts
|
|
3247
|
-
import { existsSync as
|
|
3248
|
-
import { join as
|
|
3364
|
+
import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync4, rmSync as rmSync4 } from "fs";
|
|
3365
|
+
import { join as join8 } from "path";
|
|
3249
3366
|
var MAX_NOTES = 1e4;
|
|
3250
3367
|
var MAX_HISTORY = 100;
|
|
3251
3368
|
var codepoints = (value) => Array.from(value).length;
|
|
@@ -3378,17 +3495,17 @@ function visibleTo(note, caller) {
|
|
|
3378
3495
|
return note.grants.some((grant) => grant.principal.sessionId && grant.principal.sessionId === caller.sessionId || grant.principal.agentId && grant.principal.agentId === caller.agentId);
|
|
3379
3496
|
}
|
|
3380
3497
|
function notesDir(root) {
|
|
3381
|
-
return
|
|
3498
|
+
return join8(root, "annotations");
|
|
3382
3499
|
}
|
|
3383
3500
|
function folder(root) {
|
|
3384
3501
|
const dir = notesDir(root);
|
|
3385
|
-
if (
|
|
3502
|
+
if (existsSync6(dir) && (lstatSync(dir).isSymbolicLink() || !lstatSync(dir).isDirectory()))
|
|
3386
3503
|
throw new HarnessError("invalid_data", "Note storage must be a plain folder.");
|
|
3387
3504
|
mkdirSync4(dir, { recursive: true });
|
|
3388
3505
|
return dir;
|
|
3389
3506
|
}
|
|
3390
3507
|
function file(root, id) {
|
|
3391
|
-
return
|
|
3508
|
+
return join8(folder(root), `${safeId(id)}.json`);
|
|
3392
3509
|
}
|
|
3393
3510
|
function validNote(note, id) {
|
|
3394
3511
|
if (!note || note.id !== id || note.author !== "user" || !Number.isSafeInteger(note.version) || note.version < 1 || !Array.isArray(note.grants) || !Array.isArray(note.history)) {
|
|
@@ -3414,7 +3531,7 @@ function upgradeDelivery(note) {
|
|
|
3414
3531
|
}
|
|
3415
3532
|
function read2(root, id) {
|
|
3416
3533
|
const path = file(root, id);
|
|
3417
|
-
if (!
|
|
3534
|
+
if (!existsSync6(path))
|
|
3418
3535
|
throw notFound("Note");
|
|
3419
3536
|
if (lstatSync(path).isSymbolicLink())
|
|
3420
3537
|
throw new HarnessError("invalid_data", "Note records must not be links.");
|
|
@@ -3422,7 +3539,7 @@ function read2(root, id) {
|
|
|
3422
3539
|
}
|
|
3423
3540
|
function all2(root) {
|
|
3424
3541
|
const notes = [];
|
|
3425
|
-
for (const name of
|
|
3542
|
+
for (const name of readdirSync4(folder(root))) {
|
|
3426
3543
|
if (!name.endsWith(".json"))
|
|
3427
3544
|
continue;
|
|
3428
3545
|
try {
|
|
@@ -3465,7 +3582,7 @@ function create(root, input) {
|
|
|
3465
3582
|
const target = validateTarget(input?.target);
|
|
3466
3583
|
const body = normalizeBody(input?.body);
|
|
3467
3584
|
const tags = normalizeTags(input?.tags);
|
|
3468
|
-
if (
|
|
3585
|
+
if (readdirSync4(folder(root)).filter((name) => name.endsWith(".json")).length >= MAX_NOTES) {
|
|
3469
3586
|
throw new HarnessError("annotation_limit", `At most ${MAX_NOTES} notes may be kept.`);
|
|
3470
3587
|
}
|
|
3471
3588
|
const at = now();
|
|
@@ -3531,7 +3648,7 @@ function importNotes(root, notes) {
|
|
|
3531
3648
|
if (!id)
|
|
3532
3649
|
throw invalid("A note needs an id.");
|
|
3533
3650
|
safeId(id);
|
|
3534
|
-
if (
|
|
3651
|
+
if (existsSync6(file(root, id))) {
|
|
3535
3652
|
skipped.push({ id, reason: "exists" });
|
|
3536
3653
|
continue;
|
|
3537
3654
|
}
|
|
@@ -4739,7 +4856,7 @@ function hubServerMethods(harness) {
|
|
|
4739
4856
|
}
|
|
4740
4857
|
|
|
4741
4858
|
// src/refine/runner.ts
|
|
4742
|
-
import { join as
|
|
4859
|
+
import { join as join9 } from "path";
|
|
4743
4860
|
|
|
4744
4861
|
// src/variant/surface.ts
|
|
4745
4862
|
var SYSTEM_PROMPT = "systemprompt.md";
|
|
@@ -4795,10 +4912,10 @@ var CONFORMANCE_BLOCK = `2. CONFORMANCE. Is it ONLY what was asked for? Where th
|
|
|
4795
4912
|
|
|
4796
4913
|
// src/refine/runner.ts
|
|
4797
4914
|
function refineDir(root, id) {
|
|
4798
|
-
return
|
|
4915
|
+
return join9(root, "refine", safeId(id));
|
|
4799
4916
|
}
|
|
4800
4917
|
function readSummary(root, id) {
|
|
4801
|
-
return readJson(
|
|
4918
|
+
return readJson(join9(refineDir(root, id), "summary.json"));
|
|
4802
4919
|
}
|
|
4803
4920
|
|
|
4804
4921
|
// src/refine/serverMethods.ts
|
|
@@ -4979,15 +5096,15 @@ function historyServerMethods(harness) {
|
|
|
4979
5096
|
}
|
|
4980
5097
|
|
|
4981
5098
|
// src/agents/profiles.ts
|
|
4982
|
-
import { existsSync as
|
|
4983
|
-
import { basename as basename2, dirname as
|
|
5099
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync6, rmSync as rmSync5, statSync as statSync4 } from "fs";
|
|
5100
|
+
import { basename as basename2, dirname as dirname4, join as join11 } from "path";
|
|
4984
5101
|
|
|
4985
5102
|
// src/core/effort.ts
|
|
4986
5103
|
var EFFORTS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
4987
5104
|
|
|
4988
5105
|
// src/variant/materialise.ts
|
|
4989
|
-
import { existsSync as
|
|
4990
|
-
import { join as
|
|
5106
|
+
import { existsSync as existsSync7, readdirSync as readdirSync5, readFileSync as readFileSync5 } from "fs";
|
|
5107
|
+
import { join as join10 } from "path";
|
|
4991
5108
|
function frontmatter(text, field) {
|
|
4992
5109
|
const m = new RegExp(`^${field}:\\s*(.+)$`, "m").exec(text.slice(0, 600));
|
|
4993
5110
|
return m?.[1]?.trim() ?? null;
|
|
@@ -4999,10 +5116,10 @@ function bodyAfterFrontmatter(text) {
|
|
|
4999
5116
|
function subagentOf(dir, name) {
|
|
5000
5117
|
if (!/^[a-z0-9][a-z0-9_-]*$/.test(name))
|
|
5001
5118
|
return null;
|
|
5002
|
-
const path =
|
|
5003
|
-
if (!
|
|
5119
|
+
const path = join10(dir, "sub_agents", name, "AGENT.md");
|
|
5120
|
+
if (!existsSync7(path))
|
|
5004
5121
|
return null;
|
|
5005
|
-
const text =
|
|
5122
|
+
const text = readFileSync5(path, "utf8");
|
|
5006
5123
|
return { name, description: frontmatter(text, "description") ?? "", instructions: bodyAfterFrontmatter(text) };
|
|
5007
5124
|
}
|
|
5008
5125
|
|
|
@@ -5109,16 +5226,16 @@ var PROFILE_COLORS = ["blue", "green", "yellow", "red", "magenta", "cyan", "oran
|
|
|
5109
5226
|
function projectRoot(cwd) {
|
|
5110
5227
|
let dir = cwd;
|
|
5111
5228
|
for (;; ) {
|
|
5112
|
-
if (
|
|
5229
|
+
if (existsSync8(join11(dir, ".git")))
|
|
5113
5230
|
return dir;
|
|
5114
|
-
const parent =
|
|
5231
|
+
const parent = dirname4(dir);
|
|
5115
5232
|
if (parent === dir)
|
|
5116
5233
|
return cwd;
|
|
5117
5234
|
dir = parent;
|
|
5118
5235
|
}
|
|
5119
5236
|
}
|
|
5120
5237
|
function profileDirs(root, cwd) {
|
|
5121
|
-
return { global:
|
|
5238
|
+
return { global: join11(root, "agents"), project: cwd ? join11(projectRoot(cwd), ".xingular", "agents") : null };
|
|
5122
5239
|
}
|
|
5123
5240
|
function stringList(value, field) {
|
|
5124
5241
|
if (value === undefined || value === null)
|
|
@@ -5203,25 +5320,25 @@ function profileText(profile) {
|
|
|
5203
5320
|
return serialize(front, profile.instructions);
|
|
5204
5321
|
}
|
|
5205
5322
|
function readFolder(dir, source, into, problems) {
|
|
5206
|
-
if (!dir || !
|
|
5323
|
+
if (!dir || !existsSync8(dir))
|
|
5207
5324
|
return;
|
|
5208
|
-
for (const file of
|
|
5209
|
-
const path =
|
|
5325
|
+
for (const file of readdirSync6(dir).filter((entry) => entry.endsWith(".md")).sort()) {
|
|
5326
|
+
const path = join11(dir, file);
|
|
5210
5327
|
const name = file.slice(0, -3);
|
|
5211
5328
|
try {
|
|
5212
|
-
if (!
|
|
5329
|
+
if (!statSync4(path).isFile())
|
|
5213
5330
|
continue;
|
|
5214
|
-
into.set(name, parseProfile(
|
|
5331
|
+
into.set(name, parseProfile(readFileSync6(path, "utf8"), name, source, path));
|
|
5215
5332
|
} catch (error) {
|
|
5216
5333
|
problems.push({ name, source, path, error: error instanceof Error ? error.message : String(error) });
|
|
5217
5334
|
}
|
|
5218
5335
|
}
|
|
5219
5336
|
}
|
|
5220
5337
|
function variantDefinitions(variantDir) {
|
|
5221
|
-
const folder = variantDir ?
|
|
5222
|
-
if (!folder || !
|
|
5338
|
+
const folder = variantDir ? join11(variantDir, "sub_agents") : null;
|
|
5339
|
+
if (!folder || !existsSync8(folder))
|
|
5223
5340
|
return [];
|
|
5224
|
-
return
|
|
5341
|
+
return readdirSync6(folder).sort().flatMap((name) => {
|
|
5225
5342
|
const definition = RESERVED_PROFILE_NAMES.includes(name) ? null : subagentOf(variantDir, name);
|
|
5226
5343
|
if (!definition)
|
|
5227
5344
|
return [];
|
|
@@ -5236,7 +5353,7 @@ function variantDefinitions(variantDir) {
|
|
|
5236
5353
|
model: null,
|
|
5237
5354
|
effort: null,
|
|
5238
5355
|
source: "variant",
|
|
5239
|
-
path:
|
|
5356
|
+
path: join11(folder, name, "AGENT.md")
|
|
5240
5357
|
}];
|
|
5241
5358
|
});
|
|
5242
5359
|
}
|
|
@@ -5299,7 +5416,7 @@ function listProfiles(root, cwd, variantDir, packageProfiles = []) {
|
|
|
5299
5416
|
profiles.set(definition.name, definition);
|
|
5300
5417
|
for (const path of packageProfiles) {
|
|
5301
5418
|
try {
|
|
5302
|
-
const profile = parsePackageProfile(
|
|
5419
|
+
const profile = parsePackageProfile(readFileSync6(path, "utf8"), path);
|
|
5303
5420
|
if (!profiles.has(profile.name))
|
|
5304
5421
|
profiles.set(profile.name, profile);
|
|
5305
5422
|
else
|
|
@@ -5342,8 +5459,8 @@ function createProfile(root, cwd, input) {
|
|
|
5342
5459
|
if (input.scope !== "global" && input.scope !== "project")
|
|
5343
5460
|
throw invalid("scope must be global or project.");
|
|
5344
5461
|
const dir = scopeDir(root, cwd, input.scope);
|
|
5345
|
-
const path =
|
|
5346
|
-
if (
|
|
5462
|
+
const path = join11(dir, `${name}.md`);
|
|
5463
|
+
if (existsSync8(path))
|
|
5347
5464
|
throw invalid(`There is already a ${input.scope} profile named ${name}.`);
|
|
5348
5465
|
let source = { ...TEMPLATE, name };
|
|
5349
5466
|
if (input.from) {
|
|
@@ -5362,8 +5479,8 @@ function removeProfile(root, cwd, input) {
|
|
|
5362
5479
|
throw invalid("That is not a profile name.");
|
|
5363
5480
|
if (input.scope !== "global" && input.scope !== "project")
|
|
5364
5481
|
throw invalid("scope must be global or project.");
|
|
5365
|
-
const path =
|
|
5366
|
-
if (!
|
|
5482
|
+
const path = join11(scopeDir(root, cwd, input.scope), `${input.name}.md`);
|
|
5483
|
+
if (!existsSync8(path))
|
|
5367
5484
|
throw notFound(`The ${input.scope} profile ${input.name}`);
|
|
5368
5485
|
rmSync5(path);
|
|
5369
5486
|
}
|
|
@@ -5430,9 +5547,9 @@ function sideQuestionServerMethods(harness) {
|
|
|
5430
5547
|
|
|
5431
5548
|
// src/core/path.ts
|
|
5432
5549
|
import { execFileSync } from "child_process";
|
|
5433
|
-
import { accessSync, constants, statSync as
|
|
5550
|
+
import { accessSync, constants, statSync as statSync5 } from "fs";
|
|
5434
5551
|
import { homedir } from "os";
|
|
5435
|
-
import { delimiter, extname, isAbsolute as isAbsolute2, join as
|
|
5552
|
+
import { delimiter, extname, isAbsolute as isAbsolute2, join as join12 } from "path";
|
|
5436
5553
|
var resolved = null;
|
|
5437
5554
|
function searchPath() {
|
|
5438
5555
|
const platform = process.platform;
|
|
@@ -5453,7 +5570,7 @@ function searchPath() {
|
|
|
5453
5570
|
if (platform !== "win32") {
|
|
5454
5571
|
const home = homedir();
|
|
5455
5572
|
if (home)
|
|
5456
|
-
entries.push(...[".local/bin", ".bun/bin", ".npm-global/bin", ".cargo/bin"].map((folder) =>
|
|
5573
|
+
entries.push(...[".local/bin", ".bun/bin", ".npm-global/bin", ".cargo/bin"].map((folder) => join12(home, folder)));
|
|
5457
5574
|
entries.push("/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin");
|
|
5458
5575
|
}
|
|
5459
5576
|
const path = [...new Set(entries.filter(Boolean))].join(separator);
|
|
@@ -5469,7 +5586,7 @@ function pathSeparator(platform) {
|
|
|
5469
5586
|
function isExecutable(path) {
|
|
5470
5587
|
try {
|
|
5471
5588
|
accessSync(path, process.platform === "win32" ? constants.F_OK : constants.X_OK);
|
|
5472
|
-
return
|
|
5589
|
+
return statSync5(path).isFile();
|
|
5473
5590
|
} catch {
|
|
5474
5591
|
return false;
|
|
5475
5592
|
}
|
|
@@ -5486,7 +5603,7 @@ function findBinary(name) {
|
|
|
5486
5603
|
}
|
|
5487
5604
|
for (const directory of searchPath().split(pathSeparator(platform))) {
|
|
5488
5605
|
for (const candidateName of names) {
|
|
5489
|
-
const candidate =
|
|
5606
|
+
const candidate = join12(directory, candidateName);
|
|
5490
5607
|
if (isExecutable(candidate))
|
|
5491
5608
|
return candidate;
|
|
5492
5609
|
}
|
|
@@ -5632,7 +5749,7 @@ function connectedAppServerMethods(harness) {
|
|
|
5632
5749
|
}
|
|
5633
5750
|
|
|
5634
5751
|
// src/tools/dispatch.ts
|
|
5635
|
-
import { existsSync as existsSync9, readdirSync as
|
|
5752
|
+
import { existsSync as existsSync9, readdirSync as readdirSync7, readFileSync as readFileSync7 } from "fs";
|
|
5636
5753
|
import { join as join16 } from "path";
|
|
5637
5754
|
|
|
5638
5755
|
// src/accounts/store.ts
|
|
@@ -5844,10 +5961,10 @@ function readTool2(env, context, params) {
|
|
|
5844
5961
|
|
|
5845
5962
|
// src/plugins/proposals.ts
|
|
5846
5963
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
5847
|
-
import { dirname as
|
|
5964
|
+
import { dirname as dirname5, join as join15 } from "path";
|
|
5848
5965
|
|
|
5849
5966
|
// src/pi/runtime.ts
|
|
5850
|
-
import { join as
|
|
5967
|
+
import { join as join13 } from "path";
|
|
5851
5968
|
import { pathToFileURL } from "url";
|
|
5852
5969
|
var PINNED_PI_VERSION = "1.4.0";
|
|
5853
5970
|
var HTTP_DISPATCHER_CHUNK = "dist/bundle/chunks/chunk-MM3LKT73.js";
|
|
@@ -5876,10 +5993,10 @@ function createPiLoader(importPi, importDispatcher) {
|
|
|
5876
5993
|
return loading;
|
|
5877
5994
|
};
|
|
5878
5995
|
}
|
|
5879
|
-
var loadPi = createPiLoader(() => import("@earendil-works/pi-coding-agent"), async (packageDir) => await import(pathToFileURL(
|
|
5996
|
+
var loadPi = createPiLoader(() => import("@earendil-works/pi-coding-agent"), async (packageDir) => await import(pathToFileURL(join13(packageDir, HTTP_DISPATCHER_CHUNK)).href));
|
|
5880
5997
|
|
|
5881
5998
|
// src/plugins/manifest.ts
|
|
5882
|
-
import { basename as basename3, join as
|
|
5999
|
+
import { basename as basename3, join as join14 } from "path";
|
|
5883
6000
|
import { z as z3 } from "zod";
|
|
5884
6001
|
var connectorTemplateSchema = z3.object({
|
|
5885
6002
|
id: z3.string().regex(/^[a-z0-9-]+$/),
|
|
@@ -5919,14 +6036,14 @@ var PLUGIN_MANIFESTS = ["plugin.json", ".claude-plugin/plugin.json", ".codex-plu
|
|
|
5919
6036
|
var text6 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
|
|
5920
6037
|
function pluginJson(packageRoot) {
|
|
5921
6038
|
for (const file of PLUGIN_MANIFESTS) {
|
|
5922
|
-
const found = readJsonOr(
|
|
6039
|
+
const found = readJsonOr(join14(packageRoot, file), null);
|
|
5923
6040
|
if (found && typeof found === "object" && !Array.isArray(found))
|
|
5924
6041
|
return found;
|
|
5925
6042
|
}
|
|
5926
6043
|
return {};
|
|
5927
6044
|
}
|
|
5928
6045
|
function packageManifest(packageRoot) {
|
|
5929
|
-
const manifest = readJsonOr(
|
|
6046
|
+
const manifest = readJsonOr(join14(packageRoot, "package.json"), {});
|
|
5930
6047
|
const plugin = text6(manifest.name) ? {} : pluginJson(packageRoot);
|
|
5931
6048
|
const own = manifest.harness && typeof manifest.harness === "object" && !Array.isArray(manifest.harness) ? manifest.harness : {};
|
|
5932
6049
|
const pluginName = text6(plugin.name);
|
|
@@ -5951,8 +6068,8 @@ var SKIPPED = new Set(["node_modules", ".git"]);
|
|
|
5951
6068
|
var MAX_FILES = 100;
|
|
5952
6069
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
5953
6070
|
var MAX_TOTAL_BYTES = 1024 * 1024;
|
|
5954
|
-
var dir = (root) =>
|
|
5955
|
-
var pathOf = (root, id) =>
|
|
6071
|
+
var dir = (root) => join15(root, "plugin-proposals");
|
|
6072
|
+
var pathOf = (root, id) => join15(dir(root), `${safeId(id)}.json`);
|
|
5956
6073
|
function validate2(name, description, input) {
|
|
5957
6074
|
if (typeof name !== "string" || !/^[a-z0-9][a-z0-9_-]{0,63}$/.test(name))
|
|
5958
6075
|
throw invalid("Plugin name must be lowercase letters, digits, underscores or hyphens.");
|
|
@@ -5999,26 +6116,6 @@ function create2(root, name, description, files) {
|
|
|
5999
6116
|
return proposal;
|
|
6000
6117
|
}
|
|
6001
6118
|
|
|
6002
|
-
// src/tools/defaultSkills.ts
|
|
6003
|
-
import { existsSync as existsSync8 } from "fs";
|
|
6004
|
-
import { dirname as dirname5, join as join15 } from "path";
|
|
6005
|
-
import { fileURLToPath } from "url";
|
|
6006
|
-
function defaultSkillPath() {
|
|
6007
|
-
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
6008
|
-
for (;; ) {
|
|
6009
|
-
const path = join15(dir, "builtin", "skills", "plugin-authoring", "SKILL.md");
|
|
6010
|
-
if (existsSync8(path))
|
|
6011
|
-
return path;
|
|
6012
|
-
const parent = dirname5(dir);
|
|
6013
|
-
if (parent === dir)
|
|
6014
|
-
throw new Error("The bundled plugin-authoring skill is missing.");
|
|
6015
|
-
dir = parent;
|
|
6016
|
-
}
|
|
6017
|
-
}
|
|
6018
|
-
function defaultSkillPaths(variantDir) {
|
|
6019
|
-
return variantDir && existsSync8(join15(variantDir, "skills", "plugin-authoring", "SKILL.md")) ? [] : [defaultSkillPath()];
|
|
6020
|
-
}
|
|
6021
|
-
|
|
6022
6119
|
// src/tools/dispatch.ts
|
|
6023
6120
|
function str(params, key) {
|
|
6024
6121
|
const value = params[key];
|
|
@@ -6077,25 +6174,24 @@ ${tail}` : ""}`;
|
|
|
6077
6174
|
}
|
|
6078
6175
|
}
|
|
6079
6176
|
function loadSkill(variantDir, name) {
|
|
6080
|
-
const skills = variantDir && existsSync9(join16(variantDir, "skills")) ?
|
|
6081
|
-
|
|
6082
|
-
if (defaults.length)
|
|
6177
|
+
const skills = variantDir && existsSync9(join16(variantDir, "skills")) ? readdirSync7(join16(variantDir, "skills"), { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync9(join16(variantDir, "skills", entry.name, "SKILL.md"))).map((entry) => entry.name).sort() : [];
|
|
6178
|
+
if (!skills.includes("plugin-authoring") && defaultSkillPaths(variantDir).length)
|
|
6083
6179
|
skills.push("plugin-authoring");
|
|
6084
6180
|
skills.sort();
|
|
6085
6181
|
const wanted = name.trim().replace(/^skills\//, "").replace(/`/g, "");
|
|
6086
6182
|
if (!wanted)
|
|
6087
6183
|
return skills.length ? `Your skills: ${skills.join(", ")}. Load one with skill_load and its name.` : "You have no skills.";
|
|
6088
|
-
if (
|
|
6184
|
+
if (!SKILL_NAME.test(wanted))
|
|
6089
6185
|
throw invalid("A skill name is lowercase letters, digits, _ or -.");
|
|
6090
6186
|
if (!skills.includes(wanted))
|
|
6091
6187
|
return `There is no skill named ${wanted}.${skills.length ? ` Your skills: ${skills.join(", ")}.` : ""}`;
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
const
|
|
6096
|
-
return `${readFileSync6(join16(dir, "SKILL.md"), "utf8")}${helpers.length ? `
|
|
6188
|
+
const skill = readSkillFile(variantDir, wanted);
|
|
6189
|
+
if (!skill)
|
|
6190
|
+
return `There is no skill named ${wanted}.${skills.length ? ` Your skills: ${skills.join(", ")}.` : ""}`;
|
|
6191
|
+
const helperNote = skill.helpers.length ? `
|
|
6097
6192
|
|
|
6098
|
-
(This skill's helpers are in ${join16(
|
|
6193
|
+
(This skill's helpers are in ${join16(skill.baseDir, "scripts")}: ${skill.helpers.join(", ")}.)` : "";
|
|
6194
|
+
return `${readFileSync7(skill.path, "utf8")}${helperNote}`;
|
|
6099
6195
|
}
|
|
6100
6196
|
|
|
6101
6197
|
// src/tools/harnessTools.ts
|
|
@@ -6286,6 +6382,7 @@ var VARIANT_PLUGIN = {
|
|
|
6286
6382
|
{ id: "memory", name: "Memory", description: "c3: durable facts about this kind of work." },
|
|
6287
6383
|
{ id: "subagents", name: "Subagents", description: "c5: the list of this variant's subagents." },
|
|
6288
6384
|
{ id: "scripts", name: "Scripts", description: "c7: where this variant's scripts are." },
|
|
6385
|
+
{ id: "required-skills", name: "Required skills", description: "Skills the person named as required for one request." },
|
|
6289
6386
|
{ id: "reporting", name: "Reporting", description: "Fixed foundation text, not part of the variant: how the final reply is shaped." }
|
|
6290
6387
|
],
|
|
6291
6388
|
tools: tools(SKILL_TOOLS)
|
|
@@ -6692,7 +6789,80 @@ import { matchesKey as matchesKey4, truncateToWidth as truncateToWidth4 } from "
|
|
|
6692
6789
|
import { fuzzyFilter, matchesKey as matchesKey3, parseColor as parseColor2, truncateToWidth as truncateToWidth3, visibleWidth as visibleWidth4 } from "@earendil-works/pi-tui";
|
|
6693
6790
|
|
|
6694
6791
|
// src/terminal/tui/search.ts
|
|
6695
|
-
import {
|
|
6792
|
+
import { matchesKey as matchesKey2, truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth3 } from "@earendil-works/pi-tui";
|
|
6793
|
+
|
|
6794
|
+
// src/terminal/tui/mouseReport.ts
|
|
6795
|
+
import { Input } from "@earendil-works/pi-tui";
|
|
6796
|
+
var MOUSE_REPORT_TAIL_WINDOW_MS = 200;
|
|
6797
|
+
var REPORT = /^<\d{1,3};\d{1,4};\d{1,4}[Mm]$/;
|
|
6798
|
+
var PARTIAL = /^<\d{0,3}(;\d{0,4}){0,2}$/;
|
|
6799
|
+
var WHOLE = /^(?:\x1b\[)?<\d{1,3};\d{1,4};\d{1,4}[Mm]$/;
|
|
6800
|
+
var SGR_REPORT = /\x1b\[<\d+;\d+;\d+[Mm]|\\u001b\[<\d+;\d+;\d+[Mm]|\\x1b\[<\d+;\d+;\d+[Mm]/g;
|
|
6801
|
+
var X10_REPORT = /\x1b\[M[\u0020-\u00ff]{3}/g;
|
|
6802
|
+
|
|
6803
|
+
class MouseReportTail {
|
|
6804
|
+
text = "";
|
|
6805
|
+
startedAt = 0;
|
|
6806
|
+
push(sequence, nowMs = Date.now()) {
|
|
6807
|
+
if (WHOLE.test(sequence)) {
|
|
6808
|
+
this.reset();
|
|
6809
|
+
return { report: sequence.replace(/^\x1b\[/, ""), inserted: "" };
|
|
6810
|
+
}
|
|
6811
|
+
if (sequence.length !== 1) {
|
|
6812
|
+
this.reset();
|
|
6813
|
+
return;
|
|
6814
|
+
}
|
|
6815
|
+
if (sequence === "<") {
|
|
6816
|
+
this.text = "<";
|
|
6817
|
+
this.startedAt = nowMs;
|
|
6818
|
+
return;
|
|
6819
|
+
}
|
|
6820
|
+
if (!this.text)
|
|
6821
|
+
return;
|
|
6822
|
+
if (nowMs - this.startedAt > MOUSE_REPORT_TAIL_WINDOW_MS) {
|
|
6823
|
+
this.reset();
|
|
6824
|
+
return;
|
|
6825
|
+
}
|
|
6826
|
+
const next = this.text + sequence;
|
|
6827
|
+
if (REPORT.test(next)) {
|
|
6828
|
+
const inserted = this.text;
|
|
6829
|
+
this.reset();
|
|
6830
|
+
return { report: next, inserted };
|
|
6831
|
+
}
|
|
6832
|
+
if (!PARTIAL.test(next)) {
|
|
6833
|
+
this.reset();
|
|
6834
|
+
return;
|
|
6835
|
+
}
|
|
6836
|
+
this.text = next;
|
|
6837
|
+
return;
|
|
6838
|
+
}
|
|
6839
|
+
reset() {
|
|
6840
|
+
this.text = "";
|
|
6841
|
+
this.startedAt = 0;
|
|
6842
|
+
}
|
|
6843
|
+
}
|
|
6844
|
+
|
|
6845
|
+
class MouseSafeInput extends Input {
|
|
6846
|
+
mouseTail = new MouseReportTail;
|
|
6847
|
+
handleInput(data) {
|
|
6848
|
+
super.handleInput(data);
|
|
6849
|
+
const match = this.mouseTail.push(data);
|
|
6850
|
+
if (!match)
|
|
6851
|
+
return;
|
|
6852
|
+
const value = this.getValue();
|
|
6853
|
+
if (value.endsWith(match.report))
|
|
6854
|
+
this.setValue(value.slice(0, -match.report.length));
|
|
6855
|
+
}
|
|
6856
|
+
setValue(value) {
|
|
6857
|
+
this.mouseTail.reset();
|
|
6858
|
+
super.setValue(value);
|
|
6859
|
+
}
|
|
6860
|
+
}
|
|
6861
|
+
function stripMouseReports(text) {
|
|
6862
|
+
return text.replace(SGR_REPORT, "").replace(X10_REPORT, "");
|
|
6863
|
+
}
|
|
6864
|
+
|
|
6865
|
+
// src/terminal/tui/search.ts
|
|
6696
6866
|
var SEARCH_GLYPH = "\u2315";
|
|
6697
6867
|
var SEARCH_PLACEHOLDER = "Type to search";
|
|
6698
6868
|
var SEARCH_FIELD_ROWS = 5;
|
|
@@ -6709,7 +6879,7 @@ class SearchField {
|
|
|
6709
6879
|
input;
|
|
6710
6880
|
constructor(theme, placeholder = SEARCH_PLACEHOLDER) {
|
|
6711
6881
|
this.theme = theme;
|
|
6712
|
-
this.input = new
|
|
6882
|
+
this.input = new MouseSafeInput({ prompt: "", placeholder, placeholderStyle: (text) => this.theme().fg("dim", text) });
|
|
6713
6883
|
}
|
|
6714
6884
|
get value() {
|
|
6715
6885
|
return this.input.getValue();
|
|
@@ -11278,7 +11448,7 @@ var payloadOf2 = (event) => event.payload && typeof event.payload === "object" ?
|
|
|
11278
11448
|
var stringOf2 = (value) => typeof value === "string" ? value : undefined;
|
|
11279
11449
|
var numberOf2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
11280
11450
|
function safe2(value) {
|
|
11281
|
-
return stripTerminalSequences4(value).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, " ");
|
|
11451
|
+
return stripTerminalSequences4(stripMouseReports(value)).replace(/[\x00-\x08\x0b-\x1f\x7f]/g, " ");
|
|
11282
11452
|
}
|
|
11283
11453
|
function timeOf2(value) {
|
|
11284
11454
|
const parsed = Date.parse(value);
|
|
@@ -11514,8 +11684,8 @@ class TrajectoryPage {
|
|
|
11514
11684
|
return this.events.map((event) => ({
|
|
11515
11685
|
id: String(event.sequence),
|
|
11516
11686
|
label: `${timeOf2(event.timestamp)} ${event.type}`,
|
|
11517
|
-
detail: eventDescription(event),
|
|
11518
|
-
searchText: `${event.timestamp} ${event.type} ${eventDescription(event)} ${safe2(JSON.stringify(payloadOf2(event)))}`,
|
|
11687
|
+
detail: safe2(eventDescription(event)),
|
|
11688
|
+
searchText: `${event.timestamp} ${event.type} ${safe2(eventDescription(event))} ${safe2(JSON.stringify(payloadOf2(event)))}`,
|
|
11519
11689
|
data: event
|
|
11520
11690
|
}));
|
|
11521
11691
|
}
|
|
@@ -11654,7 +11824,7 @@ class TrajectoryPage {
|
|
|
11654
11824
|
if (rows >= 4)
|
|
11655
11825
|
lines.push(theme.fg("muted", usageLine(this.events)));
|
|
11656
11826
|
if (rows >= 6)
|
|
11657
|
-
lines.push(theme.fg("dim", this.run.goal ? `Prompt: ${this.run.goal.replace(/\s+/g, " ").slice(0, 140)}` : "Prompt text unavailable"));
|
|
11827
|
+
lines.push(theme.fg("dim", this.run.goal ? `Prompt: ${safe2(this.run.goal).replace(/\s+/g, " ").slice(0, 140)}` : "Prompt text unavailable"));
|
|
11658
11828
|
if (this.error)
|
|
11659
11829
|
lines.push(theme.fg("error", `Read error: ${this.error}`));
|
|
11660
11830
|
return lines.slice(0, Math.min(rows, 5)).map((line) => truncateToWidth14(line, width, ""));
|
|
@@ -11961,7 +12131,7 @@ class WorktreesPage {
|
|
|
11961
12131
|
}
|
|
11962
12132
|
|
|
11963
12133
|
// src/terminal/tui/pages/annotatePage.ts
|
|
11964
|
-
import {
|
|
12134
|
+
import { matchesKey as matchesKey15, truncateToWidth as truncateToWidth16, visibleWidth as visibleWidth10 } from "@earendil-works/pi-tui";
|
|
11965
12135
|
|
|
11966
12136
|
// src/terminal/tui/annotateCommand.ts
|
|
11967
12137
|
function normalizeAddress(text) {
|
|
@@ -12233,7 +12403,7 @@ class AnnotatePage {
|
|
|
12233
12403
|
return "Load the bridge script in the app (shown at the top), then reload the app.";
|
|
12234
12404
|
}
|
|
12235
12405
|
openField(kind) {
|
|
12236
|
-
const input = new
|
|
12406
|
+
const input = new MouseSafeInput({ prompt: "", placeholder: FIELDS[kind].placeholder, placeholderStyle: (text) => this.options.theme().fg("dim", text) });
|
|
12237
12407
|
this.field = { kind, input };
|
|
12238
12408
|
this.syncFocus();
|
|
12239
12409
|
this.options.changed();
|
|
@@ -14378,15 +14548,6 @@ class ResumeSessionsPage {
|
|
|
14378
14548
|
}
|
|
14379
14549
|
}
|
|
14380
14550
|
|
|
14381
|
-
// src/terminal/tui/skillInvocation.ts
|
|
14382
|
-
function expandSkillInvocation(text, skills) {
|
|
14383
|
-
const trimmed = text.trimStart();
|
|
14384
|
-
const match = /^\$(\S+)(?=\s|$)/.exec(trimmed);
|
|
14385
|
-
if (!match || !skills.has(match[1]))
|
|
14386
|
-
return text;
|
|
14387
|
-
return `/skill:${match[1]}${trimmed.slice(match[0].length)}`;
|
|
14388
|
-
}
|
|
14389
|
-
|
|
14390
14551
|
// src/terminal/commandHelp.ts
|
|
14391
14552
|
function formatTerminalCommandLabel(name, aliases) {
|
|
14392
14553
|
return aliases?.length ? `${name} (${aliases.join(", ")})` : name;
|
|
@@ -14478,7 +14639,6 @@ function createTerminalCommands(host) {
|
|
|
14478
14639
|
return [];
|
|
14479
14640
|
return session.resourceLoader.getSkills().skills.map((skill) => ({ name: skill.name, description: skill.description }));
|
|
14480
14641
|
};
|
|
14481
|
-
const expandSkill = (text) => expandSkillInvocation(text, new Set(skills().map((skill) => skill.name)));
|
|
14482
14642
|
async function openPage(id, options = {}) {
|
|
14483
14643
|
let close = () => {};
|
|
14484
14644
|
const page = createCommandPage(host, id, { ...options, close: () => close() });
|
|
@@ -14930,7 +15090,7 @@ function createTerminalCommands(host) {
|
|
|
14930
15090
|
}
|
|
14931
15091
|
return true;
|
|
14932
15092
|
}
|
|
14933
|
-
return { execute, completions, skills,
|
|
15093
|
+
return { execute, completions, skills, legacyAliases };
|
|
14934
15094
|
}
|
|
14935
15095
|
|
|
14936
15096
|
// src/sessions/planMode.ts
|
|
@@ -15072,7 +15232,7 @@ function parseProposedPlan(text) {
|
|
|
15072
15232
|
};
|
|
15073
15233
|
}
|
|
15074
15234
|
// src/terminal/tui/dialogs.ts
|
|
15075
|
-
import { Editor,
|
|
15235
|
+
import { Editor, matchesKey as matchesKey18, stripTerminalSequences as stripTerminalSequences11, truncateToWidth as truncateToWidth22, visibleWidth as visibleWidth14, wrapTextWithAnsi as wrapTextWithAnsi14 } from "@earendil-works/pi-tui";
|
|
15076
15236
|
function selectTheme(theme) {
|
|
15077
15237
|
return {
|
|
15078
15238
|
selectedPrefix: (text) => selectionText(theme, text),
|
|
@@ -15083,7 +15243,7 @@ function selectTheme(theme) {
|
|
|
15083
15243
|
};
|
|
15084
15244
|
}
|
|
15085
15245
|
|
|
15086
|
-
class SecretInput extends
|
|
15246
|
+
class SecretInput extends MouseSafeInput {
|
|
15087
15247
|
render(width) {
|
|
15088
15248
|
const lines = super.render(width);
|
|
15089
15249
|
if (!this.getValue())
|
|
@@ -15357,7 +15517,7 @@ class TerminalDialogs {
|
|
|
15357
15517
|
}
|
|
15358
15518
|
input(title, placeholder, opts, secret = false) {
|
|
15359
15519
|
return this.show((done) => {
|
|
15360
|
-
const InputType = secret ? SecretInput :
|
|
15520
|
+
const InputType = secret ? SecretInput : MouseSafeInput;
|
|
15361
15521
|
const input = new InputType({ prompt: secret ? "" : "> ", placeholder, placeholderStyle: (text) => this.theme().fg("dim", text) });
|
|
15362
15522
|
input.onSubmit = done;
|
|
15363
15523
|
input.onEscape = () => done(undefined);
|
|
@@ -15449,7 +15609,7 @@ class TerminalDialogs {
|
|
|
15449
15609
|
}
|
|
15450
15610
|
|
|
15451
15611
|
// src/terminal/tui/keybindings.ts
|
|
15452
|
-
import { readFileSync as
|
|
15612
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
15453
15613
|
import { join as join17 } from "path";
|
|
15454
15614
|
import { KeybindingsManager, TUI_KEYBINDINGS } from "@earendil-works/pi-tui";
|
|
15455
15615
|
var actions = {
|
|
@@ -15469,7 +15629,7 @@ var actions = {
|
|
|
15469
15629
|
"app.thinking.toggle": "ctrl+t",
|
|
15470
15630
|
"app.editor.external": "ctrl+g",
|
|
15471
15631
|
"app.message.copy": "ctrl+x",
|
|
15472
|
-
"app.message.
|
|
15632
|
+
"app.message.steer": ["super+enter", "ctrl+enter", "alt+enter"],
|
|
15473
15633
|
"app.message.dequeue": "alt+up",
|
|
15474
15634
|
"app.clipboard.pasteImage": process.platform === "darwin" ? ["ctrl+v", "alt+v"] : "ctrl+v",
|
|
15475
15635
|
"app.session.toggleNamedFilter": "ctrl+n",
|
|
@@ -15507,7 +15667,7 @@ var HARNESS_KEYBINDINGS = {
|
|
|
15507
15667
|
function readBindings(agentDir) {
|
|
15508
15668
|
let bindings = {};
|
|
15509
15669
|
try {
|
|
15510
|
-
const source = JSON.parse(
|
|
15670
|
+
const source = JSON.parse(readFileSync8(join17(agentDir, "keybindings.json"), "utf8"));
|
|
15511
15671
|
if (source && typeof source === "object" && !Array.isArray(source)) {
|
|
15512
15672
|
bindings = Object.fromEntries(Object.entries(source).filter(([, value]) => typeof value === "string" || Array.isArray(value) && value.every((key) => typeof key === "string")));
|
|
15513
15673
|
}
|
|
@@ -15576,6 +15736,7 @@ import { closeSync as closeSync2, existsSync as existsSync11, openSync as openSy
|
|
|
15576
15736
|
import { isAbsolute as isAbsolute5, join as join21, relative as relative4, resolve as resolve4, sep as sep5 } from "path";
|
|
15577
15737
|
|
|
15578
15738
|
// src/terminal/piExtension.ts
|
|
15739
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
15579
15740
|
import { createBashTool, createEditTool, createPowerShellTool, createWriteTool } from "@earendil-works/pi-coding-agent";
|
|
15580
15741
|
import { Type as Type2 } from "typebox";
|
|
15581
15742
|
|
|
@@ -15740,8 +15901,35 @@ function parseInitialBashCommand(input) {
|
|
|
15740
15901
|
return outside ? { command: input.slice(outside[0].length).trim(), workspaceWriteScope: "outside" } : { command: input, workspaceWriteScope: "workspace" };
|
|
15741
15902
|
}
|
|
15742
15903
|
|
|
15904
|
+
// src/terminal/tui/skillInvocation.ts
|
|
15905
|
+
var SKILL_TOKEN = /(?:^|[\s(])\$([^\s$]*)$/;
|
|
15906
|
+
var SKILL_INVOCATION = /(?:^|[\s(])\$([A-Za-z0-9][A-Za-z0-9_-]*)(?=[\s.,;:!?)]|$)/gu;
|
|
15907
|
+
function skillTokenAt(text, caret) {
|
|
15908
|
+
const match = SKILL_TOKEN.exec(text.slice(0, caret));
|
|
15909
|
+
if (!match)
|
|
15910
|
+
return null;
|
|
15911
|
+
const query = match[1] ?? "";
|
|
15912
|
+
return { query, from: caret - query.length - 1, to: caret };
|
|
15913
|
+
}
|
|
15914
|
+
function skillInvocationMatches(text) {
|
|
15915
|
+
const found = [];
|
|
15916
|
+
for (const match of text.matchAll(SKILL_INVOCATION)) {
|
|
15917
|
+
const from = match.index + match[0].indexOf("$");
|
|
15918
|
+
found.push({ name: match[1], from, to: from + 1 + match[1].length });
|
|
15919
|
+
}
|
|
15920
|
+
return found;
|
|
15921
|
+
}
|
|
15922
|
+
function skillInvocations(text, skills) {
|
|
15923
|
+
const found = [];
|
|
15924
|
+
for (const match of skillInvocationMatches(text)) {
|
|
15925
|
+
if (skills.has(match.name) && !found.includes(match.name))
|
|
15926
|
+
found.push(match.name);
|
|
15927
|
+
}
|
|
15928
|
+
return found;
|
|
15929
|
+
}
|
|
15930
|
+
|
|
15743
15931
|
// src/core/workspace.ts
|
|
15744
|
-
import { lstatSync as lstatSync2, realpathSync, statSync as
|
|
15932
|
+
import { lstatSync as lstatSync2, realpathSync, statSync as statSync6 } from "fs";
|
|
15745
15933
|
import { homedir as homedir2 } from "os";
|
|
15746
15934
|
import { basename as basename6, dirname as dirname7, isAbsolute as isAbsolute4, join as join19, relative as relative3, resolve as resolve2, sep as sep4 } from "path";
|
|
15747
15935
|
function resolveWritePath(candidate, cwd = process.cwd()) {
|
|
@@ -15751,7 +15939,7 @@ function resolveWritePath(candidate, cwd = process.cwd()) {
|
|
|
15751
15939
|
}
|
|
15752
15940
|
function isWithinWorkspace(workspaceRoot, canonicalCandidate) {
|
|
15753
15941
|
const canonicalRoot = realpathSync(workspaceRoot);
|
|
15754
|
-
if (!
|
|
15942
|
+
if (!statSync6(canonicalRoot).isDirectory())
|
|
15755
15943
|
throw invalid("The workspace root must be a directory.");
|
|
15756
15944
|
const fromRoot = relative3(canonicalRoot, canonicalCandidate);
|
|
15757
15945
|
return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`) && !isAbsolute4(fromRoot);
|
|
@@ -15760,7 +15948,7 @@ function verifyWorkspaceRoot(workspaceRoot) {
|
|
|
15760
15948
|
const canonicalRoot = realpathSync(workspaceRoot);
|
|
15761
15949
|
if (dirname7(canonicalRoot) === canonicalRoot)
|
|
15762
15950
|
throw invalid("The filesystem root cannot be used as an active project workspace.");
|
|
15763
|
-
if (canonicalRoot !== resolve2(workspaceRoot) || !
|
|
15951
|
+
if (canonicalRoot !== resolve2(workspaceRoot) || !statSync6(canonicalRoot).isDirectory()) {
|
|
15764
15952
|
throw invalid("The active project workspace changed after this run began.");
|
|
15765
15953
|
}
|
|
15766
15954
|
return canonicalRoot;
|
|
@@ -15811,7 +15999,7 @@ function inspectWorkspaceWrite(workspaceRoot, cwd, tool, input) {
|
|
|
15811
15999
|
import { Type } from "typebox";
|
|
15812
16000
|
|
|
15813
16001
|
// src/core/shell.ts
|
|
15814
|
-
import { accessSync as accessSync2, constants as constants2, statSync as
|
|
16002
|
+
import { accessSync as accessSync2, constants as constants2, statSync as statSync7 } from "fs";
|
|
15815
16003
|
import { win32 } from "path";
|
|
15816
16004
|
var POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command"];
|
|
15817
16005
|
var POWERSHELL_UTF8 = `try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}
|
|
@@ -15864,7 +16052,7 @@ function isWslLauncher(path, env) {
|
|
|
15864
16052
|
function isFile(path) {
|
|
15865
16053
|
try {
|
|
15866
16054
|
accessSync2(path, constants2.F_OK);
|
|
15867
|
-
return
|
|
16055
|
+
return statSync7(path).isFile();
|
|
15868
16056
|
} catch {
|
|
15869
16057
|
return false;
|
|
15870
16058
|
}
|
|
@@ -15874,7 +16062,7 @@ function isFile(path) {
|
|
|
15874
16062
|
import { AsyncLocalStorage } from "async_hooks";
|
|
15875
16063
|
import { spawn } from "child_process";
|
|
15876
16064
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
15877
|
-
import { accessSync as accessSync3, constants as constants3, existsSync as existsSync10, lstatSync as lstatSync3, mkdtempSync, realpathSync as realpathSync2, readdirSync as
|
|
16065
|
+
import { accessSync as accessSync3, constants as constants3, existsSync as existsSync10, lstatSync as lstatSync3, mkdtempSync, realpathSync as realpathSync2, readdirSync as readdirSync8, rmSync as rmSync6, statSync as statSync8 } from "fs";
|
|
15878
16066
|
import { tmpdir } from "os";
|
|
15879
16067
|
import { join as join20 } from "path";
|
|
15880
16068
|
import { spawnSync } from "child_process";
|
|
@@ -15896,7 +16084,7 @@ function systemExecutable(candidates) {
|
|
|
15896
16084
|
for (const candidate of candidates) {
|
|
15897
16085
|
try {
|
|
15898
16086
|
accessSync3(candidate, constants3.X_OK);
|
|
15899
|
-
if (
|
|
16087
|
+
if (statSync8(candidate).isFile())
|
|
15900
16088
|
return candidate;
|
|
15901
16089
|
} catch {}
|
|
15902
16090
|
}
|
|
@@ -16133,7 +16321,7 @@ function assertNoExternalHardlinks(workspaceRoot) {
|
|
|
16133
16321
|
const visit = (directory) => {
|
|
16134
16322
|
let entries;
|
|
16135
16323
|
try {
|
|
16136
|
-
entries =
|
|
16324
|
+
entries = readdirSync8(directory, { withFileTypes: true });
|
|
16137
16325
|
} catch (error) {
|
|
16138
16326
|
throw new Error(`Workspace hard-link inspection failed at ${directory}; Bash was not run: ${error instanceof Error ? error.message : String(error)}`);
|
|
16139
16327
|
}
|
|
@@ -16268,7 +16456,7 @@ function withWorkspaceWriteScope(tool, note = "") {
|
|
|
16268
16456
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
16269
16457
|
import { spawn as spawn2 } from "child_process";
|
|
16270
16458
|
import { createHash as createHash3 } from "crypto";
|
|
16271
|
-
import { constants as fsConstants, realpathSync as realpathSync3, statSync as
|
|
16459
|
+
import { constants as fsConstants, realpathSync as realpathSync3, statSync as statSync9 } from "fs";
|
|
16272
16460
|
import { access, readFile } from "fs/promises";
|
|
16273
16461
|
import { dirname as dirname8, resolve as resolve3 } from "path";
|
|
16274
16462
|
var writeAuthorization = new AsyncLocalStorage2;
|
|
@@ -16342,7 +16530,7 @@ function nearestExistingDirectory(path) {
|
|
|
16342
16530
|
while (true) {
|
|
16343
16531
|
try {
|
|
16344
16532
|
const canonical = realpathSync3(current);
|
|
16345
|
-
if (!
|
|
16533
|
+
if (!statSync9(canonical).isDirectory())
|
|
16346
16534
|
throw new Error(`Write scope is not a directory: ${canonical}`);
|
|
16347
16535
|
return canonical;
|
|
16348
16536
|
} catch (error) {
|
|
@@ -16734,6 +16922,32 @@ async function piExtension_default(pi, planRun, getSession) {
|
|
|
16734
16922
|
if (notice)
|
|
16735
16923
|
return { message: { customType: SUBAGENT_RESULTS, content: notice.text, display: false, details: { [SUBAGENT_RECEIPT]: notice.receipt } } };
|
|
16736
16924
|
});
|
|
16925
|
+
pi.on("before_agent_start", async (event) => {
|
|
16926
|
+
if (event.messageType !== "user" || /^\s*!/u.test(event.prompt))
|
|
16927
|
+
return;
|
|
16928
|
+
if (pi.getSettings?.().enableSkillCommands === false)
|
|
16929
|
+
return;
|
|
16930
|
+
const skills = (pi.getCommands?.() ?? []).filter((command) => command.source === "skill").map((command) => ({
|
|
16931
|
+
name: command.name.replace(/^skill:/, ""),
|
|
16932
|
+
path: command.sourceInfo.path,
|
|
16933
|
+
...command.sourceInfo.baseDir ? { baseDir: command.sourceInfo.baseDir } : {}
|
|
16934
|
+
}));
|
|
16935
|
+
if (!skills.length)
|
|
16936
|
+
return;
|
|
16937
|
+
const names = skillInvocations(event.prompt, new Set(skills.map((skill) => skill.name)));
|
|
16938
|
+
if (!names.length)
|
|
16939
|
+
return;
|
|
16940
|
+
const entries = names.map((name) => {
|
|
16941
|
+
const skill = skills.find((candidate) => candidate.name === name);
|
|
16942
|
+
try {
|
|
16943
|
+
return { name, path: skill.path, baseDir: skill.baseDir, body: bodyOf(readFileSync9(skill.path, "utf8")) };
|
|
16944
|
+
} catch {
|
|
16945
|
+
return { name, path: skill.path, body: "" };
|
|
16946
|
+
}
|
|
16947
|
+
});
|
|
16948
|
+
const content = requiredSkillsText(entries);
|
|
16949
|
+
return content ? { message: { customType: "xingular-required-skills", content, display: false, details: { skills: names } } } : undefined;
|
|
16950
|
+
});
|
|
16737
16951
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
16738
16952
|
if (event.messageType !== "user")
|
|
16739
16953
|
return;
|
|
@@ -17111,7 +17325,7 @@ class ModeChoices {
|
|
|
17111
17325
|
this.quiet -= 1;
|
|
17112
17326
|
}
|
|
17113
17327
|
}
|
|
17114
|
-
async apply(session, mode, modelFor) {
|
|
17328
|
+
async apply(session, mode, modelFor, fallback) {
|
|
17115
17329
|
const choice = this.resolve(mode);
|
|
17116
17330
|
await this.quietly(async () => {
|
|
17117
17331
|
const model = choice.model ? modelFor(choice.model) : undefined;
|
|
@@ -17120,8 +17334,7 @@ class ModeChoices {
|
|
|
17120
17334
|
await session.setModel(model, { persist: false });
|
|
17121
17335
|
} catch {}
|
|
17122
17336
|
}
|
|
17123
|
-
|
|
17124
|
-
session.setThinkingLevel(choice.effort, { persist: false });
|
|
17337
|
+
session.setThinkingLevel(choice.effort ?? fallback, { persist: false });
|
|
17125
17338
|
});
|
|
17126
17339
|
}
|
|
17127
17340
|
async record(mode, patch) {
|
|
@@ -17271,7 +17484,11 @@ async function createTerminalRuntime(args, host) {
|
|
|
17271
17484
|
const modeChoices = new ModeChoices(choiceStore);
|
|
17272
17485
|
const recordingMode = () => planRun.active ? "plan" : currentMode;
|
|
17273
17486
|
const modelFor = (reference) => resolveCliModel({ cliModel: reference, modelRuntime }).model;
|
|
17274
|
-
const
|
|
17487
|
+
const defaultEffort = (session) => {
|
|
17488
|
+
const model = session.model;
|
|
17489
|
+
return (model ? session.settingsManager.getModelThinkingLevel(model.provider, model.id) : undefined) ?? session.settingsManager.getDefaultThinkingLevel() ?? "medium";
|
|
17490
|
+
};
|
|
17491
|
+
const applyMode = () => modeChoices.apply(native.session, currentMode, modelFor, defaultEffort(native.session));
|
|
17275
17492
|
const restoreChoice = async (session, previous) => {
|
|
17276
17493
|
if (previous.model && (session.model?.provider !== previous.model.provider || session.model?.id !== previous.model.id)) {
|
|
17277
17494
|
await session.setModel(previous.model, { persist: false }).catch(() => {
|
|
@@ -17467,7 +17684,7 @@ async function createTerminalRuntime(args, host) {
|
|
|
17467
17684
|
planRun.active = true;
|
|
17468
17685
|
try {
|
|
17469
17686
|
session.setActiveToolsByName(allowedTools);
|
|
17470
|
-
await modeChoices.apply(session, "plan", modelFor);
|
|
17687
|
+
await modeChoices.apply(session, "plan", modelFor, defaultEffort(session));
|
|
17471
17688
|
await session.prompt(text, { expandPromptTemplates: false, ...images?.length ? { images } : {} });
|
|
17472
17689
|
} finally {
|
|
17473
17690
|
planRun.active = false;
|
|
@@ -19860,6 +20077,7 @@ class OpenTuiApplication {
|
|
|
19860
20077
|
host = new PiDialogHost(() => this.change());
|
|
19861
20078
|
transcript = new TranscriptModel;
|
|
19862
20079
|
listeners = new Set;
|
|
20080
|
+
animationListeners = new Set;
|
|
19863
20081
|
cleanup = [];
|
|
19864
20082
|
statuses = new Map;
|
|
19865
20083
|
widgets = new Map;
|
|
@@ -19884,8 +20102,10 @@ class OpenTuiApplication {
|
|
|
19884
20102
|
editorFactory;
|
|
19885
20103
|
customEditor;
|
|
19886
20104
|
draft = "";
|
|
20105
|
+
mouseTail = new MouseReportTail;
|
|
19887
20106
|
draftEpoch = 0;
|
|
19888
20107
|
draftCursorOffset = null;
|
|
20108
|
+
paletteCaret = null;
|
|
19889
20109
|
attachments = new ComposerAttachments;
|
|
19890
20110
|
insertion = null;
|
|
19891
20111
|
fileCompletion = null;
|
|
@@ -19930,8 +20150,9 @@ class OpenTuiApplication {
|
|
|
19930
20150
|
done = new Promise((resolve) => {
|
|
19931
20151
|
this.finish = resolve;
|
|
19932
20152
|
});
|
|
19933
|
-
pendingPolling = false;
|
|
19934
20153
|
pendingBusy = false;
|
|
20154
|
+
decisionWatch;
|
|
20155
|
+
pendingRequests = [];
|
|
19935
20156
|
pendingAbort;
|
|
19936
20157
|
pendingId;
|
|
19937
20158
|
dismissedDecisions = new Set;
|
|
@@ -19955,14 +20176,17 @@ class OpenTuiApplication {
|
|
|
19955
20176
|
const target = raw ? linkTarget(raw, { base: this.runtime?.cwd }) : null;
|
|
19956
20177
|
const next = target ? `${target.kind}:${"url" in target ? target.url : ("address" in target) ? target.address : target.path}` : raw;
|
|
19957
20178
|
const entered = next !== this.hoveredLink;
|
|
20179
|
+
if (!entered && !next)
|
|
20180
|
+
return;
|
|
19958
20181
|
if (!entered && x === this.hoveredAt.x && y === this.hoveredAt.y)
|
|
19959
20182
|
return;
|
|
19960
20183
|
this.hoveredLink = next;
|
|
19961
20184
|
this.hoveredLinkId = next ? this.renderer?.getLinkIdAt(x, y) ?? 0 : 0;
|
|
19962
20185
|
this.hoveredAt = { x, y };
|
|
19963
|
-
if (entered)
|
|
20186
|
+
if (entered) {
|
|
19964
20187
|
this.renderer?.setMousePointer(next ? "pointer" : "default");
|
|
19965
|
-
|
|
20188
|
+
this.change();
|
|
20189
|
+
}
|
|
19966
20190
|
this.renderer?.requestRender();
|
|
19967
20191
|
}
|
|
19968
20192
|
activateLinkAt(x, y, modifiers = {}) {
|
|
@@ -20003,6 +20227,29 @@ class OpenTuiApplication {
|
|
|
20003
20227
|
this.listeners.delete(listener);
|
|
20004
20228
|
};
|
|
20005
20229
|
}
|
|
20230
|
+
subscribeAnimation(listener) {
|
|
20231
|
+
this.animationListeners.add(listener);
|
|
20232
|
+
return () => {
|
|
20233
|
+
this.animationListeners.delete(listener);
|
|
20234
|
+
};
|
|
20235
|
+
}
|
|
20236
|
+
animationView() {
|
|
20237
|
+
return {
|
|
20238
|
+
working: this.working(),
|
|
20239
|
+
subagents: subagentRoster(this.subagents, Math.max(20, this.host.columns - 4), this.workingSpinnerFrame(this.startedAt ?? Date.now()))
|
|
20240
|
+
};
|
|
20241
|
+
}
|
|
20242
|
+
animate() {
|
|
20243
|
+
if (this.startedAt !== null || this.compacting) {
|
|
20244
|
+
for (const listener of this.animationListeners) {
|
|
20245
|
+
try {
|
|
20246
|
+
listener();
|
|
20247
|
+
} catch {}
|
|
20248
|
+
}
|
|
20249
|
+
}
|
|
20250
|
+
if (this.sideQuestion?.state === "thinking")
|
|
20251
|
+
this.change();
|
|
20252
|
+
}
|
|
20006
20253
|
get hasFailure() {
|
|
20007
20254
|
return !!this.failure;
|
|
20008
20255
|
}
|
|
@@ -20255,16 +20502,18 @@ class OpenTuiApplication {
|
|
|
20255
20502
|
this.initialized = true;
|
|
20256
20503
|
this.refresh();
|
|
20257
20504
|
this.refreshSubagents(true);
|
|
20505
|
+
const decisionWatch = new AbortController;
|
|
20506
|
+
this.decisionWatch = decisionWatch;
|
|
20507
|
+
this.cleanup.push(() => decisionWatch.abort());
|
|
20508
|
+
this.watchDecisions(decisionWatch.signal);
|
|
20258
20509
|
let lastSubagentPoll = 0;
|
|
20259
20510
|
const tick = setInterval(() => {
|
|
20260
|
-
|
|
20261
|
-
this.change();
|
|
20511
|
+
this.animate();
|
|
20262
20512
|
const now = Date.now();
|
|
20263
20513
|
if (this.startedAt !== null && now - lastSubagentPoll >= 1000) {
|
|
20264
20514
|
lastSubagentPoll = now;
|
|
20265
20515
|
this.refreshSubagents();
|
|
20266
20516
|
}
|
|
20267
|
-
this.decisions();
|
|
20268
20517
|
}, 100);
|
|
20269
20518
|
const git = setInterval(() => {
|
|
20270
20519
|
this.refreshBranch();
|
|
@@ -20570,20 +20819,37 @@ class OpenTuiApplication {
|
|
|
20570
20819
|
this.transcript.invalidate();
|
|
20571
20820
|
this.change();
|
|
20572
20821
|
}
|
|
20573
|
-
async
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20822
|
+
async watchDecisions(signal) {
|
|
20823
|
+
let revision;
|
|
20824
|
+
while (!signal.aborted && !this.stopped) {
|
|
20825
|
+
try {
|
|
20826
|
+
const snapshot = await controlCall("terminal.pending.wait", { revision }, 25000, signal);
|
|
20827
|
+
revision = snapshot.revision;
|
|
20828
|
+
this.decisions(snapshot.requests);
|
|
20829
|
+
} catch (error) {
|
|
20830
|
+
if (signal.aborted || this.stopped)
|
|
20831
|
+
return;
|
|
20582
20832
|
this.notify(error instanceof Error ? error.message : String(error), "error");
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
20833
|
+
revision = undefined;
|
|
20834
|
+
await new Promise((resolve) => {
|
|
20835
|
+
const timer = setTimeout(done, 1000);
|
|
20836
|
+
function done() {
|
|
20837
|
+
clearTimeout(timer);
|
|
20838
|
+
signal.removeEventListener("abort", done);
|
|
20839
|
+
resolve();
|
|
20840
|
+
}
|
|
20841
|
+
signal.addEventListener("abort", done, { once: true });
|
|
20842
|
+
if (signal.aborted)
|
|
20843
|
+
done();
|
|
20844
|
+
});
|
|
20845
|
+
}
|
|
20586
20846
|
}
|
|
20847
|
+
}
|
|
20848
|
+
async decisions(requests) {
|
|
20849
|
+
if (!this.runtime || this.stopped)
|
|
20850
|
+
return;
|
|
20851
|
+
requests ??= await this.runtime.pending();
|
|
20852
|
+
this.pendingRequests = requests;
|
|
20587
20853
|
if (this.pendingId && !requests.some((request) => request.requestId === this.pendingId))
|
|
20588
20854
|
this.pendingAbort?.abort();
|
|
20589
20855
|
if (this.pendingBusy)
|
|
@@ -20624,6 +20890,11 @@ class OpenTuiApplication {
|
|
|
20624
20890
|
this.pendingBusy = false;
|
|
20625
20891
|
this.pendingId = undefined;
|
|
20626
20892
|
this.pendingAbort = undefined;
|
|
20893
|
+
if (!this.stopped && this.pendingRequests.some((item) => !this.dismissedDecisions.has(item.requestId))) {
|
|
20894
|
+
setTimeout(() => {
|
|
20895
|
+
this.decisions(this.pendingRequests);
|
|
20896
|
+
}, 100);
|
|
20897
|
+
}
|
|
20627
20898
|
}
|
|
20628
20899
|
}
|
|
20629
20900
|
async showQuestionDock(questions, signal) {
|
|
@@ -20916,7 +21187,7 @@ class OpenTuiApplication {
|
|
|
20916
21187
|
if (this.paletteSuppressed)
|
|
20917
21188
|
return null;
|
|
20918
21189
|
const slash = /^\/([^\s]*)$/u.exec(this.draft);
|
|
20919
|
-
const skill =
|
|
21190
|
+
const skill = this.bashMode ? null : skillTokenAt(this.draft, this.paletteCaret ?? this.draft.length);
|
|
20920
21191
|
if (!this.paletteOpen && (this.bashMode || !slash && !skill))
|
|
20921
21192
|
return null;
|
|
20922
21193
|
let items = [];
|
|
@@ -20929,7 +21200,7 @@ class OpenTuiApplication {
|
|
|
20929
21200
|
} catch {
|
|
20930
21201
|
return null;
|
|
20931
21202
|
}
|
|
20932
|
-
const filter = this.paletteOpen ? this.paletteFilter : skill?.
|
|
21203
|
+
const filter = this.paletteOpen ? this.paletteFilter : skill?.query ?? slash?.[1] ?? "";
|
|
20933
21204
|
const matched = matchPalette(items, filter);
|
|
20934
21205
|
if (!matched.length)
|
|
20935
21206
|
return null;
|
|
@@ -20994,6 +21265,14 @@ class OpenTuiApplication {
|
|
|
20994
21265
|
this.paletteOpen = false;
|
|
20995
21266
|
this.paletteSuppressed = true;
|
|
20996
21267
|
this.paletteIndex = 0;
|
|
21268
|
+
const token = this.bashMode ? null : skillTokenAt(this.draft, this.paletteCaret ?? this.draft.length);
|
|
21269
|
+
if (token) {
|
|
21270
|
+
const tail = this.draft.slice(token.to);
|
|
21271
|
+
const suffix = tail === "" ? " " : /^[\s.,;:!?)]/u.test(tail) ? "" : " ";
|
|
21272
|
+
const inserted = `${item.prefix ?? "$"}${item.name}${suffix}`;
|
|
21273
|
+
this.setDraftExternal(`${this.draft.slice(0, token.from)}${inserted}${tail}`, token.from + inserted.length);
|
|
21274
|
+
return true;
|
|
21275
|
+
}
|
|
20997
21276
|
const text = `${item.prefix ?? "/"}${item.name} `;
|
|
20998
21277
|
this.setDraftExternal(text, text.length);
|
|
20999
21278
|
return true;
|
|
@@ -21007,7 +21286,7 @@ class OpenTuiApplication {
|
|
|
21007
21286
|
this.draftEpoch += 1;
|
|
21008
21287
|
this.change();
|
|
21009
21288
|
}
|
|
21010
|
-
setDraft(value) {
|
|
21289
|
+
setDraft(value, caret) {
|
|
21011
21290
|
if (this.paletteOpen) {
|
|
21012
21291
|
this.paletteFilter = value.startsWith("/") && !/\s/u.test(value) ? value.slice(1) : value;
|
|
21013
21292
|
this.paletteIndex = 0;
|
|
@@ -21015,15 +21294,20 @@ class OpenTuiApplication {
|
|
|
21015
21294
|
this.change();
|
|
21016
21295
|
return;
|
|
21017
21296
|
}
|
|
21297
|
+
let nextCaret = caret;
|
|
21018
21298
|
let modeChanged = false;
|
|
21019
21299
|
if (!this.bashMode) {
|
|
21020
21300
|
const bash = splitBashDraft(value);
|
|
21021
21301
|
if (bash.bashMode) {
|
|
21022
21302
|
modeChanged = true;
|
|
21023
21303
|
this.applyComposerMode("bash");
|
|
21304
|
+
if (nextCaret !== undefined)
|
|
21305
|
+
nextCaret = Math.max(0, nextCaret - (value.length - bash.body.length));
|
|
21024
21306
|
}
|
|
21025
21307
|
value = bash.body;
|
|
21026
21308
|
}
|
|
21309
|
+
if (nextCaret !== undefined)
|
|
21310
|
+
this.paletteCaret = nextCaret;
|
|
21027
21311
|
if (value === this.draft) {
|
|
21028
21312
|
if (modeChanged)
|
|
21029
21313
|
this.change();
|
|
@@ -21042,12 +21326,14 @@ class OpenTuiApplication {
|
|
|
21042
21326
|
this.change();
|
|
21043
21327
|
}
|
|
21044
21328
|
setDraftExternal(value, cursorOffset, preserveBashMode = false) {
|
|
21329
|
+
this.mouseTail.reset();
|
|
21045
21330
|
this.resetHistoryBrowse();
|
|
21046
21331
|
const bash = preserveBashMode ? { body: value, bashMode: this.bashMode } : splitBashDraft(value);
|
|
21047
21332
|
this.invalidateFileCompletion();
|
|
21048
21333
|
this.applyComposerMode(bash.bashMode ? "bash" : this.planMode ? "plan" : "normal");
|
|
21049
21334
|
const body = bash.body;
|
|
21050
21335
|
this.draft = body;
|
|
21336
|
+
this.paletteCaret = null;
|
|
21051
21337
|
this.attachments.retain(body);
|
|
21052
21338
|
this.draftCursorOffset = cursorOffset ?? null;
|
|
21053
21339
|
this.draftEpoch += 1;
|
|
@@ -21073,6 +21359,7 @@ class OpenTuiApplication {
|
|
|
21073
21359
|
this.invalidateFileCompletion();
|
|
21074
21360
|
this.applyComposerMode("bash");
|
|
21075
21361
|
this.draft = body;
|
|
21362
|
+
this.paletteCaret = null;
|
|
21076
21363
|
this.draftCursorOffset = null;
|
|
21077
21364
|
this.draftEpoch += 1;
|
|
21078
21365
|
this.paletteSuppressed = false;
|
|
@@ -21088,11 +21375,13 @@ class OpenTuiApplication {
|
|
|
21088
21375
|
this.change();
|
|
21089
21376
|
}
|
|
21090
21377
|
clearDraft() {
|
|
21378
|
+
this.mouseTail.reset();
|
|
21091
21379
|
this.resetHistoryBrowse();
|
|
21092
21380
|
this.invalidateFileCompletion();
|
|
21093
21381
|
this.applyComposerMode(this.planMode ? "plan" : "normal");
|
|
21094
21382
|
this.draft = "";
|
|
21095
21383
|
this.attachments.clear();
|
|
21384
|
+
this.paletteCaret = null;
|
|
21096
21385
|
this.draftCursorOffset = null;
|
|
21097
21386
|
this.draftEpoch += 1;
|
|
21098
21387
|
this.paletteSuppressed = false;
|
|
@@ -21136,7 +21425,8 @@ class OpenTuiApplication {
|
|
|
21136
21425
|
this.input(() => this.onSubmitPrompt());
|
|
21137
21426
|
}
|
|
21138
21427
|
onSubmitPrompt() {
|
|
21139
|
-
this.
|
|
21428
|
+
this.mouseTail.reset();
|
|
21429
|
+
this.submit(this.bashMode ? `!${this.draft}` : this.draft, true);
|
|
21140
21430
|
}
|
|
21141
21431
|
async runBash(text) {
|
|
21142
21432
|
const command = /^\s*!\s*([\s\S]+)$/u.exec(text)?.[1]?.trim();
|
|
@@ -21203,16 +21493,15 @@ class OpenTuiApplication {
|
|
|
21203
21493
|
this.enqueuePlan(text, images);
|
|
21204
21494
|
return;
|
|
21205
21495
|
}
|
|
21206
|
-
const expanded = this.commands.expandSkill(text);
|
|
21207
21496
|
if (this.planBarrierOpen) {
|
|
21208
|
-
this.deferredInputs.push({ kind: "prompt", text
|
|
21497
|
+
this.deferredInputs.push({ kind: "prompt", text, followUp, images });
|
|
21209
21498
|
this.notify("Message queued to run after the current response.");
|
|
21210
21499
|
this.change();
|
|
21211
21500
|
return;
|
|
21212
21501
|
}
|
|
21213
21502
|
if (!this.session.isIdle)
|
|
21214
21503
|
this.notify(followUp ? "Follow-up message queued." : "Steering message queued.");
|
|
21215
|
-
await this.runtime.submit(
|
|
21504
|
+
await this.runtime.submit(text, followUp, images);
|
|
21216
21505
|
} finally {
|
|
21217
21506
|
if (command)
|
|
21218
21507
|
this.commandBusy = false;
|
|
@@ -21353,6 +21642,7 @@ class OpenTuiApplication {
|
|
|
21353
21642
|
this.applyComposerMode(mode);
|
|
21354
21643
|
this.draft = parsed.body;
|
|
21355
21644
|
this.attachments.clearForDraft(this.draft);
|
|
21645
|
+
this.paletteCaret = null;
|
|
21356
21646
|
this.draftCursorOffset = this.draft.length;
|
|
21357
21647
|
this.draftEpoch += 1;
|
|
21358
21648
|
this.paletteSuppressed = true;
|
|
@@ -21607,9 +21897,9 @@ class OpenTuiApplication {
|
|
|
21607
21897
|
const hidden = !this.session.settingsManager.getHideThinkingBlock();
|
|
21608
21898
|
this.session.settingsManager.setHideThinkingBlock(hidden);
|
|
21609
21899
|
this.transcript.setThinkingHidden(hidden);
|
|
21610
|
-
} else if (is("app.message.
|
|
21900
|
+
} else if (is("app.message.steer")) {
|
|
21611
21901
|
const editor = this.customEditor;
|
|
21612
|
-
this.submit(editor?.getExpandedText?.() ?? (this.bashMode ? `!${this.draft}` : this.draft),
|
|
21902
|
+
this.submit(editor?.getExpandedText?.() ?? (this.bashMode ? `!${this.draft}` : this.draft), false);
|
|
21613
21903
|
} else if (is("app.message.dequeue")) {
|
|
21614
21904
|
const pending = this.session.clearQueue();
|
|
21615
21905
|
const deferred = this.deferredInputs.map((input) => input.kind === "plan" ? `/plan ${input.text}` : input.text);
|
|
@@ -21634,11 +21924,29 @@ class OpenTuiApplication {
|
|
|
21634
21924
|
this.attempt(() => this.commands.execute("/new"));
|
|
21635
21925
|
else if (data === "?" && this.draft === "")
|
|
21636
21926
|
this.notify(this.hotkeys());
|
|
21927
|
+
else if (this.dropMouseReportTail(key, data))
|
|
21928
|
+
return true;
|
|
21637
21929
|
else if (!this.extensionShortcut(data))
|
|
21638
21930
|
return false;
|
|
21639
21931
|
this.change();
|
|
21640
21932
|
return true;
|
|
21641
21933
|
}
|
|
21934
|
+
dropMouseReportTail(key, data) {
|
|
21935
|
+
const match = this.mouseTail.push(data);
|
|
21936
|
+
if (!match)
|
|
21937
|
+
return false;
|
|
21938
|
+
if (!match.inserted) {
|
|
21939
|
+
this.change();
|
|
21940
|
+
return true;
|
|
21941
|
+
}
|
|
21942
|
+
const caret = key.cursorOffset ?? this.draft.length;
|
|
21943
|
+
const start = caret - match.inserted.length;
|
|
21944
|
+
const at = start >= 0 && this.draft.slice(start, caret) === match.inserted ? start : this.draft.endsWith(match.inserted) ? this.draft.length - match.inserted.length : -1;
|
|
21945
|
+
if (at < 0)
|
|
21946
|
+
return false;
|
|
21947
|
+
this.setDraftExternal(this.draft.slice(0, at) + this.draft.slice(at + match.inserted.length), at);
|
|
21948
|
+
return true;
|
|
21949
|
+
}
|
|
21642
21950
|
hotkeys() {
|
|
21643
21951
|
return [
|
|
21644
21952
|
"Tab files/folders \xB7 Shift+Tab plan mode",
|
|
@@ -21647,7 +21955,7 @@ class OpenTuiApplication {
|
|
|
21647
21955
|
"Ctrl+L model",
|
|
21648
21956
|
"Ctrl+N new session",
|
|
21649
21957
|
"\u2191/\u2193 at either draft endpoint browse older/newer history \xB7 Ctrl+\u2191/\u2193 anywhere",
|
|
21650
|
-
"Enter send or
|
|
21958
|
+
"Enter send or queue \xB7 Cmd/Ctrl/Alt+Enter steer \xB7 Esc dismisses btw or stops reply",
|
|
21651
21959
|
"Ctrl+C clear or quit \xB7 Ctrl+D quit",
|
|
21652
21960
|
"PgUp/PgDn transcript"
|
|
21653
21961
|
].join(`
|
|
@@ -21844,6 +22152,7 @@ class OpenTuiApplication {
|
|
|
21844
22152
|
insert(text) {
|
|
21845
22153
|
if (!text)
|
|
21846
22154
|
return;
|
|
22155
|
+
this.mouseTail.reset();
|
|
21847
22156
|
if (this.customEditor?.getExpandedText) {
|
|
21848
22157
|
const editor = this.customEditor;
|
|
21849
22158
|
if (editor.insertTextAtCursor) {
|
|
@@ -21910,6 +22219,8 @@ class OpenTuiApplication {
|
|
|
21910
22219
|
}
|
|
21911
22220
|
};
|
|
21912
22221
|
this.stopped = true;
|
|
22222
|
+
this.mouseTail.reset();
|
|
22223
|
+
this.decisionWatch?.abort();
|
|
21913
22224
|
protect(() => this.renderer?.setMousePointer("default"));
|
|
21914
22225
|
this.hoveredLink = null;
|
|
21915
22226
|
if (this.toastTimer)
|
|
@@ -23043,32 +23354,40 @@ function bufferOffset(text, index) {
|
|
|
23043
23354
|
offset += visibleWidth16(line) + (position < lines.length - 1 ? 1 : 0);
|
|
23044
23355
|
return offset;
|
|
23045
23356
|
}
|
|
23046
|
-
function
|
|
23047
|
-
|
|
23048
|
-
|
|
23049
|
-
|
|
23050
|
-
|
|
23051
|
-
|
|
23052
|
-
|
|
23053
|
-
|
|
23054
|
-
|
|
23055
|
-
for (const mark of marks)
|
|
23056
|
-
target.extmarks.delete(mark.id);
|
|
23057
|
-
return;
|
|
23357
|
+
function indexForBufferOffset(text, offset) {
|
|
23358
|
+
let cells = 0;
|
|
23359
|
+
let index = 0;
|
|
23360
|
+
for (const character of text) {
|
|
23361
|
+
if (cells >= offset)
|
|
23362
|
+
return index;
|
|
23363
|
+
cells += character === `
|
|
23364
|
+
` ? 1 : visibleWidth16(character);
|
|
23365
|
+
index += character.length;
|
|
23058
23366
|
}
|
|
23059
|
-
|
|
23060
|
-
|
|
23061
|
-
|
|
23367
|
+
return text.length;
|
|
23368
|
+
}
|
|
23369
|
+
function skillInvocationRanges(text, names) {
|
|
23370
|
+
return skillInvocationMatches(text).filter((match) => names.includes(match.name)).map((match) => ({ start: match.from, end: match.to }));
|
|
23371
|
+
}
|
|
23372
|
+
function syncTextMarks(target, ranges, typeId, styleId) {
|
|
23373
|
+
const expected = ranges.map((range) => {
|
|
23374
|
+
const start = bufferOffset(target.plainText, range.start);
|
|
23375
|
+
return { start, end: start + visibleWidth16(target.plainText.slice(range.start, range.end)) };
|
|
23376
|
+
});
|
|
23377
|
+
const marks = target.extmarks.getAllForTypeId(typeId).sort((left, right) => left.start - right.start);
|
|
23378
|
+
if (marks.length === expected.length && expected.every((range, index) => marks[index].start === range.start && marks[index].end === range.end))
|
|
23062
23379
|
return;
|
|
23063
23380
|
for (const mark of marks)
|
|
23064
23381
|
target.extmarks.delete(mark.id);
|
|
23065
|
-
|
|
23382
|
+
for (const range of expected)
|
|
23383
|
+
target.extmarks.create({ start: range.start, end: range.end, virtual: true, styleId, typeId });
|
|
23066
23384
|
}
|
|
23067
23385
|
function syncSlashCommandMark(target, names, typeId, styleId) {
|
|
23068
|
-
|
|
23386
|
+
const range = leadingSlashCommandRange(target.plainText, names);
|
|
23387
|
+
syncTextMarks(target, range ? [range] : [], typeId, styleId);
|
|
23069
23388
|
}
|
|
23070
23389
|
function syncSkillInvocationMark(target, names, typeId, styleId) {
|
|
23071
|
-
|
|
23390
|
+
syncTextMarks(target, skillInvocationRanges(target.plainText, names), typeId, styleId);
|
|
23072
23391
|
}
|
|
23073
23392
|
function syncAttachmentMarks(target, labels, typeId, styleId) {
|
|
23074
23393
|
const byId = new Map(labels.map((label) => [label.id, label]));
|
|
@@ -23695,8 +24014,9 @@ function TranscriptItemView(props) {
|
|
|
23695
24014
|
}
|
|
23696
24015
|
if (item.kind === "plan") {
|
|
23697
24016
|
return (() => {
|
|
23698
|
-
var _el$23 = _$createElement2("box"), _el$24 = _$createElement2("text"), _el$25 = _$
|
|
24017
|
+
var _el$23 = _$createElement2("box"), _el$24 = _$createElement2("text"), _el$25 = _$createElement2("box"), _el$26 = _$createElement2("markdown");
|
|
23699
24018
|
_$insertNode2(_el$23, _el$24);
|
|
24019
|
+
_$insertNode2(_el$23, _el$25);
|
|
23700
24020
|
_$insertNode2(_el$23, _el$26);
|
|
23701
24021
|
_$setProp2(_el$23, "border", ["left"]);
|
|
23702
24022
|
_$setProp2(_el$23, "flexDirection", "column");
|
|
@@ -23705,7 +24025,6 @@ function TranscriptItemView(props) {
|
|
|
23705
24025
|
_$setProp2(_el$23, "paddingTop", 1);
|
|
23706
24026
|
_$setProp2(_el$23, "paddingBottom", 1);
|
|
23707
24027
|
_$setProp2(_el$23, "flexShrink", 0);
|
|
23708
|
-
_$insertNode2(_el$24, _el$25);
|
|
23709
24028
|
_$setProp2(_el$24, "wrapMode", "word");
|
|
23710
24029
|
_$insert2(_el$24, () => item.title, null);
|
|
23711
24030
|
_$insert2(_el$24, _$createComponent2(Show2, {
|
|
@@ -23718,14 +24037,16 @@ function TranscriptItemView(props) {
|
|
|
23718
24037
|
return t.dim;
|
|
23719
24038
|
},
|
|
23720
24039
|
attributes: 0,
|
|
23721
|
-
children: "
|
|
24040
|
+
children: " streaming\u2026"
|
|
23722
24041
|
});
|
|
23723
24042
|
}
|
|
23724
24043
|
}), null);
|
|
24044
|
+
_$setProp2(_el$25, "height", 1);
|
|
24045
|
+
_$setProp2(_el$25, "flexShrink", 0);
|
|
23725
24046
|
_$setProp2(_el$26, "bg", "transparent");
|
|
23726
24047
|
_$setProp2(_el$26, "conceal", true);
|
|
23727
24048
|
_$effect2((_p$) => {
|
|
23728
|
-
var { planMode: _v$16, panel: _v$17, planMode: _v$18 } = t, _v$19 = TextAttributes2.BOLD, _v$20 = addHeadingOneSpacing(item.body), _v$21 = item.streaming, _v$22 = props.syntax, _v$23 = t.text, _v$24 = {
|
|
24049
|
+
var { planMode: _v$16, panel: _v$17, planMode: _v$18 } = t, _v$19 = TextAttributes2.BOLD | TextAttributes2.UNDERLINE, _v$20 = addHeadingOneSpacing(item.body), _v$21 = item.streaming, _v$22 = props.syntax, _v$23 = t.text, _v$24 = {
|
|
23729
24050
|
borderColor: t.dim,
|
|
23730
24051
|
cellPaddingX: 1
|
|
23731
24052
|
};
|
|
@@ -24685,7 +25006,7 @@ function Prompt(props) {
|
|
|
24685
25006
|
} finally {
|
|
24686
25007
|
syncing = false;
|
|
24687
25008
|
}
|
|
24688
|
-
props.app.setDraft(area.plainText);
|
|
25009
|
+
props.app.setDraft(area.plainText, indexForBufferOffset(area.plainText, area.cursorOffset));
|
|
24689
25010
|
};
|
|
24690
25011
|
createEffect2(() => {
|
|
24691
25012
|
view().attachments;
|
|
@@ -25347,6 +25668,15 @@ function OpenTuiRoot(props) {
|
|
|
25347
25668
|
const [state, setState] = createStore(props.app.view());
|
|
25348
25669
|
const off = props.app.subscribe(() => setState(reconcile(props.app.view())));
|
|
25349
25670
|
onCleanup2(off);
|
|
25671
|
+
const offAnimation = props.app.subscribeAnimation(() => {
|
|
25672
|
+
const animation = props.app.animationView();
|
|
25673
|
+
setState("working", reconcile({
|
|
25674
|
+
...animation.working,
|
|
25675
|
+
sessionName: state.working.sessionName
|
|
25676
|
+
}));
|
|
25677
|
+
setState("subagents", reconcile(animation.subagents));
|
|
25678
|
+
});
|
|
25679
|
+
onCleanup2(offAnimation);
|
|
25350
25680
|
useSelectionHandler((selection) => {
|
|
25351
25681
|
const selectedText = terminalSelectionText(selection);
|
|
25352
25682
|
if (selectedText !== undefined)
|
|
@@ -25497,7 +25827,7 @@ function OpenTuiRoot(props) {
|
|
|
25497
25827
|
if (key.eventType !== "press" || !key.sequence)
|
|
25498
25828
|
return;
|
|
25499
25829
|
if (prompt?.focused)
|
|
25500
|
-
props.app.setDraft(prompt.plainText);
|
|
25830
|
+
props.app.setDraft(prompt.plainText, indexForBufferOffset(prompt.plainText, prompt.cursorOffset));
|
|
25501
25831
|
const current = view();
|
|
25502
25832
|
const page = Math.max(1, Math.floor((dimensions().height - 8) / 2));
|
|
25503
25833
|
if (!current.overlay && !current.questionDock && (key.name === "pageup" || key.name === "pagedown" || key.ctrl && (key.name === "home" || key.name === "end"))) {
|