flavor-code 1.4.0-beta.1 → 1.4.0-beta.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/dist/agent/loop.d.ts +3 -0
- package/dist/{app-C5ZQUOKJ.js → app-H7RYDMD3.js} +6 -6
- package/dist/{chunk-FOTMP4ZS.js → chunk-3BHKW5A7.js} +1 -1
- package/dist/{chunk-ODRWR4KK.js → chunk-IQAZ42TM.js} +1 -1
- package/dist/{chunk-URUVFECH.js → chunk-KBHWS7M6.js} +4 -4
- package/dist/{chunk-6MNOZ2TX.js → chunk-NIKCPCJG.js} +559 -377
- package/dist/{chunk-3WCDZ6ZL.js → chunk-ZJCAQXA7.js} +2 -1
- package/dist/{claude-ink-LP32NSX5.js → claude-ink-ECZ6QF3P.js} +2 -2
- package/dist/cli-main.js +7 -7
- package/dist/desktop/main.js +626 -443
- package/dist/desktop-renderer/assets/{index-CqRIr-hz.js → index-CYd5JkTR.js} +2 -2
- package/dist/desktop-renderer/assets/{interactive-terminal-C4o7MNPO.js → interactive-terminal-j52Ty3QO.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/git/service.d.ts +3 -3
- package/dist/git/tools.d.ts +1 -1
- package/dist/harness/local.d.ts +2 -0
- package/dist/init/project.d.ts +0 -6
- package/dist/jobs/registry.d.ts +1 -1
- package/dist/{load-V2UIYYQI.js → load-SLSL3IWR.js} +1 -1
- package/dist/sdk/index.js +4 -4
- package/dist/tools/lsp.d.ts +13 -10
- package/dist/tools/runtime.d.ts +2 -1
- package/dist/tools/search.d.ts +1 -0
- package/dist/tools/types.d.ts +1 -1
- package/dist/utils/abort.d.ts +4 -0
- package/dist/utils/execFileNoThrow.d.ts +1 -0
- package/dist/utils/process-tree.d.ts +2 -0
- package/package.json +2 -2
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +7 -7
- package/dist/astgraph/db.mjs +0 -237
- package/dist/astgraph/extract.mjs +0 -239
- package/dist/astgraph/flavor-plugin.json +0 -25
- package/dist/astgraph/grammars.mjs +0 -68
- package/dist/astgraph/index.js +0 -301
- package/dist/astgraph/indexer.mjs +0 -175
- package/dist/astgraph/query.mjs +0 -165
- package/dist/astgraph/resolve.mjs +0 -93
- package/dist/astgraph/vendor/tree-sitter-javascript.wasm +0 -0
- package/dist/astgraph/vendor/tree-sitter-tsx.wasm +0 -0
- package/dist/astgraph/vendor/tree-sitter-typescript.wasm +0 -0
- package/dist/astgraph/vendor/tree-sitter.js +0 -3980
- package/dist/astgraph/vendor/tree-sitter.wasm +0 -0
- package/dist/astgraph/vendor/zod/LICENSE +0 -21
- package/dist/astgraph/vendor/zod/index.js +0 -4
- package/dist/astgraph/vendor/zod/locales/index.js +0 -1
- package/dist/astgraph/vendor/zod/locales/package.json +0 -7
- package/dist/astgraph/vendor/zod/package.json +0 -135
- package/dist/astgraph/vendor/zod/v4/classic/checks.js +0 -1
- package/dist/astgraph/vendor/zod/v4/classic/coerce.js +0 -17
- package/dist/astgraph/vendor/zod/v4/classic/compat.js +0 -31
- package/dist/astgraph/vendor/zod/v4/classic/errors.js +0 -48
- package/dist/astgraph/vendor/zod/v4/classic/external.js +0 -20
- package/dist/astgraph/vendor/zod/v4/classic/from-json-schema.js +0 -599
- package/dist/astgraph/vendor/zod/v4/classic/index.js +0 -4
- package/dist/astgraph/vendor/zod/v4/classic/iso.js +0 -30
- package/dist/astgraph/vendor/zod/v4/classic/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/classic/parse.js +0 -15
- package/dist/astgraph/vendor/zod/v4/classic/schemas.js +0 -1395
- package/dist/astgraph/vendor/zod/v4/core/api.js +0 -1087
- package/dist/astgraph/vendor/zod/v4/core/checks.js +0 -575
- package/dist/astgraph/vendor/zod/v4/core/core.js +0 -78
- package/dist/astgraph/vendor/zod/v4/core/doc.js +0 -35
- package/dist/astgraph/vendor/zod/v4/core/errors.js +0 -185
- package/dist/astgraph/vendor/zod/v4/core/index.js +0 -16
- package/dist/astgraph/vendor/zod/v4/core/json-schema-generator.js +0 -95
- package/dist/astgraph/vendor/zod/v4/core/json-schema-processors.js +0 -601
- package/dist/astgraph/vendor/zod/v4/core/json-schema.js +0 -1
- package/dist/astgraph/vendor/zod/v4/core/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/core/parse.js +0 -93
- package/dist/astgraph/vendor/zod/v4/core/regexes.js +0 -139
- package/dist/astgraph/vendor/zod/v4/core/registries.js +0 -51
- package/dist/astgraph/vendor/zod/v4/core/schemas.js +0 -2239
- package/dist/astgraph/vendor/zod/v4/core/standard-schema.js +0 -1
- package/dist/astgraph/vendor/zod/v4/core/to-json-schema.js +0 -448
- package/dist/astgraph/vendor/zod/v4/core/util.js +0 -674
- package/dist/astgraph/vendor/zod/v4/core/versions.js +0 -5
- package/dist/astgraph/vendor/zod/v4/index.js +0 -3
- package/dist/astgraph/vendor/zod/v4/locales/ar.js +0 -106
- package/dist/astgraph/vendor/zod/v4/locales/az.js +0 -105
- package/dist/astgraph/vendor/zod/v4/locales/be.js +0 -156
- package/dist/astgraph/vendor/zod/v4/locales/bg.js +0 -120
- package/dist/astgraph/vendor/zod/v4/locales/ca.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/cs.js +0 -111
- package/dist/astgraph/vendor/zod/v4/locales/da.js +0 -115
- package/dist/astgraph/vendor/zod/v4/locales/de.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/el.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/en.js +0 -113
- package/dist/astgraph/vendor/zod/v4/locales/eo.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/es.js +0 -132
- package/dist/astgraph/vendor/zod/v4/locales/fa.js +0 -114
- package/dist/astgraph/vendor/zod/v4/locales/fi.js +0 -112
- package/dist/astgraph/vendor/zod/v4/locales/fr-CA.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/fr.js +0 -125
- package/dist/astgraph/vendor/zod/v4/locales/he.js +0 -214
- package/dist/astgraph/vendor/zod/v4/locales/hr.js +0 -122
- package/dist/astgraph/vendor/zod/v4/locales/hu.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/hy.js +0 -147
- package/dist/astgraph/vendor/zod/v4/locales/id.js +0 -106
- package/dist/astgraph/vendor/zod/v4/locales/index.js +0 -52
- package/dist/astgraph/vendor/zod/v4/locales/is.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/it.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ja.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/ka.js +0 -112
- package/dist/astgraph/vendor/zod/v4/locales/kh.js +0 -5
- package/dist/astgraph/vendor/zod/v4/locales/km.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/ko.js +0 -111
- package/dist/astgraph/vendor/zod/v4/locales/lt.js +0 -203
- package/dist/astgraph/vendor/zod/v4/locales/mk.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/ms.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/nl.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/no.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ota.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/locales/pl.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/ps.js +0 -114
- package/dist/astgraph/vendor/zod/v4/locales/pt.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ro.js +0 -119
- package/dist/astgraph/vendor/zod/v4/locales/ru.js +0 -156
- package/dist/astgraph/vendor/zod/v4/locales/sl.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/sv.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/ta.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/th.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/tr.js +0 -105
- package/dist/astgraph/vendor/zod/v4/locales/ua.js +0 -5
- package/dist/astgraph/vendor/zod/v4/locales/uk.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ur.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/uz.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/vi.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/yo.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/zh-CN.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/zh-TW.js +0 -107
- package/dist/astgraph/vendor/zod/v4/mini/checks.js +0 -1
- package/dist/astgraph/vendor/zod/v4/mini/coerce.js +0 -22
- package/dist/astgraph/vendor/zod/v4/mini/external.js +0 -14
- package/dist/astgraph/vendor/zod/v4/mini/index.js +0 -3
- package/dist/astgraph/vendor/zod/v4/mini/iso.js +0 -34
- package/dist/astgraph/vendor/zod/v4/mini/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/mini/parse.js +0 -1
- package/dist/astgraph/vendor/zod/v4/mini/schemas.js +0 -961
- package/dist/astgraph/vendor/zod/v4/package.json +0 -7
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
redactConfig,
|
|
19
19
|
setProjectMcpServerDisabled,
|
|
20
20
|
updateProtectedFile
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-KBHWS7M6.js";
|
|
22
22
|
import {
|
|
23
23
|
AuditLogger,
|
|
24
24
|
appendUsageLog,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
resolveLanguage,
|
|
33
33
|
setUsageSession,
|
|
34
34
|
usageLogPath
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-ZJCAQXA7.js";
|
|
36
36
|
|
|
37
37
|
// src/models/types.ts
|
|
38
38
|
function modelContentText(content) {
|
|
@@ -75,12 +75,12 @@ function normalizeProviderError(error) {
|
|
|
75
75
|
|
|
76
76
|
// src/utils/async.ts
|
|
77
77
|
function withTimeout(promise, ms) {
|
|
78
|
-
return new Promise((
|
|
78
|
+
return new Promise((resolve32, reject) => {
|
|
79
79
|
let settled = false;
|
|
80
80
|
const timer = setTimeout(() => {
|
|
81
81
|
if (settled) return;
|
|
82
82
|
settled = true;
|
|
83
|
-
|
|
83
|
+
resolve32({ timedOut: true, value: void 0 });
|
|
84
84
|
}, ms);
|
|
85
85
|
timer.unref?.();
|
|
86
86
|
promise.then(
|
|
@@ -88,7 +88,7 @@ function withTimeout(promise, ms) {
|
|
|
88
88
|
if (!settled) {
|
|
89
89
|
settled = true;
|
|
90
90
|
clearTimeout(timer);
|
|
91
|
-
|
|
91
|
+
resolve32({ timedOut: false, value });
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
(error) => {
|
|
@@ -103,13 +103,13 @@ function withTimeout(promise, ms) {
|
|
|
103
103
|
}
|
|
104
104
|
function awaitWithSignal(promise, signal) {
|
|
105
105
|
signal.throwIfAborted();
|
|
106
|
-
return new Promise((
|
|
106
|
+
return new Promise((resolve32, reject) => {
|
|
107
107
|
const onAbort = () => reject(signal.reason);
|
|
108
108
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
109
109
|
promise.then(
|
|
110
110
|
(value) => {
|
|
111
111
|
signal.removeEventListener("abort", onAbort);
|
|
112
|
-
|
|
112
|
+
resolve32(value);
|
|
113
113
|
},
|
|
114
114
|
(error) => {
|
|
115
115
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -572,9 +572,7 @@ function redactSecrets(input, secrets) {
|
|
|
572
572
|
|
|
573
573
|
// src/init/project.ts
|
|
574
574
|
import { lstat, mkdir, readdir, readFile, realpath, writeFile } from "fs/promises";
|
|
575
|
-
import {
|
|
576
|
-
import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join, relative as relative2, sep as sep2 } from "path";
|
|
577
|
-
import { fileURLToPath } from "url";
|
|
575
|
+
import { basename, isAbsolute as isAbsolute2, join, relative as relative2, sep as sep2 } from "path";
|
|
578
576
|
var GENERATED_START = "<!-- flavor-code:start -->";
|
|
579
577
|
var GENERATED_END = "<!-- flavor-code:end -->";
|
|
580
578
|
var MAX_MANIFEST_BYTES = 1e6;
|
|
@@ -1025,29 +1023,8 @@ async function createExampleFlavorConfig(cwd) {
|
|
|
1025
1023
|
};
|
|
1026
1024
|
await writeFile(configPath, JSON.stringify(example, null, 2) + "\n");
|
|
1027
1025
|
}
|
|
1028
|
-
var ASTGRAPH_SRC_DIR = join(dirname2(fileURLToPath(import.meta.url)), "astgraph");
|
|
1029
|
-
async function copyAstgraphPlugin(cwd) {
|
|
1030
|
-
const pluginDir = join(cwd, ".flavor", "plugins", "astgraph");
|
|
1031
|
-
try {
|
|
1032
|
-
await lstat(pluginDir);
|
|
1033
|
-
return;
|
|
1034
|
-
} catch (error) {
|
|
1035
|
-
if (!isMissingPathError(error)) throw error;
|
|
1036
|
-
}
|
|
1037
|
-
copyDirectory(ASTGRAPH_SRC_DIR, pluginDir);
|
|
1038
|
-
}
|
|
1039
|
-
function copyDirectory(from, to) {
|
|
1040
|
-
mkdirSync(to, { recursive: true, mode: 448 });
|
|
1041
|
-
for (const entry2 of readdirSync(from, { withFileTypes: true })) {
|
|
1042
|
-
const source = join(from, entry2.name);
|
|
1043
|
-
const target = join(to, entry2.name);
|
|
1044
|
-
if (entry2.isDirectory()) copyDirectory(source, target);
|
|
1045
|
-
else if (entry2.isFile()) copyFileSync(source, target);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
1026
|
async function initializeFlavor(cwd) {
|
|
1049
1027
|
await ensureFlavorDirectories(cwd);
|
|
1050
|
-
await copyAstgraphPlugin(cwd);
|
|
1051
1028
|
await createExampleFlavorConfig(cwd);
|
|
1052
1029
|
const facts = await inspectProject(cwd);
|
|
1053
1030
|
const path = join(cwd, "FLAVOR.md");
|
|
@@ -1069,12 +1046,12 @@ async function initializeFlavor(cwd) {
|
|
|
1069
1046
|
|
|
1070
1047
|
// src/utils/version.ts
|
|
1071
1048
|
import { readFileSync } from "fs";
|
|
1072
|
-
import { dirname as
|
|
1073
|
-
import { fileURLToPath
|
|
1049
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
1050
|
+
import { fileURLToPath } from "url";
|
|
1074
1051
|
var cached;
|
|
1075
1052
|
function packageVersion() {
|
|
1076
1053
|
if (cached !== void 0) return cached;
|
|
1077
|
-
let directory =
|
|
1054
|
+
let directory = dirname2(fileURLToPath(import.meta.url));
|
|
1078
1055
|
for (; ; ) {
|
|
1079
1056
|
try {
|
|
1080
1057
|
const manifest = JSON.parse(readFileSync(join2(directory, "package.json"), "utf8"));
|
|
@@ -1084,7 +1061,7 @@ function packageVersion() {
|
|
|
1084
1061
|
}
|
|
1085
1062
|
} catch {
|
|
1086
1063
|
}
|
|
1087
|
-
const parent =
|
|
1064
|
+
const parent = dirname2(directory);
|
|
1088
1065
|
if (parent === directory) break;
|
|
1089
1066
|
directory = parent;
|
|
1090
1067
|
}
|
|
@@ -1879,7 +1856,7 @@ function addText(turn, text, onNewLine = false) {
|
|
|
1879
1856
|
import { createHash, randomUUID } from "crypto";
|
|
1880
1857
|
import { execFile } from "child_process";
|
|
1881
1858
|
import { lstat as lstat2, mkdir as mkdir2, open, readFile as readFile2, readdir as readdir2, rename, rm, writeFile as writeFile2 } from "fs/promises";
|
|
1882
|
-
import { dirname as
|
|
1859
|
+
import { dirname as dirname3, join as join3, relative as relative3, resolve as resolve2, sep as sep3 } from "path";
|
|
1883
1860
|
var EXCLUDED = /* @__PURE__ */ new Set([".git", ".flavor", "node_modules", "dist", "build", "release", ".worktrees"]);
|
|
1884
1861
|
var MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
1885
1862
|
var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
@@ -1965,7 +1942,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
1965
1942
|
for (const file of checkpoint.files) {
|
|
1966
1943
|
const target = contained(this.#workspace, file.path);
|
|
1967
1944
|
const object = objects.get(file.digest);
|
|
1968
|
-
await mkdir2(
|
|
1945
|
+
await mkdir2(dirname3(target), { recursive: true });
|
|
1969
1946
|
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
1970
1947
|
await writeFile2(temporary, object, { mode: file.mode });
|
|
1971
1948
|
await rename(temporary, target);
|
|
@@ -2037,7 +2014,7 @@ function contained(workspace, path) {
|
|
|
2037
2014
|
return absolute;
|
|
2038
2015
|
}
|
|
2039
2016
|
async function atomicJson(path, value) {
|
|
2040
|
-
await mkdir2(
|
|
2017
|
+
await mkdir2(dirname3(path), { recursive: true });
|
|
2041
2018
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
2042
2019
|
await writeFile2(temporary, `${JSON.stringify(value)}
|
|
2043
2020
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -2050,7 +2027,7 @@ function isCode(error, code) {
|
|
|
2050
2027
|
// src/session/tree.ts
|
|
2051
2028
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2052
2029
|
import { mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
|
|
2053
|
-
import { dirname as
|
|
2030
|
+
import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
|
|
2054
2031
|
var MAX_SESSION_HISTORY_NODES = 100;
|
|
2055
2032
|
var MAX_SESSION_HISTORY_CONTEXT_CHARS = 2e6;
|
|
2056
2033
|
var SessionHistory = class _SessionHistory {
|
|
@@ -2155,7 +2132,7 @@ var SessionHistory = class _SessionHistory {
|
|
|
2155
2132
|
leafId: this.#leafId,
|
|
2156
2133
|
nodes: this.#nodes
|
|
2157
2134
|
};
|
|
2158
|
-
await mkdir3(
|
|
2135
|
+
await mkdir3(dirname4(this.#path), { recursive: true });
|
|
2159
2136
|
const temporary = `${this.#path}.${process.pid}.${randomUUID2()}.tmp`;
|
|
2160
2137
|
await writeFile3(temporary, `${JSON.stringify(document)}
|
|
2161
2138
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -2201,7 +2178,7 @@ function isCode2(error, code) {
|
|
|
2201
2178
|
// src/tools/search.ts
|
|
2202
2179
|
import { spawn } from "child_process";
|
|
2203
2180
|
import { open as open2, opendir, stat } from "fs/promises";
|
|
2204
|
-
import {
|
|
2181
|
+
import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve4, sep as sep4 } from "path";
|
|
2205
2182
|
import { rgPath as bundledRgPath } from "@vscode/ripgrep";
|
|
2206
2183
|
import createIgnore from "ignore";
|
|
2207
2184
|
import { z } from "zod";
|
|
@@ -2292,8 +2269,9 @@ var GlobInput = z.object({
|
|
|
2292
2269
|
limit: z.coerce.number().int().positive().max(1e5).nullable().optional()
|
|
2293
2270
|
});
|
|
2294
2271
|
var GrepInput = z.object({
|
|
2295
|
-
pattern: z.string().min(1),
|
|
2296
|
-
|
|
2272
|
+
pattern: z.string().min(1).describe("Regular expression without / delimiters, or literal text when fixedStrings=true."),
|
|
2273
|
+
fixedStrings: BooleanInput.describe("Match literal text, useful for Markdown links and paths containing regex punctuation.").nullable().optional(),
|
|
2274
|
+
path: z.string().min(1).describe("Workspace-relative file or directory. A file restricts the search to that file. Use glob for multiple filenames and JSON null for the workspace root.").nullable().optional(),
|
|
2297
2275
|
glob: z.string().min(1).nullable().optional(),
|
|
2298
2276
|
fileType: z.enum(FILE_TYPES).nullable().optional(),
|
|
2299
2277
|
includeIgnored: BooleanInput.describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
|
|
@@ -2342,7 +2320,7 @@ function createGrepTool(workspace, options = {}) {
|
|
|
2342
2320
|
const resources = searchResources(options);
|
|
2343
2321
|
return {
|
|
2344
2322
|
name: "Grep",
|
|
2345
|
-
description: `Search workspace text with a regular expression. fileType must be one of: ${FILE_TYPES.join(", ")}.`,
|
|
2323
|
+
description: `Search workspace text with a regular expression or fixedStrings=true for literal text. Zero matches is a successful search, not a path error. fileType must be one of: ${FILE_TYPES.join(", ")}.`,
|
|
2346
2324
|
inputSchema: GrepInput,
|
|
2347
2325
|
paths: (input) => [scope(root, searchPath(input.path))],
|
|
2348
2326
|
summarize: (input) => {
|
|
@@ -2350,10 +2328,10 @@ function createGrepTool(workspace, options = {}) {
|
|
|
2350
2328
|
const where = path ? ` in ${path}` : "";
|
|
2351
2329
|
const kind = input.fileType ? ` [${input.fileType}]` : "";
|
|
2352
2330
|
const ignored2 = input.includeIgnored === true ? " (including ignored files)" : "";
|
|
2353
|
-
return `pattern:
|
|
2331
|
+
return `pattern: ${input.fixedStrings === true ? JSON.stringify(input.pattern) : `/${input.pattern}/`}${kind}${where}${ignored2}`;
|
|
2354
2332
|
},
|
|
2355
2333
|
execute: async (input, signal) => {
|
|
2356
|
-
const expression = new RegExp(input.pattern);
|
|
2334
|
+
const expression = new RegExp(input.fixedStrings === true ? input.pattern.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&") : input.pattern);
|
|
2357
2335
|
const limit = input.limit ?? options.defaultLimit ?? DEFAULT_RESULT_LIMIT;
|
|
2358
2336
|
const context = input.context ?? 0;
|
|
2359
2337
|
const start = await resolveGrepPath(root, searchPath(input.path));
|
|
@@ -2404,10 +2382,12 @@ async function rgGrep(root, start, input, context, signal, executable, argsPrefi
|
|
|
2404
2382
|
}
|
|
2405
2383
|
const args = ["--json", "--line-number", "--column", "--sort", "path", "--hidden", "--glob", "!.git", "--glob", "!.git/**", "--max-filesize", String(maxFileBytes), "--context", String(context)];
|
|
2406
2384
|
if (input.includeIgnored === true) args.push("--no-ignore");
|
|
2385
|
+
if (input.fixedStrings === true) args.push("--fixed-strings");
|
|
2407
2386
|
if (input.glob !== void 0 && input.glob !== null) args.push("--glob", input.glob);
|
|
2408
2387
|
else for (const extension of typeExtensions ?? []) args.push("--glob", `*${extension}`);
|
|
2409
2388
|
args.push("--regexp", input.pattern, "--", relative4(root, start) || ".");
|
|
2410
2389
|
const matches = [];
|
|
2390
|
+
const matcher = input.glob == null ? void 0 : globRegex(input.glob);
|
|
2411
2391
|
const ignoreLayers = input.includeIgnored === true ? [] : await collectIgnoreLayers(root, start, signal, resources, ignoreBudget);
|
|
2412
2392
|
const parser = delimitedParser(10, (record) => {
|
|
2413
2393
|
if (record.length === 0) return true;
|
|
@@ -2420,6 +2400,7 @@ async function rgGrep(root, start, input, context, signal, executable, argsPrefi
|
|
|
2420
2400
|
const lineWithEnding = tryDecodeUtf8(lineValue);
|
|
2421
2401
|
if (decodedPath === void 0 || lineWithEnding === void 0) return true;
|
|
2422
2402
|
const path = normalizePath(decodedPath).replace(/^\.\//, "");
|
|
2403
|
+
if (matcher !== void 0 && !matcher.test(path)) return true;
|
|
2423
2404
|
if (typeExtensions !== void 0 && !typeExtensions.some((extension) => path.endsWith(extension))) return true;
|
|
2424
2405
|
if (ignored(path, false, ignoreLayers)) return true;
|
|
2425
2406
|
const text = lineWithEnding.replace(/\r?\n$/, "");
|
|
@@ -2544,7 +2525,12 @@ async function nodeFiles(root, start, signal, accept, limit, resources, ignoreBu
|
|
|
2544
2525
|
}
|
|
2545
2526
|
ignores.length = ruleCount;
|
|
2546
2527
|
}
|
|
2547
|
-
await
|
|
2528
|
+
if ((await stat(start)).isFile()) {
|
|
2529
|
+
const path = normalizedRelative(root, start);
|
|
2530
|
+
if (!isGitMetadataPath(root, start) && (includeIgnored || !ignored(path, false, ignores)) && accept(path)) output.push(path);
|
|
2531
|
+
} else {
|
|
2532
|
+
await walk(start);
|
|
2533
|
+
}
|
|
2548
2534
|
return output;
|
|
2549
2535
|
}
|
|
2550
2536
|
async function collectIgnoreLayers(root, start, signal, resources, ignoreBudget) {
|
|
@@ -2574,7 +2560,7 @@ async function collectIgnoreLayers(root, start, signal, resources, ignoreBudget)
|
|
|
2574
2560
|
if (!ignored(path, true, layers)) await walk(absolute);
|
|
2575
2561
|
}
|
|
2576
2562
|
}
|
|
2577
|
-
await walk(start);
|
|
2563
|
+
if (!(await stat(start)).isFile()) await walk(start);
|
|
2578
2564
|
return layers;
|
|
2579
2565
|
}
|
|
2580
2566
|
async function loadIgnoreFiles(directory, base, rules, budget, signal) {
|
|
@@ -2694,8 +2680,12 @@ async function resolveGrepPath(root, path = ".") {
|
|
|
2694
2680
|
const candidate = scope(root, path);
|
|
2695
2681
|
try {
|
|
2696
2682
|
const info = await stat(candidate);
|
|
2697
|
-
if (info.isFile())
|
|
2698
|
-
} catch {
|
|
2683
|
+
if (!info.isFile() && !info.isDirectory()) throw new Error("Search path must be a file or directory");
|
|
2684
|
+
} catch (error) {
|
|
2685
|
+
if (error.code === "ENOENT") {
|
|
2686
|
+
throw new Error(`Search path does not exist: ${path}. Use Glob to find the file; put filename patterns in glob, not path.`);
|
|
2687
|
+
}
|
|
2688
|
+
throw error;
|
|
2699
2689
|
}
|
|
2700
2690
|
return candidate;
|
|
2701
2691
|
}
|
|
@@ -4200,9 +4190,9 @@ Address this now (for example run the required verification or fix), then finish
|
|
|
4200
4190
|
}
|
|
4201
4191
|
#waitForPlanningIdle(key) {
|
|
4202
4192
|
if (!this.#activePlanning.has(key)) return Promise.resolve();
|
|
4203
|
-
return new Promise((
|
|
4193
|
+
return new Promise((resolve32) => {
|
|
4204
4194
|
const check = () => {
|
|
4205
|
-
if (!this.#activePlanning.has(key))
|
|
4195
|
+
if (!this.#activePlanning.has(key)) resolve32();
|
|
4206
4196
|
else this.#planningIdleWaiters.add(check);
|
|
4207
4197
|
};
|
|
4208
4198
|
this.#planningIdleWaiters.add(check);
|
|
@@ -4321,8 +4311,8 @@ async function fetchLatestVersion(options = {}) {
|
|
|
4321
4311
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
4322
4312
|
const fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));
|
|
4323
4313
|
let timer;
|
|
4324
|
-
const timeout = new Promise((
|
|
4325
|
-
timer = setTimeout(() =>
|
|
4314
|
+
const timeout = new Promise((resolve32) => {
|
|
4315
|
+
timer = setTimeout(() => resolve32(void 0), timeoutMs);
|
|
4326
4316
|
timer.unref?.();
|
|
4327
4317
|
});
|
|
4328
4318
|
try {
|
|
@@ -4447,7 +4437,7 @@ var PluginManifestSchema = z4.object({
|
|
|
4447
4437
|
}).strict();
|
|
4448
4438
|
|
|
4449
4439
|
// src/tools/shell.ts
|
|
4450
|
-
import { spawn as
|
|
4440
|
+
import { spawn as spawn3 } from "child_process";
|
|
4451
4441
|
import { existsSync as existsSync3 } from "fs";
|
|
4452
4442
|
import { basename as basename2, delimiter as delimiter2, isAbsolute as isAbsolute5, join as join5, relative as relative5, resolve as resolve6, sep as sep5 } from "path";
|
|
4453
4443
|
import { z as z6 } from "zod";
|
|
@@ -4490,7 +4480,7 @@ function resolveWindowsCommand(command, cwd, env) {
|
|
|
4490
4480
|
...pathValue.split(";").filter(Boolean).map((entry2) => resolve5(unquote(entry2), command))
|
|
4491
4481
|
];
|
|
4492
4482
|
for (const base of bases) {
|
|
4493
|
-
const candidates = extname(base).length > 0 ? [base] : [
|
|
4483
|
+
const candidates = extname(base).length > 0 ? [base] : [...pathExt.map((extension) => `${base}${extension}`), base];
|
|
4494
4484
|
for (const candidate of candidates) {
|
|
4495
4485
|
if (isRegularFile(candidate)) return resolve5(candidate);
|
|
4496
4486
|
}
|
|
@@ -4568,7 +4558,7 @@ function createJobTools(registry) {
|
|
|
4568
4558
|
inputSchema: IdInput,
|
|
4569
4559
|
paths: () => [],
|
|
4570
4560
|
execute: async (input, _signal, context) => {
|
|
4571
|
-
registry.kill(input.id, owner(context));
|
|
4561
|
+
await registry.kill(input.id, owner(context));
|
|
4572
4562
|
return registry.read(input.id, owner(context));
|
|
4573
4563
|
},
|
|
4574
4564
|
presentResult: (output) => jobPresentation("kill", output)
|
|
@@ -4608,11 +4598,51 @@ function owner(context) {
|
|
|
4608
4598
|
return context?.ownerId ?? context?.agent ?? "main";
|
|
4609
4599
|
}
|
|
4610
4600
|
|
|
4601
|
+
// src/utils/process-tree.ts
|
|
4602
|
+
import { spawn as spawn2 } from "child_process";
|
|
4603
|
+
async function terminateProcessTree(pid) {
|
|
4604
|
+
if (pid === void 0) return;
|
|
4605
|
+
if (process.platform === "win32") {
|
|
4606
|
+
await new Promise((resolve32) => {
|
|
4607
|
+
const child = spawn2("taskkill", ["/pid", String(pid), "/T", "/F"], { shell: false, windowsHide: true });
|
|
4608
|
+
const finish = () => {
|
|
4609
|
+
clearTimeout(timer);
|
|
4610
|
+
resolve32();
|
|
4611
|
+
};
|
|
4612
|
+
const timer = setTimeout(finish, 5e3);
|
|
4613
|
+
timer.unref();
|
|
4614
|
+
child.once("error", finish);
|
|
4615
|
+
child.once("close", finish);
|
|
4616
|
+
});
|
|
4617
|
+
try {
|
|
4618
|
+
process.kill(pid);
|
|
4619
|
+
} catch {
|
|
4620
|
+
}
|
|
4621
|
+
} else {
|
|
4622
|
+
try {
|
|
4623
|
+
process.kill(-pid, "SIGTERM");
|
|
4624
|
+
} catch {
|
|
4625
|
+
try {
|
|
4626
|
+
process.kill(pid, "SIGTERM");
|
|
4627
|
+
} catch {
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
await new Promise((resolve32) => setTimeout(resolve32, 250));
|
|
4631
|
+
try {
|
|
4632
|
+
process.kill(-pid, "SIGKILL");
|
|
4633
|
+
} catch {
|
|
4634
|
+
try {
|
|
4635
|
+
process.kill(pid, "SIGKILL");
|
|
4636
|
+
} catch {
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4611
4642
|
// src/tools/shell.ts
|
|
4612
4643
|
var DEFAULT_MAX_OUTPUT_BYTES = 1048576;
|
|
4613
4644
|
var DEFAULT_SHELL_TIMEOUT_MS = 10 * 6e4;
|
|
4614
4645
|
var ELLIPSIS = "\u2026";
|
|
4615
|
-
var TERMINATION_GRACE_MS = 250;
|
|
4616
4646
|
var TERMINATION_FAILURE_MS = 5e3;
|
|
4617
4647
|
var STREAM_CLOSE_GRACE_MS = 500;
|
|
4618
4648
|
var ShellInput = z6.object({
|
|
@@ -4631,12 +4661,6 @@ function normalizeShellCommand(input) {
|
|
|
4631
4661
|
if ((executable === "cmd" || executable === "cmd.exe") && /^\/[ck]$/iu.test(args[0] ?? "")) {
|
|
4632
4662
|
return { command, args: [args[0], ...args.slice(1).map(unquoteToken)] };
|
|
4633
4663
|
}
|
|
4634
|
-
if ((executable === "pwsh" || executable === "pwsh.exe" || executable === "powershell" || executable === "powershell.exe") && /^(?:-c|-command)$/iu.test(args[0] ?? "") && args.length > 1) {
|
|
4635
|
-
return { command, args: [args[0], ...args.slice(1).map(unquoteToken)] };
|
|
4636
|
-
}
|
|
4637
|
-
if (/^(?:sh|bash|zsh|fish)$/iu.test(executable) && args[0] === "-c" && args[1] !== void 0) {
|
|
4638
|
-
return { command, args: [args[0], unquoteToken(args[1]), ...args.slice(2)] };
|
|
4639
|
-
}
|
|
4640
4664
|
if (!/\s/u.test(command) || containsShellSyntax(command)) return { command, args };
|
|
4641
4665
|
const tokens = tokenizeCommandLine(command);
|
|
4642
4666
|
if (tokens.length <= 1) return { command, args };
|
|
@@ -4754,7 +4778,8 @@ function shellInvocation(input, cwd, environment) {
|
|
|
4754
4778
|
const payload = serializeCmdPayload(input.args.slice(commandFlag + 1));
|
|
4755
4779
|
return {
|
|
4756
4780
|
command: resolved,
|
|
4757
|
-
args: [...input.args.slice(0, commandFlag + 1), `chcp 65001>nul & ${payload}`]
|
|
4781
|
+
args: [...input.args.slice(0, commandFlag + 1), `chcp 65001>nul & ${payload}`],
|
|
4782
|
+
windowsVerbatimArguments: true
|
|
4758
4783
|
};
|
|
4759
4784
|
}
|
|
4760
4785
|
}
|
|
@@ -4770,7 +4795,8 @@ function shellInvocation(input, cwd, environment) {
|
|
|
4770
4795
|
if (shell.kind === "posix") return { command: shell.command, args: ["-c", script] };
|
|
4771
4796
|
if (shell.kind === "cmd") return {
|
|
4772
4797
|
command: shell.command,
|
|
4773
|
-
args: ["/d", "/s", "/c", `chcp 65001>nul & ${script}`]
|
|
4798
|
+
args: ["/d", "/s", "/c", `chcp 65001>nul & ${script}`],
|
|
4799
|
+
windowsVerbatimArguments: true
|
|
4774
4800
|
};
|
|
4775
4801
|
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
4776
4802
|
return {
|
|
@@ -4793,8 +4819,8 @@ function executionEnvironmentCommand(environment, command, args) {
|
|
|
4793
4819
|
function structuredShellScript(shell, command, args) {
|
|
4794
4820
|
if (shell.kind === "posix") return [command, ...args].map(posixQuote).join(" ");
|
|
4795
4821
|
if (shell.kind === "cmd") return serializeCmdCommand([command, ...args]);
|
|
4796
|
-
const invocation = `& ${psQuote(command)}${args.length > 0 ? ` ${args.map(psQuote).join(" ")}` : ""}`;
|
|
4797
|
-
return `$ErrorActionPreference='Stop'; try { ${invocation}; if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE } else { exit 0 } } catch { [Console]::Error.WriteLine($_.Exception.Message); exit 127 }`;
|
|
4822
|
+
const invocation = `& ${psQuote(command)}${args.length > 0 ? ` ${args.map((arg) => /^-[A-Za-z][A-Za-z0-9-]*$/u.test(arg) ? arg : psQuote(arg)).join(" ")}` : ""}`;
|
|
4823
|
+
return `$ErrorActionPreference='Stop'; try { ${invocation}; if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE } else { exit 0 } } catch { [Console]::Error.WriteLine($_.Exception.Message); if ($_.Exception -is [System.Management.Automation.CommandNotFoundException]) { exit 127 } else { exit 1 } }`;
|
|
4798
4824
|
}
|
|
4799
4825
|
function posixQuote(value) {
|
|
4800
4826
|
return `'${value.replace(/'/gu, `'"'"'`)}'`;
|
|
@@ -4827,7 +4853,7 @@ ${result.stderr}`;
|
|
|
4827
4853
|
return {
|
|
4828
4854
|
kind: "command-not-found",
|
|
4829
4855
|
message: `Executable "${command}" was not found by the selected runtime shell.`,
|
|
4830
|
-
hint: "Use a command available on PATH, or pass an existing executable path without surrounding quotes."
|
|
4856
|
+
hint: "Use a command available on PATH, or pass an existing executable path without surrounding quotes. For an unavailable PowerShell cmdlet, check Get-Command and its module in the selected shell; do not keep retrying the same command."
|
|
4831
4857
|
};
|
|
4832
4858
|
}
|
|
4833
4859
|
if (code === "EACCES" || /(?:permission denied|access is denied|拒绝访问)/iu.test(detail)) {
|
|
@@ -4836,11 +4862,11 @@ ${result.stderr}`;
|
|
|
4836
4862
|
if (/(?:no such file or directory|cannot find (?:the )?path|system cannot find|系统找不到指定的路径)/iu.test(detail)) {
|
|
4837
4863
|
return { kind: "path-not-found", message: "The command referenced a path that does not exist." };
|
|
4838
4864
|
}
|
|
4839
|
-
if (/(?:syntax
|
|
4865
|
+
if (/(?:syntax\s*error|unexpected token|parsererror|unterminated (?:regexp|string)|语法错误)/iu.test(detail)) {
|
|
4840
4866
|
return {
|
|
4841
4867
|
kind: "shell-syntax",
|
|
4842
|
-
message: "The
|
|
4843
|
-
hint:
|
|
4868
|
+
message: "The command or interpreter rejected the syntax.",
|
|
4869
|
+
hint: 'Pass normal arguments through args. For JavaScript use command="node", args=["-e", "<complete script>"] without an extra shell or surrounding quotes. Legacy Windows PowerShell can strip quotes when forwarding native arguments.'
|
|
4844
4870
|
};
|
|
4845
4871
|
}
|
|
4846
4872
|
return {
|
|
@@ -4862,7 +4888,7 @@ function createShellTool(workspace, options = {}) {
|
|
|
4862
4888
|
}
|
|
4863
4889
|
return {
|
|
4864
4890
|
name: "Shell",
|
|
4865
|
-
description:
|
|
4891
|
+
description: 'Run a bounded command reliably inside the workspace. Pass a bare executable in command and one logical value per args entry, without adding surrounding shell quotes. For Node scripts use command="node", args=["-e", "<complete JavaScript>"]; do not wrap native commands in cmd /c or PowerShell -Command unless shell syntax is needed, since legacy PowerShell can strip embedded quotes. Use Grep for text searches and Read/Edit for file changes. stdout/stderr are returned verbatim and failures include a structured diagnostic. background=true returns a managed job id.',
|
|
4866
4892
|
inputSchema: ShellInput,
|
|
4867
4893
|
paths: (input) => [workingDirectory(root, input.cwd ?? void 0)],
|
|
4868
4894
|
summarize: (input) => [input.command, ...input.args].join(" "),
|
|
@@ -4962,7 +4988,7 @@ async function executeShell(root, input, cancellation, maxBytes, environment = p
|
|
|
4962
4988
|
return;
|
|
4963
4989
|
}
|
|
4964
4990
|
const invocation = shellInvocation(input, cwd, environment);
|
|
4965
|
-
const child =
|
|
4991
|
+
const child = spawn3(invocation.command, invocation.args, {
|
|
4966
4992
|
cwd,
|
|
4967
4993
|
shell: false,
|
|
4968
4994
|
windowsHide: true,
|
|
@@ -4989,7 +5015,7 @@ async function executeShell(root, input, cancellation, maxBytes, environment = p
|
|
|
4989
5015
|
void finishResolve(observedExitCode ?? child.exitCode, observedSignal ?? child.signalCode);
|
|
4990
5016
|
return;
|
|
4991
5017
|
}
|
|
4992
|
-
termination =
|
|
5018
|
+
termination = terminateProcessTree(child.pid);
|
|
4993
5019
|
terminalTimer = setTimeout(() => finishReject(new Error(`Process did not close after ${reason} termination`)), TERMINATION_FAILURE_MS);
|
|
4994
5020
|
terminalTimer.unref();
|
|
4995
5021
|
};
|
|
@@ -5149,52 +5175,6 @@ function workingDirectory(root, cwd = ".") {
|
|
|
5149
5175
|
if (delta === ".." || delta.startsWith(`..${sep5}`) || isAbsolute5(delta)) throw new Error("Working directory is outside the workspace");
|
|
5150
5176
|
return candidate;
|
|
5151
5177
|
}
|
|
5152
|
-
async function terminateTree(pid) {
|
|
5153
|
-
if (pid === void 0) return;
|
|
5154
|
-
if (process.platform === "win32") {
|
|
5155
|
-
await waitForProcess(spawn2("taskkill", ["/pid", String(pid), "/T", "/F"], { shell: false, windowsHide: true }));
|
|
5156
|
-
try {
|
|
5157
|
-
process.kill(pid);
|
|
5158
|
-
} catch {
|
|
5159
|
-
}
|
|
5160
|
-
} else {
|
|
5161
|
-
try {
|
|
5162
|
-
process.kill(-pid, "SIGTERM");
|
|
5163
|
-
} catch {
|
|
5164
|
-
try {
|
|
5165
|
-
process.kill(pid, "SIGTERM");
|
|
5166
|
-
} catch {
|
|
5167
|
-
}
|
|
5168
|
-
}
|
|
5169
|
-
await delay(TERMINATION_GRACE_MS);
|
|
5170
|
-
try {
|
|
5171
|
-
process.kill(-pid, "SIGKILL");
|
|
5172
|
-
} catch {
|
|
5173
|
-
try {
|
|
5174
|
-
process.kill(pid, "SIGKILL");
|
|
5175
|
-
} catch {
|
|
5176
|
-
}
|
|
5177
|
-
}
|
|
5178
|
-
}
|
|
5179
|
-
}
|
|
5180
|
-
function waitForProcess(child) {
|
|
5181
|
-
return new Promise((resolvePromise) => {
|
|
5182
|
-
let settled = false;
|
|
5183
|
-
const finish = () => {
|
|
5184
|
-
if (settled) return;
|
|
5185
|
-
settled = true;
|
|
5186
|
-
clearTimeout(timer);
|
|
5187
|
-
resolvePromise();
|
|
5188
|
-
};
|
|
5189
|
-
const timer = setTimeout(finish, TERMINATION_FAILURE_MS);
|
|
5190
|
-
timer.unref();
|
|
5191
|
-
child.once("error", finish);
|
|
5192
|
-
child.once("close", finish);
|
|
5193
|
-
});
|
|
5194
|
-
}
|
|
5195
|
-
function delay(milliseconds) {
|
|
5196
|
-
return new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
5197
|
-
}
|
|
5198
5178
|
function decodeOutput(buffer) {
|
|
5199
5179
|
const encoding = outputEncoding(buffer);
|
|
5200
5180
|
return new TextDecoder(encoding).decode(buffer);
|
|
@@ -5478,7 +5458,7 @@ function errorMessage(error) {
|
|
|
5478
5458
|
}
|
|
5479
5459
|
|
|
5480
5460
|
// src/utils/crash-guard.ts
|
|
5481
|
-
import { mkdirSync
|
|
5461
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
5482
5462
|
import { join as join7 } from "path";
|
|
5483
5463
|
var installed = false;
|
|
5484
5464
|
var crashing = false;
|
|
@@ -5518,7 +5498,7 @@ function installCrashGuard(options = {}) {
|
|
|
5518
5498
|
const writeCrashLog = (kind, detail) => {
|
|
5519
5499
|
try {
|
|
5520
5500
|
const dir = crashLogDir();
|
|
5521
|
-
|
|
5501
|
+
mkdirSync(dir, { recursive: true });
|
|
5522
5502
|
const path = join7(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
|
|
5523
5503
|
writeFileSync(path, [
|
|
5524
5504
|
`kind: ${kind}`,
|
|
@@ -5573,7 +5553,7 @@ ${reason.stack ?? ""}`;
|
|
|
5573
5553
|
}
|
|
5574
5554
|
|
|
5575
5555
|
// src/execution/docker.ts
|
|
5576
|
-
import { spawn as
|
|
5556
|
+
import { spawn as spawn4 } from "child_process";
|
|
5577
5557
|
import { relative as relative7, resolve as resolve8, sep as sep7, win32 } from "path";
|
|
5578
5558
|
function buildDockerInvocation(options) {
|
|
5579
5559
|
const workspace = options.workspace;
|
|
@@ -5632,7 +5612,7 @@ function runDockerInvocation(invocation, signal, timeoutMs, maxOutputBytes = DEF
|
|
|
5632
5612
|
resolvePromise({ exitCode: null, signal: null, stdout: "", stderr: "", terminationReason: "cancelled" });
|
|
5633
5613
|
return;
|
|
5634
5614
|
}
|
|
5635
|
-
const child =
|
|
5615
|
+
const child = spawn4(invocation.command, invocation.args, { windowsHide: true, shell: false });
|
|
5636
5616
|
const stdout = new BoundedUtf8Output(maxOutputBytes);
|
|
5637
5617
|
const stderr = new BoundedUtf8Output(maxOutputBytes);
|
|
5638
5618
|
let reason = null;
|
|
@@ -5741,13 +5721,13 @@ import { createHash as createHash3 } from "crypto";
|
|
|
5741
5721
|
import { Socket as Socket2 } from "net";
|
|
5742
5722
|
import { tmpdir } from "os";
|
|
5743
5723
|
import { join as join9 } from "path";
|
|
5744
|
-
import { spawn as
|
|
5724
|
+
import { spawn as spawn5 } from "child_process";
|
|
5745
5725
|
|
|
5746
5726
|
// src/pals/broker.ts
|
|
5747
5727
|
import { createHash as createHash2, randomUUID as randomUUID5, timingSafeEqual } from "crypto";
|
|
5748
5728
|
import { chmod as chmod2, lstat as lstat4, mkdir as mkdir6, unlink as unlink3 } from "fs/promises";
|
|
5749
5729
|
import { createServer, Socket } from "net";
|
|
5750
|
-
import { dirname as
|
|
5730
|
+
import { dirname as dirname6 } from "path";
|
|
5751
5731
|
|
|
5752
5732
|
// src/pals/auth.ts
|
|
5753
5733
|
import { randomBytes, randomUUID as randomUUID3 } from "crypto";
|
|
@@ -5798,7 +5778,7 @@ function validatePalAuthToken(token) {
|
|
|
5798
5778
|
// src/pals/lifecycle.ts
|
|
5799
5779
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5800
5780
|
import { mkdir as mkdir5, open as open4, readFile as readFile6, rename as rename3, stat as stat3, unlink as unlink2 } from "fs/promises";
|
|
5801
|
-
import { dirname as
|
|
5781
|
+
import { dirname as dirname5 } from "path";
|
|
5802
5782
|
function defaultProcessAlive(pid) {
|
|
5803
5783
|
if (!Number.isSafeInteger(pid) || pid <= 0) return true;
|
|
5804
5784
|
try {
|
|
@@ -5819,7 +5799,7 @@ function parseMetadata(value) {
|
|
|
5819
5799
|
}
|
|
5820
5800
|
async function acquirePalFileLock(options) {
|
|
5821
5801
|
if (await options.endpointLive()) return void 0;
|
|
5822
|
-
await mkdir5(
|
|
5802
|
+
await mkdir5(dirname5(options.path), { recursive: true, mode: 448 });
|
|
5823
5803
|
const pid = options.pid ?? process.pid;
|
|
5824
5804
|
const now = options.now ?? Date.now;
|
|
5825
5805
|
const nonce = randomUUID4();
|
|
@@ -5883,12 +5863,12 @@ var BrokerStateError = class extends Error {
|
|
|
5883
5863
|
};
|
|
5884
5864
|
var WINDOWS_PIPE_PREFIX = "\\\\.\\pipe\\";
|
|
5885
5865
|
function canConnect(address) {
|
|
5886
|
-
return new Promise((
|
|
5866
|
+
return new Promise((resolve32) => {
|
|
5887
5867
|
const socket = new Socket();
|
|
5888
5868
|
const finish = (connected) => {
|
|
5889
5869
|
socket.removeAllListeners();
|
|
5890
5870
|
socket.destroy();
|
|
5891
|
-
|
|
5871
|
+
resolve32(connected);
|
|
5892
5872
|
};
|
|
5893
5873
|
socket.once("connect", () => finish(true));
|
|
5894
5874
|
socket.once("error", () => finish(false));
|
|
@@ -5937,7 +5917,7 @@ var PalBrokerServer = class {
|
|
|
5937
5917
|
this.authToken ??= await loadOrCreatePalAuthToken(this.authHome === void 0 ? {} : { home: this.authHome });
|
|
5938
5918
|
try {
|
|
5939
5919
|
await this.prepareAddress();
|
|
5940
|
-
await new Promise((
|
|
5920
|
+
await new Promise((resolve32, reject) => {
|
|
5941
5921
|
const onError = (error) => {
|
|
5942
5922
|
this.server.off("listening", onListening);
|
|
5943
5923
|
reject(error);
|
|
@@ -5946,7 +5926,7 @@ var PalBrokerServer = class {
|
|
|
5946
5926
|
this.server.off("error", onError);
|
|
5947
5927
|
this.started = true;
|
|
5948
5928
|
this.ownsAddress = true;
|
|
5949
|
-
|
|
5929
|
+
resolve32();
|
|
5950
5930
|
};
|
|
5951
5931
|
this.server.once("error", onError);
|
|
5952
5932
|
this.server.once("listening", onListening);
|
|
@@ -5974,7 +5954,7 @@ var PalBrokerServer = class {
|
|
|
5974
5954
|
const ownsAddress = this.ownsAddress;
|
|
5975
5955
|
for (const connection of this.connections) connection.socket.destroy();
|
|
5976
5956
|
if (this.started) {
|
|
5977
|
-
await new Promise((
|
|
5957
|
+
await new Promise((resolve32) => this.server.close(() => resolve32()));
|
|
5978
5958
|
this.started = false;
|
|
5979
5959
|
}
|
|
5980
5960
|
this.ownsAddress = false;
|
|
@@ -5983,7 +5963,7 @@ var PalBrokerServer = class {
|
|
|
5983
5963
|
}
|
|
5984
5964
|
async prepareAddress() {
|
|
5985
5965
|
if (isWindowsPipeAddress(this.address, this.platform)) return;
|
|
5986
|
-
const parent =
|
|
5966
|
+
const parent = dirname6(this.address);
|
|
5987
5967
|
await mkdir6(parent, { recursive: true, mode: 448 });
|
|
5988
5968
|
await chmod2(parent, 448);
|
|
5989
5969
|
this.endpointLock = await acquirePalFileLock({
|
|
@@ -6642,16 +6622,16 @@ var PalBrokerState = class {
|
|
|
6642
6622
|
|
|
6643
6623
|
// src/pals/broker-cli.ts
|
|
6644
6624
|
var startupAttempts = /* @__PURE__ */ new Map();
|
|
6645
|
-
function
|
|
6646
|
-
return new Promise((
|
|
6625
|
+
function delay(milliseconds) {
|
|
6626
|
+
return new Promise((resolve32) => setTimeout(resolve32, milliseconds));
|
|
6647
6627
|
}
|
|
6648
6628
|
function canConnect2(address) {
|
|
6649
|
-
return new Promise((
|
|
6629
|
+
return new Promise((resolve32) => {
|
|
6650
6630
|
const socket = new Socket2();
|
|
6651
6631
|
const finish = (connected) => {
|
|
6652
6632
|
socket.removeAllListeners();
|
|
6653
6633
|
socket.destroy();
|
|
6654
|
-
|
|
6634
|
+
resolve32(connected);
|
|
6655
6635
|
};
|
|
6656
6636
|
socket.once("connect", () => finish(true));
|
|
6657
6637
|
socket.once("error", () => finish(false));
|
|
@@ -6674,7 +6654,7 @@ async function spawnPalBrokerProcess(options) {
|
|
|
6674
6654
|
const lock = await acquirePalFileLock({ path: lockPath, endpointLive: () => canConnect2(address) });
|
|
6675
6655
|
if (lock === void 0) return;
|
|
6676
6656
|
try {
|
|
6677
|
-
const child =
|
|
6657
|
+
const child = spawn5(options.executable, [...options.args], {
|
|
6678
6658
|
detached: true,
|
|
6679
6659
|
stdio: "ignore",
|
|
6680
6660
|
windowsHide: true,
|
|
@@ -6682,7 +6662,7 @@ async function spawnPalBrokerProcess(options) {
|
|
|
6682
6662
|
});
|
|
6683
6663
|
child.unref();
|
|
6684
6664
|
const deadline = Date.now() + 5e3;
|
|
6685
|
-
while (Date.now() < deadline && !await canConnect2(address)) await
|
|
6665
|
+
while (Date.now() < deadline && !await canConnect2(address)) await delay(25);
|
|
6686
6666
|
if (!await canConnect2(address)) throw new Error(`Pals broker process did not become ready at '${address}'`);
|
|
6687
6667
|
} finally {
|
|
6688
6668
|
await lock.release();
|
|
@@ -6694,8 +6674,8 @@ async function runPalBroker(options) {
|
|
|
6694
6674
|
const sweepIntervalMs = options.sweepIntervalMs ?? 5e3;
|
|
6695
6675
|
const idleShutdownMs = options.idleShutdownMs ?? 3e4;
|
|
6696
6676
|
let resolveClosed;
|
|
6697
|
-
const closed = new Promise((
|
|
6698
|
-
resolveClosed =
|
|
6677
|
+
const closed = new Promise((resolve32) => {
|
|
6678
|
+
resolveClosed = resolve32;
|
|
6699
6679
|
});
|
|
6700
6680
|
let closing;
|
|
6701
6681
|
const timer = setInterval(() => {
|
|
@@ -6729,17 +6709,17 @@ async function ensureOnce(options) {
|
|
|
6729
6709
|
const deadline = Date.now() + timeoutMs;
|
|
6730
6710
|
do {
|
|
6731
6711
|
if (await canConnect2(options.address)) return;
|
|
6732
|
-
await
|
|
6712
|
+
await delay(retryDelayMs);
|
|
6733
6713
|
} while (Date.now() < deadline);
|
|
6734
6714
|
throw new Error(`Timed out waiting for pals broker at '${options.address}'`);
|
|
6735
6715
|
}
|
|
6736
6716
|
|
|
6737
6717
|
// src/production.ts
|
|
6738
6718
|
import { readFile as readFile23 } from "fs/promises";
|
|
6739
|
-
import { mkdirSync as
|
|
6719
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
6740
6720
|
import { createHash as createHash18, randomUUID as randomUUID20 } from "crypto";
|
|
6741
6721
|
import { homedir as homedir3, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
6742
|
-
import { basename as basename7, dirname as
|
|
6722
|
+
import { basename as basename7, dirname as dirname17, join as join29, relative as relative22, resolve as resolve31, sep as sep22 } from "path";
|
|
6743
6723
|
|
|
6744
6724
|
// src/agent/subagents.ts
|
|
6745
6725
|
import { z as z9 } from "zod";
|
|
@@ -7159,7 +7139,7 @@ var TaskPlanSchema = z10.object({
|
|
|
7159
7139
|
}
|
|
7160
7140
|
dependencies.add(dependency);
|
|
7161
7141
|
}
|
|
7162
|
-
if (task.status === "completed") {
|
|
7142
|
+
if (task.status === "completed" || task.status === "in_progress") {
|
|
7163
7143
|
const incomplete = task.dependencies.find((dependency) => plan.tasks.find((candidate) => candidate.id === dependency)?.status !== "completed");
|
|
7164
7144
|
if (incomplete !== void 0) {
|
|
7165
7145
|
context.addIssue({
|
|
@@ -7188,7 +7168,7 @@ function updatePlanTask(plan, input) {
|
|
|
7188
7168
|
if (!isValidTransition(previousStatus, update.status)) {
|
|
7189
7169
|
throw new Error(`Invalid task transition for ${update.taskId}: ${previousStatus} -> ${update.status}`);
|
|
7190
7170
|
}
|
|
7191
|
-
if (update.status === "completed") {
|
|
7171
|
+
if (update.status === "completed" || update.status === "in_progress") {
|
|
7192
7172
|
const incomplete = current.tasks[index].dependencies.find((dependency) => current.tasks.find((task) => task.id === dependency)?.status !== "completed");
|
|
7193
7173
|
if (incomplete !== void 0) throw new Error(`Task ${update.taskId} has an incomplete dependency: ${incomplete}`);
|
|
7194
7174
|
}
|
|
@@ -7213,6 +7193,7 @@ function isValidTransition(from, to) {
|
|
|
7213
7193
|
if (from === "in_progress") {
|
|
7214
7194
|
return to === "completed" || to === "failed" || to === "blocked" || to === "cancelled";
|
|
7215
7195
|
}
|
|
7196
|
+
if (from === "blocked" || from === "failed") return to === "pending" || to === "in_progress" || to === "cancelled";
|
|
7216
7197
|
return false;
|
|
7217
7198
|
}
|
|
7218
7199
|
function normalizeAbandonedPlan(plan) {
|
|
@@ -8085,7 +8066,7 @@ function providerValidMessages(input) {
|
|
|
8085
8066
|
// src/context/workspace-instructions.ts
|
|
8086
8067
|
import { createHash as createHash5 } from "crypto";
|
|
8087
8068
|
import { lstat as lstat5, readFile as readFile7, realpath as realpath2 } from "fs/promises";
|
|
8088
|
-
import { dirname as
|
|
8069
|
+
import { dirname as dirname7, isAbsolute as isAbsolute7, relative as relative8, resolve as resolve9, sep as sep8 } from "path";
|
|
8089
8070
|
var NAMES = ["AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
8090
8071
|
var ROOT_NAMES = ["AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
8091
8072
|
var WorkspaceInstructions = class {
|
|
@@ -8113,7 +8094,7 @@ var WorkspaceInstructions = class {
|
|
|
8113
8094
|
const depth = depthOf(this.#root, current);
|
|
8114
8095
|
directories.set(current, depth);
|
|
8115
8096
|
if (current === this.#root) break;
|
|
8116
|
-
current =
|
|
8097
|
+
current = dirname7(current);
|
|
8117
8098
|
}
|
|
8118
8099
|
}
|
|
8119
8100
|
const ordered = [...directories].map(([path, depth]) => ({ path, depth })).sort((a, b) => a.depth - b.depth || a.path.localeCompare(b.path));
|
|
@@ -8187,9 +8168,9 @@ ${document.content}
|
|
|
8187
8168
|
};
|
|
8188
8169
|
async function directoryForPath(path) {
|
|
8189
8170
|
try {
|
|
8190
|
-
return (await lstat5(path)).isDirectory() ? path :
|
|
8171
|
+
return (await lstat5(path)).isDirectory() ? path : dirname7(path);
|
|
8191
8172
|
} catch {
|
|
8192
|
-
return
|
|
8173
|
+
return dirname7(path);
|
|
8193
8174
|
}
|
|
8194
8175
|
}
|
|
8195
8176
|
function depthOf(root, path) {
|
|
@@ -8288,8 +8269,8 @@ function withStructuredOutput(options) {
|
|
|
8288
8269
|
if (retry.backoffMs.length < retry.maxRetries) {
|
|
8289
8270
|
throw new Error("Structured output backoffMs must provide one delay per retry");
|
|
8290
8271
|
}
|
|
8291
|
-
for (const
|
|
8292
|
-
if (!Number.isSafeInteger(
|
|
8272
|
+
for (const delay2 of retry.backoffMs.slice(0, retry.maxRetries)) {
|
|
8273
|
+
if (!Number.isSafeInteger(delay2) || delay2 < 0) {
|
|
8293
8274
|
throw new Error("Structured output retry delays must be non-negative integers");
|
|
8294
8275
|
}
|
|
8295
8276
|
}
|
|
@@ -8650,10 +8631,10 @@ function sanitize(message2, secrets) {
|
|
|
8650
8631
|
}
|
|
8651
8632
|
function waitForRetry(delayMs, signal) {
|
|
8652
8633
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
8653
|
-
return new Promise((
|
|
8634
|
+
return new Promise((resolve32, reject) => {
|
|
8654
8635
|
const timer = setTimeout(() => {
|
|
8655
8636
|
signal?.removeEventListener("abort", onAbort);
|
|
8656
|
-
|
|
8637
|
+
resolve32();
|
|
8657
8638
|
}, delayMs);
|
|
8658
8639
|
const onAbort = () => {
|
|
8659
8640
|
clearTimeout(timer);
|
|
@@ -8724,6 +8705,15 @@ var AgentLoop = class {
|
|
|
8724
8705
|
get iterationLimitMode() {
|
|
8725
8706
|
return this.#iterationLimitMode;
|
|
8726
8707
|
}
|
|
8708
|
+
get maxIterations() {
|
|
8709
|
+
return this.#options.maxIterations;
|
|
8710
|
+
}
|
|
8711
|
+
get softLimitFactor() {
|
|
8712
|
+
return this.#options.softLimitFactor;
|
|
8713
|
+
}
|
|
8714
|
+
get extendIterations() {
|
|
8715
|
+
return this.#options.extendIterations;
|
|
8716
|
+
}
|
|
8727
8717
|
setModel(modelId) {
|
|
8728
8718
|
this.#options.registry.get(modelId);
|
|
8729
8719
|
this.#options.modelId = modelId;
|
|
@@ -9291,10 +9281,10 @@ function serializeToolInput(input) {
|
|
|
9291
9281
|
}
|
|
9292
9282
|
function waitForRetry2(delayMs, signal) {
|
|
9293
9283
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
9294
|
-
return new Promise((
|
|
9284
|
+
return new Promise((resolve32, reject) => {
|
|
9295
9285
|
const timer = setTimeout(() => {
|
|
9296
9286
|
signal?.removeEventListener("abort", onAbort);
|
|
9297
|
-
|
|
9287
|
+
resolve32();
|
|
9298
9288
|
}, delayMs);
|
|
9299
9289
|
const onAbort = () => {
|
|
9300
9290
|
clearTimeout(timer);
|
|
@@ -9504,8 +9494,9 @@ var ToolRuntime = class {
|
|
|
9504
9494
|
}
|
|
9505
9495
|
}
|
|
9506
9496
|
async execute(call, context) {
|
|
9497
|
+
if (this.#disposed) return { ok: false, error: { code: "runtime_disposed", message: "ToolRuntime is disposed" } };
|
|
9507
9498
|
const definition = this.#tools.get(call.name);
|
|
9508
|
-
const journalId = definition === void 0 || this.#journal === void 0 ? void 0 : this.#journal.start(call.name, call.input, definition.retrySafe === true || definition.readOnly === true);
|
|
9499
|
+
const journalId = definition === void 0 || this.#journal === void 0 ? void 0 : this.#journal.start(call.name, call.input, definition.retrySafe === true || definition.readOnly === true || getToolCategory(call.name) === "read");
|
|
9509
9500
|
try {
|
|
9510
9501
|
const result = await this.#execute(call, context);
|
|
9511
9502
|
if (journalId !== void 0) this.#journal?.complete(journalId, result);
|
|
@@ -9532,6 +9523,10 @@ var ToolRuntime = class {
|
|
|
9532
9523
|
if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
|
|
9533
9524
|
const signal = context.signal ?? new AbortController().signal;
|
|
9534
9525
|
const toolCallId = call.id ?? randomUUID7();
|
|
9526
|
+
if (tool.agents !== void 0 && !tool.agents.includes(context.agent)) {
|
|
9527
|
+
return this.#fail(toolCallId, tool.name, call.input, context.agent, "permission_denied", `Tool ${tool.name} is not available to ${context.agent} agents`);
|
|
9528
|
+
}
|
|
9529
|
+
if (signal.aborted) return this.#fail(toolCallId, tool.name, call.input, context.agent, "cancelled", message(signal.reason));
|
|
9535
9530
|
const validation = this.normalize(call);
|
|
9536
9531
|
if (!validation.ok) {
|
|
9537
9532
|
return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
@@ -9639,17 +9634,37 @@ var ToolRuntime = class {
|
|
|
9639
9634
|
} catch (error) {
|
|
9640
9635
|
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
|
|
9641
9636
|
}
|
|
9642
|
-
const
|
|
9643
|
-
|
|
9644
|
-
|
|
9637
|
+
const warnings = [];
|
|
9638
|
+
let presentation = getToolPresentation(rawOutput);
|
|
9639
|
+
try {
|
|
9640
|
+
presentation = tool.presentResult?.(rawOutput, input) ?? presentation;
|
|
9641
|
+
} catch (error) {
|
|
9642
|
+
warnings.push(`Tool succeeded; result presentation failed: ${message(error)}`);
|
|
9643
|
+
}
|
|
9644
|
+
let rendered;
|
|
9645
|
+
try {
|
|
9646
|
+
rendered = tool.renderForModel?.(rawOutput, input);
|
|
9647
|
+
} catch (error) {
|
|
9648
|
+
warnings.push(`Tool succeeded; result rendering failed: ${message(error)}`);
|
|
9649
|
+
}
|
|
9645
9650
|
const output = rendered === void 0 ? await this.#limitOutput(tool.name, rawOutput) : rawOutput;
|
|
9646
9651
|
const content = rendered === void 0 ? void 0 : await this.#limitText(tool.name, rendered);
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
}
|
|
9652
|
+
let additionalContext = [];
|
|
9653
|
+
try {
|
|
9654
|
+
additionalContext = await this.#afterSuccess?.(tool.name, tool.paths(input), input, rawOutput, context) ?? [];
|
|
9655
|
+
} catch (error) {
|
|
9656
|
+
warnings.push(`Tool succeeded; context update failed: ${message(error)}`);
|
|
9657
|
+
}
|
|
9658
|
+
try {
|
|
9659
|
+
await this.#hooks.emit({
|
|
9660
|
+
version: 1,
|
|
9661
|
+
type: "PostToolUse",
|
|
9662
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
|
|
9663
|
+
});
|
|
9664
|
+
} catch (error) {
|
|
9665
|
+
warnings.push(`Tool succeeded; PostToolUse hook failed: ${message(error)}`);
|
|
9666
|
+
}
|
|
9667
|
+
additionalContext = [...additionalContext, ...warnings];
|
|
9653
9668
|
return {
|
|
9654
9669
|
ok: true,
|
|
9655
9670
|
output,
|
|
@@ -9680,17 +9695,23 @@ var ToolRuntime = class {
|
|
|
9680
9695
|
const preview = headTailPreview(serialized, previewChars);
|
|
9681
9696
|
this.#turnOutputChars += preview.length;
|
|
9682
9697
|
const directory = join10(this.#workspace, ".flavor", "tool-results");
|
|
9683
|
-
await mkdir7(directory, { recursive: true });
|
|
9684
9698
|
const filename2 = `${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-")}-${randomUUID7()}-${safeFilename(tool)}.txt`;
|
|
9685
9699
|
const savedTo = join10(directory, filename2);
|
|
9686
|
-
|
|
9700
|
+
let storage;
|
|
9701
|
+
try {
|
|
9702
|
+
await mkdir7(directory, { recursive: true });
|
|
9703
|
+
await writeFile4(savedTo, serialized, "utf8");
|
|
9704
|
+
storage = { savedTo };
|
|
9705
|
+
} catch (error) {
|
|
9706
|
+
storage = { storageError: `Tool succeeded, but the full output could not be saved: ${message(error)}` };
|
|
9707
|
+
}
|
|
9687
9708
|
return {
|
|
9688
9709
|
truncated: true,
|
|
9689
9710
|
reason,
|
|
9690
9711
|
preview,
|
|
9691
9712
|
originalChars: serialized.length,
|
|
9692
9713
|
previewChars: preview.length,
|
|
9693
|
-
|
|
9714
|
+
...storage
|
|
9694
9715
|
};
|
|
9695
9716
|
}
|
|
9696
9717
|
callPresentation(call) {
|
|
@@ -9891,6 +9912,8 @@ var LocalHarness = class {
|
|
|
9891
9912
|
agent,
|
|
9892
9913
|
ownerId,
|
|
9893
9914
|
...maxIterations === void 0 ? {} : { maxIterations },
|
|
9915
|
+
...this.#options.softLimitFactor === void 0 ? {} : { softLimitFactor: this.#options.softLimitFactor },
|
|
9916
|
+
...this.#options.extendIterations === void 0 ? {} : { extendIterations: this.#options.extendIterations },
|
|
9894
9917
|
...isMain && this.#options.hasActiveProgress !== void 0 ? { hasActiveProgress: this.#options.hasActiveProgress } : {},
|
|
9895
9918
|
...isMain && this.#options.hallucinationGuard !== void 0 ? { hallucinationGuard: this.#options.hallucinationGuard } : {},
|
|
9896
9919
|
...this.#options.modelJournal === void 0 ? {} : { modelJournal: this.#options.modelJournal }
|
|
@@ -9920,7 +9943,7 @@ function toModelTool(tool) {
|
|
|
9920
9943
|
|
|
9921
9944
|
// src/harness/journal.ts
|
|
9922
9945
|
import { createHash as createHash6, randomUUID as randomUUID8 } from "crypto";
|
|
9923
|
-
import { closeSync, constants as constants3, existsSync as existsSync4, fsyncSync, mkdirSync as
|
|
9946
|
+
import { closeSync, constants as constants3, existsSync as existsSync4, fsyncSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, realpathSync as realpathSync2, renameSync, statSync as statSync2, truncateSync, unlinkSync, writeSync } from "fs";
|
|
9924
9947
|
import { join as join11, relative as relative9, resolve as resolve11, sep as sep9 } from "path";
|
|
9925
9948
|
import { z as z14 } from "zod";
|
|
9926
9949
|
|
|
@@ -10013,7 +10036,7 @@ var HarnessJournal = class {
|
|
|
10013
10036
|
const workspace = resolve11(options.workspace);
|
|
10014
10037
|
const directory = join11(workspace, ".flavor", "sessions");
|
|
10015
10038
|
assertSessionId(options.sessionId);
|
|
10016
|
-
|
|
10039
|
+
mkdirSync2(directory, { recursive: true, mode: 448 });
|
|
10017
10040
|
assertWithin(workspace, directory);
|
|
10018
10041
|
const canonicalWorkspace = realpathSync2.native(workspace);
|
|
10019
10042
|
const canonicalDirectory = realpathSync2.native(directory);
|
|
@@ -10332,7 +10355,7 @@ function isWithin2(root, target) {
|
|
|
10332
10355
|
}
|
|
10333
10356
|
|
|
10334
10357
|
// src/hooks/bus.ts
|
|
10335
|
-
import { spawn as
|
|
10358
|
+
import { spawn as spawn6 } from "child_process";
|
|
10336
10359
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
10337
10360
|
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
10338
10361
|
var DEFAULT_MAX_OUTPUT_BYTES2 = 1048576;
|
|
@@ -10439,10 +10462,10 @@ function decoratePayload(payload, envelope) {
|
|
|
10439
10462
|
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
10440
10463
|
}
|
|
10441
10464
|
function runShellHandler(descriptor, event, signal) {
|
|
10442
|
-
return new Promise((
|
|
10465
|
+
return new Promise((resolve32, reject) => {
|
|
10443
10466
|
const env = { ...process.env, ...descriptor.env };
|
|
10444
10467
|
const invocation = prepareSpawnInvocation(descriptor.command, descriptor.args ?? [], { env });
|
|
10445
|
-
const child =
|
|
10468
|
+
const child = spawn6(invocation.command, invocation.args, {
|
|
10446
10469
|
env,
|
|
10447
10470
|
shell: false,
|
|
10448
10471
|
detached: process.platform !== "win32",
|
|
@@ -10459,7 +10482,7 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
10459
10482
|
if (settled) return;
|
|
10460
10483
|
settled = true;
|
|
10461
10484
|
signal.removeEventListener("abort", onAbort);
|
|
10462
|
-
|
|
10485
|
+
terminateProcessTree2(child);
|
|
10463
10486
|
reject(error);
|
|
10464
10487
|
};
|
|
10465
10488
|
const append = (stream, chunk) => {
|
|
@@ -10485,7 +10508,7 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
10485
10508
|
return;
|
|
10486
10509
|
}
|
|
10487
10510
|
try {
|
|
10488
|
-
|
|
10511
|
+
resolve32(HookDecisionSchema.parse(JSON.parse(stdout)));
|
|
10489
10512
|
} catch (error) {
|
|
10490
10513
|
reject(error);
|
|
10491
10514
|
}
|
|
@@ -10493,10 +10516,10 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
10493
10516
|
child.stdin.end(JSON.stringify(event));
|
|
10494
10517
|
});
|
|
10495
10518
|
}
|
|
10496
|
-
function
|
|
10519
|
+
function terminateProcessTree2(child) {
|
|
10497
10520
|
if (child.pid === void 0) return;
|
|
10498
10521
|
if (process.platform === "win32") {
|
|
10499
|
-
const killer =
|
|
10522
|
+
const killer = spawn6("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
10500
10523
|
stdio: "ignore",
|
|
10501
10524
|
windowsHide: true
|
|
10502
10525
|
});
|
|
@@ -10682,14 +10705,14 @@ async function createIslandControlServer(options) {
|
|
|
10682
10705
|
void dispatch(line, socket, options, token);
|
|
10683
10706
|
});
|
|
10684
10707
|
});
|
|
10685
|
-
await new Promise((
|
|
10708
|
+
await new Promise((resolve32, reject) => {
|
|
10686
10709
|
const onError = (error) => {
|
|
10687
10710
|
server.removeListener("listening", onListening);
|
|
10688
10711
|
reject(error);
|
|
10689
10712
|
};
|
|
10690
10713
|
const onListening = () => {
|
|
10691
10714
|
server.removeListener("error", onError);
|
|
10692
|
-
|
|
10715
|
+
resolve32();
|
|
10693
10716
|
};
|
|
10694
10717
|
server.once("error", onError);
|
|
10695
10718
|
server.once("listening", onListening);
|
|
@@ -10760,17 +10783,17 @@ function write(socket, payload) {
|
|
|
10760
10783
|
}
|
|
10761
10784
|
}
|
|
10762
10785
|
function closeServer(server) {
|
|
10763
|
-
return new Promise((
|
|
10786
|
+
return new Promise((resolve32) => {
|
|
10764
10787
|
try {
|
|
10765
|
-
server.close(() =>
|
|
10788
|
+
server.close(() => resolve32());
|
|
10766
10789
|
} catch {
|
|
10767
|
-
|
|
10790
|
+
resolve32();
|
|
10768
10791
|
}
|
|
10769
10792
|
});
|
|
10770
10793
|
}
|
|
10771
10794
|
|
|
10772
10795
|
// src/evolve/service.ts
|
|
10773
|
-
import { spawn as
|
|
10796
|
+
import { spawn as spawn7 } from "child_process";
|
|
10774
10797
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
10775
10798
|
import { join as join15 } from "path";
|
|
10776
10799
|
import { z as z16 } from "zod";
|
|
@@ -10790,7 +10813,7 @@ import { z as z15 } from "zod";
|
|
|
10790
10813
|
|
|
10791
10814
|
// src/plugins/worker-host.ts
|
|
10792
10815
|
import { realpath as realpath3 } from "fs/promises";
|
|
10793
|
-
import { dirname as
|
|
10816
|
+
import { dirname as dirname8 } from "path";
|
|
10794
10817
|
import { Worker } from "worker_threads";
|
|
10795
10818
|
var DEFAULT_ACTIVATION_TIMEOUT_MS = 1e4;
|
|
10796
10819
|
var DEFAULT_INVOCATION_TIMEOUT_MS = 3e4;
|
|
@@ -10798,7 +10821,7 @@ var DEFAULT_MAX_OLD_SPACE_MB = 128;
|
|
|
10798
10821
|
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
10799
10822
|
async function startPluginSandbox(options) {
|
|
10800
10823
|
const entryPath = await realpath3(options.entryPath);
|
|
10801
|
-
const pluginRoot = await realpath3(options.pluginRoot ??
|
|
10824
|
+
const pluginRoot = await realpath3(options.pluginRoot ?? dirname8(entryPath));
|
|
10802
10825
|
const activationTimeoutMs = options.activationTimeoutMs ?? DEFAULT_ACTIVATION_TIMEOUT_MS;
|
|
10803
10826
|
const invocationTimeoutMs = options.invocationTimeoutMs ?? DEFAULT_INVOCATION_TIMEOUT_MS;
|
|
10804
10827
|
let configJson;
|
|
@@ -11618,7 +11641,7 @@ async function revertFixPlugin(workspace, name) {
|
|
|
11618
11641
|
// src/evolve/store.ts
|
|
11619
11642
|
import { createHash as createHash9 } from "crypto";
|
|
11620
11643
|
import { appendFile, mkdir as mkdir9, readFile as readFile9, rm as rm3, writeFile as writeFile7 } from "fs/promises";
|
|
11621
|
-
import { dirname as
|
|
11644
|
+
import { dirname as dirname9, join as join14 } from "path";
|
|
11622
11645
|
function normalizeError(message2) {
|
|
11623
11646
|
return String(message2 ?? "").replace(/\s+/g, " ").replace(/"[^"]*"/g, '"\u2026"').replace(/`[^`]*`/g, '"\u2026"').trim().slice(0, 160);
|
|
11624
11647
|
}
|
|
@@ -11647,7 +11670,7 @@ async function readJsonLines(file) {
|
|
|
11647
11670
|
}
|
|
11648
11671
|
}
|
|
11649
11672
|
async function writeJsonLines(file, records) {
|
|
11650
|
-
await mkdir9(
|
|
11673
|
+
await mkdir9(dirname9(file), { recursive: true, mode: 448 });
|
|
11651
11674
|
const body = records.map((record) => JSON.stringify(record)).join("\n");
|
|
11652
11675
|
await writeFile7(file, body.length > 0 ? `${body}
|
|
11653
11676
|
` : "", { encoding: "utf8", mode: 384 });
|
|
@@ -11675,7 +11698,7 @@ async function readRules(file) {
|
|
|
11675
11698
|
}
|
|
11676
11699
|
}
|
|
11677
11700
|
async function writeRules(file, rules) {
|
|
11678
|
-
await mkdir9(
|
|
11701
|
+
await mkdir9(dirname9(file), { recursive: true, mode: 448 });
|
|
11679
11702
|
await writeFile7(file, JSON.stringify(rules, null, 2), { encoding: "utf8", mode: 384 });
|
|
11680
11703
|
}
|
|
11681
11704
|
var EvolveStore = class {
|
|
@@ -11915,9 +11938,9 @@ var USAGE = [
|
|
|
11915
11938
|
" clear reset signals, done markers, and verified markers"
|
|
11916
11939
|
].join("\n");
|
|
11917
11940
|
function runCommand(command, cwd, timeoutMs) {
|
|
11918
|
-
return new Promise((
|
|
11941
|
+
return new Promise((resolve32) => {
|
|
11919
11942
|
const isWindows = process.platform === "win32";
|
|
11920
|
-
const child =
|
|
11943
|
+
const child = spawn7(isWindows ? "cmd.exe" : "/bin/sh", isWindows ? ["/d", "/s", "/c", command] : ["-c", command], {
|
|
11921
11944
|
cwd,
|
|
11922
11945
|
windowsHide: true
|
|
11923
11946
|
});
|
|
@@ -11925,7 +11948,7 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
11925
11948
|
let stderr = "";
|
|
11926
11949
|
const timer = setTimeout(() => {
|
|
11927
11950
|
child.kill();
|
|
11928
|
-
|
|
11951
|
+
resolve32({ ok: false, stdout, stderr: `${stderr}
|
|
11929
11952
|
[killed: exceeded ${timeoutMs}ms]`, code: "timeout" });
|
|
11930
11953
|
}, timeoutMs);
|
|
11931
11954
|
child.stdout.on("data", (chunk) => {
|
|
@@ -11936,12 +11959,12 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
11936
11959
|
});
|
|
11937
11960
|
child.on("error", (error) => {
|
|
11938
11961
|
clearTimeout(timer);
|
|
11939
|
-
|
|
11962
|
+
resolve32({ ok: false, stdout, stderr: `${stderr}
|
|
11940
11963
|
${error.message}`, code: "spawn" });
|
|
11941
11964
|
});
|
|
11942
11965
|
child.on("close", (code) => {
|
|
11943
11966
|
clearTimeout(timer);
|
|
11944
|
-
|
|
11967
|
+
resolve32({ ok: code === 0, stdout, stderr, code: code ?? "unknown" });
|
|
11945
11968
|
});
|
|
11946
11969
|
});
|
|
11947
11970
|
}
|
|
@@ -12312,10 +12335,12 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
12312
12335
|
name: "evolve_improve",
|
|
12313
12336
|
description: "Implement a fix for one repeated tool failure. Default kind=plugin scaffolds the fix-<tool>/ plugin dir and writes PLAN.md with instructions for implementing, verifying, reloading, and testing it. kind=prompt_rule instead stores a concise guardrail rule (from `implementation`) that is injected into future system prompts and closes the suggestion. Use when the model proposes a concrete fix for a suggestion in the system prompt.",
|
|
12314
12337
|
inputSchema,
|
|
12315
|
-
paths: () => [],
|
|
12316
|
-
execute: async (input) => {
|
|
12338
|
+
paths: (input) => [join15(workspace, ".flavor", "evolve"), ...input.kind === "prompt_rule" ? [] : [join15(workspace, ".flavor", "plugins")]],
|
|
12339
|
+
execute: async (input, signal) => {
|
|
12340
|
+
signal.throwIfAborted();
|
|
12317
12341
|
const { suggestionId, implementation, kind } = input;
|
|
12318
12342
|
const suggestions = await openWithTrends(100);
|
|
12343
|
+
signal.throwIfAborted();
|
|
12319
12344
|
const suggestion = suggestions.find((item) => item.id === suggestionId);
|
|
12320
12345
|
if (suggestion === void 0) throw new Error(`No open suggestion with id "${suggestionId}".`);
|
|
12321
12346
|
if (kind === "prompt_rule") {
|
|
@@ -13427,7 +13452,7 @@ function formatPriorGaps(gaps) {
|
|
|
13427
13452
|
import { constants as constants4 } from "fs";
|
|
13428
13453
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
13429
13454
|
import { lstat as lstat7, mkdir as mkdir11, open as open6, rename as rename4, rm as rm4 } from "fs/promises";
|
|
13430
|
-
import { dirname as
|
|
13455
|
+
import { dirname as dirname10, join as join18, relative as relative11, resolve as resolve14, sep as sep11 } from "path";
|
|
13431
13456
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
13432
13457
|
var GoalStore = class {
|
|
13433
13458
|
#workspace;
|
|
@@ -13440,7 +13465,7 @@ var GoalStore = class {
|
|
|
13440
13465
|
}
|
|
13441
13466
|
async save(input) {
|
|
13442
13467
|
const state = GoalStateSchema.parse(input);
|
|
13443
|
-
await assertNoSymlink(this.#workspace,
|
|
13468
|
+
await assertNoSymlink(this.#workspace, dirname10(this.#root));
|
|
13444
13469
|
await mkdir11(this.#root, { recursive: true, mode: 448 });
|
|
13445
13470
|
await assertNoSymlink(this.#workspace, this.#root);
|
|
13446
13471
|
const target = join18(this.#root, `${state.id}.json`);
|
|
@@ -13474,7 +13499,7 @@ async function assertNoSymlink(root, target) {
|
|
|
13474
13499
|
const chain = [];
|
|
13475
13500
|
while (cursor !== stop) {
|
|
13476
13501
|
chain.push(cursor);
|
|
13477
|
-
cursor =
|
|
13502
|
+
cursor = dirname10(cursor);
|
|
13478
13503
|
}
|
|
13479
13504
|
for (const path of chain.reverse()) {
|
|
13480
13505
|
try {
|
|
@@ -13561,7 +13586,7 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
13561
13586
|
// src/loop/store.ts
|
|
13562
13587
|
import { constants as constants5 } from "fs";
|
|
13563
13588
|
import { lstat as lstat8, mkdir as mkdir13, open as open7, readFile as readFile12, realpath as realpath5, rename as rename5, rm as rm5, stat as stat6 } from "fs/promises";
|
|
13564
|
-
import { dirname as
|
|
13589
|
+
import { dirname as dirname11, join as join20, relative as relative12, resolve as resolve16, sep as sep12 } from "path";
|
|
13565
13590
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
13566
13591
|
var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
13567
13592
|
var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
|
|
@@ -13643,7 +13668,7 @@ var LoopStore = class {
|
|
|
13643
13668
|
}
|
|
13644
13669
|
async #prepare(loopId) {
|
|
13645
13670
|
validateId(loopId);
|
|
13646
|
-
await assertNoSymlink2(this.#workspace,
|
|
13671
|
+
await assertNoSymlink2(this.#workspace, dirname11(this.#root));
|
|
13647
13672
|
await mkdir13(this.#root, { recursive: true, mode: 448 });
|
|
13648
13673
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
13649
13674
|
const directory = join20(this.#root, loopId);
|
|
@@ -13676,7 +13701,7 @@ async function assertNoSymlink2(root, target) {
|
|
|
13676
13701
|
const chain = [];
|
|
13677
13702
|
while (cursor !== stop) {
|
|
13678
13703
|
chain.push(cursor);
|
|
13679
|
-
cursor =
|
|
13704
|
+
cursor = dirname11(cursor);
|
|
13680
13705
|
}
|
|
13681
13706
|
for (const path of chain.reverse()) {
|
|
13682
13707
|
try {
|
|
@@ -14908,8 +14933,8 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14908
14933
|
async #startCallbackServer(expectedState, signal) {
|
|
14909
14934
|
let serverResolve;
|
|
14910
14935
|
let serverReject;
|
|
14911
|
-
const codePromise = new Promise((
|
|
14912
|
-
serverResolve =
|
|
14936
|
+
const codePromise = new Promise((resolve32, reject) => {
|
|
14937
|
+
serverResolve = resolve32;
|
|
14913
14938
|
serverReject = reject;
|
|
14914
14939
|
});
|
|
14915
14940
|
const server = createServer3((req, res) => {
|
|
@@ -14943,7 +14968,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14943
14968
|
);
|
|
14944
14969
|
serverResolve(code);
|
|
14945
14970
|
});
|
|
14946
|
-
return new Promise((
|
|
14971
|
+
return new Promise((resolve32, reject) => {
|
|
14947
14972
|
server.once("error", (error) => {
|
|
14948
14973
|
reject(new Error(`OAuth callback server failed: ${error.message}`));
|
|
14949
14974
|
});
|
|
@@ -14960,7 +14985,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14960
14985
|
signal.addEventListener("abort", () => {
|
|
14961
14986
|
serverReject(new Error("OAuth flow aborted"));
|
|
14962
14987
|
}, { once: true });
|
|
14963
|
-
|
|
14988
|
+
resolve32({ redirectUri, promise: codePromise });
|
|
14964
14989
|
});
|
|
14965
14990
|
});
|
|
14966
14991
|
}
|
|
@@ -15042,7 +15067,7 @@ function escapeHtml(text) {
|
|
|
15042
15067
|
}
|
|
15043
15068
|
|
|
15044
15069
|
// src/auth/store.ts
|
|
15045
|
-
import { dirname as
|
|
15070
|
+
import { dirname as dirname12 } from "path";
|
|
15046
15071
|
async function retainOnlyCredentials(store, keepCredentialId) {
|
|
15047
15072
|
const tokens = await store.load();
|
|
15048
15073
|
const retained = Object.fromEntries(Object.entries(tokens).filter(([id]) => id === keepCredentialId));
|
|
@@ -15053,7 +15078,7 @@ function createFileTokenStore(filePath) {
|
|
|
15053
15078
|
const store = {
|
|
15054
15079
|
tokens: {},
|
|
15055
15080
|
async load() {
|
|
15056
|
-
const key = await loadOrCreateConfigKey(
|
|
15081
|
+
const key = await loadOrCreateConfigKey(dirname12(filePath));
|
|
15057
15082
|
const result = await readRecoverableFile(filePath, (raw) => parseTokens(decryptDocument(raw, key)));
|
|
15058
15083
|
const tokens = result?.value ?? {};
|
|
15059
15084
|
baseline = { ...tokens };
|
|
@@ -15061,7 +15086,7 @@ function createFileTokenStore(filePath) {
|
|
|
15061
15086
|
return tokens;
|
|
15062
15087
|
},
|
|
15063
15088
|
async save(tokens) {
|
|
15064
|
-
const key = await loadOrCreateConfigKey(
|
|
15089
|
+
const key = await loadOrCreateConfigKey(dirname12(filePath));
|
|
15065
15090
|
const removals = Object.keys(baseline).filter((providerId) => !(providerId in tokens));
|
|
15066
15091
|
const upserts = Object.fromEntries(Object.entries(tokens).filter(
|
|
15067
15092
|
([providerId, token]) => !sameToken(baseline[providerId], token)
|
|
@@ -15890,10 +15915,14 @@ function createSkillResourceTool(registry) {
|
|
|
15890
15915
|
name: "SkillResource",
|
|
15891
15916
|
description: "Read a bounded resource explicitly referenced by a discovered skill; scripts are returned as data and never executed",
|
|
15892
15917
|
inputSchema: SkillResourceInput,
|
|
15918
|
+
readOnly: true,
|
|
15893
15919
|
paths: () => [],
|
|
15894
15920
|
execute: async (input, signal) => {
|
|
15895
15921
|
signal.throwIfAborted();
|
|
15896
|
-
const
|
|
15922
|
+
const requested = input.skill.trim();
|
|
15923
|
+
const alias = requested.includes(":") ? requested.slice(requested.lastIndexOf(":") + 1) : requested;
|
|
15924
|
+
const discovered = await registry.discover();
|
|
15925
|
+
const skill = discovered.find((candidate) => candidate.name === requested) ?? discovered.find((candidate) => candidate.name === alias);
|
|
15897
15926
|
if (skill === void 0) throw new Error(`Unknown skill: ${input.skill}`);
|
|
15898
15927
|
const capability = await registry.resolveResource(skill, input.reference);
|
|
15899
15928
|
const content = await registry.readResource(capability);
|
|
@@ -15911,7 +15940,7 @@ function createSkillResourceTool(registry) {
|
|
|
15911
15940
|
// src/session/store.ts
|
|
15912
15941
|
import { constants as constants7 } from "fs";
|
|
15913
15942
|
import { lstat as lstat10, mkdir as mkdir14, open as open8, readFile as readFile17, readdir as readdir7, realpath as realpath7, rename as rename6, rm as rm6, stat as stat7 } from "fs/promises";
|
|
15914
|
-
import { basename as basename4, dirname as
|
|
15943
|
+
import { basename as basename4, dirname as dirname13, join as join23, relative as relative14, resolve as resolve20, sep as sep14 } from "path";
|
|
15915
15944
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
15916
15945
|
import { z as z22 } from "zod";
|
|
15917
15946
|
var SESSION_VERSION = 4;
|
|
@@ -16265,7 +16294,7 @@ var SessionStore = class {
|
|
|
16265
16294
|
}
|
|
16266
16295
|
}
|
|
16267
16296
|
async #prepareDirectory() {
|
|
16268
|
-
await assertNoSymlink3(this.#workspace,
|
|
16297
|
+
await assertNoSymlink3(this.#workspace, dirname13(this.#sessions));
|
|
16269
16298
|
await mkdir14(this.#sessions, { recursive: true, mode: 448 });
|
|
16270
16299
|
await this.#assertSafeExistingDirectory();
|
|
16271
16300
|
}
|
|
@@ -16493,7 +16522,7 @@ async function assertNoSymlink3(root, target) {
|
|
|
16493
16522
|
const chain = [];
|
|
16494
16523
|
while (cursor !== stop) {
|
|
16495
16524
|
chain.push(cursor);
|
|
16496
|
-
cursor =
|
|
16525
|
+
cursor = dirname13(cursor);
|
|
16497
16526
|
}
|
|
16498
16527
|
for (const path of chain.reverse()) {
|
|
16499
16528
|
try {
|
|
@@ -16767,14 +16796,14 @@ var ProjectSleepScheduler = class {
|
|
|
16767
16796
|
const next = new Date(now);
|
|
16768
16797
|
next.setDate(next.getDate() + 1);
|
|
16769
16798
|
next.setHours(0, 0, 0, 0);
|
|
16770
|
-
const
|
|
16799
|
+
const delay2 = Math.max(1, next.getTime() - now.getTime());
|
|
16771
16800
|
const targetDate = localDateKey(now);
|
|
16772
16801
|
this.#dailyTimer = setTimeout(() => {
|
|
16773
16802
|
this.#dailyTimer = void 0;
|
|
16774
16803
|
if (this.#disposed) return;
|
|
16775
16804
|
this.#enqueue(targetDate);
|
|
16776
16805
|
this.#scheduleNextMidnight();
|
|
16777
|
-
},
|
|
16806
|
+
}, delay2);
|
|
16778
16807
|
this.#dailyTimer.unref?.();
|
|
16779
16808
|
}
|
|
16780
16809
|
#enqueue(date) {
|
|
@@ -17069,7 +17098,7 @@ function computeSessionStats(documents) {
|
|
|
17069
17098
|
// src/tools/files.ts
|
|
17070
17099
|
import { constants as constants9 } from "fs";
|
|
17071
17100
|
import { mkdir as mkdir16, open as open10, readFile as readFile18, realpath as realpath9, rename as rename8, rm as rm8, stat as stat8 } from "fs/promises";
|
|
17072
|
-
import { dirname as
|
|
17101
|
+
import { dirname as dirname14, isAbsolute as isAbsolute11, relative as relative16, resolve as resolve22, sep as sep16 } from "path";
|
|
17073
17102
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
17074
17103
|
import { z as z24 } from "zod";
|
|
17075
17104
|
|
|
@@ -17187,8 +17216,12 @@ var ReadInput2 = z24.object({
|
|
|
17187
17216
|
force: z24.union([z24.boolean(), z24.string().refine((value) => value === "true" || value === "false")]).nullable().optional()
|
|
17188
17217
|
});
|
|
17189
17218
|
var WriteInput = z24.object({ path: z24.string().min(1), content: z24.string() });
|
|
17190
|
-
var EditInput = z24.object({
|
|
17191
|
-
|
|
17219
|
+
var EditInput = z24.object({
|
|
17220
|
+
path: z24.string().min(1),
|
|
17221
|
+
oldText: z24.string().min(1).describe("Copy exact current file text from Read, without displayed line numbers or diff prefixes. Include enough context for one unique match."),
|
|
17222
|
+
newText: z24.string()
|
|
17223
|
+
});
|
|
17224
|
+
var ApplyPatchInput = z24.object({ patch: z24.string().min(1).describe("One-file unified diff with --- a/path, +++ b/path and @@ hunk headers. Use /dev/null as the old path to create a file.") });
|
|
17192
17225
|
var FileObservationStore = class {
|
|
17193
17226
|
#versions = /* @__PURE__ */ new Map();
|
|
17194
17227
|
get(path) {
|
|
@@ -17231,9 +17264,9 @@ function createReadTool(workspace, options = {}) {
|
|
|
17231
17264
|
const windowBytes = rangeRequested ? Math.max(maxBytes, Math.min(info.size, MAX_READ_BYTES)) : maxBytes;
|
|
17232
17265
|
const contents = await readBounded2(path, windowBytes, signal, openFile2);
|
|
17233
17266
|
if (isBinary(contents)) throw new Error("Cannot read binary file as text");
|
|
17234
|
-
const textEnd = utf8SafeEnd(contents, windowBytes);
|
|
17267
|
+
const textEnd = contents.length > windowBytes ? utf8SafeEnd(contents, windowBytes) : contents.length;
|
|
17235
17268
|
const truncated = contents.length > windowBytes;
|
|
17236
|
-
const text = contents.subarray(0, textEnd)
|
|
17269
|
+
const text = decodeFileText(contents.subarray(0, textEnd)).replaceAll("\r\n", "\n");
|
|
17237
17270
|
options.observations?.set(path, versionOfStat(info));
|
|
17238
17271
|
const lines = text.split("\n");
|
|
17239
17272
|
if (lines.at(-1) === "") lines.pop();
|
|
@@ -17309,7 +17342,7 @@ function createEditTool(workspace, options = {}) {
|
|
|
17309
17342
|
const guard = createPathGuard(workspace);
|
|
17310
17343
|
return {
|
|
17311
17344
|
name: "Edit",
|
|
17312
|
-
description: "Replace one unique exact text match",
|
|
17345
|
+
description: "Replace one unique exact text match, accepting LF or CRLF input. Read the file before editing; after a mismatch, re-read with force=true and copy the current text instead of retrying the same oldText.",
|
|
17313
17346
|
inputSchema: EditInput,
|
|
17314
17347
|
paths: (input) => [guard.lexical(input.path)],
|
|
17315
17348
|
execute: async (input, signal) => {
|
|
@@ -17319,14 +17352,14 @@ function createEditTool(workspace, options = {}) {
|
|
|
17319
17352
|
const expected = await expectedMutationVersion(path, options.observations);
|
|
17320
17353
|
const contents = await readText(path);
|
|
17321
17354
|
const hasCRLF = contents.includes("\r\n");
|
|
17322
|
-
const norm = (s) =>
|
|
17355
|
+
const norm = (s) => s.replace(/\r\n/g, "\n");
|
|
17323
17356
|
const contentsLF = norm(contents);
|
|
17324
17357
|
const oldTextLF = norm(input.oldText);
|
|
17325
17358
|
const newTextLF = norm(input.newText);
|
|
17326
17359
|
const first = contentsLF.indexOf(oldTextLF);
|
|
17327
|
-
const second = first < 0 ? -1 : contentsLF.indexOf(oldTextLF, first +
|
|
17360
|
+
const second = first < 0 ? -1 : contentsLF.indexOf(oldTextLF, first + 1);
|
|
17328
17361
|
if (first < 0 || second >= 0) {
|
|
17329
|
-
const diagnosis = buildEditDiagnosis(contentsLF, oldTextLF,
|
|
17362
|
+
const diagnosis = buildEditDiagnosis(contentsLF, oldTextLF, second);
|
|
17330
17363
|
throw new Error(diagnosis);
|
|
17331
17364
|
}
|
|
17332
17365
|
const updatedLF = contentsLF.slice(0, first) + newTextLF + contentsLF.slice(first + oldTextLF.length);
|
|
@@ -17346,7 +17379,7 @@ function createApplyPatchTool(workspace, options = {}) {
|
|
|
17346
17379
|
const guard = createPathGuard(workspace);
|
|
17347
17380
|
return {
|
|
17348
17381
|
name: "ApplyPatch",
|
|
17349
|
-
description: "Apply a workspace-limited unified diff, relocating hunks only by unique exact context",
|
|
17382
|
+
description: "Apply a single-file workspace-limited unified diff, relocating hunks only by unique exact context. Submit separate calls for multiple files.",
|
|
17350
17383
|
inputSchema: ApplyPatchInput,
|
|
17351
17384
|
paths: (input) => parsePatch(input.patch).map((file) => guard.lexical(file.path)),
|
|
17352
17385
|
execute: async (input, signal) => {
|
|
@@ -17411,7 +17444,7 @@ function createPathGuard(workspace) {
|
|
|
17411
17444
|
break;
|
|
17412
17445
|
} catch (error) {
|
|
17413
17446
|
if (!isMissing3(error)) throw error;
|
|
17414
|
-
const parent =
|
|
17447
|
+
const parent = dirname14(ancestor);
|
|
17415
17448
|
if (parent === ancestor) throw error;
|
|
17416
17449
|
ancestor = parent;
|
|
17417
17450
|
}
|
|
@@ -17422,12 +17455,19 @@ function createPathGuard(workspace) {
|
|
|
17422
17455
|
}
|
|
17423
17456
|
async function atomicWrite(path, content, signal, expectedVersion) {
|
|
17424
17457
|
abortIfNeeded(signal);
|
|
17425
|
-
await mkdir16(
|
|
17458
|
+
await mkdir16(dirname14(path), { recursive: true });
|
|
17426
17459
|
const temporary = `${path}.${randomUUID15()}.tmp`;
|
|
17460
|
+
let mode;
|
|
17461
|
+
try {
|
|
17462
|
+
mode = (await stat8(path)).mode & 511;
|
|
17463
|
+
} catch (error) {
|
|
17464
|
+
if (!isMissing3(error)) throw error;
|
|
17465
|
+
}
|
|
17427
17466
|
try {
|
|
17428
17467
|
const handle = await open10(temporary, constants9.O_CREAT | constants9.O_EXCL | constants9.O_WRONLY, 384);
|
|
17429
17468
|
try {
|
|
17430
17469
|
await handle.writeFile(content, "utf8");
|
|
17470
|
+
if (mode !== void 0) await handle.chmod(mode);
|
|
17431
17471
|
await handle.sync();
|
|
17432
17472
|
} finally {
|
|
17433
17473
|
await handle.close();
|
|
@@ -17444,7 +17484,14 @@ async function atomicWrite(path, content, signal, expectedVersion) {
|
|
|
17444
17484
|
async function readText(path) {
|
|
17445
17485
|
const contents = await readFile18(path);
|
|
17446
17486
|
if (isBinary(contents)) throw new Error("Cannot edit binary file as text");
|
|
17447
|
-
return contents
|
|
17487
|
+
return decodeFileText(contents);
|
|
17488
|
+
}
|
|
17489
|
+
function decodeFileText(contents) {
|
|
17490
|
+
try {
|
|
17491
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(contents);
|
|
17492
|
+
} catch {
|
|
17493
|
+
throw new Error("File is not valid UTF-8 text. Convert its encoding before using Read/Edit/ApplyPatch to avoid corrupting its contents.");
|
|
17494
|
+
}
|
|
17448
17495
|
}
|
|
17449
17496
|
var ABSENT_VERSION = "absent";
|
|
17450
17497
|
async function expectedMutationVersion(path, observations) {
|
|
@@ -17654,11 +17701,12 @@ function patchPath(header) {
|
|
|
17654
17701
|
const raw = header.split(" ", 1)[0].trim();
|
|
17655
17702
|
return raw.startsWith("a/") || raw.startsWith("b/") ? raw.slice(2) : raw;
|
|
17656
17703
|
}
|
|
17657
|
-
function buildEditDiagnosis(contentsLF, oldTextLF,
|
|
17704
|
+
function buildEditDiagnosis(contentsLF, oldTextLF, second) {
|
|
17658
17705
|
let diagnosis = "oldText must match exactly once";
|
|
17659
17706
|
if (second >= 0) {
|
|
17660
|
-
|
|
17661
|
-
|
|
17707
|
+
let count = 0;
|
|
17708
|
+
for (let index = contentsLF.indexOf(oldTextLF); index >= 0; index = contentsLF.indexOf(oldTextLF, index + 1)) count += 1;
|
|
17709
|
+
diagnosis += ` \u2014 matched ${count} times in the file. No changes made. Include more surrounding text to select one occurrence.`;
|
|
17662
17710
|
return diagnosis;
|
|
17663
17711
|
}
|
|
17664
17712
|
const oldLines = oldTextLF.split("\n");
|
|
@@ -17694,7 +17742,8 @@ ${snippet}
|
|
|
17694
17742
|
First lines of file:
|
|
17695
17743
|
${fileSnippet}`;
|
|
17696
17744
|
}
|
|
17697
|
-
return diagnosis
|
|
17745
|
+
return `${diagnosis}
|
|
17746
|
+
No changes made. Use Read with force=true to retrieve current text, or Grep with fixedStrings=true to locate it. Copy oldText exactly without line-number prefixes; do not repeat the same failed edit.`;
|
|
17698
17747
|
}
|
|
17699
17748
|
function applyHunks(original, hunks) {
|
|
17700
17749
|
const hasCRLF = original.includes("\r\n");
|
|
@@ -17785,6 +17834,19 @@ import { isIP } from "net";
|
|
|
17785
17834
|
import { request as httpRequest } from "http";
|
|
17786
17835
|
import { request as httpsRequest } from "https";
|
|
17787
17836
|
import { z as z25 } from "zod";
|
|
17837
|
+
|
|
17838
|
+
// src/utils/abort.ts
|
|
17839
|
+
function waitWithSignal(operation, signal) {
|
|
17840
|
+
if (signal === void 0) return operation;
|
|
17841
|
+
return new Promise((resolve32, reject) => {
|
|
17842
|
+
const abort2 = () => reject(signal.reason);
|
|
17843
|
+
if (signal.aborted) abort2();
|
|
17844
|
+
else signal.addEventListener("abort", abort2, { once: true });
|
|
17845
|
+
operation.then(resolve32, reject).finally(() => signal.removeEventListener("abort", abort2));
|
|
17846
|
+
});
|
|
17847
|
+
}
|
|
17848
|
+
|
|
17849
|
+
// src/tools/web.ts
|
|
17788
17850
|
var WebFetchInput = z25.object({
|
|
17789
17851
|
url: z25.string().url().max(8192),
|
|
17790
17852
|
timeoutMs: z25.coerce.number().int().positive().max(12e4).optional(),
|
|
@@ -17830,7 +17892,12 @@ function createWebSearchTool(provider = createDefaultWebSearchProvider()) {
|
|
|
17830
17892
|
summarize: (input) => input.query,
|
|
17831
17893
|
presentCall: (input) => ({ kind: "web", title: `Searching: ${input.query}` }),
|
|
17832
17894
|
permissions: () => ({ paths: [] }),
|
|
17833
|
-
execute: async (input, signal) =>
|
|
17895
|
+
execute: async (input, signal) => {
|
|
17896
|
+
signal.throwIfAborted();
|
|
17897
|
+
const maxResults = input.maxResults ?? 8;
|
|
17898
|
+
const results = await waitWithSignal(provider.search(input.query, maxResults, signal), signal);
|
|
17899
|
+
return { query: input.query, results: [...results].slice(0, maxResults) };
|
|
17900
|
+
},
|
|
17834
17901
|
renderForModel: (output) => output.results.length === 0 ? `No web results found for: ${output.query}` : output.results.map((item, index) => `${index + 1}. ${item.title}
|
|
17835
17902
|
${item.url}
|
|
17836
17903
|
${item.snippet}`).join("\n\n"),
|
|
@@ -17875,10 +17942,14 @@ var MultiSourceWebSearchProvider = class {
|
|
|
17875
17942
|
}
|
|
17876
17943
|
};
|
|
17877
17944
|
async function fetchPublic(initialUrl, options) {
|
|
17945
|
+
options.signal.throwIfAborted();
|
|
17946
|
+
const deadline = AbortSignal.timeout(options.timeoutMs);
|
|
17947
|
+
options = { ...options, signal: AbortSignal.any([options.signal, deadline]) };
|
|
17878
17948
|
let current = new URL(initialUrl);
|
|
17879
17949
|
for (let redirects = 0; redirects <= 5; redirects += 1) {
|
|
17950
|
+
options.signal.throwIfAborted();
|
|
17880
17951
|
validateUrl(current);
|
|
17881
|
-
const addresses = await lookup(normalizedHostname(current.hostname), { all: true, verbatim: true });
|
|
17952
|
+
const addresses = await waitWithSignal(lookup(normalizedHostname(current.hostname), { all: true, verbatim: true }), options.signal);
|
|
17882
17953
|
if (addresses.length === 0) throw new Error("Host did not resolve");
|
|
17883
17954
|
if (addresses.some((entry2) => isBlockedResolvedAddress(entry2.address, current.hostname))) {
|
|
17884
17955
|
throw new Error("Blocked private or special network destination");
|
|
@@ -17890,7 +17961,13 @@ async function fetchPublic(initialUrl, options) {
|
|
|
17890
17961
|
continue;
|
|
17891
17962
|
}
|
|
17892
17963
|
const contentType = response.contentType;
|
|
17893
|
-
|
|
17964
|
+
let decoder;
|
|
17965
|
+
try {
|
|
17966
|
+
decoder = new TextDecoder(charset(contentType), { fatal: false });
|
|
17967
|
+
} catch {
|
|
17968
|
+
decoder = new TextDecoder("utf-8");
|
|
17969
|
+
}
|
|
17970
|
+
const decoded = decoder.decode(response.body);
|
|
17894
17971
|
const content = !options.rawHtml && /text\/html|application\/xhtml\+xml/i.test(contentType) ? htmlToText(decoded) : decoded;
|
|
17895
17972
|
return { url: current.href, status: response.status, contentType, content, truncated: response.truncated };
|
|
17896
17973
|
}
|
|
@@ -17925,8 +18002,8 @@ async function requestAddress(url, address, options) {
|
|
|
17925
18002
|
const status = response.statusCode ?? 0;
|
|
17926
18003
|
const location = response.headers.location;
|
|
17927
18004
|
if (location !== void 0 && [301, 302, 303, 307, 308].includes(status)) {
|
|
17928
|
-
response.resume();
|
|
17929
18005
|
finish({ status, contentType: "", body: new Uint8Array(), truncated: false, redirect: location });
|
|
18006
|
+
response.destroy();
|
|
17930
18007
|
return;
|
|
17931
18008
|
}
|
|
17932
18009
|
const chunks = [];
|
|
@@ -17942,7 +18019,11 @@ async function requestAddress(url, address, options) {
|
|
|
17942
18019
|
const accepted = chunk.subarray(0, options.maxBytes - bytes);
|
|
17943
18020
|
chunks.push(accepted);
|
|
17944
18021
|
bytes += accepted.length;
|
|
17945
|
-
if (accepted.length < chunk.length)
|
|
18022
|
+
if (accepted.length < chunk.length) {
|
|
18023
|
+
truncated = true;
|
|
18024
|
+
finish({ status, contentType: String(response.headers["content-type"] ?? "application/octet-stream"), body: Buffer.concat(chunks), truncated });
|
|
18025
|
+
response.destroy();
|
|
18026
|
+
}
|
|
17946
18027
|
});
|
|
17947
18028
|
response.once("end", () => finish({
|
|
17948
18029
|
status,
|
|
@@ -18088,11 +18169,11 @@ function charset(contentType) {
|
|
|
18088
18169
|
}
|
|
18089
18170
|
|
|
18090
18171
|
// src/tools/lsp.ts
|
|
18091
|
-
import { spawn as
|
|
18172
|
+
import { spawn as spawn8 } from "child_process";
|
|
18092
18173
|
import { existsSync as existsSync5 } from "fs";
|
|
18093
18174
|
import { access as access2, constants as constants10, readFile as readFile19 } from "fs/promises";
|
|
18094
|
-
import { basename as basename5, dirname as
|
|
18095
|
-
import { fileURLToPath as
|
|
18175
|
+
import { basename as basename5, dirname as dirname15, extname as extname2, isAbsolute as isAbsolute12, relative as relative17, resolve as resolve23, sep as sep17 } from "path";
|
|
18176
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "url";
|
|
18096
18177
|
import { z as z26 } from "zod";
|
|
18097
18178
|
var LSP_REQUEST_TIMEOUT_MS = 15e3;
|
|
18098
18179
|
var LspConnection = class {
|
|
@@ -18104,12 +18185,14 @@ var LspConnection = class {
|
|
|
18104
18185
|
#nextId = 1;
|
|
18105
18186
|
#disposed = false;
|
|
18106
18187
|
#initError;
|
|
18188
|
+
#shutdownPromise;
|
|
18107
18189
|
constructor(command, args, cwd) {
|
|
18108
18190
|
const invocation = prepareSpawnInvocation(command, args, { cwd });
|
|
18109
|
-
this.#process =
|
|
18191
|
+
this.#process = spawn8(invocation.command, invocation.args, {
|
|
18110
18192
|
cwd,
|
|
18111
18193
|
stdio: ["pipe", "pipe", "pipe"],
|
|
18112
18194
|
windowsHide: true,
|
|
18195
|
+
detached: process.platform !== "win32",
|
|
18113
18196
|
...invocation.windowsVerbatimArguments === void 0 ? {} : { windowsVerbatimArguments: invocation.windowsVerbatimArguments }
|
|
18114
18197
|
});
|
|
18115
18198
|
this.#process.on("error", (err) => {
|
|
@@ -18118,9 +18201,15 @@ var LspConnection = class {
|
|
|
18118
18201
|
});
|
|
18119
18202
|
this.#process.once("exit", (code, signal) => {
|
|
18120
18203
|
const reason = signal !== null ? `LSP server "${command}" exited with signal ${signal}` : `LSP server "${command}" exited with code ${code}`;
|
|
18121
|
-
this.#
|
|
18204
|
+
this.#initError = new Error(reason);
|
|
18205
|
+
this.#rejectAll(this.#initError);
|
|
18122
18206
|
});
|
|
18123
18207
|
this.#process.stdout.on("data", (chunk) => this.#buffer.feed(chunk, this.#onMessage));
|
|
18208
|
+
this.#process.stderr.resume();
|
|
18209
|
+
this.#process.stdin.on("error", (error) => {
|
|
18210
|
+
this.#initError = error;
|
|
18211
|
+
this.#rejectAll(error);
|
|
18212
|
+
});
|
|
18124
18213
|
}
|
|
18125
18214
|
async initialize(rootUri) {
|
|
18126
18215
|
if (this.#initError !== void 0) throw this.#initError;
|
|
@@ -18142,67 +18231,84 @@ var LspConnection = class {
|
|
|
18142
18231
|
if (initResult === void 0) throw new Error("LSP initialize returned no capabilities");
|
|
18143
18232
|
this.#notify("initialized", {});
|
|
18144
18233
|
}
|
|
18145
|
-
async findReferences(uri, line, character) {
|
|
18234
|
+
async findReferences(uri, line, character, signal) {
|
|
18235
|
+
signal?.throwIfAborted();
|
|
18146
18236
|
await this.#syncDocument(uri);
|
|
18147
18237
|
const result = await this.#request("textDocument/references", {
|
|
18148
18238
|
textDocument: { uri },
|
|
18149
18239
|
position: { line, character },
|
|
18150
18240
|
context: { includeDeclaration: false }
|
|
18151
|
-
});
|
|
18241
|
+
}, signal);
|
|
18152
18242
|
if (result === null || result === void 0) return [];
|
|
18153
18243
|
if (!Array.isArray(result)) return [];
|
|
18154
18244
|
return result.filter(isLocation);
|
|
18155
18245
|
}
|
|
18156
|
-
async hover(uri, line, character) {
|
|
18246
|
+
async hover(uri, line, character, signal) {
|
|
18247
|
+
signal?.throwIfAborted();
|
|
18157
18248
|
await this.#syncDocument(uri);
|
|
18158
18249
|
const result = await this.#request("textDocument/hover", {
|
|
18159
18250
|
textDocument: { uri },
|
|
18160
18251
|
position: { line, character }
|
|
18161
|
-
});
|
|
18252
|
+
}, signal);
|
|
18162
18253
|
if (result === null || result === void 0) return null;
|
|
18163
18254
|
if (typeof result === "object" && "contents" in result) {
|
|
18164
18255
|
return result;
|
|
18165
18256
|
}
|
|
18166
18257
|
return null;
|
|
18167
18258
|
}
|
|
18168
|
-
async diagnostics(uri) {
|
|
18259
|
+
async diagnostics(uri, signal) {
|
|
18260
|
+
signal?.throwIfAborted();
|
|
18169
18261
|
await this.#syncDocument(uri);
|
|
18170
18262
|
const result = await this.#request("textDocument/diagnostic", {
|
|
18171
18263
|
textDocument: { uri }
|
|
18172
|
-
});
|
|
18264
|
+
}, signal);
|
|
18173
18265
|
if (result === null || result === void 0) return [];
|
|
18174
18266
|
const items = result?.items ?? result;
|
|
18175
18267
|
if (!Array.isArray(items)) return [];
|
|
18176
18268
|
return items.filter(isDiagnostic);
|
|
18177
18269
|
}
|
|
18178
|
-
|
|
18179
|
-
if (this.#
|
|
18270
|
+
shutdown() {
|
|
18271
|
+
if (this.#shutdownPromise !== void 0) return this.#shutdownPromise;
|
|
18180
18272
|
this.#disposed = true;
|
|
18181
|
-
try {
|
|
18182
|
-
await this.#request("shutdown", null);
|
|
18183
|
-
} catch {
|
|
18184
|
-
}
|
|
18185
|
-
this.#notify("exit", null);
|
|
18186
|
-
this.#process.kill();
|
|
18187
18273
|
this.#rejectAll(new Error("LSP connection disposed"));
|
|
18274
|
+
this.#shutdownPromise = (async () => {
|
|
18275
|
+
await terminateProcessTree(this.#process.pid);
|
|
18276
|
+
this.#process.stdin.destroy();
|
|
18277
|
+
this.#process.stdout.destroy();
|
|
18278
|
+
this.#process.stderr.destroy();
|
|
18279
|
+
})();
|
|
18280
|
+
return this.#shutdownPromise;
|
|
18188
18281
|
}
|
|
18189
18282
|
// -- JSON-RPC core -------------------------------------------------------
|
|
18190
|
-
async #request(method, params) {
|
|
18283
|
+
async #request(method, params, signal) {
|
|
18191
18284
|
if (this.#disposed) throw new Error("LSP connection disposed");
|
|
18285
|
+
if (this.#initError !== void 0) throw this.#initError;
|
|
18286
|
+
signal?.throwIfAborted();
|
|
18192
18287
|
const id = this.#nextId++;
|
|
18193
18288
|
const request = { jsonrpc: "2.0", id, method, params };
|
|
18194
|
-
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
|
|
18199
|
-
|
|
18200
|
-
|
|
18289
|
+
let timer;
|
|
18290
|
+
let onAbort;
|
|
18291
|
+
const promise = new Promise((resolve32, reject) => {
|
|
18292
|
+
this.#pending.set(id, { resolve: resolve32, reject });
|
|
18293
|
+
timer = setTimeout(() => reject(new Error(`LSP request "${method}" timed out after ${LSP_REQUEST_TIMEOUT_MS}ms`)), LSP_REQUEST_TIMEOUT_MS);
|
|
18294
|
+
timer.unref();
|
|
18295
|
+
onAbort = () => {
|
|
18296
|
+
try {
|
|
18297
|
+
this.#notify("$/cancelRequest", { id });
|
|
18298
|
+
} catch {
|
|
18201
18299
|
}
|
|
18202
|
-
|
|
18300
|
+
reject(signal?.reason);
|
|
18301
|
+
};
|
|
18302
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
18203
18303
|
});
|
|
18204
|
-
|
|
18205
|
-
|
|
18304
|
+
try {
|
|
18305
|
+
this.#send(request);
|
|
18306
|
+
return await promise;
|
|
18307
|
+
} finally {
|
|
18308
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
18309
|
+
if (onAbort !== void 0) signal?.removeEventListener("abort", onAbort);
|
|
18310
|
+
this.#pending.delete(id);
|
|
18311
|
+
}
|
|
18206
18312
|
}
|
|
18207
18313
|
#notify(method, params) {
|
|
18208
18314
|
const notification = { jsonrpc: "2.0", method, params };
|
|
@@ -18222,6 +18328,7 @@ var LspConnection = class {
|
|
|
18222
18328
|
} catch {
|
|
18223
18329
|
return;
|
|
18224
18330
|
}
|
|
18331
|
+
if (typeof parsed !== "object" || parsed === null) return;
|
|
18225
18332
|
if ("method" in parsed) {
|
|
18226
18333
|
if ("id" in parsed) this.#handleServerRequest(parsed);
|
|
18227
18334
|
return;
|
|
@@ -18257,7 +18364,7 @@ var LspConnection = class {
|
|
|
18257
18364
|
async #syncDocumentNow(uri) {
|
|
18258
18365
|
const url = new URL(uri);
|
|
18259
18366
|
if (url.protocol !== "file:") return;
|
|
18260
|
-
const filePath =
|
|
18367
|
+
const filePath = fileURLToPath2(url);
|
|
18261
18368
|
const text = await readFile19(filePath, "utf8");
|
|
18262
18369
|
const current = this.#documents.get(uri);
|
|
18263
18370
|
if (current === void 0) {
|
|
@@ -18310,7 +18417,7 @@ var LspBuffer = class {
|
|
|
18310
18417
|
}
|
|
18311
18418
|
};
|
|
18312
18419
|
var TYPESCRIPT_CLI = resolve23(
|
|
18313
|
-
|
|
18420
|
+
dirname15(fileURLToPath2(import.meta.resolve("typescript/package.json"))),
|
|
18314
18421
|
"lib",
|
|
18315
18422
|
"tsc.js"
|
|
18316
18423
|
);
|
|
@@ -18345,6 +18452,8 @@ var KNOWN_SERVERS = [
|
|
|
18345
18452
|
}
|
|
18346
18453
|
];
|
|
18347
18454
|
var RealLspManager = class {
|
|
18455
|
+
#disposed = false;
|
|
18456
|
+
#disposal;
|
|
18348
18457
|
#workspace;
|
|
18349
18458
|
#connections = /* @__PURE__ */ new Map();
|
|
18350
18459
|
#pendingStarts = /* @__PURE__ */ new Map();
|
|
@@ -18356,27 +18465,32 @@ var RealLspManager = class {
|
|
|
18356
18465
|
this.#onStatus = options.onStatus;
|
|
18357
18466
|
}
|
|
18358
18467
|
dispose() {
|
|
18359
|
-
|
|
18360
|
-
|
|
18361
|
-
|
|
18362
|
-
|
|
18468
|
+
if (this.#disposal !== void 0) return this.#disposal;
|
|
18469
|
+
this.#disposed = true;
|
|
18470
|
+
const shutdowns = [...this.#connections.values()].map((connection) => connection.shutdown());
|
|
18471
|
+
shutdowns.push(...[...this.#pendingStarts.values()].map(async (start) => {
|
|
18472
|
+
const connection = await start;
|
|
18473
|
+
await connection.shutdown();
|
|
18474
|
+
}));
|
|
18363
18475
|
this.#connections.clear();
|
|
18364
18476
|
this.#pendingStarts.clear();
|
|
18477
|
+
this.#disposal = Promise.allSettled(shutdowns).then(() => void 0);
|
|
18478
|
+
return this.#disposal;
|
|
18365
18479
|
}
|
|
18366
|
-
async findReferences(uri, line, character) {
|
|
18367
|
-
|
|
18368
|
-
|
|
18369
|
-
return connection.findReferences(uri, line, character);
|
|
18480
|
+
async findReferences(uri, line, character, signal) {
|
|
18481
|
+
signal?.throwIfAborted();
|
|
18482
|
+
const connection = await waitWithSignal(this.#getConnection(uri), signal);
|
|
18483
|
+
return connection.findReferences(uri, line, character, signal);
|
|
18370
18484
|
}
|
|
18371
|
-
async hover(uri, line, character) {
|
|
18372
|
-
|
|
18373
|
-
|
|
18374
|
-
return connection.hover(uri, line, character);
|
|
18485
|
+
async hover(uri, line, character, signal) {
|
|
18486
|
+
signal?.throwIfAborted();
|
|
18487
|
+
const connection = await waitWithSignal(this.#getConnection(uri), signal);
|
|
18488
|
+
return connection.hover(uri, line, character, signal);
|
|
18375
18489
|
}
|
|
18376
|
-
async diagnostics(uri) {
|
|
18377
|
-
|
|
18378
|
-
|
|
18379
|
-
return connection.diagnostics(uri);
|
|
18490
|
+
async diagnostics(uri, signal) {
|
|
18491
|
+
signal?.throwIfAborted();
|
|
18492
|
+
const connection = await waitWithSignal(this.#getConnection(uri), signal);
|
|
18493
|
+
return connection.diagnostics(uri, signal);
|
|
18380
18494
|
}
|
|
18381
18495
|
#languageForUri(uri) {
|
|
18382
18496
|
try {
|
|
@@ -18392,20 +18506,25 @@ var RealLspManager = class {
|
|
|
18392
18506
|
return void 0;
|
|
18393
18507
|
}
|
|
18394
18508
|
async #getConnection(uri) {
|
|
18509
|
+
if (this.#disposed) throw new Error("LSP manager is disposed");
|
|
18395
18510
|
const language = this.#languageForUri(uri);
|
|
18396
|
-
if (language === void 0)
|
|
18511
|
+
if (language === void 0) throw new Error(`No language server configured for ${uri}; use Read/Grep or the project compiler`);
|
|
18397
18512
|
const existing = this.#connections.get(language);
|
|
18398
18513
|
if (existing !== void 0) return existing;
|
|
18399
18514
|
const pending = this.#pendingStarts.get(language);
|
|
18400
18515
|
if (pending !== void 0) return pending;
|
|
18401
18516
|
const config = this.#serverConfigs.find((c) => c.language === language);
|
|
18402
|
-
if (config === void 0)
|
|
18517
|
+
if (config === void 0) throw new Error(`No language server configured for ${language}`);
|
|
18403
18518
|
const hasRoot = config.rootFiles.some((file) => existsSync5(resolve23(this.#workspace, file)));
|
|
18404
|
-
if (!hasRoot)
|
|
18519
|
+
if (!hasRoot) throw new Error(`Cannot start ${language} language server: workspace needs ${config.rootFiles.join(" or ")}`);
|
|
18405
18520
|
const startPromise = this.#startServer(config);
|
|
18406
18521
|
this.#pendingStarts.set(language, startPromise);
|
|
18407
18522
|
try {
|
|
18408
18523
|
const connection = await startPromise;
|
|
18524
|
+
if (this.#disposed) {
|
|
18525
|
+
await connection.shutdown();
|
|
18526
|
+
throw new Error("LSP manager is disposed");
|
|
18527
|
+
}
|
|
18409
18528
|
this.#connections.set(language, connection);
|
|
18410
18529
|
return connection;
|
|
18411
18530
|
} finally {
|
|
@@ -18423,6 +18542,7 @@ var RealLspManager = class {
|
|
|
18423
18542
|
await connection.initialize(rootUri);
|
|
18424
18543
|
this.#onStatus?.(`${config.language} Language Server ready (${config.command})`);
|
|
18425
18544
|
} catch (error) {
|
|
18545
|
+
await connection.shutdown();
|
|
18426
18546
|
this.#onStatus?.(`${config.language} Language Server failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
18427
18547
|
throw error;
|
|
18428
18548
|
}
|
|
@@ -18431,13 +18551,13 @@ var RealLspManager = class {
|
|
|
18431
18551
|
};
|
|
18432
18552
|
var LspFindRefsInput = z26.object({
|
|
18433
18553
|
file: z26.string().min(1),
|
|
18434
|
-
line: z26.coerce.number().int().nonnegative(),
|
|
18435
|
-
character: z26.coerce.number().int().nonnegative()
|
|
18554
|
+
line: z26.coerce.number().int().nonnegative().describe("Zero-based line number (first line is 0)."),
|
|
18555
|
+
character: z26.coerce.number().int().nonnegative().describe("Zero-based UTF-16 character offset.")
|
|
18436
18556
|
});
|
|
18437
18557
|
var LspHoverInput = z26.object({
|
|
18438
18558
|
file: z26.string().min(1),
|
|
18439
|
-
line: z26.coerce.number().int().nonnegative(),
|
|
18440
|
-
character: z26.coerce.number().int().nonnegative()
|
|
18559
|
+
line: z26.coerce.number().int().nonnegative().describe("Zero-based line number (first line is 0)."),
|
|
18560
|
+
character: z26.coerce.number().int().nonnegative().describe("Zero-based UTF-16 character offset.")
|
|
18441
18561
|
});
|
|
18442
18562
|
var LspDiagnosticsInput = z26.object({
|
|
18443
18563
|
file: z26.string().min(1)
|
|
@@ -18463,10 +18583,11 @@ function createLspTools(workspace, options = {}) {
|
|
|
18463
18583
|
inputSchema: LspFindRefsInput,
|
|
18464
18584
|
paths: (input) => [guard(input.file)],
|
|
18465
18585
|
summarize: (input) => `${basename5(input.file)}:${input.line}:${input.character}${langTag(input.file)}`,
|
|
18466
|
-
execute: async (input,
|
|
18586
|
+
execute: async (input, signal) => {
|
|
18587
|
+
signal.throwIfAborted();
|
|
18467
18588
|
const path = guard(input.file);
|
|
18468
18589
|
const uri = toUri(path);
|
|
18469
|
-
const refs = await manager.findReferences(uri, input.line, input.character);
|
|
18590
|
+
const refs = await waitWithSignal(manager.findReferences(uri, input.line, input.character, signal), signal);
|
|
18470
18591
|
if (refs.length === 0) return `No references found at ${input.file}:${input.line}:${input.character}`;
|
|
18471
18592
|
return refs.map((loc) => {
|
|
18472
18593
|
const filePath = uriToPath(loc.uri);
|
|
@@ -18480,12 +18601,14 @@ function createLspTools(workspace, options = {}) {
|
|
|
18480
18601
|
inputSchema: LspHoverInput,
|
|
18481
18602
|
paths: (input) => [guard(input.file)],
|
|
18482
18603
|
summarize: (input) => `${basename5(input.file)}:${input.line}:${input.character}${langTag(input.file)}`,
|
|
18483
|
-
execute: async (input,
|
|
18604
|
+
execute: async (input, signal) => {
|
|
18605
|
+
signal.throwIfAborted();
|
|
18484
18606
|
const path = guard(input.file);
|
|
18485
18607
|
const uri = toUri(path);
|
|
18486
|
-
const info = await manager.hover(uri, input.line, input.character);
|
|
18608
|
+
const info = await waitWithSignal(manager.hover(uri, input.line, input.character, signal), signal);
|
|
18487
18609
|
if (info === null) return `No hover information at ${input.file}:${input.line}:${input.character}`;
|
|
18488
|
-
const
|
|
18610
|
+
const parts = Array.isArray(info.contents) ? info.contents : [info.contents];
|
|
18611
|
+
const contents = parts.map((part) => typeof part === "string" ? part : part.value).join("\n\n");
|
|
18489
18612
|
return contents;
|
|
18490
18613
|
}
|
|
18491
18614
|
};
|
|
@@ -18495,10 +18618,11 @@ function createLspTools(workspace, options = {}) {
|
|
|
18495
18618
|
inputSchema: LspDiagnosticsInput,
|
|
18496
18619
|
paths: (input) => [guard(input.file)],
|
|
18497
18620
|
summarize: (input) => `${basename5(input.file)}${langTag(input.file)}`,
|
|
18498
|
-
execute: async (input,
|
|
18621
|
+
execute: async (input, signal) => {
|
|
18622
|
+
signal.throwIfAborted();
|
|
18499
18623
|
const path = guard(input.file);
|
|
18500
18624
|
const uri = toUri(path);
|
|
18501
|
-
const diags = await manager.diagnostics(uri);
|
|
18625
|
+
const diags = await waitWithSignal(manager.diagnostics(uri, signal), signal);
|
|
18502
18626
|
if (diags.length === 0) return `No diagnostics for ${input.file}`;
|
|
18503
18627
|
return diags.map((d) => {
|
|
18504
18628
|
const severity = severityLabel(d.severity);
|
|
@@ -18517,6 +18641,14 @@ function languageIdForPath(filePath) {
|
|
|
18517
18641
|
return "typescript";
|
|
18518
18642
|
case ".cts":
|
|
18519
18643
|
return "typescript";
|
|
18644
|
+
case ".py":
|
|
18645
|
+
case ".pyi":
|
|
18646
|
+
case ".pyx":
|
|
18647
|
+
return "python";
|
|
18648
|
+
case ".rs":
|
|
18649
|
+
return "rust";
|
|
18650
|
+
case ".go":
|
|
18651
|
+
return "go";
|
|
18520
18652
|
default:
|
|
18521
18653
|
return "typescript";
|
|
18522
18654
|
}
|
|
@@ -18528,7 +18660,7 @@ function isWithin7(root, candidate) {
|
|
|
18528
18660
|
function uriToPath(uri) {
|
|
18529
18661
|
try {
|
|
18530
18662
|
const url = new URL(uri);
|
|
18531
|
-
if (url.protocol === "file:") return
|
|
18663
|
+
if (url.protocol === "file:") return fileURLToPath2(url);
|
|
18532
18664
|
} catch {
|
|
18533
18665
|
}
|
|
18534
18666
|
return uri;
|
|
@@ -18555,12 +18687,14 @@ function severityLabel(severity) {
|
|
|
18555
18687
|
}
|
|
18556
18688
|
|
|
18557
18689
|
// src/git/service.ts
|
|
18558
|
-
async function git2(workspace, args, timeoutMs = 3e4) {
|
|
18559
|
-
|
|
18690
|
+
async function git2(workspace, args, timeoutMs = 3e4, signal) {
|
|
18691
|
+
signal?.throwIfAborted();
|
|
18692
|
+
const result = await execFileNoThrow("git", ["-C", workspace, ...args], { timeout: timeoutMs, useCwd: false, ...signal === void 0 ? {} : { signal } });
|
|
18693
|
+
signal?.throwIfAborted();
|
|
18560
18694
|
return { ok: result.code === 0, stdout: result.stdout, stderr: result.stderr, code: result.code };
|
|
18561
18695
|
}
|
|
18562
|
-
async function isGitRepository(workspace) {
|
|
18563
|
-
const result = await git2(workspace, ["rev-parse", "--is-inside-work-tree"], 2e3);
|
|
18696
|
+
async function isGitRepository(workspace, signal) {
|
|
18697
|
+
const result = await git2(workspace, ["rev-parse", "--is-inside-work-tree"], 2e3, signal);
|
|
18564
18698
|
return result.ok && result.stdout.trim() === "true";
|
|
18565
18699
|
}
|
|
18566
18700
|
function gitFailure(result, action) {
|
|
@@ -18638,16 +18772,21 @@ async function commit(workspace, commitMessage) {
|
|
|
18638
18772
|
const head = await git2(workspace, ["log", "-1", "--pretty=%h %s"]);
|
|
18639
18773
|
return head.stdout.trim() || "committed";
|
|
18640
18774
|
}
|
|
18641
|
-
async function fileHistory(workspace, path, limit) {
|
|
18775
|
+
async function fileHistory(workspace, path, limit, signal) {
|
|
18642
18776
|
const args = [
|
|
18777
|
+
"--literal-pathspecs",
|
|
18643
18778
|
"log",
|
|
18644
18779
|
`--max-count=${limit}`,
|
|
18645
18780
|
"--date=short",
|
|
18646
18781
|
"--pretty=format:%h%x1f%ad%x1f%an%x1f%s",
|
|
18647
18782
|
...path === void 0 ? [] : ["--follow", "--", path]
|
|
18648
18783
|
];
|
|
18649
|
-
const result = await git2(workspace, args, 15e3);
|
|
18650
|
-
if (!result.ok)
|
|
18784
|
+
const result = await git2(workspace, args, 15e3, signal);
|
|
18785
|
+
if (!result.ok) {
|
|
18786
|
+
const head = await git2(workspace, ["rev-parse", "--verify", "HEAD"], 2e3, signal);
|
|
18787
|
+
if (!head.ok && await isGitRepository(workspace, signal)) return [];
|
|
18788
|
+
throw gitFailure(result, "log");
|
|
18789
|
+
}
|
|
18651
18790
|
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => {
|
|
18652
18791
|
const [hash = "", date = "", author = "", ...subject] = line.split("");
|
|
18653
18792
|
return { hash, date, author, subject: subject.join("") };
|
|
@@ -18986,24 +19125,33 @@ function mapNode(value) {
|
|
|
18986
19125
|
|
|
18987
19126
|
// src/git/tools.ts
|
|
18988
19127
|
import { z as z28 } from "zod";
|
|
19128
|
+
import { isAbsolute as isAbsolute13, relative as relative18, resolve as resolve25, sep as sep18 } from "path";
|
|
18989
19129
|
var GitHistoryInput = z28.object({
|
|
18990
|
-
path: z28.string().describe("
|
|
18991
|
-
limit: z28.number().int().min(1).max(50).describe("Maximum number of commits to return (default 20)").optional()
|
|
19130
|
+
path: z28.string().min(1).describe("Workspace-relative or absolute file path; omit for the whole repository history").optional(),
|
|
19131
|
+
limit: z28.coerce.number().int().min(1).max(50).describe("Maximum number of commits to return (default 20)").optional()
|
|
18992
19132
|
});
|
|
18993
19133
|
function createGitHistoryTool(workspace) {
|
|
19134
|
+
const root = resolve25(workspace);
|
|
19135
|
+
const file = (path) => {
|
|
19136
|
+
const absolute = resolve25(root, path);
|
|
19137
|
+
const delta = relative18(root, absolute);
|
|
19138
|
+
if (delta === ".." || delta.startsWith(`..${sep18}`) || isAbsolute13(delta)) throw new Error("GitHistory path is outside the workspace");
|
|
19139
|
+
return absolute;
|
|
19140
|
+
};
|
|
18994
19141
|
return {
|
|
18995
19142
|
name: "GitHistory",
|
|
18996
19143
|
description: "Show git commit history for the repository or for one file (follows renames). Use to understand when and why code changed.",
|
|
18997
19144
|
inputSchema: GitHistoryInput,
|
|
18998
19145
|
readOnly: true,
|
|
18999
|
-
paths: (input) => input.path === void 0 ? [] : [input.path],
|
|
19146
|
+
paths: (input) => input.path === void 0 ? [root] : [file(input.path)],
|
|
19000
19147
|
summarize: (input) => input.path ?? "repository",
|
|
19001
19148
|
renderForModel: (output) => output,
|
|
19002
19149
|
async execute(input, signal) {
|
|
19003
19150
|
signal.throwIfAborted();
|
|
19004
|
-
|
|
19151
|
+
const path = input.path === void 0 ? void 0 : relative18(root, file(input.path));
|
|
19152
|
+
if (!await isGitRepository(root, signal)) throw new Error("Not a git repository");
|
|
19005
19153
|
const limit = input.limit ?? 20;
|
|
19006
|
-
const entries = await fileHistory(
|
|
19154
|
+
const entries = await fileHistory(root, path, limit, signal);
|
|
19007
19155
|
if (entries.length === 0) {
|
|
19008
19156
|
return input.path === void 0 ? "No commits found." : `No commits found for ${input.path} (the file may be untracked).`;
|
|
19009
19157
|
}
|
|
@@ -19066,7 +19214,8 @@ var QuestionBridge = class {
|
|
|
19066
19214
|
}
|
|
19067
19215
|
async #askRelayed(questions, signal) {
|
|
19068
19216
|
try {
|
|
19069
|
-
const answers = await this.#relay(questions, signal);
|
|
19217
|
+
const answers = await waitWithSignal(this.#relay(questions, signal), signal);
|
|
19218
|
+
signal.throwIfAborted();
|
|
19070
19219
|
if (answers !== void 0) {
|
|
19071
19220
|
this.#active = false;
|
|
19072
19221
|
return answers;
|
|
@@ -19078,10 +19227,15 @@ var QuestionBridge = class {
|
|
|
19078
19227
|
}
|
|
19079
19228
|
}
|
|
19080
19229
|
#askLocally(questions, signal) {
|
|
19081
|
-
|
|
19230
|
+
if (signal.aborted) {
|
|
19231
|
+
this.#active = false;
|
|
19232
|
+
return Promise.reject(signal.reason);
|
|
19233
|
+
}
|
|
19234
|
+
return new Promise((resolve32, reject) => {
|
|
19082
19235
|
const onAbort = () => {
|
|
19083
19236
|
this.#pending = void 0;
|
|
19084
19237
|
this.#active = false;
|
|
19238
|
+
this.#onChange?.();
|
|
19085
19239
|
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
|
|
19086
19240
|
};
|
|
19087
19241
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -19092,7 +19246,7 @@ var QuestionBridge = class {
|
|
|
19092
19246
|
this.#pending = void 0;
|
|
19093
19247
|
this.#active = false;
|
|
19094
19248
|
this.#onChange?.();
|
|
19095
|
-
|
|
19249
|
+
resolve32(answers);
|
|
19096
19250
|
},
|
|
19097
19251
|
reject: (error) => {
|
|
19098
19252
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -19185,7 +19339,7 @@ function createTodoWriteTool() {
|
|
|
19185
19339
|
// src/tools/managed.ts
|
|
19186
19340
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
19187
19341
|
import { link as link2, lstat as lstat12, mkdir as mkdir17, readFile as readFile20, readdir as readdir9, unlink as unlink5, writeFile as writeFile10 } from "fs/promises";
|
|
19188
|
-
import { dirname as
|
|
19342
|
+
import { dirname as dirname16, join as join26, resolve as resolve26 } from "path";
|
|
19189
19343
|
import { z as z31 } from "zod";
|
|
19190
19344
|
var MANAGED_TOOL_VERSION = 1;
|
|
19191
19345
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -19227,9 +19381,9 @@ var ManagedToolStore = class {
|
|
|
19227
19381
|
#entries = /* @__PURE__ */ new Map();
|
|
19228
19382
|
#diagnostics = [];
|
|
19229
19383
|
constructor(options) {
|
|
19230
|
-
this.#workspace =
|
|
19384
|
+
this.#workspace = resolve26(options.workspace);
|
|
19231
19385
|
this.#projectRoot = join26(this.#workspace, ".flavor", "tools");
|
|
19232
|
-
this.#globalRoot = join26(
|
|
19386
|
+
this.#globalRoot = join26(resolve26(options.home), ".flavor-code", "tools");
|
|
19233
19387
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
19234
19388
|
}
|
|
19235
19389
|
get diagnostics() {
|
|
@@ -19469,7 +19623,7 @@ ${sourceUrl}`
|
|
|
19469
19623
|
}
|
|
19470
19624
|
}
|
|
19471
19625
|
async function writeExclusive(path, content) {
|
|
19472
|
-
await mkdir17(
|
|
19626
|
+
await mkdir17(dirname16(path), { recursive: true });
|
|
19473
19627
|
const temporary = `${path}.${randomUUID16()}.tmp`;
|
|
19474
19628
|
try {
|
|
19475
19629
|
await writeFile10(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
@@ -20233,7 +20387,7 @@ ${total}`;
|
|
|
20233
20387
|
// src/memory/store.ts
|
|
20234
20388
|
import { createHash as createHash16 } from "crypto";
|
|
20235
20389
|
import { readFile as readFile21, rm as rm9 } from "fs/promises";
|
|
20236
|
-
import { resolve as
|
|
20390
|
+
import { resolve as resolve27, join as join27, relative as relative19, sep as sep19 } from "path";
|
|
20237
20391
|
|
|
20238
20392
|
// src/memory/similarity.ts
|
|
20239
20393
|
function normalizeForSimilarity(value) {
|
|
@@ -20335,7 +20489,7 @@ var MemoryStore = class {
|
|
|
20335
20489
|
constructor(options) {
|
|
20336
20490
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
20337
20491
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
20338
|
-
this.workspace =
|
|
20492
|
+
this.workspace = resolve27(options.workspace);
|
|
20339
20493
|
this.#memoryRoot = join27(this.workspace, ".flavor", "memory");
|
|
20340
20494
|
this.path = join27(this.#memoryRoot, "MEMORY.md");
|
|
20341
20495
|
this.#behaviorPath = join27(this.#memoryRoot, "behavior.json");
|
|
@@ -20561,9 +20715,9 @@ var MemoryStore = class {
|
|
|
20561
20715
|
}
|
|
20562
20716
|
#resolveContentPath(contentPath) {
|
|
20563
20717
|
if (!/^tasks\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.md$/.test(contentPath)) throw new Error("Invalid memory content path");
|
|
20564
|
-
const path =
|
|
20565
|
-
const rel =
|
|
20566
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
20718
|
+
const path = resolve27(this.#memoryRoot, ...contentPath.split("/"));
|
|
20719
|
+
const rel = relative19(this.#memoryRoot, path);
|
|
20720
|
+
if (rel === ".." || rel.startsWith(`..${sep19}`)) throw new Error("Memory content path escapes the memory directory");
|
|
20567
20721
|
return path;
|
|
20568
20722
|
}
|
|
20569
20723
|
async #updateIndex(update) {
|
|
@@ -21033,7 +21187,7 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
21033
21187
|
|
|
21034
21188
|
// src/ide/client.ts
|
|
21035
21189
|
import { readFile as readFile22, readdir as readdir10, stat as stat10 } from "fs/promises";
|
|
21036
|
-
import { basename as basename6, join as join28, relative as
|
|
21190
|
+
import { basename as basename6, join as join28, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
|
|
21037
21191
|
var FlavorIdeClient = class {
|
|
21038
21192
|
#workspace;
|
|
21039
21193
|
#home;
|
|
@@ -21044,8 +21198,8 @@ var FlavorIdeClient = class {
|
|
|
21044
21198
|
#sessionId;
|
|
21045
21199
|
#eventTail = Promise.resolve();
|
|
21046
21200
|
constructor(options) {
|
|
21047
|
-
this.#workspace =
|
|
21048
|
-
this.#home =
|
|
21201
|
+
this.#workspace = resolve28(options.workspace);
|
|
21202
|
+
this.#home = resolve28(options.home);
|
|
21049
21203
|
this.#environment = options.environment ?? process.env;
|
|
21050
21204
|
this.#fetch = options.fetch ?? fetch;
|
|
21051
21205
|
}
|
|
@@ -21243,16 +21397,16 @@ function parsePort(value) {
|
|
|
21243
21397
|
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : void 0;
|
|
21244
21398
|
}
|
|
21245
21399
|
function isWithin8(parent, child) {
|
|
21246
|
-
const base = normalize(
|
|
21247
|
-
const target = normalize(
|
|
21248
|
-
return target === base || target.startsWith(`${base}${
|
|
21400
|
+
const base = normalize(resolve28(parent));
|
|
21401
|
+
const target = normalize(resolve28(child));
|
|
21402
|
+
return target === base || target.startsWith(`${base}${sep20}`);
|
|
21249
21403
|
}
|
|
21250
21404
|
function normalize(path) {
|
|
21251
21405
|
const normalized = path.normalize("NFC");
|
|
21252
21406
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
21253
21407
|
}
|
|
21254
21408
|
function relativeDisplay(workspace, filePath) {
|
|
21255
|
-
const path =
|
|
21409
|
+
const path = relative20(workspace, filePath).replaceAll("\\", "/");
|
|
21256
21410
|
return path && !path.startsWith("../") ? path : basename6(filePath);
|
|
21257
21411
|
}
|
|
21258
21412
|
|
|
@@ -21271,11 +21425,12 @@ var JobRegistry = class {
|
|
|
21271
21425
|
}
|
|
21272
21426
|
create(input) {
|
|
21273
21427
|
this.#pruneCompleted();
|
|
21428
|
+
if (this.#jobs.size >= this.#maxJobs) throw new Error(`Background job limit (${this.#maxJobs}) reached; wait for or cancel an existing job first`);
|
|
21274
21429
|
const id = `job-${randomUUID17().slice(0, 12)}`;
|
|
21275
21430
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21276
21431
|
let settle;
|
|
21277
|
-
const done = new Promise((
|
|
21278
|
-
settle =
|
|
21432
|
+
const done = new Promise((resolve32) => {
|
|
21433
|
+
settle = resolve32;
|
|
21279
21434
|
});
|
|
21280
21435
|
const record = {
|
|
21281
21436
|
id,
|
|
@@ -21309,7 +21464,7 @@ var JobRegistry = class {
|
|
|
21309
21464
|
},
|
|
21310
21465
|
complete: (result = {}) => this.#finish(
|
|
21311
21466
|
record,
|
|
21312
|
-
result.exitCode !== void 0 && result.exitCode !== null && result.exitCode !== 0 ? "failed" : "completed",
|
|
21467
|
+
result.error !== void 0 || result.exitCode !== void 0 && result.exitCode !== null && result.exitCode !== 0 ? "failed" : "completed",
|
|
21313
21468
|
result
|
|
21314
21469
|
),
|
|
21315
21470
|
fail: (error) => this.#finish(record, "failed", { error: error instanceof Error ? error.message : String(error) }),
|
|
@@ -21325,21 +21480,24 @@ var JobRegistry = class {
|
|
|
21325
21480
|
return { ...snapshot2(job), output: job.output.slice(effective - job.outputStart), cursor: job.outputStart + job.output.length };
|
|
21326
21481
|
}
|
|
21327
21482
|
async wait(id, owner2, signal) {
|
|
21483
|
+
signal?.throwIfAborted();
|
|
21328
21484
|
const job = this.#owned(id, owner2);
|
|
21329
21485
|
if (job.state !== "running") return snapshot2(job);
|
|
21330
|
-
|
|
21331
|
-
return Promise.race([job.done, new Promise((_resolve, reject) => {
|
|
21332
|
-
if (signal.aborted) {
|
|
21333
|
-
reject(signal.reason);
|
|
21334
|
-
return;
|
|
21335
|
-
}
|
|
21336
|
-
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
21337
|
-
})]);
|
|
21486
|
+
return waitWithSignal(job.done, signal);
|
|
21338
21487
|
}
|
|
21339
21488
|
kill(id, owner2) {
|
|
21340
21489
|
const job = this.#owned(id, owner2);
|
|
21341
21490
|
if (job.state !== "running") return;
|
|
21342
|
-
|
|
21491
|
+
try {
|
|
21492
|
+
const cancellation = job.cancel?.();
|
|
21493
|
+
if (cancellation !== void 0) return cancellation.then(
|
|
21494
|
+
() => this.#finish(job, "cancelled", {}),
|
|
21495
|
+
(error) => this.#finish(job, "failed", { error: `Cancellation failed: ${error instanceof Error ? error.message : String(error)}` })
|
|
21496
|
+
);
|
|
21497
|
+
} catch (error) {
|
|
21498
|
+
this.#finish(job, "failed", { error: `Cancellation failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
21499
|
+
return;
|
|
21500
|
+
}
|
|
21343
21501
|
this.#finish(job, "cancelled", {});
|
|
21344
21502
|
}
|
|
21345
21503
|
subscribe(listener) {
|
|
@@ -21399,7 +21557,7 @@ function snapshot2(job) {
|
|
|
21399
21557
|
// src/terminal/service.ts
|
|
21400
21558
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
21401
21559
|
import { createRequire as nodeCreateRequire2 } from "module";
|
|
21402
|
-
import { isAbsolute as
|
|
21560
|
+
import { isAbsolute as isAbsolute14, relative as relative21, resolve as resolve29, sep as sep21 } from "path";
|
|
21403
21561
|
var TerminalService = class {
|
|
21404
21562
|
#root;
|
|
21405
21563
|
#factory;
|
|
@@ -21407,10 +21565,11 @@ var TerminalService = class {
|
|
|
21407
21565
|
#maxOutputChars;
|
|
21408
21566
|
#sessions = /* @__PURE__ */ new Map();
|
|
21409
21567
|
constructor(workspace, options = {}) {
|
|
21410
|
-
this.#root =
|
|
21568
|
+
this.#root = resolve29(workspace);
|
|
21411
21569
|
this.#factory = options.factory ?? defaultFactory;
|
|
21412
21570
|
this.#jobs = options.jobs;
|
|
21413
21571
|
this.#maxOutputChars = options.maxOutputChars ?? 2e5;
|
|
21572
|
+
if (!Number.isSafeInteger(this.#maxOutputChars) || this.#maxOutputChars <= 0) throw new Error("maxOutputChars must be a positive integer");
|
|
21414
21573
|
}
|
|
21415
21574
|
open(input) {
|
|
21416
21575
|
const cwd = workspacePath(this.#root, input.cwd ?? ".");
|
|
@@ -21437,7 +21596,13 @@ var TerminalService = class {
|
|
|
21437
21596
|
truncated: false,
|
|
21438
21597
|
disposables: []
|
|
21439
21598
|
};
|
|
21440
|
-
|
|
21599
|
+
let job;
|
|
21600
|
+
try {
|
|
21601
|
+
job = this.#jobs?.create({ kind: "terminal", owner: input.owner, label: shell, cancel: () => this.close(id, input.owner) });
|
|
21602
|
+
} catch (error) {
|
|
21603
|
+
pty.kill();
|
|
21604
|
+
throw error;
|
|
21605
|
+
}
|
|
21441
21606
|
if (job !== void 0) {
|
|
21442
21607
|
jobId = job.id;
|
|
21443
21608
|
record.jobId = job.id;
|
|
@@ -21463,10 +21628,10 @@ var TerminalService = class {
|
|
|
21463
21628
|
return { ...snapshot3(record), ...jobId === void 0 ? {} : { jobId } };
|
|
21464
21629
|
}
|
|
21465
21630
|
write(id, owner2, data2) {
|
|
21466
|
-
this.#
|
|
21631
|
+
this.#running(id, owner2).pty.write(data2);
|
|
21467
21632
|
}
|
|
21468
21633
|
resize(id, owner2, columns, rows) {
|
|
21469
|
-
this.#
|
|
21634
|
+
this.#running(id, owner2).pty.resize(columns, rows);
|
|
21470
21635
|
}
|
|
21471
21636
|
read(id, owner2, cursor = 0) {
|
|
21472
21637
|
const record = this.#owned(id, owner2);
|
|
@@ -21497,6 +21662,11 @@ var TerminalService = class {
|
|
|
21497
21662
|
if (record.owner !== owner2) throw new Error("Terminal owner mismatch");
|
|
21498
21663
|
return record;
|
|
21499
21664
|
}
|
|
21665
|
+
#running(id, owner2) {
|
|
21666
|
+
const record = this.#owned(id, owner2);
|
|
21667
|
+
if (record.state !== "running") throw new Error(`Terminal ${id} is ${record.state}; use TerminalOpen to start a new session`);
|
|
21668
|
+
return record;
|
|
21669
|
+
}
|
|
21500
21670
|
};
|
|
21501
21671
|
function snapshot3(record) {
|
|
21502
21672
|
return {
|
|
@@ -21525,15 +21695,15 @@ function cleanEnvironment(environment) {
|
|
|
21525
21695
|
return Object.fromEntries(Object.entries(environment).filter((entry2) => entry2[1] !== void 0));
|
|
21526
21696
|
}
|
|
21527
21697
|
function workspacePath(root, input) {
|
|
21528
|
-
const path =
|
|
21529
|
-
const delta =
|
|
21530
|
-
if (delta === ".." || delta.startsWith(`..${
|
|
21698
|
+
const path = resolve29(root, input);
|
|
21699
|
+
const delta = relative21(root, path);
|
|
21700
|
+
if (delta === ".." || delta.startsWith(`..${sep21}`) || isAbsolute14(delta)) throw new Error("Terminal cwd is outside the workspace");
|
|
21531
21701
|
return path;
|
|
21532
21702
|
}
|
|
21533
21703
|
|
|
21534
21704
|
// src/tools/terminal.ts
|
|
21535
21705
|
import { z as z33 } from "zod";
|
|
21536
|
-
import { resolve as
|
|
21706
|
+
import { resolve as resolve30 } from "path";
|
|
21537
21707
|
var Id = z33.string().regex(/^term-[a-f0-9-]+$/);
|
|
21538
21708
|
var OpenInput = z33.object({ cwd: z33.string().optional(), shell: z33.string().optional(), args: z33.array(z33.string()).optional(), columns: z33.coerce.number().int().min(10).max(500).optional(), rows: z33.coerce.number().int().min(2).max(300).optional() });
|
|
21539
21709
|
var OptionalBoolean = z33.union([z33.boolean(), z33.string().refine((value) => value === "true" || value === "false")]);
|
|
@@ -21547,8 +21717,8 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
21547
21717
|
name: "TerminalOpen",
|
|
21548
21718
|
description: "Open a persistent interactive pseudo-terminal in the workspace",
|
|
21549
21719
|
inputSchema: OpenInput,
|
|
21550
|
-
paths: (input) => [
|
|
21551
|
-
permissions: (input) => ({ paths: [
|
|
21720
|
+
paths: (input) => [resolve30(workspace, input.cwd ?? ".")],
|
|
21721
|
+
permissions: (input) => ({ paths: [resolve30(workspace, input.cwd ?? ".")], command: input.shell ?? "system-shell", args: input.args ?? [], cwd: resolve30(workspace, input.cwd ?? ".") }),
|
|
21552
21722
|
execute: async (input, _signal, context) => terminals.open({
|
|
21553
21723
|
owner: owner(context),
|
|
21554
21724
|
...input.cwd === void 0 ? {} : { cwd: input.cwd },
|
|
@@ -21577,7 +21747,7 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
21577
21747
|
inputSchema: ReadInput3,
|
|
21578
21748
|
paths: () => [],
|
|
21579
21749
|
execute: async (input, _signal, context) => terminals.read(input.id, owner(context), input.cursor ?? 0),
|
|
21580
|
-
renderForModel: (output) =>
|
|
21750
|
+
renderForModel: (output) => JSON.stringify(output),
|
|
21581
21751
|
presentResult: (output) => {
|
|
21582
21752
|
const result = output;
|
|
21583
21753
|
return {
|
|
@@ -21774,8 +21944,8 @@ var PalClient = class {
|
|
|
21774
21944
|
let disconnectTimer;
|
|
21775
21945
|
await Promise.race([
|
|
21776
21946
|
this.request({ version: 1, type: "disconnect", requestId: randomUUID19() }).catch(() => void 0),
|
|
21777
|
-
new Promise((
|
|
21778
|
-
disconnectTimer = setTimeout(
|
|
21947
|
+
new Promise((resolve32) => {
|
|
21948
|
+
disconnectTimer = setTimeout(resolve32, Math.min(this.requestTimeoutMs, 250));
|
|
21779
21949
|
})
|
|
21780
21950
|
]);
|
|
21781
21951
|
if (disconnectTimer !== void 0) clearTimeout(disconnectTimer);
|
|
@@ -21821,7 +21991,7 @@ var PalClient = class {
|
|
|
21821
21991
|
return presence;
|
|
21822
21992
|
}
|
|
21823
21993
|
openSocket() {
|
|
21824
|
-
return new Promise((
|
|
21994
|
+
return new Promise((resolve32, reject) => {
|
|
21825
21995
|
let settled = false;
|
|
21826
21996
|
const rejectConnection = (error) => {
|
|
21827
21997
|
if (settled) return;
|
|
@@ -21844,7 +22014,7 @@ var PalClient = class {
|
|
|
21844
22014
|
settled = true;
|
|
21845
22015
|
if (this.connectingReject === rejectConnection) this.connectingReject = void 0;
|
|
21846
22016
|
socket.off("error", onInitialError);
|
|
21847
|
-
|
|
22017
|
+
resolve32();
|
|
21848
22018
|
};
|
|
21849
22019
|
const onInitialError = (error) => {
|
|
21850
22020
|
socket.off("connect", onConnect);
|
|
@@ -21875,12 +22045,12 @@ var PalClient = class {
|
|
|
21875
22045
|
if (this.closed || socket === void 0 || socket.destroyed || !socket.writable) {
|
|
21876
22046
|
return Promise.reject(new Error(this.closed ? "Pal client is closed" : "Pal client is not connected"));
|
|
21877
22047
|
}
|
|
21878
|
-
return new Promise((
|
|
22048
|
+
return new Promise((resolve32, reject) => {
|
|
21879
22049
|
const timer = setTimeout(() => {
|
|
21880
22050
|
this.pending.delete(request.requestId);
|
|
21881
22051
|
reject(new Error(`Pal request '${request.type}' timed out`));
|
|
21882
22052
|
}, this.requestTimeoutMs);
|
|
21883
|
-
this.pending.set(request.requestId, { resolve:
|
|
22053
|
+
this.pending.set(request.requestId, { resolve: resolve32, reject, timer });
|
|
21884
22054
|
let frame;
|
|
21885
22055
|
try {
|
|
21886
22056
|
frame = encodeControlFrame(BrokerRequestSchema.parse(request));
|
|
@@ -21984,12 +22154,12 @@ var PalClient = class {
|
|
|
21984
22154
|
}
|
|
21985
22155
|
scheduleReconnect() {
|
|
21986
22156
|
if (this.reconnectTimer !== void 0 || this.closed) return;
|
|
21987
|
-
const
|
|
22157
|
+
const delay2 = this.reconnectDelayMs;
|
|
21988
22158
|
this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, this.reconnectMaxDelayMs);
|
|
21989
22159
|
this.reconnectTimer = setTimeout(() => {
|
|
21990
22160
|
this.reconnectTimer = void 0;
|
|
21991
22161
|
void this.start().catch(() => this.scheduleReconnect());
|
|
21992
|
-
},
|
|
22162
|
+
}, delay2);
|
|
21993
22163
|
this.reconnectTimer.unref?.();
|
|
21994
22164
|
}
|
|
21995
22165
|
};
|
|
@@ -22091,6 +22261,7 @@ function createPalsTools(client, options) {
|
|
|
22091
22261
|
execute: async (input, signal) => {
|
|
22092
22262
|
signal.throwIfAborted();
|
|
22093
22263
|
const recipientId = await assertSendTarget(client, input.target, input.coWorkId);
|
|
22264
|
+
signal.throwIfAborted();
|
|
22094
22265
|
return client.sendChat(recipientId, shareGuard.protect(input.message));
|
|
22095
22266
|
}
|
|
22096
22267
|
},
|
|
@@ -22114,6 +22285,7 @@ function createPalsTools(client, options) {
|
|
|
22114
22285
|
execute: async (input, signal) => {
|
|
22115
22286
|
signal.throwIfAborted();
|
|
22116
22287
|
assertRequiredParticipant(await client.coWorkStatus(input.plan.coWorkId), options.selfId);
|
|
22288
|
+
signal.throwIfAborted();
|
|
22117
22289
|
const plan = CoWorkPlanSchema.parse({
|
|
22118
22290
|
...input.plan,
|
|
22119
22291
|
goal: shareGuard.protect(input.plan.goal),
|
|
@@ -22130,11 +22302,13 @@ function createPalsTools(client, options) {
|
|
|
22130
22302
|
execute: async (input, signal) => {
|
|
22131
22303
|
signal.throwIfAborted();
|
|
22132
22304
|
let state = await assertCurrentToken(client, input);
|
|
22305
|
+
signal.throwIfAborted();
|
|
22133
22306
|
assertRequiredParticipant(state, options.selfId);
|
|
22134
22307
|
if (!state.planAcceptedParticipantIds.includes(options.selfId)) {
|
|
22135
22308
|
state = await client.coWorkAction({ type: "cowork-plan-accept", ...input });
|
|
22136
22309
|
}
|
|
22137
22310
|
if (state.readyParticipantIds.includes(options.selfId) || state.phase === "running") return state;
|
|
22311
|
+
signal.throwIfAborted();
|
|
22138
22312
|
return client.coWorkAction({ type: "cowork-ready", ...input });
|
|
22139
22313
|
}
|
|
22140
22314
|
},
|
|
@@ -22148,6 +22322,7 @@ function createPalsTools(client, options) {
|
|
|
22148
22322
|
execute: async (input, signal) => {
|
|
22149
22323
|
signal.throwIfAborted();
|
|
22150
22324
|
const recipientId = await assertSendTarget(client, input.target, input.coWorkId);
|
|
22325
|
+
signal.throwIfAborted();
|
|
22151
22326
|
return client.sendChat(recipientId, `[co-work ${input.coWorkId} progress] ${shareGuard.protect(input.detail)}`);
|
|
22152
22327
|
}
|
|
22153
22328
|
},
|
|
@@ -22161,6 +22336,7 @@ function createPalsTools(client, options) {
|
|
|
22161
22336
|
execute: async (input, signal) => {
|
|
22162
22337
|
signal.throwIfAborted();
|
|
22163
22338
|
const state = await assertCurrentToken(client, input);
|
|
22339
|
+
signal.throwIfAborted();
|
|
22164
22340
|
assertRequiredParticipant(state, options.selfId);
|
|
22165
22341
|
if (state.phase !== "running" || !state.acceptedParticipantIds.includes(options.selfId) || !state.readyParticipantIds.includes(options.selfId)) {
|
|
22166
22342
|
throw new Error("A required participant must be accepted, ready, and running before reporting completion");
|
|
@@ -22178,6 +22354,7 @@ function createPalsTools(client, options) {
|
|
|
22178
22354
|
execute: async (input, signal) => {
|
|
22179
22355
|
signal.throwIfAborted();
|
|
22180
22356
|
const state = await assertCurrentToken(client, input);
|
|
22357
|
+
signal.throwIfAborted();
|
|
22181
22358
|
assertRequiredParticipant(state, options.selfId);
|
|
22182
22359
|
if (state.integrationOwnerId !== options.selfId) throw new Error("Only the broker-designated integration owner can finalize integration");
|
|
22183
22360
|
if (state.phase !== "verifying") throw new Error("Co-work is not ready for integration verification");
|
|
@@ -22284,8 +22461,8 @@ var ApprovalBridge = class {
|
|
|
22284
22461
|
}
|
|
22285
22462
|
};
|
|
22286
22463
|
async function createProductionRuntime(options) {
|
|
22287
|
-
const workspace =
|
|
22288
|
-
const home =
|
|
22464
|
+
const workspace = resolve31(options.workspace ?? process.cwd());
|
|
22465
|
+
const home = resolve31(options.home ?? homedir3());
|
|
22289
22466
|
const environment = options.environment ?? process.env;
|
|
22290
22467
|
const ide = new FlavorIdeClient({ workspace, home, environment });
|
|
22291
22468
|
const ideReady = ide.initialize();
|
|
@@ -22459,6 +22636,7 @@ async function createProductionRuntime(options) {
|
|
|
22459
22636
|
const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
|
|
22460
22637
|
const jobs = new JobRegistry();
|
|
22461
22638
|
const terminals = new TerminalService(workspace, { jobs });
|
|
22639
|
+
const lspManager = new RealLspManager({ workspace, onStatus: (message2) => emitOutput({ type: "notice", message: message2 }) });
|
|
22462
22640
|
const observations = new FileObservationStore();
|
|
22463
22641
|
const workspaceInstructions = new WorkspaceInstructions(workspace);
|
|
22464
22642
|
const fileMutationOptions = {
|
|
@@ -22482,9 +22660,7 @@ async function createProductionRuntime(options) {
|
|
|
22482
22660
|
createWebSearchTool(),
|
|
22483
22661
|
...createJobTools(jobs),
|
|
22484
22662
|
...createTerminalTools(terminals, workspace),
|
|
22485
|
-
...createLspTools(workspace, {
|
|
22486
|
-
onStatus: (message2) => emitOutput({ type: "notice", message: message2 })
|
|
22487
|
-
}),
|
|
22663
|
+
...createLspTools(workspace, { manager: lspManager }),
|
|
22488
22664
|
...options.approvalPolicy === "deny" ? [] : [createAskUserQuestionTool(askUserQuestionHandler)],
|
|
22489
22665
|
createTaskOutputTool(),
|
|
22490
22666
|
createTodoWriteTool()
|
|
@@ -23021,6 +23197,8 @@ async function createProductionRuntime(options) {
|
|
|
23021
23197
|
permissionPolicy,
|
|
23022
23198
|
maxIterationsMain: config.maxIterations.main,
|
|
23023
23199
|
maxIterationsSubagent: config.maxIterations.subagent,
|
|
23200
|
+
softLimitFactor: config.maxIterations.softLimitFactor,
|
|
23201
|
+
extendIterations: config.maxIterations.extendBy,
|
|
23024
23202
|
hasActiveProgress,
|
|
23025
23203
|
afterToolSuccess: async (_tool, paths, _input, _output, context) => workspaceInstructions.discover(paths, context.ownerId ?? context.agent),
|
|
23026
23204
|
toolJournal: {
|
|
@@ -23412,6 +23590,8 @@ async function createProductionRuntime(options) {
|
|
|
23412
23590
|
permissionPolicy,
|
|
23413
23591
|
maxIterationsMain: config.maxIterations.main,
|
|
23414
23592
|
maxIterationsSubagent: config.maxIterations.subagent,
|
|
23593
|
+
softLimitFactor: config.maxIterations.softLimitFactor,
|
|
23594
|
+
extendIterations: config.maxIterations.extendBy,
|
|
23415
23595
|
loopMode: true,
|
|
23416
23596
|
afterToolSuccess: async (_tool, paths, _input, _output, context) => loopInstructions.discover(paths, context.ownerId ?? context.agent),
|
|
23417
23597
|
toolJournal: {
|
|
@@ -23701,9 +23881,9 @@ async function createProductionRuntime(options) {
|
|
|
23701
23881
|
explain: (query, focus, signal, onText) => runExplain({
|
|
23702
23882
|
graph: new DesktopAstGraphService(workspace),
|
|
23703
23883
|
readFile: async (path) => {
|
|
23704
|
-
const root =
|
|
23705
|
-
const abs =
|
|
23706
|
-
if (abs !== root && !abs.startsWith(root +
|
|
23884
|
+
const root = resolve31(workspace);
|
|
23885
|
+
const abs = resolve31(root, path);
|
|
23886
|
+
if (abs !== root && !abs.startsWith(root + sep22)) throw new Error(`path outside workspace: ${path}`);
|
|
23707
23887
|
return readFile23(abs, "utf8");
|
|
23708
23888
|
},
|
|
23709
23889
|
history: async (path, limit) => fileHistory(workspace, path, limit),
|
|
@@ -24055,6 +24235,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
24055
24235
|
if (executionEnvironment !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "execution-environment", executionEnvironment.dispose());
|
|
24056
24236
|
if (islandControlServer !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "island-control", islandControlServer.close());
|
|
24057
24237
|
terminals.dispose();
|
|
24238
|
+
await boundedStep(stepTimeoutMs, diagnostics, "language-servers", lspManager.dispose());
|
|
24058
24239
|
await boundedStep(stepTimeoutMs, diagnostics, "jobs", jobs.dispose());
|
|
24059
24240
|
auditLogger.close();
|
|
24060
24241
|
evolveService.dispose();
|
|
@@ -24095,6 +24276,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
24095
24276
|
if (sleepScheduler !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "sleep-scheduler", sleepScheduler.dispose());
|
|
24096
24277
|
if (executionEnvironment !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "execution-environment", executionEnvironment.dispose());
|
|
24097
24278
|
terminals.dispose();
|
|
24279
|
+
await boundedStep(stepTimeoutMs, diagnostics, "language-servers", lspManager.dispose());
|
|
24098
24280
|
await boundedStep(stepTimeoutMs, diagnostics, "jobs", jobs.dispose());
|
|
24099
24281
|
unsubscribeCollaboration?.();
|
|
24100
24282
|
pendingCollaborationEvents.length = 0;
|
|
@@ -24248,9 +24430,9 @@ async function workspaceFingerprint(workspace) {
|
|
|
24248
24430
|
let remaining = 5 * 1024 * 1024;
|
|
24249
24431
|
for (const name of untracked.stdout.split("\0").filter(Boolean).sort()) {
|
|
24250
24432
|
if (name === ".flavor/loops" || name.startsWith(".flavor/loops/")) continue;
|
|
24251
|
-
const path =
|
|
24252
|
-
const relativePath =
|
|
24253
|
-
if (relativePath === ".." || relativePath.startsWith(`..${
|
|
24433
|
+
const path = resolve31(workspace, name);
|
|
24434
|
+
const relativePath = relative22(resolve31(workspace), path);
|
|
24435
|
+
if (relativePath === ".." || relativePath.startsWith(`..${sep22}`)) continue;
|
|
24254
24436
|
hash.update(name);
|
|
24255
24437
|
if (remaining <= 0) continue;
|
|
24256
24438
|
try {
|
|
@@ -24382,7 +24564,7 @@ function requestMemoryRestart(workspace, sessionId, emitOutput) {
|
|
|
24382
24564
|
previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
|
|
24383
24565
|
} catch {
|
|
24384
24566
|
}
|
|
24385
|
-
|
|
24567
|
+
mkdirSync3(dirname17(markerPath), { recursive: true });
|
|
24386
24568
|
writeFileSync2(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
|
|
24387
24569
|
`, "utf8");
|
|
24388
24570
|
} catch {
|