reasonix 0.33.1 → 0.34.0

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 (74) hide show
  1. package/dashboard/dist/app.js +1 -21
  2. package/dashboard/dist/app.js.map +1 -1
  3. package/dist/cli/{chat-Q5ZCVIOO.js → chat-TL4HMNEQ.js} +15 -13
  4. package/dist/cli/chunk-2EBODRRO.js +149 -0
  5. package/dist/cli/chunk-2EBODRRO.js.map +1 -0
  6. package/dist/cli/{chunk-DULSP7JH.js → chunk-5JXXEPDM.js} +34 -1
  7. package/dist/cli/chunk-5JXXEPDM.js.map +1 -0
  8. package/dist/cli/{chunk-MDHVWCJ4.js → chunk-5SAMVHA3.js} +591 -299
  9. package/dist/cli/chunk-5SAMVHA3.js.map +1 -0
  10. package/dist/cli/chunk-DAEAAVDF.js +199 -0
  11. package/dist/cli/chunk-DAEAAVDF.js.map +1 -0
  12. package/dist/cli/{chunk-D5DKXIP5.js → chunk-F3ILWP2L.js} +18 -27
  13. package/dist/cli/chunk-F3ILWP2L.js.map +1 -0
  14. package/dist/cli/{chunk-SDE5U32Z.js → chunk-KZHMKOJH.js} +13 -8
  15. package/dist/cli/{chunk-SDE5U32Z.js.map → chunk-KZHMKOJH.js.map} +1 -1
  16. package/dist/cli/{chunk-Q6YFXW7H.js → chunk-LNTORE5K.js} +246 -339
  17. package/dist/cli/chunk-LNTORE5K.js.map +1 -0
  18. package/dist/cli/chunk-MRLXEMZ7.js +26 -0
  19. package/dist/cli/chunk-MRLXEMZ7.js.map +1 -0
  20. package/dist/cli/{chunk-WBDE4IRI.js → chunk-OERAGRJX.js} +2 -2
  21. package/dist/cli/{chunk-QGE6AF76.js → chunk-Q36KBLSU.js} +207 -8
  22. package/dist/cli/chunk-Q36KBLSU.js.map +1 -0
  23. package/dist/cli/{chunk-RZILUXUC.js → chunk-RXGEGA7K.js} +2 -2
  24. package/dist/cli/{chunk-FXGQ5NHE.js → chunk-SA4UGZPG.js} +21 -1
  25. package/dist/cli/chunk-SA4UGZPG.js.map +1 -0
  26. package/dist/cli/{chunk-J5VLP23S.js → chunk-SW3CCXEV.js} +2 -2
  27. package/dist/cli/{chunk-W4LDFAZ6.js → chunk-SX6L4HZZ.js} +2 -2
  28. package/dist/cli/chunk-WUI3P4RA.js +319 -0
  29. package/dist/cli/chunk-WUI3P4RA.js.map +1 -0
  30. package/dist/cli/{code-DLR77NPZ.js → code-V6F4BKMG.js} +16 -14
  31. package/dist/cli/{code-DLR77NPZ.js.map → code-V6F4BKMG.js.map} +1 -1
  32. package/dist/cli/{commands-JWT2MWVH.js → commands-MEZPSEHV.js} +4 -3
  33. package/dist/cli/{commands-JWT2MWVH.js.map → commands-MEZPSEHV.js.map} +1 -1
  34. package/dist/cli/{commit-RPZBOZS2.js → commit-CE4EFTUQ.js} +3 -2
  35. package/dist/cli/{commit-RPZBOZS2.js.map → commit-CE4EFTUQ.js.map} +1 -1
  36. package/dist/cli/{doctor-3TGB2NZN.js → doctor-YASM64X6.js} +8 -6
  37. package/dist/cli/index.js +23 -21
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/cli/{mcp-ARTNQ24O.js → mcp-LDFK5QJI.js} +3 -2
  40. package/dist/cli/{mcp-ARTNQ24O.js.map → mcp-LDFK5QJI.js.map} +1 -1
  41. package/dist/cli/{mcp-browse-HLO2ENDL.js → mcp-browse-FYHEITCM.js} +3 -2
  42. package/dist/cli/{mcp-browse-HLO2ENDL.js.map → mcp-browse-FYHEITCM.js.map} +1 -1
  43. package/dist/cli/{replay-Q43DSMG6.js → replay-JEDLU7F2.js} +8 -6
  44. package/dist/cli/{replay-Q43DSMG6.js.map → replay-JEDLU7F2.js.map} +1 -1
  45. package/dist/cli/{run-JMEOTQCG.js → run-NHD2RSTD.js} +8 -6
  46. package/dist/cli/{run-JMEOTQCG.js.map → run-NHD2RSTD.js.map} +1 -1
  47. package/dist/cli/{server-SYC3OVOP.js → server-MC4A4WAJ.js} +9 -8
  48. package/dist/cli/{server-SYC3OVOP.js.map → server-MC4A4WAJ.js.map} +1 -1
  49. package/dist/cli/{sessions-MOJAALJI.js → sessions-ZHWJEW4L.js} +8 -6
  50. package/dist/cli/{sessions-MOJAALJI.js.map → sessions-ZHWJEW4L.js.map} +1 -1
  51. package/dist/cli/{setup-CCJZAWTY.js → setup-DK43MT47.js} +6 -5
  52. package/dist/cli/{setup-CCJZAWTY.js.map → setup-DK43MT47.js.map} +1 -1
  53. package/dist/cli/{version-3MYFE4G6.js → version-O362UKPM.js} +8 -6
  54. package/dist/cli/{version-3MYFE4G6.js.map → version-O362UKPM.js.map} +1 -1
  55. package/dist/index.d.ts +45 -1
  56. package/dist/index.js +569 -27
  57. package/dist/index.js.map +1 -1
  58. package/package.json +1 -1
  59. package/dist/cli/chunk-63KAV5DX.js +0 -106
  60. package/dist/cli/chunk-63KAV5DX.js.map +0 -1
  61. package/dist/cli/chunk-D5DKXIP5.js.map +0 -1
  62. package/dist/cli/chunk-DULSP7JH.js.map +0 -1
  63. package/dist/cli/chunk-FXGQ5NHE.js.map +0 -1
  64. package/dist/cli/chunk-MDHVWCJ4.js.map +0 -1
  65. package/dist/cli/chunk-Q6YFXW7H.js.map +0 -1
  66. package/dist/cli/chunk-QGE6AF76.js.map +0 -1
  67. package/dist/cli/chunk-ZPTSJGX5.js +0 -88
  68. package/dist/cli/chunk-ZPTSJGX5.js.map +0 -1
  69. /package/dist/cli/{chat-Q5ZCVIOO.js.map → chat-TL4HMNEQ.js.map} +0 -0
  70. /package/dist/cli/{chunk-WBDE4IRI.js.map → chunk-OERAGRJX.js.map} +0 -0
  71. /package/dist/cli/{chunk-RZILUXUC.js.map → chunk-RXGEGA7K.js.map} +0 -0
  72. /package/dist/cli/{chunk-J5VLP23S.js.map → chunk-SW3CCXEV.js.map} +0 -0
  73. /package/dist/cli/{chunk-W4LDFAZ6.js.map → chunk-SX6L4HZZ.js.map} +0 -0
  74. /package/dist/cli/{doctor-3TGB2NZN.js.map → doctor-YASM64X6.js.map} +0 -0
