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.
Files changed (140) hide show
  1. package/dist/agent/loop.d.ts +3 -0
  2. package/dist/{app-C5ZQUOKJ.js → app-H7RYDMD3.js} +6 -6
  3. package/dist/{chunk-FOTMP4ZS.js → chunk-3BHKW5A7.js} +1 -1
  4. package/dist/{chunk-ODRWR4KK.js → chunk-IQAZ42TM.js} +1 -1
  5. package/dist/{chunk-URUVFECH.js → chunk-KBHWS7M6.js} +4 -4
  6. package/dist/{chunk-6MNOZ2TX.js → chunk-NIKCPCJG.js} +559 -377
  7. package/dist/{chunk-3WCDZ6ZL.js → chunk-ZJCAQXA7.js} +2 -1
  8. package/dist/{claude-ink-LP32NSX5.js → claude-ink-ECZ6QF3P.js} +2 -2
  9. package/dist/cli-main.js +7 -7
  10. package/dist/desktop/main.js +626 -443
  11. package/dist/desktop-renderer/assets/{index-CqRIr-hz.js → index-CYd5JkTR.js} +2 -2
  12. package/dist/desktop-renderer/assets/{interactive-terminal-C4o7MNPO.js → interactive-terminal-j52Ty3QO.js} +1 -1
  13. package/dist/desktop-renderer/index.html +1 -1
  14. package/dist/git/service.d.ts +3 -3
  15. package/dist/git/tools.d.ts +1 -1
  16. package/dist/harness/local.d.ts +2 -0
  17. package/dist/init/project.d.ts +0 -6
  18. package/dist/jobs/registry.d.ts +1 -1
  19. package/dist/{load-V2UIYYQI.js → load-SLSL3IWR.js} +1 -1
  20. package/dist/sdk/index.js +4 -4
  21. package/dist/tools/lsp.d.ts +13 -10
  22. package/dist/tools/runtime.d.ts +2 -1
  23. package/dist/tools/search.d.ts +1 -0
  24. package/dist/tools/types.d.ts +1 -1
  25. package/dist/utils/abort.d.ts +4 -0
  26. package/dist/utils/execFileNoThrow.d.ts +1 -0
  27. package/dist/utils/process-tree.d.ts +2 -0
  28. package/package.json +2 -2
  29. package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +7 -7
  30. package/dist/astgraph/db.mjs +0 -237
  31. package/dist/astgraph/extract.mjs +0 -239
  32. package/dist/astgraph/flavor-plugin.json +0 -25
  33. package/dist/astgraph/grammars.mjs +0 -68
  34. package/dist/astgraph/index.js +0 -301
  35. package/dist/astgraph/indexer.mjs +0 -175
  36. package/dist/astgraph/query.mjs +0 -165
  37. package/dist/astgraph/resolve.mjs +0 -93
  38. package/dist/astgraph/vendor/tree-sitter-javascript.wasm +0 -0
  39. package/dist/astgraph/vendor/tree-sitter-tsx.wasm +0 -0
  40. package/dist/astgraph/vendor/tree-sitter-typescript.wasm +0 -0
  41. package/dist/astgraph/vendor/tree-sitter.js +0 -3980
  42. package/dist/astgraph/vendor/tree-sitter.wasm +0 -0
  43. package/dist/astgraph/vendor/zod/LICENSE +0 -21
  44. package/dist/astgraph/vendor/zod/index.js +0 -4
  45. package/dist/astgraph/vendor/zod/locales/index.js +0 -1
  46. package/dist/astgraph/vendor/zod/locales/package.json +0 -7
  47. package/dist/astgraph/vendor/zod/package.json +0 -135
  48. package/dist/astgraph/vendor/zod/v4/classic/checks.js +0 -1
  49. package/dist/astgraph/vendor/zod/v4/classic/coerce.js +0 -17
  50. package/dist/astgraph/vendor/zod/v4/classic/compat.js +0 -31
  51. package/dist/astgraph/vendor/zod/v4/classic/errors.js +0 -48
  52. package/dist/astgraph/vendor/zod/v4/classic/external.js +0 -20
  53. package/dist/astgraph/vendor/zod/v4/classic/from-json-schema.js +0 -599
  54. package/dist/astgraph/vendor/zod/v4/classic/index.js +0 -4
  55. package/dist/astgraph/vendor/zod/v4/classic/iso.js +0 -30
  56. package/dist/astgraph/vendor/zod/v4/classic/package.json +0 -7
  57. package/dist/astgraph/vendor/zod/v4/classic/parse.js +0 -15
  58. package/dist/astgraph/vendor/zod/v4/classic/schemas.js +0 -1395
  59. package/dist/astgraph/vendor/zod/v4/core/api.js +0 -1087
  60. package/dist/astgraph/vendor/zod/v4/core/checks.js +0 -575
  61. package/dist/astgraph/vendor/zod/v4/core/core.js +0 -78
  62. package/dist/astgraph/vendor/zod/v4/core/doc.js +0 -35
  63. package/dist/astgraph/vendor/zod/v4/core/errors.js +0 -185
  64. package/dist/astgraph/vendor/zod/v4/core/index.js +0 -16
  65. package/dist/astgraph/vendor/zod/v4/core/json-schema-generator.js +0 -95
  66. package/dist/astgraph/vendor/zod/v4/core/json-schema-processors.js +0 -601
  67. package/dist/astgraph/vendor/zod/v4/core/json-schema.js +0 -1
  68. package/dist/astgraph/vendor/zod/v4/core/package.json +0 -7
  69. package/dist/astgraph/vendor/zod/v4/core/parse.js +0 -93
  70. package/dist/astgraph/vendor/zod/v4/core/regexes.js +0 -139
  71. package/dist/astgraph/vendor/zod/v4/core/registries.js +0 -51
  72. package/dist/astgraph/vendor/zod/v4/core/schemas.js +0 -2239
  73. package/dist/astgraph/vendor/zod/v4/core/standard-schema.js +0 -1
  74. package/dist/astgraph/vendor/zod/v4/core/to-json-schema.js +0 -448
  75. package/dist/astgraph/vendor/zod/v4/core/util.js +0 -674
  76. package/dist/astgraph/vendor/zod/v4/core/versions.js +0 -5
  77. package/dist/astgraph/vendor/zod/v4/index.js +0 -3
  78. package/dist/astgraph/vendor/zod/v4/locales/ar.js +0 -106
  79. package/dist/astgraph/vendor/zod/v4/locales/az.js +0 -105
  80. package/dist/astgraph/vendor/zod/v4/locales/be.js +0 -156
  81. package/dist/astgraph/vendor/zod/v4/locales/bg.js +0 -120
  82. package/dist/astgraph/vendor/zod/v4/locales/ca.js +0 -107
  83. package/dist/astgraph/vendor/zod/v4/locales/cs.js +0 -111
  84. package/dist/astgraph/vendor/zod/v4/locales/da.js +0 -115
  85. package/dist/astgraph/vendor/zod/v4/locales/de.js +0 -108
  86. package/dist/astgraph/vendor/zod/v4/locales/el.js +0 -109
  87. package/dist/astgraph/vendor/zod/v4/locales/en.js +0 -113
  88. package/dist/astgraph/vendor/zod/v4/locales/eo.js +0 -109
  89. package/dist/astgraph/vendor/zod/v4/locales/es.js +0 -132
  90. package/dist/astgraph/vendor/zod/v4/locales/fa.js +0 -114
  91. package/dist/astgraph/vendor/zod/v4/locales/fi.js +0 -112
  92. package/dist/astgraph/vendor/zod/v4/locales/fr-CA.js +0 -107
  93. package/dist/astgraph/vendor/zod/v4/locales/fr.js +0 -125
  94. package/dist/astgraph/vendor/zod/v4/locales/he.js +0 -214
  95. package/dist/astgraph/vendor/zod/v4/locales/hr.js +0 -122
  96. package/dist/astgraph/vendor/zod/v4/locales/hu.js +0 -108
  97. package/dist/astgraph/vendor/zod/v4/locales/hy.js +0 -147
  98. package/dist/astgraph/vendor/zod/v4/locales/id.js +0 -106
  99. package/dist/astgraph/vendor/zod/v4/locales/index.js +0 -52
  100. package/dist/astgraph/vendor/zod/v4/locales/is.js +0 -109
  101. package/dist/astgraph/vendor/zod/v4/locales/it.js +0 -108
  102. package/dist/astgraph/vendor/zod/v4/locales/ja.js +0 -107
  103. package/dist/astgraph/vendor/zod/v4/locales/ka.js +0 -112
  104. package/dist/astgraph/vendor/zod/v4/locales/kh.js +0 -5
  105. package/dist/astgraph/vendor/zod/v4/locales/km.js +0 -110
  106. package/dist/astgraph/vendor/zod/v4/locales/ko.js +0 -111
  107. package/dist/astgraph/vendor/zod/v4/locales/lt.js +0 -203
  108. package/dist/astgraph/vendor/zod/v4/locales/mk.js +0 -109
  109. package/dist/astgraph/vendor/zod/v4/locales/ms.js +0 -107
  110. package/dist/astgraph/vendor/zod/v4/locales/nl.js +0 -110
  111. package/dist/astgraph/vendor/zod/v4/locales/no.js +0 -108
  112. package/dist/astgraph/vendor/zod/v4/locales/ota.js +0 -109
  113. package/dist/astgraph/vendor/zod/v4/locales/package.json +0 -7
  114. package/dist/astgraph/vendor/zod/v4/locales/pl.js +0 -109
  115. package/dist/astgraph/vendor/zod/v4/locales/ps.js +0 -114
  116. package/dist/astgraph/vendor/zod/v4/locales/pt.js +0 -108
  117. package/dist/astgraph/vendor/zod/v4/locales/ro.js +0 -119
  118. package/dist/astgraph/vendor/zod/v4/locales/ru.js +0 -156
  119. package/dist/astgraph/vendor/zod/v4/locales/sl.js +0 -109
  120. package/dist/astgraph/vendor/zod/v4/locales/sv.js +0 -110
  121. package/dist/astgraph/vendor/zod/v4/locales/ta.js +0 -110
  122. package/dist/astgraph/vendor/zod/v4/locales/th.js +0 -110
  123. package/dist/astgraph/vendor/zod/v4/locales/tr.js +0 -105
  124. package/dist/astgraph/vendor/zod/v4/locales/ua.js +0 -5
  125. package/dist/astgraph/vendor/zod/v4/locales/uk.js +0 -108
  126. package/dist/astgraph/vendor/zod/v4/locales/ur.js +0 -110
  127. package/dist/astgraph/vendor/zod/v4/locales/uz.js +0 -110
  128. package/dist/astgraph/vendor/zod/v4/locales/vi.js +0 -108
  129. package/dist/astgraph/vendor/zod/v4/locales/yo.js +0 -107
  130. package/dist/astgraph/vendor/zod/v4/locales/zh-CN.js +0 -109
  131. package/dist/astgraph/vendor/zod/v4/locales/zh-TW.js +0 -107
  132. package/dist/astgraph/vendor/zod/v4/mini/checks.js +0 -1
  133. package/dist/astgraph/vendor/zod/v4/mini/coerce.js +0 -22
  134. package/dist/astgraph/vendor/zod/v4/mini/external.js +0 -14
  135. package/dist/astgraph/vendor/zod/v4/mini/index.js +0 -3
  136. package/dist/astgraph/vendor/zod/v4/mini/iso.js +0 -34
  137. package/dist/astgraph/vendor/zod/v4/mini/package.json +0 -7
  138. package/dist/astgraph/vendor/zod/v4/mini/parse.js +0 -1
  139. package/dist/astgraph/vendor/zod/v4/mini/schemas.js +0 -961
  140. 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-URUVFECH.js";
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-3WCDZ6ZL.js";
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((resolve31, reject) => {
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
- resolve31({ timedOut: true, value: void 0 });
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
- resolve31({ timedOut: false, value });
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((resolve31, reject) => {
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
- resolve31(value);
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 { copyFileSync, mkdirSync, readdirSync } from "fs";
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 dirname3, join as join2 } from "path";
1073
- import { fileURLToPath as fileURLToPath2 } from "url";
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 = dirname3(fileURLToPath2(import.meta.url));
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 = dirname3(directory);
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 dirname4, join as join3, relative as relative3, resolve as resolve2, sep as sep3 } from "path";
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(dirname4(target), { recursive: true });
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(dirname4(path), { recursive: true });
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 dirname5, join as join4, resolve as resolve3 } from "path";
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(dirname5(this.#path), { recursive: true });
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 { dirname as dirname6, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve4, sep as sep4 } from "path";
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
- path: z.string().min(1).describe('Workspace-relative search path. Use JSON null, not the string "null", for the workspace root.').nullable().optional(),
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: /${input.pattern}/${kind}${where}${ignored2}`;
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 walk(start);
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()) return dirname6(candidate);
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((resolve31) => {
4193
+ return new Promise((resolve32) => {
4204
4194
  const check = () => {
4205
- if (!this.#activePlanning.has(key)) resolve31();
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((resolve31) => {
4325
- timer = setTimeout(() => resolve31(void 0), timeoutMs);
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 spawn2 } from "child_process";
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] : [base, ...pathExt.map((extension) => `${base}${extension}`)];
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 error|unexpected token|parsererror|语法错误)/iu.test(detail)) {
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 selected runtime shell rejected the command syntax.",
4843
- hint: "Pass normal arguments through args; reserve shell syntax for pipes, redirects, and operators."
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: "Run a bounded command reliably inside the workspace. Pass a bare executable in command and one unquoted logical value per args entry; shell syntax is supported only when command contains pipes, redirects, or operators. stdout/stderr are returned verbatim and failures include a structured diagnostic. background=true returns a managed job id.",
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 = spawn2(invocation.command, invocation.args, {
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 = terminateTree(child.pid);
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 as mkdirSync2, writeFileSync } from "fs";
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
- mkdirSync2(dir, { recursive: true });
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 spawn3 } from "child_process";
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 = spawn3(invocation.command, invocation.args, { windowsHide: true, shell: false });
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 spawn4 } from "child_process";
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 dirname8 } from "path";
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 dirname7 } from "path";
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(dirname7(options.path), { recursive: true, mode: 448 });
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((resolve31) => {
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
- resolve31(connected);
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((resolve31, reject) => {
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
- resolve31();
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((resolve31) => this.server.close(() => resolve31()));
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 = dirname8(this.address);
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 delay2(milliseconds) {
6646
- return new Promise((resolve31) => setTimeout(resolve31, milliseconds));
6625
+ function delay(milliseconds) {
6626
+ return new Promise((resolve32) => setTimeout(resolve32, milliseconds));
6647
6627
  }
6648
6628
  function canConnect2(address) {
6649
- return new Promise((resolve31) => {
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
- resolve31(connected);
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 = spawn4(options.executable, [...options.args], {
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 delay2(25);
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((resolve31) => {
6698
- resolveClosed = resolve31;
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 delay2(retryDelayMs);
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 mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
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 dirname19, join as join29, relative as relative21, resolve as resolve30, sep as sep21 } from "path";
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 dirname9, isAbsolute as isAbsolute7, relative as relative8, resolve as resolve9, sep as sep8 } from "path";
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 = dirname9(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 : dirname9(path);
8171
+ return (await lstat5(path)).isDirectory() ? path : dirname7(path);
8191
8172
  } catch {
8192
- return dirname9(path);
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 delay3 of retry.backoffMs.slice(0, retry.maxRetries)) {
8292
- if (!Number.isSafeInteger(delay3) || delay3 < 0) {
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((resolve31, reject) => {
8634
+ return new Promise((resolve32, reject) => {
8654
8635
  const timer = setTimeout(() => {
8655
8636
  signal?.removeEventListener("abort", onAbort);
8656
- resolve31();
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((resolve31, reject) => {
9284
+ return new Promise((resolve32, reject) => {
9295
9285
  const timer = setTimeout(() => {
9296
9286
  signal?.removeEventListener("abort", onAbort);
9297
- resolve31();
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 declaredPresentation = tool.presentResult?.(rawOutput, input);
9643
- const presentation = declaredPresentation ?? getToolPresentation(rawOutput);
9644
- const rendered = tool.renderForModel?.(rawOutput, input);
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
- const additionalContext = await this.#afterSuccess?.(tool.name, tool.paths(input), input, rawOutput, context);
9648
- await this.#hooks.emit({
9649
- version: 1,
9650
- type: "PostToolUse",
9651
- payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
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
- await writeFile4(savedTo, serialized, "utf8");
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
- savedTo
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 mkdirSync3, openSync, readFileSync as readFileSync2, realpathSync as realpathSync2, renameSync, statSync as statSync2, truncateSync, unlinkSync, writeSync } from "fs";
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
- mkdirSync3(directory, { recursive: true, mode: 448 });
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 spawn5 } from "child_process";
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((resolve31, reject) => {
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 = spawn5(invocation.command, invocation.args, {
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
- terminateProcessTree(child);
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
- resolve31(HookDecisionSchema.parse(JSON.parse(stdout)));
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 terminateProcessTree(child) {
10519
+ function terminateProcessTree2(child) {
10497
10520
  if (child.pid === void 0) return;
10498
10521
  if (process.platform === "win32") {
10499
- const killer = spawn5("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
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((resolve31, reject) => {
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
- resolve31();
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((resolve31) => {
10786
+ return new Promise((resolve32) => {
10764
10787
  try {
10765
- server.close(() => resolve31());
10788
+ server.close(() => resolve32());
10766
10789
  } catch {
10767
- resolve31();
10790
+ resolve32();
10768
10791
  }
10769
10792
  });
10770
10793
  }
10771
10794
 
10772
10795
  // src/evolve/service.ts
10773
- import { spawn as spawn6 } from "child_process";
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 dirname10 } from "path";
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 ?? dirname10(entryPath));
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 dirname11, join as join14 } from "path";
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(dirname11(file), { recursive: true, mode: 448 });
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(dirname11(file), { recursive: true, mode: 448 });
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((resolve31) => {
11941
+ return new Promise((resolve32) => {
11919
11942
  const isWindows = process.platform === "win32";
11920
- const child = spawn6(isWindows ? "cmd.exe" : "/bin/sh", isWindows ? ["/d", "/s", "/c", command] : ["-c", command], {
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
- resolve31({ ok: false, stdout, stderr: `${stderr}
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
- resolve31({ ok: false, stdout, stderr: `${stderr}
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
- resolve31({ ok: code === 0, stdout, stderr, code: code ?? "unknown" });
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 dirname12, join as join18, relative as relative11, resolve as resolve14, sep as sep11 } from "path";
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, dirname12(this.#root));
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 = dirname12(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 dirname13, join as join20, relative as relative12, resolve as resolve16, sep as sep12 } from "path";
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, dirname13(this.#root));
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 = dirname13(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((resolve31, reject) => {
14912
- serverResolve = resolve31;
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((resolve31, reject) => {
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
- resolve31({ redirectUri, promise: codePromise });
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 dirname14 } from "path";
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(dirname14(filePath));
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(dirname14(filePath));
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 skill = (await registry.discover()).find((candidate) => candidate.name === input.skill);
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 dirname15, join as join23, relative as relative14, resolve as resolve20, sep as sep14 } from "path";
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, dirname15(this.#sessions));
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 = dirname15(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 delay3 = Math.max(1, next.getTime() - now.getTime());
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
- }, delay3);
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 dirname16, isAbsolute as isAbsolute11, relative as relative16, resolve as resolve22, sep as sep16 } from "path";
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({ path: z24.string().min(1), oldText: z24.string().min(1), newText: z24.string() });
17191
- var ApplyPatchInput = z24.object({ patch: z24.string().min(1) });
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).toString("utf8").replaceAll("\r\n", "\n");
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) => hasCRLF ? s.replace(/\r\n/g, "\n") : 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 + oldTextLF.length);
17360
+ const second = first < 0 ? -1 : contentsLF.indexOf(oldTextLF, first + 1);
17328
17361
  if (first < 0 || second >= 0) {
17329
- const diagnosis = buildEditDiagnosis(contentsLF, oldTextLF, first, second);
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 = dirname16(ancestor);
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(dirname16(path), { recursive: true });
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.toString("utf8");
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, first, second) {
17704
+ function buildEditDiagnosis(contentsLF, oldTextLF, second) {
17658
17705
  let diagnosis = "oldText must match exactly once";
17659
17706
  if (second >= 0) {
17660
- const count = contentsLF.split(oldTextLF).length - 1;
17661
- diagnosis += ` \u2014 matched ${count} times in the file`;
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) => ({ query: input.query, results: [...await provider.search(input.query, input.maxResults ?? 8, 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
- const decoded = new TextDecoder(charset(contentType), { fatal: false }).decode(response.body);
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) truncated = true;
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 spawn7 } from "child_process";
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 dirname17, extname as extname2, isAbsolute as isAbsolute12, relative as relative17, resolve as resolve23, sep as sep17 } from "path";
18095
- import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "url";
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 = spawn7(invocation.command, invocation.args, {
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.#rejectAll(new Error(reason));
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
- async shutdown() {
18179
- if (this.#disposed) return;
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
- const timeout = AbortSignal.timeout(LSP_REQUEST_TIMEOUT_MS);
18195
- const promise = new Promise((resolve31, reject) => {
18196
- this.#pending.set(id, { resolve: resolve31, reject });
18197
- timeout.addEventListener("abort", () => {
18198
- if (this.#pending.has(id)) {
18199
- this.#pending.delete(id);
18200
- reject(new Error(`LSP request "${method}" timed out after ${LSP_REQUEST_TIMEOUT_MS}ms`));
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
- }, { once: true });
18300
+ reject(signal?.reason);
18301
+ };
18302
+ signal?.addEventListener("abort", onAbort, { once: true });
18203
18303
  });
18204
- this.#send(request);
18205
- return promise;
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 = fileURLToPath3(url);
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
- dirname17(fileURLToPath3(import.meta.resolve("typescript/package.json"))),
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
- for (const [, connection] of this.#connections) {
18360
- connection.shutdown().catch(() => {
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
- const connection = await this.#getConnection(uri);
18368
- if (connection === void 0) return [];
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
- const connection = await this.#getConnection(uri);
18373
- if (connection === void 0) return null;
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
- const connection = await this.#getConnection(uri);
18378
- if (connection === void 0) return [];
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) return 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) return 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) return void 0;
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, _signal) => {
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, _signal) => {
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 contents = typeof info.contents === "string" ? info.contents : info.contents.value;
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, _signal) => {
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 decodeURIComponent(url.pathname);
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
- const result = await execFileNoThrow("git", ["-C", workspace, ...args], { timeout: timeoutMs, useCwd: false });
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) throw gitFailure(result, "log");
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("File to inspect; omit for the whole repository history").optional(),
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
- if (!await isGitRepository(workspace)) throw new Error("Not a git repository");
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(workspace, input.path, limit);
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
- return new Promise((resolve31, reject) => {
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
- resolve31(answers);
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 dirname18, join as join26, resolve as resolve25 } from "path";
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 = resolve25(options.workspace);
19384
+ this.#workspace = resolve26(options.workspace);
19231
19385
  this.#projectRoot = join26(this.#workspace, ".flavor", "tools");
19232
- this.#globalRoot = join26(resolve25(options.home), ".flavor-code", "tools");
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(dirname18(path), { recursive: true });
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 resolve26, join as join27, relative as relative18, sep as sep18 } from "path";
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 = resolve26(options.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 = resolve26(this.#memoryRoot, ...contentPath.split("/"));
20565
- const rel = relative18(this.#memoryRoot, path);
20566
- if (rel === ".." || rel.startsWith(`..${sep18}`)) throw new Error("Memory content path escapes the memory directory");
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 relative19, resolve as resolve27, sep as sep19 } from "path";
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 = resolve27(options.workspace);
21048
- this.#home = resolve27(options.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(resolve27(parent));
21247
- const target = normalize(resolve27(child));
21248
- return target === base || target.startsWith(`${base}${sep19}`);
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 = relative19(workspace, filePath).replaceAll("\\", "/");
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((resolve31) => {
21278
- settle = resolve31;
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
- if (signal === void 0) return job.done;
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
- void job.cancel?.();
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 isAbsolute13, relative as relative20, resolve as resolve28, sep as sep20 } from "path";
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 = resolve28(workspace);
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
- const job = this.#jobs?.create({ kind: "terminal", owner: input.owner, label: shell, cancel: () => this.close(id, input.owner) });
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.#owned(id, owner2).pty.write(data2);
21631
+ this.#running(id, owner2).pty.write(data2);
21467
21632
  }
21468
21633
  resize(id, owner2, columns, rows) {
21469
- this.#owned(id, owner2).pty.resize(columns, rows);
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 = resolve28(root, input);
21529
- const delta = relative20(root, path);
21530
- if (delta === ".." || delta.startsWith(`..${sep20}`) || isAbsolute13(delta)) throw new Error("Terminal cwd is outside the workspace");
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 resolve29 } from "path";
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) => [resolve29(workspace, input.cwd ?? ".")],
21551
- permissions: (input) => ({ paths: [resolve29(workspace, input.cwd ?? ".")], command: input.shell ?? "system-shell", args: input.args ?? [], cwd: resolve29(workspace, input.cwd ?? ".") }),
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) => output.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((resolve31) => {
21778
- disconnectTimer = setTimeout(resolve31, Math.min(this.requestTimeoutMs, 250));
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((resolve31, reject) => {
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
- resolve31();
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((resolve31, reject) => {
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: resolve31, reject, timer });
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 delay3 = this.reconnectDelayMs;
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
- }, delay3);
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 = resolve30(options.workspace ?? process.cwd());
22288
- const home = resolve30(options.home ?? homedir3());
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 = resolve30(workspace);
23705
- const abs = resolve30(root, path);
23706
- if (abs !== root && !abs.startsWith(root + sep21)) throw new Error(`path outside workspace: ${path}`);
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 = resolve30(workspace, name);
24252
- const relativePath = relative21(resolve30(workspace), path);
24253
- if (relativePath === ".." || relativePath.startsWith(`..${sep21}`)) continue;
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
- mkdirSync4(dirname19(markerPath), { recursive: true });
24567
+ mkdirSync3(dirname17(markerPath), { recursive: true });
24386
24568
  writeFileSync2(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
24387
24569
  `, "utf8");
24388
24570
  } catch {