kimiflare 0.65.0 → 0.67.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.
- package/dist/index.js +145 -39
- package/dist/index.js.map +1 -1
- package/dist/sdk/index.js +127 -36
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -401,11 +401,12 @@ var init_logger = __esm({
|
|
|
401
401
|
});
|
|
402
402
|
|
|
403
403
|
// src/util/sse.ts
|
|
404
|
-
async function* readSSE(stream, signal, idleTimeoutMs) {
|
|
404
|
+
async function* readSSE(stream, signal, idleTimeoutMs, postFirstByteIdleTimeoutMs) {
|
|
405
405
|
const reader = stream.getReader();
|
|
406
406
|
const decoder = new TextDecoder("utf-8");
|
|
407
407
|
let buffer = "";
|
|
408
408
|
let lastDataAt = Date.now();
|
|
409
|
+
let gotFirstByte = false;
|
|
409
410
|
const onAbort = () => {
|
|
410
411
|
reader.cancel(new DOMException("aborted", "AbortError")).catch(() => {
|
|
411
412
|
});
|
|
@@ -427,16 +428,18 @@ async function* readSSE(stream, signal, idleTimeoutMs) {
|
|
|
427
428
|
try {
|
|
428
429
|
while (true) {
|
|
429
430
|
if (signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
430
|
-
|
|
431
|
-
|
|
431
|
+
const activeIdleTimeoutMs = gotFirstByte && postFirstByteIdleTimeoutMs !== void 0 ? postFirstByteIdleTimeoutMs : idleTimeoutMs;
|
|
432
|
+
if (activeIdleTimeoutMs !== void 0 && Date.now() - lastDataAt > activeIdleTimeoutMs) {
|
|
433
|
+
logger.warn("sse:idle_timeout", { idleTimeoutMs: activeIdleTimeoutMs, gotFirstByte });
|
|
432
434
|
throw new DOMException(
|
|
433
|
-
`kimiflare: stream idle for ${
|
|
435
|
+
`kimiflare: stream idle for ${activeIdleTimeoutMs}ms \u2014 no data received from API`,
|
|
434
436
|
"TimeoutError"
|
|
435
437
|
);
|
|
436
438
|
}
|
|
437
439
|
const { done, value } = await abortRace(reader.read());
|
|
438
440
|
if (done) break;
|
|
439
441
|
lastDataAt = Date.now();
|
|
442
|
+
gotFirstByte = true;
|
|
440
443
|
buffer += decoder.decode(value, { stream: true });
|
|
441
444
|
buffer = buffer.replace(/\r\n/g, "\n");
|
|
442
445
|
let sep3;
|
|
@@ -717,7 +720,7 @@ async function* runKimi(opts2) {
|
|
|
717
720
|
if (meta) yield { type: "gateway_meta", meta };
|
|
718
721
|
let lastUsage = null;
|
|
719
722
|
logger.debug("runKimi:stream_start", { requestId });
|
|
720
|
-
for await (const ev of parseStream(res.body, opts2.signal, opts2.idleTimeoutMs)) {
|
|
723
|
+
for await (const ev of parseStream(res.body, opts2.signal, opts2.idleTimeoutMs, opts2.postFirstByteIdleTimeoutMs)) {
|
|
721
724
|
if (ev.type === "usage") lastUsage = ev.usage;
|
|
722
725
|
yield ev;
|
|
723
726
|
}
|
|
@@ -799,12 +802,11 @@ function readGatewayMeta(headers) {
|
|
|
799
802
|
if (model) meta.model = model;
|
|
800
803
|
return Object.keys(meta).length > 0 ? meta : null;
|
|
801
804
|
}
|
|
802
|
-
async function* parseStream(body, signal, idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS) {
|
|
805
|
+
async function* parseStream(body, signal, idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS, postFirstByteIdleTimeoutMs = DEFAULT_POST_FIRST_BYTE_IDLE_TIMEOUT_MS) {
|
|
803
806
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
804
807
|
let lastUsage = null;
|
|
805
808
|
let finishReason = null;
|
|
806
|
-
|
|
807
|
-
for await (const dataStr of readSSE(body, signal, idleTimeoutMs)) {
|
|
809
|
+
for await (const dataStr of readSSE(body, signal, idleTimeoutMs, postFirstByteIdleTimeoutMs)) {
|
|
808
810
|
if (dataStr === "[DONE]") break;
|
|
809
811
|
let chunk = null;
|
|
810
812
|
try {
|
|
@@ -942,7 +944,7 @@ function sleep(ms, signal) {
|
|
|
942
944
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
943
945
|
});
|
|
944
946
|
}
|
|
945
|
-
var RETRYABLE_CODES, MAX_ATTEMPTS, DEFAULT_IDLE_TIMEOUT_MS;
|
|
947
|
+
var RETRYABLE_CODES, MAX_ATTEMPTS, DEFAULT_IDLE_TIMEOUT_MS, DEFAULT_POST_FIRST_BYTE_IDLE_TIMEOUT_MS;
|
|
946
948
|
var init_client = __esm({
|
|
947
949
|
"src/agent/client.ts"() {
|
|
948
950
|
"use strict";
|
|
@@ -954,6 +956,7 @@ var init_client = __esm({
|
|
|
954
956
|
RETRYABLE_CODES = /* @__PURE__ */ new Set([3040]);
|
|
955
957
|
MAX_ATTEMPTS = 5;
|
|
956
958
|
DEFAULT_IDLE_TIMEOUT_MS = 6e4;
|
|
959
|
+
DEFAULT_POST_FIRST_BYTE_IDLE_TIMEOUT_MS = 3e4;
|
|
957
960
|
}
|
|
958
961
|
});
|
|
959
962
|
|
|
@@ -2155,7 +2158,7 @@ var init_session_state = __esm({
|
|
|
2155
2158
|
}
|
|
2156
2159
|
add(a) {
|
|
2157
2160
|
while (this.totalChars() + a.raw.length > this.maxTotalChars && this.artifacts.size > 0) {
|
|
2158
|
-
this.
|
|
2161
|
+
this.evictSizeWeighted();
|
|
2159
2162
|
}
|
|
2160
2163
|
while (this.artifacts.size >= this.maxArtifacts) {
|
|
2161
2164
|
this.evictOldest();
|
|
@@ -2196,6 +2199,21 @@ var init_session_state = __esm({
|
|
|
2196
2199
|
}
|
|
2197
2200
|
if (oldest) this.artifacts.delete(oldest.id);
|
|
2198
2201
|
}
|
|
2202
|
+
/** Evict the largest artifact among the oldest quartile (by timestamp).
|
|
2203
|
+
* Bounded by the oldest quartile so we never evict freshly-added artifacts;
|
|
2204
|
+
* size-weighted within that window so one big artifact gets dropped instead
|
|
2205
|
+
* of many small ones. */
|
|
2206
|
+
evictSizeWeighted() {
|
|
2207
|
+
const sorted = [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
|
|
2208
|
+
if (sorted.length === 0) return;
|
|
2209
|
+
const quartile = Math.max(1, Math.ceil(sorted.length / 4));
|
|
2210
|
+
const candidates = sorted.slice(0, quartile);
|
|
2211
|
+
let pick3 = candidates[0];
|
|
2212
|
+
for (const a of candidates) {
|
|
2213
|
+
if (a.raw.length > pick3.raw.length) pick3 = a;
|
|
2214
|
+
}
|
|
2215
|
+
this.artifacts.delete(pick3.id);
|
|
2216
|
+
}
|
|
2199
2217
|
};
|
|
2200
2218
|
}
|
|
2201
2219
|
});
|
|
@@ -3132,6 +3150,15 @@ var init_system_prompt = __esm({
|
|
|
3132
3150
|
});
|
|
3133
3151
|
|
|
3134
3152
|
// src/agent/loop.ts
|
|
3153
|
+
function getSessionWebFetchHistory(sessionId) {
|
|
3154
|
+
const key = sessionId ?? "default";
|
|
3155
|
+
let arr = sessionWebFetchHistory.get(key);
|
|
3156
|
+
if (!arr) {
|
|
3157
|
+
arr = [];
|
|
3158
|
+
sessionWebFetchHistory.set(key, arr);
|
|
3159
|
+
}
|
|
3160
|
+
return arr;
|
|
3161
|
+
}
|
|
3135
3162
|
function isHighSignalMemory(memory) {
|
|
3136
3163
|
return memory.topicKey === "project_dependencies" || memory.topicKey === "project_tsconfig" || memory.topicKey === "project_entry_point" || memory.category === "instruction" || memory.category === "preference" || memory.category === "event" && memory.importance >= 3;
|
|
3137
3164
|
}
|
|
@@ -3286,7 +3313,8 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3286
3313
|
const recentToolCalls = [];
|
|
3287
3314
|
const LOOP_WINDOW = 8;
|
|
3288
3315
|
const LOOP_THRESHOLD = 2;
|
|
3289
|
-
const webFetchHistory =
|
|
3316
|
+
const webFetchHistory = getSessionWebFetchHistory(opts2.sessionId);
|
|
3317
|
+
let webFetchesThisTurn = 0;
|
|
3290
3318
|
const MAX_WEB_FETCH_PER_TURN = 5;
|
|
3291
3319
|
const WEB_FETCH_DOMAIN_THRESHOLD = 2;
|
|
3292
3320
|
let cumulativePromptTokens = 0;
|
|
@@ -3404,7 +3432,8 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3404
3432
|
cloudMode: opts2.cloudMode,
|
|
3405
3433
|
cloudToken: opts2.cloudToken,
|
|
3406
3434
|
cloudDeviceId: opts2.cloudDeviceId,
|
|
3407
|
-
idleTimeoutMs: 6e4
|
|
3435
|
+
idleTimeoutMs: opts2.idleTimeoutMs ?? 6e4,
|
|
3436
|
+
postFirstByteIdleTimeoutMs: opts2.postFirstByteIdleTimeoutMs
|
|
3408
3437
|
});
|
|
3409
3438
|
let gotFirstChunk = false;
|
|
3410
3439
|
for await (const ev of events) {
|
|
@@ -3454,7 +3483,7 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3454
3483
|
if (lastUsage) {
|
|
3455
3484
|
opts2.callbacks.onUsageFinal?.(lastUsage, gatewayMeta);
|
|
3456
3485
|
cumulativePromptTokens += lastUsage.prompt_tokens;
|
|
3457
|
-
if (!budgetExhausted && opts2.maxInputTokens !== void 0 && opts2.maxInputTokens > 0 && cumulativePromptTokens >= opts2.maxInputTokens
|
|
3486
|
+
if (!budgetExhausted && opts2.maxInputTokens !== void 0 && opts2.maxInputTokens > 0 && cumulativePromptTokens >= opts2.maxInputTokens) {
|
|
3458
3487
|
budgetExhausted = true;
|
|
3459
3488
|
}
|
|
3460
3489
|
}
|
|
@@ -3524,8 +3553,8 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3524
3553
|
try {
|
|
3525
3554
|
const domain = new URL(url).hostname;
|
|
3526
3555
|
const domainCount = webFetchHistory.filter((h) => h.domain === domain).length;
|
|
3527
|
-
const
|
|
3528
|
-
if (
|
|
3556
|
+
const totalSessionFetches = webFetchHistory.length;
|
|
3557
|
+
if (webFetchesThisTurn >= MAX_WEB_FETCH_PER_TURN) {
|
|
3529
3558
|
const warning = `Research budget exceeded: you have already made ${MAX_WEB_FETCH_PER_TURN} web requests this turn. Synthesize what you have learned instead of fetching more pages.`;
|
|
3530
3559
|
const budgetResult = {
|
|
3531
3560
|
tool_call_id: tc.id,
|
|
@@ -3546,6 +3575,27 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3546
3575
|
blockedCount++;
|
|
3547
3576
|
continue;
|
|
3548
3577
|
}
|
|
3578
|
+
if (totalSessionFetches >= SESSION_WEB_FETCH_CAP) {
|
|
3579
|
+
const warning = `Session research budget exceeded: ${totalSessionFetches} web fetches across this session. Synthesize what you have learned from prior fetches instead of starting another page.`;
|
|
3580
|
+
const sessionCapResult = {
|
|
3581
|
+
tool_call_id: tc.id,
|
|
3582
|
+
name: "web_fetch",
|
|
3583
|
+
content: warning,
|
|
3584
|
+
ok: false
|
|
3585
|
+
};
|
|
3586
|
+
toolResults.push(sessionCapResult);
|
|
3587
|
+
opts2.messages.push({
|
|
3588
|
+
role: "tool",
|
|
3589
|
+
tool_call_id: tc.id,
|
|
3590
|
+
content: sanitizeString(warning),
|
|
3591
|
+
name: "web_fetch"
|
|
3592
|
+
});
|
|
3593
|
+
opts2.callbacks.onToolResult?.(sessionCapResult);
|
|
3594
|
+
recentToolCalls.push(loopSignature);
|
|
3595
|
+
if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
|
|
3596
|
+
blockedCount++;
|
|
3597
|
+
continue;
|
|
3598
|
+
}
|
|
3549
3599
|
if (domainCount >= WEB_FETCH_DOMAIN_THRESHOLD) {
|
|
3550
3600
|
const warning = `Loop detected: you have fetched from ${domain} multiple times. Consider a different approach or synthesize existing findings.`;
|
|
3551
3601
|
const loopResult = {
|
|
@@ -3568,6 +3618,7 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3568
3618
|
continue;
|
|
3569
3619
|
}
|
|
3570
3620
|
webFetchHistory.push({ url, domain });
|
|
3621
|
+
webFetchesThisTurn++;
|
|
3571
3622
|
} catch {
|
|
3572
3623
|
}
|
|
3573
3624
|
}
|
|
@@ -3603,9 +3654,16 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
3603
3654
|
Output:
|
|
3604
3655
|
${sandboxResult.output}` : sandboxResult.output;
|
|
3605
3656
|
if (resultContent.length > MAX_TOOL_CONTENT_CHARS) {
|
|
3657
|
+
const rawBytes = resultContent.length;
|
|
3606
3658
|
resultContent = resultContent.slice(0, MAX_TOOL_CONTENT_CHARS) + `
|
|
3607
3659
|
|
|
3608
|
-
[truncated: ${
|
|
3660
|
+
[truncated: ${rawBytes - MAX_TOOL_CONTENT_CHARS} chars omitted]`;
|
|
3661
|
+
opts2.callbacks.onTruncation?.({
|
|
3662
|
+
tool: "execute_code",
|
|
3663
|
+
toolCallId: tc.id,
|
|
3664
|
+
rawBytes,
|
|
3665
|
+
reducedBytes: resultContent.length
|
|
3666
|
+
});
|
|
3609
3667
|
}
|
|
3610
3668
|
const result = {
|
|
3611
3669
|
tool_call_id: tc.id,
|
|
@@ -3634,9 +3692,17 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
3634
3692
|
);
|
|
3635
3693
|
let content2 = result.content;
|
|
3636
3694
|
if (content2.length > MAX_TOOL_CONTENT_CHARS) {
|
|
3695
|
+
const rawBytes = content2.length;
|
|
3637
3696
|
content2 = content2.slice(0, MAX_TOOL_CONTENT_CHARS) + `
|
|
3638
3697
|
|
|
3639
|
-
[truncated: ${
|
|
3698
|
+
[truncated: ${rawBytes - MAX_TOOL_CONTENT_CHARS} chars omitted]`;
|
|
3699
|
+
opts2.callbacks.onTruncation?.({
|
|
3700
|
+
tool: tc.function.name,
|
|
3701
|
+
toolCallId: tc.id,
|
|
3702
|
+
rawBytes,
|
|
3703
|
+
reducedBytes: content2.length,
|
|
3704
|
+
artifactId: result.artifactId
|
|
3705
|
+
});
|
|
3640
3706
|
}
|
|
3641
3707
|
logger.debug("turn:tool_end", { sessionId: opts2.sessionId, tool: tc.function.name, toolCallId: tc.id, ok: result.ok });
|
|
3642
3708
|
toolResults.push(result);
|
|
@@ -3660,6 +3726,7 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
3660
3726
|
);
|
|
3661
3727
|
const assistantMessage = lastAssistant?.content ?? "";
|
|
3662
3728
|
const llmOpts = opts2.memoryManager.getExtractionLlmOpts();
|
|
3729
|
+
const turnAtMemoryCommit = turn;
|
|
3663
3730
|
for (const extractor of EXTRACTORS) {
|
|
3664
3731
|
if (extractor.match(tc.function.name, filePath)) {
|
|
3665
3732
|
void (async () => {
|
|
@@ -3685,15 +3752,33 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
3685
3752
|
);
|
|
3686
3753
|
if (isHighSignalMemory(memory)) {
|
|
3687
3754
|
const sid = opts2.sessionId ?? "default";
|
|
3688
|
-
const
|
|
3689
|
-
|
|
3690
|
-
|
|
3755
|
+
const events2 = driftEvents.get(sid) ?? [];
|
|
3756
|
+
events2.push(turnAtMemoryCommit);
|
|
3757
|
+
const cutoff = turnAtMemoryCommit - DRIFT_WINDOW + 1;
|
|
3758
|
+
const recent = events2.filter((t) => t >= cutoff);
|
|
3759
|
+
driftEvents.set(sid, recent);
|
|
3760
|
+
if (recent.length >= DRIFT_THRESHOLD) {
|
|
3691
3761
|
opts2.callbacks.onKimiMdStale?.();
|
|
3692
|
-
|
|
3762
|
+
driftEvents.set(sid, []);
|
|
3693
3763
|
}
|
|
3694
3764
|
}
|
|
3695
3765
|
}
|
|
3696
|
-
} catch {
|
|
3766
|
+
} catch (err) {
|
|
3767
|
+
const sid = opts2.sessionId ?? "default";
|
|
3768
|
+
const next = (memoryExtractionErrorCounts.get(sid) ?? 0) + 1;
|
|
3769
|
+
memoryExtractionErrorCounts.set(sid, next);
|
|
3770
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3771
|
+
logger.debug("memory:extract_error", {
|
|
3772
|
+
sessionId: opts2.sessionId,
|
|
3773
|
+
tool: tc.function.name,
|
|
3774
|
+
count: next,
|
|
3775
|
+
error: msg
|
|
3776
|
+
});
|
|
3777
|
+
if (next === 1) {
|
|
3778
|
+
opts2.callbacks.onWarning?.(
|
|
3779
|
+
`[memory] auto-extraction failed (${msg}). Subsequent failures will be counted silently; check /memory health.`
|
|
3780
|
+
);
|
|
3781
|
+
}
|
|
3697
3782
|
}
|
|
3698
3783
|
})();
|
|
3699
3784
|
}
|
|
@@ -3706,12 +3791,6 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
3706
3791
|
if (blockedCount === toolCalls.length && toolCalls.length > 0) {
|
|
3707
3792
|
loopExhausted = true;
|
|
3708
3793
|
}
|
|
3709
|
-
if (opts2.sessionId) {
|
|
3710
|
-
const current = driftAccumulator.get(opts2.sessionId) ?? 0;
|
|
3711
|
-
if (current > 0) {
|
|
3712
|
-
driftAccumulator.set(opts2.sessionId, Math.max(0, current - 1));
|
|
3713
|
-
}
|
|
3714
|
-
}
|
|
3715
3794
|
if (opts2.onIterationEnd) {
|
|
3716
3795
|
opts2.messages = await opts2.onIterationEnd(opts2.messages, opts2.signal);
|
|
3717
3796
|
if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
@@ -3770,7 +3849,7 @@ function validateToolArguments(raw) {
|
|
|
3770
3849
|
return "{}";
|
|
3771
3850
|
}
|
|
3772
3851
|
}
|
|
3773
|
-
var BudgetExhaustedError, AgentLoopError, codeModeApiCache,
|
|
3852
|
+
var BudgetExhaustedError, AgentLoopError, codeModeApiCache, driftEvents, DRIFT_WINDOW, DRIFT_THRESHOLD, memoryExtractionErrorCounts, sessionWebFetchHistory, SESSION_WEB_FETCH_CAP, MAX_PROMPT_TOKENS, MAX_TOOL_CONTENT_CHARS;
|
|
3774
3853
|
var init_loop = __esm({
|
|
3775
3854
|
"src/agent/loop.ts"() {
|
|
3776
3855
|
"use strict";
|
|
@@ -3798,8 +3877,12 @@ var init_loop = __esm({
|
|
|
3798
3877
|
}
|
|
3799
3878
|
};
|
|
3800
3879
|
codeModeApiCache = /* @__PURE__ */ new Map();
|
|
3801
|
-
|
|
3802
|
-
|
|
3880
|
+
driftEvents = /* @__PURE__ */ new Map();
|
|
3881
|
+
DRIFT_WINDOW = 10;
|
|
3882
|
+
DRIFT_THRESHOLD = 3;
|
|
3883
|
+
memoryExtractionErrorCounts = /* @__PURE__ */ new Map();
|
|
3884
|
+
sessionWebFetchHistory = /* @__PURE__ */ new Map();
|
|
3885
|
+
SESSION_WEB_FETCH_CAP = 25;
|
|
3803
3886
|
MAX_PROMPT_TOKENS = 24e4;
|
|
3804
3887
|
MAX_TOOL_CONTENT_CHARS = 1e4;
|
|
3805
3888
|
}
|
|
@@ -3867,10 +3950,13 @@ var init_read = __esm({
|
|
|
3867
3950
|
needsPermission: false,
|
|
3868
3951
|
render: ({ path }) => ({ title: `read ${collapsePath(path, process.cwd())}` }),
|
|
3869
3952
|
async run(args, ctx) {
|
|
3953
|
+
if (ctx.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
3870
3954
|
const abs = resolvePath(ctx.cwd, args.path);
|
|
3871
3955
|
const st = await stat2(abs);
|
|
3872
3956
|
if (st.size > MAX_BYTES) throw new Error(`file too large: ${st.size} bytes (max ${MAX_BYTES})`);
|
|
3873
|
-
|
|
3957
|
+
if (ctx.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
3958
|
+
const text = await readFile3(abs, { encoding: "utf8", signal: ctx.signal });
|
|
3959
|
+
if (ctx.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
3874
3960
|
const lines = text.split("\n");
|
|
3875
3961
|
const start = Math.max(0, (args.offset ?? 1) - 1);
|
|
3876
3962
|
const end = args.limit ? Math.min(lines.length, start + args.limit) : lines.length;
|
|
@@ -4162,14 +4248,31 @@ var init_glob = __esm({
|
|
|
4162
4248
|
needsPermission: false,
|
|
4163
4249
|
render: (args) => ({ title: `glob ${args.pattern ?? ""}${args.path ? ` in ${collapsePath(String(args.path), process.cwd())}` : ""}` }),
|
|
4164
4250
|
async run(args, ctx) {
|
|
4251
|
+
if (ctx.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
4165
4252
|
const root = args.path ? resolvePath(ctx.cwd, args.path) : ctx.cwd;
|
|
4166
|
-
const
|
|
4253
|
+
const stream = fg.stream(args.pattern, {
|
|
4167
4254
|
cwd: root,
|
|
4168
4255
|
absolute: true,
|
|
4169
4256
|
dot: false,
|
|
4170
4257
|
onlyFiles: false,
|
|
4171
4258
|
stats: true
|
|
4172
4259
|
});
|
|
4260
|
+
const entries = [];
|
|
4261
|
+
const onAbort = () => {
|
|
4262
|
+
try {
|
|
4263
|
+
stream.destroy(new DOMException("aborted", "AbortError"));
|
|
4264
|
+
} catch {
|
|
4265
|
+
}
|
|
4266
|
+
};
|
|
4267
|
+
ctx.signal?.addEventListener("abort", onAbort, { once: true });
|
|
4268
|
+
try {
|
|
4269
|
+
for await (const entry of stream) {
|
|
4270
|
+
if (ctx.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
4271
|
+
entries.push(entry);
|
|
4272
|
+
}
|
|
4273
|
+
} finally {
|
|
4274
|
+
ctx.signal?.removeEventListener("abort", onAbort);
|
|
4275
|
+
}
|
|
4173
4276
|
entries.sort((a, b) => (b.stats?.mtimeMs ?? 0) - (a.stats?.mtimeMs ?? 0));
|
|
4174
4277
|
const paths = entries.slice(0, 200).map((e) => e.path);
|
|
4175
4278
|
return paths.length ? paths.join("\n") : "(no matches)";
|
|
@@ -4193,14 +4296,14 @@ async function hasRipgrep() {
|
|
|
4193
4296
|
}
|
|
4194
4297
|
return cachedHasRg;
|
|
4195
4298
|
}
|
|
4196
|
-
async function runRipgrep(args, root, mode) {
|
|
4299
|
+
async function runRipgrep(args, root, mode, signal) {
|
|
4197
4300
|
const rgArgs = ["--no-heading", "--color=never", "--line-number"];
|
|
4198
4301
|
if (args.case_insensitive) rgArgs.push("-i");
|
|
4199
4302
|
if (args.glob) rgArgs.push("--glob", args.glob);
|
|
4200
4303
|
if (mode === "files") rgArgs.push("-l");
|
|
4201
4304
|
rgArgs.push("--", args.pattern, root);
|
|
4202
4305
|
try {
|
|
4203
|
-
const { stdout } = await pExecFile("rg", rgArgs, { maxBuffer: 10 * 1024 * 1024 });
|
|
4306
|
+
const { stdout } = await pExecFile("rg", rgArgs, { maxBuffer: 10 * 1024 * 1024, signal });
|
|
4204
4307
|
const trimmed = stdout.trim();
|
|
4205
4308
|
if (!trimmed) return { content: "(no matches)", rawBytes: 0, reducedBytes: 0 };
|
|
4206
4309
|
return {
|
|
@@ -4214,7 +4317,7 @@ async function runRipgrep(args, root, mode) {
|
|
|
4214
4317
|
throw new Error(err.stderr || String(e));
|
|
4215
4318
|
}
|
|
4216
4319
|
}
|
|
4217
|
-
async function runJsFallback(args, root, mode) {
|
|
4320
|
+
async function runJsFallback(args, root, mode, signal) {
|
|
4218
4321
|
const re = new RegExp(args.pattern, args.case_insensitive ? "i" : "");
|
|
4219
4322
|
const globPattern = args.glob ? `**/${args.glob}` : "**/*";
|
|
4220
4323
|
const files = await fg2(globPattern, {
|
|
@@ -4225,7 +4328,9 @@ async function runJsFallback(args, root, mode) {
|
|
|
4225
4328
|
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"]
|
|
4226
4329
|
});
|
|
4227
4330
|
const out = [];
|
|
4228
|
-
for (
|
|
4331
|
+
for (let fi = 0; fi < Math.min(files.length, 5e3); fi++) {
|
|
4332
|
+
if (signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
4333
|
+
const file = files[fi];
|
|
4229
4334
|
try {
|
|
4230
4335
|
const content = await readFile6(file, "utf8");
|
|
4231
4336
|
if (mode === "files") {
|
|
@@ -4280,10 +4385,11 @@ var init_grep = __esm({
|
|
|
4280
4385
|
needsPermission: false,
|
|
4281
4386
|
render: (args) => ({ title: `grep ${args.pattern ?? ""}${args.glob ? ` (${args.glob})` : ""}` }),
|
|
4282
4387
|
async run(args, ctx) {
|
|
4388
|
+
if (ctx.signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
4283
4389
|
const root = args.path ? resolvePath(ctx.cwd, args.path) : ctx.cwd;
|
|
4284
4390
|
const mode = args.output_mode ?? "content";
|
|
4285
|
-
if (await hasRipgrep()) return runRipgrep(args, root, mode);
|
|
4286
|
-
return runJsFallback(args, root, mode);
|
|
4391
|
+
if (await hasRipgrep()) return runRipgrep(args, root, mode, ctx.signal);
|
|
4392
|
+
return runJsFallback(args, root, mode, ctx.signal);
|
|
4287
4393
|
}
|
|
4288
4394
|
};
|
|
4289
4395
|
}
|