@@ -3,12 +3,17 @@ import {
3
3
  MemoryStore,
4
4
  sanitizeMemoryName
5
5
  } from "./chunk-6TMHAK5D.js";
6
+ import {
7
+ countTokens,
8
+ estimateConversationTokens,
9
+ estimateRequestTokens
10
+ } from "./chunk-DAEAAVDF.js";
6
11
  import {
7
12
  Usage
8
13
  } from "./chunk-KMWKGPFZ.js";
9
14
  import {
10
15
  pauseGate
11
- } from "./chunk-W4LDFAZ6.js";
16
+ } from "./chunk-SX6L4HZZ.js";
12
17
  import {
13
18
  ESCALATION_CONTRACT,
14
19
  NEGATIVE_CLAIM_RULE,
@@ -17,7 +22,7 @@ import {
17
22
  import {
18
23
  formatHookOutcomeMessage,
19
24
  runHooks
20
- } from "./chunk-WBDE4IRI.js";
25
+ } from "./chunk-OERAGRJX.js";
21
26
  import {
22
27
  ignoredByLayers,
23
28
  loadGitignoreAt,
@@ -31,208 +36,18 @@ import {
31
36
  } from "./chunk-DFP4YSVM.js";
32
37
  import {
33
38
  t
34
- } from "./chunk-QGE6AF76.js";
39
+ } from "./chunk-Q36KBLSU.js";
35
40
  import {
36
41
  DEFAULT_INDEX_EXCLUDES,
37
42
  webSearchEndpoint,
38
43
  webSearchEngine
39
- } from "./chunk-DULSP7JH.js";
44
+ } from "./chunk-5JXXEPDM.js";
40
45
  import {
41
46
  DEEPSEEK_CONTEXT_TOKENS,
42
47
  DEFAULT_CONTEXT_TOKENS,
43
48
  SessionStats
44
49
  } from "./chunk-ORM6PK57.js";
45
50
 
46
- // src/tokenizer.ts
47
- import { existsSync, readFileSync } from "fs";
48
- import { createRequire } from "module";
49
- import { dirname, join } from "path";
50
- import { fileURLToPath } from "url";
51
- import { gunzipSync } from "zlib";
52
- function buildByteToChar() {
53
- const result = new Array(256);
54
- const bs = [];
55
- for (let b = 33; b <= 126; b++) bs.push(b);
56
- for (let b = 161; b <= 172; b++) bs.push(b);
57
- for (let b = 174; b <= 255; b++) bs.push(b);
58
- const cs = bs.slice();
59
- let n = 0;
60
- for (let b = 0; b < 256; b++) {
61
- if (!bs.includes(b)) {
62
- bs.push(b);
63
- cs.push(256 + n);
64
- n++;
65
- }
66
- }
67
- for (let i = 0; i < bs.length; i++) {
68
- result[bs[i]] = String.fromCodePoint(cs[i]);
69
- }
70
- return result;
71
- }
72
- var cached = null;
73
- function resolveDataPath() {
74
- if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
75
- const candidates = [];
76
- try {
77
- const here = dirname(fileURLToPath(import.meta.url));
78
- candidates.push(join(here, "..", "data", "deepseek-tokenizer.json.gz"));
79
- candidates.push(join(here, "..", "..", "data", "deepseek-tokenizer.json.gz"));
80
- } catch {
81
- }
82
- try {
83
- const req = createRequire(import.meta.url);
84
- candidates.push(
85
- join(dirname(req.resolve("reasonix/package.json")), "data", "deepseek-tokenizer.json.gz")
86
- );
87
- } catch {
88
- }
89
- for (const p of candidates) {
90
- if (existsSync(p)) return p;
91
- }
92
- return candidates[0] ?? join(process.cwd(), "data", "deepseek-tokenizer.json.gz");
93
- }
94
- function loadTokenizer() {
95
- if (cached) return cached;
96
- const buf = readFileSync(resolveDataPath());
97
- const json = gunzipSync(buf).toString("utf8");
98
- const data = JSON.parse(json);
99
- const mergeRank = /* @__PURE__ */ new Map();
100
- for (let i = 0; i < data.model.merges.length; i++) {
101
- mergeRank.set(data.model.merges[i], i);
102
- }
103
- const splitRegexes = [];
104
- for (const p of data.pre_tokenizer.pretokenizers) {
105
- if (p.type === "Split") {
106
- splitRegexes.push(new RegExp(p.pattern.Regex, "gu"));
107
- }
108
- }
109
- const addedMap = /* @__PURE__ */ new Map();
110
- const addedContents = [];
111
- for (const t2 of data.added_tokens) {
112
- if (!t2.special) {
113
- addedMap.set(t2.content, t2.id);
114
- addedContents.push(t2.content);
115
- }
116
- }
117
- addedContents.sort((a, b) => b.length - a.length);
118
- const addedPattern = addedContents.length ? new RegExp(addedContents.map(escapeRegex).join("|"), "g") : null;
119
- cached = {
120
- vocab: data.model.vocab,
121
- mergeRank,
122
- splitRegexes,
123
- byteToChar: buildByteToChar(),
124
- addedPattern,
125
- addedMap
126
- };
127
- return cached;
128
- }
129
- function escapeRegex(s) {
130
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
131
- }
132
- function applySplit(chunks, re) {
133
- const out = [];
134
- for (const chunk of chunks) {
135
- if (!chunk) continue;
136
- re.lastIndex = 0;
137
- let last = 0;
138
- for (const m of chunk.matchAll(re)) {
139
- const idx = m.index ?? 0;
140
- if (idx > last) out.push(chunk.slice(last, idx));
141
- if (m[0].length > 0) out.push(m[0]);
142
- last = idx + m[0].length;
143
- }
144
- if (last < chunk.length) out.push(chunk.slice(last));
145
- }
146
- return out;
147
- }
148
- function byteLevelEncode(s, byteToChar) {
149
- const bytes = new TextEncoder().encode(s);
150
- let out = "";
151
- for (let i = 0; i < bytes.length; i++) out += byteToChar[bytes[i]];
152
- return out;
153
- }
154
- function bpeEncode(piece, mergeRank) {
155
- if (piece.length <= 1) return piece ? [piece] : [];
156
- let word = Array.from(piece);
157
- while (true) {
158
- let bestIdx = -1;
159
- let bestRank = Number.POSITIVE_INFINITY;
160
- for (let i = 0; i < word.length - 1; i++) {
161
- const pair = `${word[i]} ${word[i + 1]}`;
162
- const rank = mergeRank.get(pair);
163
- if (rank !== void 0 && rank < bestRank) {
164
- bestRank = rank;
165
- bestIdx = i;
166
- if (rank === 0) break;
167
- }
168
- }
169
- if (bestIdx < 0) break;
170
- word = [
171
- ...word.slice(0, bestIdx),
172
- word[bestIdx] + word[bestIdx + 1],
173
- ...word.slice(bestIdx + 2)
174
- ];
175
- if (word.length === 1) break;
176
- }
177
- return word;
178
- }
179
- function encode(text) {
180
- if (!text) return [];
181
- const t2 = loadTokenizer();
182
- const ids = [];
183
- const process2 = (segment) => {
184
- if (!segment) return;
185
- let chunks = [segment];
186
- for (const re of t2.splitRegexes) chunks = applySplit(chunks, re);
187
- for (const chunk of chunks) {
188
- if (!chunk) continue;
189
- const byteLevel = byteLevelEncode(chunk, t2.byteToChar);
190
- const pieces = bpeEncode(byteLevel, t2.mergeRank);
191
- for (const p of pieces) {
192
- const id = t2.vocab[p];
193
- if (id !== void 0) ids.push(id);
194
- }
195
- }
196
- };
197
- if (t2.addedPattern) {
198
- t2.addedPattern.lastIndex = 0;
199
- let last = 0;
200
- for (const m of text.matchAll(t2.addedPattern)) {
201
- const idx = m.index ?? 0;
202
- if (idx > last) process2(text.slice(last, idx));
203
- const id = t2.addedMap.get(m[0]);
204
- if (id !== void 0) ids.push(id);
205
- last = idx + m[0].length;
206
- }
207
- if (last < text.length) process2(text.slice(last));
208
- } else {
209
- process2(text);
210
- }
211
- return ids;
212
- }
213
- function countTokens(text) {
214
- return encode(text).length;
215
- }
216
- function estimateConversationTokens(messages) {
217
- let total = 0;
218
- for (const m of messages) {
219
- if (typeof m.content === "string" && m.content) {
220
- total += countTokens(m.content);
221
- }
222
- if (m.tool_calls && Array.isArray(m.tool_calls) && m.tool_calls.length > 0) {
223
- total += countTokens(JSON.stringify(m.tool_calls));
224
- }
225
- }
226
- return total;
227
- }
228
- function estimateRequestTokens(messages, toolSpecs) {
229
- let total = estimateConversationTokens(messages);
230
- if (toolSpecs && toolSpecs.length > 0) {
231
- total += countTokens(JSON.stringify(toolSpecs));
232
- }
233
- return total;
234
- }
235
-
236
51
  // src/mcp/latency.ts
237
52
  var SAMPLE_SIZE = 5;
238
53
  var DEFAULT_THRESHOLD_MS = 4e3;
@@ -4021,9 +3836,115 @@ ${i + 1}. ${r.title}`);
4021
3836
  }
4022
3837
 
4023
3838
  // src/at-mentions.ts
4024
- import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
3839
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
4025
3840
  import { readdir, stat } from "fs/promises";
4026
- import { isAbsolute as isAbsolute2, join as join5, relative as relative5, resolve as resolve2 } from "path";
3841
+ import { isAbsolute as isAbsolute2, join as join4, relative as relative5, resolve as resolve2 } from "path";
3842
+
3843
+ // src/at-mentions-url.ts
3844
+ var AT_URL_PATTERN = /(?<=^|\s)@(https?:\/\/\S+)/g;
3845
+ var DEFAULT_AT_URL_MAX_CHARS = 32e3;
3846
+ async function expandAtUrls(text, opts = {}) {
3847
+ const maxChars = opts.maxChars ?? DEFAULT_AT_URL_MAX_CHARS;
3848
+ const fetcher = opts.fetcher;
3849
+ if (!fetcher) {
3850
+ throw new Error("expandAtUrls: fetcher option is required (wire src/tools/web.ts:webFetch)");
3851
+ }
3852
+ const seen = /* @__PURE__ */ new Map();
3853
+ const bodies = /* @__PURE__ */ new Map();
3854
+ const order = [];
3855
+ for (const match of text.matchAll(AT_URL_PATTERN)) {
3856
+ const rawUrl = match[1] ?? "";
3857
+ const url = stripUrlTail(rawUrl);
3858
+ if (!url) continue;
3859
+ if (seen.has(url)) continue;
3860
+ const cached = opts.cache?.get(url);
3861
+ if (cached) {
3862
+ seen.set(url, cached);
3863
+ if (cached.body) bodies.set(url, cached.body);
3864
+ order.push(url);
3865
+ continue;
3866
+ }
3867
+ let expansion;
3868
+ let body = "";
3869
+ try {
3870
+ const page = await fetcher(url, {
3871
+ maxChars,
3872
+ timeoutMs: opts.timeoutMs,
3873
+ signal: opts.signal
3874
+ });
3875
+ body = page.text;
3876
+ expansion = {
3877
+ token: `@${url}`,
3878
+ url,
3879
+ ok: true,
3880
+ title: page.title,
3881
+ chars: body.length,
3882
+ truncated: page.truncated
3883
+ };
3884
+ } catch (err) {
3885
+ const message = err.message ?? String(err);
3886
+ let skip = "fetch-error";
3887
+ if (/aborted|timeout/i.test(message)) skip = "timeout";
3888
+ else if (/40\d|forbidden|access denied|captcha/i.test(message)) skip = "blocked";
3889
+ expansion = {
3890
+ token: `@${url}`,
3891
+ url,
3892
+ ok: false,
3893
+ skip,
3894
+ error: message
3895
+ };
3896
+ }
3897
+ seen.set(url, expansion);
3898
+ if (body) bodies.set(url, body);
3899
+ if (opts.cache) opts.cache.set(url, { ...expansion, body });
3900
+ order.push(url);
3901
+ }
3902
+ if (seen.size === 0) return { text, expansions: [] };
3903
+ const expansions = order.map((u) => seen.get(u)).filter(Boolean);
3904
+ const blocks = [];
3905
+ for (const ex of expansions) {
3906
+ if (ex.ok) {
3907
+ const titleAttr = ex.title ? ` title="${escapeAttr(ex.title)}"` : "";
3908
+ const truncTag = ex.truncated ? ' truncated="true"' : "";
3909
+ const body = bodies.get(ex.url) ?? "";
3910
+ blocks.push(`<url href="${ex.url}"${titleAttr}${truncTag}>
3911
+ ${body}
3912
+ </url>`);
3913
+ } else {
3914
+ const reasonAttr = ex.skip ?? "fetch-error";
3915
+ blocks.push(`<url href="${ex.url}" skipped="${reasonAttr}" />`);
3916
+ }
3917
+ }
3918
+ const augmented = `${text}
3919
+
3920
+ [Referenced URLs]
3921
+ ${blocks.join("\n\n")}`;
3922
+ return { text: augmented, expansions };
3923
+ }
3924
+ function stripUrlTail(raw) {
3925
+ let s = raw;
3926
+ while (s.length > 0) {
3927
+ const last = s[s.length - 1];
3928
+ if (".,;:!?".includes(last)) {
3929
+ s = s.slice(0, -1);
3930
+ continue;
3931
+ }
3932
+ if (")]}>".includes(last)) {
3933
+ const open = { ")": "(", "]": "[", "}": "{", ">": "<" }[last];
3934
+ if (!s.includes(open)) {
3935
+ s = s.slice(0, -1);
3936
+ continue;
3937
+ }
3938
+ }
3939
+ break;
3940
+ }
3941
+ return s;
3942
+ }
3943
+ function escapeAttr(s) {
3944
+ return s.replace(/"/g, "&quot;").replace(/[\r\n]+/g, " ").trim();
3945
+ }
3946
+
3947
+ // src/at-mentions.ts
4027
3948
  var DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;
4028
3949
  var DEFAULT_AT_DIR_MAX_ENTRIES = 200;
4029
3950
  var DEFAULT_PICKER_IGNORE_DIRS = [
@@ -4068,7 +3989,7 @@ function listFilesWithStatsSync(root, opts = {}) {
4068
3989
  for (const ent of entries) {
4069
3990
  if (out.length >= maxResults) return;
4070
3991
  const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
4071
- const absPath = join5(dirAbs, ent.name);
3992
+ const absPath = join4(dirAbs, ent.name);
4072
3993
  if (ent.isDirectory()) {
4073
3994
  if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
4074
3995
  if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
@@ -4097,14 +4018,30 @@ function listFilesWithStatsSync(root, opts = {}) {
4097
4018
  walk2(rootAbs, "", []);
4098
4019
  return out;
4099
4020
  }
4100
- async function listFilesWithStatsAsync(root, opts = {}) {
4101
- const maxResults = Math.max(1, opts.maxResults ?? 2e3);
4021
+ async function walkFilesStream(root, opts) {
4102
4022
  const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
4103
- const rootAbs = resolve2(root);
4104
4023
  const respectGi = opts.respectGitignore !== false;
4105
- const out = [];
4024
+ const rootAbs = resolve2(root);
4025
+ const progressGap = Math.max(0, opts.progressIntervalMs ?? 100);
4026
+ let scanned = 0;
4027
+ let halted = false;
4028
+ let lastProgress = 0;
4029
+ const reportProgress = (force) => {
4030
+ if (!opts.onProgress) return;
4031
+ const now = Date.now();
4032
+ if (force || now - lastProgress >= progressGap) {
4033
+ lastProgress = now;
4034
+ opts.onProgress(scanned);
4035
+ }
4036
+ };
4037
+ const emit = (entry) => {
4038
+ scanned++;
4039
+ if (halted) return;
4040
+ if (opts.onEntry(entry) === false) halted = true;
4041
+ reportProgress(false);
4042
+ };
4106
4043
  const walk2 = async (dirAbs, dirRel, layers) => {
4107
- if (out.length >= maxResults) return;
4044
+ if (halted || opts.signal?.aborted) return;
4108
4045
  let effectiveLayers = layers;
4109
4046
  if (respectGi) {
4110
4047
  const ig = await loadGitignoreAt(dirAbs);
@@ -4119,52 +4056,123 @@ async function listFilesWithStatsAsync(root, opts = {}) {
4119
4056
  entries.sort((a, b) => a.name.localeCompare(b.name));
4120
4057
  const fileEnts = [];
4121
4058
  for (const ent of entries) {
4122
- if (out.length >= maxResults) break;
4123
- const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
4124
- const absPath = join5(dirAbs, ent.name);
4059
+ if (halted || opts.signal?.aborted) break;
4060
+ const absPath = join4(dirAbs, ent.name);
4125
4061
  if (ent.isDirectory()) {
4126
4062
  if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
4127
4063
  if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
4128
4064
  if (fileEnts.length > 0) {
4129
- await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);
4065
+ await flushFiles(fileEnts, dirAbs, dirRel, effectiveLayers, emit);
4130
4066
  fileEnts.length = 0;
4131
- if (out.length >= maxResults) return;
4067
+ if (halted || opts.signal?.aborted) return;
4132
4068
  }
4133
- await walk2(absPath, relPath, effectiveLayers);
4069
+ await walk2(absPath, dirRel ? `${dirRel}/${ent.name}` : ent.name, effectiveLayers);
4134
4070
  } else if (ent.isFile() || ent.isSymbolicLink()) {
4135
4071
  fileEnts.push(ent);
4136
4072
  }
4137
4073
  }
4138
- if (fileEnts.length > 0 && out.length < maxResults) {
4139
- await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);
4074
+ if (fileEnts.length > 0 && !halted && !opts.signal?.aborted) {
4075
+ await flushFiles(fileEnts, dirAbs, dirRel, effectiveLayers, emit);
4140
4076
  }
4141
4077
  };
4142
4078
  await walk2(rootAbs, "", []);
4143
- return out;
4079
+ reportProgress(true);
4080
+ return { scanned, cancelled: !!opts.signal?.aborted };
4144
4081
  }
4145
- async function statBatch(ents, dirAbs, dirRel, out, maxResults, layers) {
4146
- const accepted = [];
4147
- for (const e of ents) {
4148
- if (out.length + accepted.length >= maxResults) break;
4149
- if (ignoredByLayers(layers, join5(dirAbs, e.name), false)) continue;
4150
- accepted.push(e);
4151
- }
4082
+ async function flushFiles(ents, dirAbs, dirRel, layers, emit) {
4083
+ const accepted = ents.filter((e) => !ignoredByLayers(layers, join4(dirAbs, e.name), false));
4152
4084
  const stats = await Promise.all(
4153
4085
  accepted.map(
4154
- (e) => stat(join5(dirAbs, e.name)).then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() })).catch(() => null)
4086
+ (e) => stat(join4(dirAbs, e.name)).then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() })).catch(() => null)
4155
4087
  )
4156
4088
  );
4157
4089
  for (let i = 0; i < accepted.length; i++) {
4158
4090
  const ent = accepted[i];
4159
4091
  const s = stats[i];
4160
- if (ent.isSymbolicLink()) {
4161
- if (!s || !s.isFile) continue;
4092
+ if (ent.isSymbolicLink() && (!s || !s.isFile)) continue;
4093
+ emit({
4094
+ path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
4095
+ mtimeMs: s?.mtimeMs ?? 0
4096
+ });
4097
+ }
4098
+ }
4099
+ async function listDirectory(root, relDir, opts = {}) {
4100
+ const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
4101
+ const respectGi = opts.respectGitignore !== false;
4102
+ const rootAbs = resolve2(root);
4103
+ const dirAbs = resolve2(rootAbs, relDir);
4104
+ const rel = relative5(rootAbs, dirAbs);
4105
+ if (rel.startsWith("..") || isAbsolute2(rel)) return [];
4106
+ const layers = [];
4107
+ if (respectGi) {
4108
+ const segs = rel ? rel.split(/[\\/]/) : [];
4109
+ let cursor = rootAbs;
4110
+ const ig = await loadGitignoreAt(cursor);
4111
+ if (ig) layers.push({ dirAbs: cursor, ig });
4112
+ for (const seg of segs) {
4113
+ cursor = join4(cursor, seg);
4114
+ const igSeg = await loadGitignoreAt(cursor);
4115
+ if (igSeg) layers.push({ dirAbs: cursor, ig: igSeg });
4116
+ }
4117
+ }
4118
+ let raw;
4119
+ try {
4120
+ raw = await readdir(dirAbs, { withFileTypes: true });
4121
+ } catch {
4122
+ return [];
4123
+ }
4124
+ const dirRel = rel.split(/[\\/]/).join("/");
4125
+ const dirs = [];
4126
+ const files = [];
4127
+ for (const ent of raw) {
4128
+ const absPath = join4(dirAbs, ent.name);
4129
+ if (ent.isDirectory()) {
4130
+ if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
4131
+ if (ignoredByLayers(layers, absPath, true)) continue;
4132
+ dirs.push({
4133
+ name: ent.name,
4134
+ path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
4135
+ isDir: true,
4136
+ mtimeMs: 0
4137
+ });
4138
+ } else if (ent.isFile() || ent.isSymbolicLink()) {
4139
+ if (ignoredByLayers(layers, absPath, false)) continue;
4140
+ files.push(ent);
4162
4141
  }
4163
- out.push({
4142
+ }
4143
+ const stats = await Promise.all(
4144
+ files.map(
4145
+ (e) => stat(join4(dirAbs, e.name)).then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() })).catch(() => null)
4146
+ )
4147
+ );
4148
+ const fileEntries = [];
4149
+ for (let i = 0; i < files.length; i++) {
4150
+ const ent = files[i];
4151
+ const s = stats[i];
4152
+ if (ent.isSymbolicLink() && (!s || !s.isFile)) continue;
4153
+ fileEntries.push({
4154
+ name: ent.name,
4164
4155
  path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
4156
+ isDir: false,
4165
4157
  mtimeMs: s?.mtimeMs ?? 0
4166
4158
  });
4167
4159
  }
4160
+ dirs.sort((a, b) => a.name.localeCompare(b.name));
4161
+ fileEntries.sort((a, b) => a.name.localeCompare(b.name));
4162
+ return [...dirs, ...fileEntries];
4163
+ }
4164
+ function parseAtQuery(query) {
4165
+ const normalized = query.replace(/\\/g, "/");
4166
+ const trailingSlash = normalized.endsWith("/");
4167
+ const trimmed = trailingSlash ? normalized.slice(0, -1) : normalized;
4168
+ const lastSlash = trimmed.lastIndexOf("/");
4169
+ if (trailingSlash) return { dir: trimmed, filter: "", trailingSlash: true };
4170
+ if (lastSlash < 0) return { dir: "", filter: trimmed, trailingSlash: false };
4171
+ return {
4172
+ dir: trimmed.slice(0, lastSlash),
4173
+ filter: trimmed.slice(lastSlash + 1),
4174
+ trailingSlash: false
4175
+ };
4168
4176
  }
4169
4177
  var AT_PICKER_PREFIX = /(?:^|\s)@([a-zA-Z0-9_./\\-]*)$/;
4170
4178
  function detectAtPicker(input) {
@@ -4342,7 +4350,7 @@ function readSafe(root, rawPath, fs5) {
4342
4350
  }
4343
4351
  }
4344
4352
  var defaultFs = {
4345
- exists: (p) => existsSync2(p),
4353
+ exists: (p) => existsSync(p),
4346
4354
  isFile: (p) => {
4347
4355
  try {
4348
4356
  return statSync(p).isFile();
@@ -4375,110 +4383,8 @@ var defaultFs = {
4375
4383
  return 0;
4376
4384
  }
4377
4385
  },
4378
- read: (p) => readFileSync2(p, "utf8")
4386
+ read: (p) => readFileSync(p, "utf8")
4379
4387
  };
4380
- var AT_URL_PATTERN = /(?<=^|\s)@(https?:\/\/\S+)/g;
4381
- var DEFAULT_AT_URL_MAX_CHARS = 32e3;
4382
- async function expandAtUrls(text, opts = {}) {
4383
- const maxChars = opts.maxChars ?? DEFAULT_AT_URL_MAX_CHARS;
4384
- const fetcher = opts.fetcher;
4385
- if (!fetcher) {
4386
- throw new Error("expandAtUrls: fetcher option is required (wire src/tools/web.ts:webFetch)");
4387
- }
4388
- const seen = /* @__PURE__ */ new Map();
4389
- const bodies = /* @__PURE__ */ new Map();
4390
- const order = [];
4391
- for (const match of text.matchAll(AT_URL_PATTERN)) {
4392
- const rawUrl = match[1] ?? "";
4393
- const url = stripUrlTail(rawUrl);
4394
- if (!url) continue;
4395
- if (seen.has(url)) continue;
4396
- const cached2 = opts.cache?.get(url);
4397
- if (cached2) {
4398
- seen.set(url, cached2);
4399
- if (cached2.body) bodies.set(url, cached2.body);
4400
- order.push(url);
4401
- continue;
4402
- }
4403
- let expansion;
4404
- let body = "";
4405
- try {
4406
- const page = await fetcher(url, {
4407
- maxChars,
4408
- timeoutMs: opts.timeoutMs,
4409
- signal: opts.signal
4410
- });
4411
- body = page.text;
4412
- expansion = {
4413
- token: `@${url}`,
4414
- url,
4415
- ok: true,
4416
- title: page.title,
4417
- chars: body.length,
4418
- truncated: page.truncated
4419
- };
4420
- } catch (err) {
4421
- const message = err.message ?? String(err);
4422
- let skip = "fetch-error";
4423
- if (/aborted|timeout/i.test(message)) skip = "timeout";
4424
- else if (/40\d|forbidden|access denied|captcha/i.test(message)) skip = "blocked";
4425
- expansion = {
4426
- token: `@${url}`,
4427
- url,
4428
- ok: false,
4429
- skip,
4430
- error: message
4431
- };
4432
- }
4433
- seen.set(url, expansion);
4434
- if (body) bodies.set(url, body);
4435
- if (opts.cache) opts.cache.set(url, { ...expansion, body });
4436
- order.push(url);
4437
- }
4438
- if (seen.size === 0) return { text, expansions: [] };
4439
- const expansions = order.map((u) => seen.get(u)).filter(Boolean);
4440
- const blocks = [];
4441
- for (const ex of expansions) {
4442
- if (ex.ok) {
4443
- const titleAttr = ex.title ? ` title="${escapeAttr(ex.title)}"` : "";
4444
- const truncTag = ex.truncated ? ' truncated="true"' : "";
4445
- const body = bodies.get(ex.url) ?? "";
4446
- blocks.push(`<url href="${ex.url}"${titleAttr}${truncTag}>
4447
- ${body}
4448
- </url>`);
4449
- } else {
4450
- const reasonAttr = ex.skip ?? "fetch-error";
4451
- blocks.push(`<url href="${ex.url}" skipped="${reasonAttr}" />`);
4452
- }
4453
- }
4454
- const augmented = `${text}
4455
-
4456
- [Referenced URLs]
4457
- ${blocks.join("\n\n")}`;
4458
- return { text: augmented, expansions };
4459
- }
4460
- function stripUrlTail(raw) {
4461
- let s = raw;
4462
- while (s.length > 0) {
4463
- const last = s[s.length - 1];
4464
- if (".,;:!?".includes(last)) {
4465
- s = s.slice(0, -1);
4466
- continue;
4467
- }
4468
- if (")]}>".includes(last)) {
4469
- const open = { ")": "(", "]": "[", "}": "{", ">": "<" }[last];
4470
- if (!s.includes(open)) {
4471
- s = s.slice(0, -1);
4472
- continue;
4473
- }
4474
- }
4475
- break;
4476
- }
4477
- return s;
4478
- }
4479
- function escapeAttr(s) {
4480
- return s.replace(/"/g, "&quot;").replace(/[\r\n]+/g, " ").trim();
4481
- }
4482
4388
 
4483
4389
  // src/tools/subagent-types.ts
4484
4390
  var EXPLORE_SYSTEM = `You are an exploration subagent. Wide-net read-only investigation; return one distilled answer.
@@ -4770,18 +4676,18 @@ function forkRegistryWithAllowList(parent, allow, alsoExclude) {
4770
4676
  // src/code/edit-blocks.ts
4771
4677
  import {
4772
4678
  closeSync,
4773
- existsSync as existsSync3,
4679
+ existsSync as existsSync2,
4774
4680
  fstatSync,
4775
4681
  ftruncateSync,
4776
4682
  mkdirSync,
4777
4683
  openSync,
4778
- readFileSync as readFileSync3,
4684
+ readFileSync as readFileSync2,
4779
4685
  readSync,
4780
4686
  unlinkSync,
4781
4687
  writeFileSync,
4782
4688
  writeSync
4783
4689
  } from "fs";
4784
- import { dirname as dirname3, resolve as resolve3 } from "path";
4690
+ import { dirname as dirname2, resolve as resolve3 } from "path";
4785
4691
  var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
4786
4692
  function parseEditBlocks(text) {
4787
4693
  const out = [];
@@ -4811,7 +4717,7 @@ function applyEditBlock(block, rootDir) {
4811
4717
  const searchEmpty = block.search.length === 0;
4812
4718
  if (searchEmpty) {
4813
4719
  try {
4814
- mkdirSync(dirname3(absTarget), { recursive: true });
4720
+ mkdirSync(dirname2(absTarget), { recursive: true });
4815
4721
  const fd = openSync(absTarget, "wx");
4816
4722
  try {
4817
4723
  writeSync(fd, block.replace);
@@ -4889,9 +4795,9 @@ function applyEditBlocks(blocks, rootDir) {
4889
4795
  function toWholeFileEditBlock(path, content, rootDir) {
4890
4796
  const abs = resolve3(rootDir, path);
4891
4797
  let search = "";
4892
- if (existsSync3(abs)) {
4798
+ if (existsSync2(abs)) {
4893
4799
  try {
4894
- search = readFileSync3(abs, "utf8");
4800
+ search = readFileSync2(abs, "utf8");
4895
4801
  } catch {
4896
4802
  search = "";
4897
4803
  }
@@ -4906,12 +4812,12 @@ function snapshotBeforeEdits(blocks, rootDir) {
4906
4812
  if (seen.has(b.path)) continue;
4907
4813
  seen.add(b.path);
4908
4814
  const abs = resolve3(absRoot, b.path);
4909
- if (!existsSync3(abs)) {
4815
+ if (!existsSync2(abs)) {
4910
4816
  snapshots.push({ path: b.path, prevContent: null });
4911
4817
  continue;
4912
4818
  }
4913
4819
  try {
4914
- snapshots.push({ path: b.path, prevContent: readFileSync3(abs, "utf8") });
4820
+ snapshots.push({ path: b.path, prevContent: readFileSync2(abs, "utf8") });
4915
4821
  } catch {
4916
4822
  snapshots.push({ path: b.path, prevContent: null });
4917
4823
  }
@@ -4931,7 +4837,7 @@ function restoreSnapshots(snapshots, rootDir) {
4931
4837
  }
4932
4838
  try {
4933
4839
  if (snap.prevContent === null) {
4934
- if (existsSync3(abs)) unlinkSync(abs);
4840
+ if (existsSync2(abs)) unlinkSync(abs);
4935
4841
  return {
4936
4842
  path: snap.path,
4937
4843
  status: "applied",
@@ -4957,17 +4863,18 @@ function lineEndingOf(text) {
4957
4863
  }
4958
4864
 
4959
4865
  export {
4960
- countTokens,
4961
4866
  ToolRegistry,
4962
4867
  registerSingleMcpTool,
4963
4868
  bridgeMcpTools,
4964
4869
  ImmutablePrefix,
4965
4870
  CacheFirstLoop,
4966
- listFilesWithStatsAsync,
4871
+ expandAtUrls,
4872
+ walkFilesStream,
4873
+ listDirectory,
4874
+ parseAtQuery,
4967
4875
  detectAtPicker,
4968
4876
  rankPickerCandidates,
4969
4877
  expandAtMentions,
4970
- expandAtUrls,
4971
4878
  registerFilesystemTools,
4972
4879
  registerMemoryTools,
4973
4880
  registerChoiceTool,
@@ -4983,4 +4890,4 @@ export {
4983
4890
  snapshotBeforeEdits,
4984
4891
  restoreSnapshots
4985
4892
  };
4986
- //# sourceMappingURL=chunk-Q6YFXW7H.js.map
4893
+ //# sourceMappingURL=chunk-LNTORE5K.js.map