@cydm/pie 1.0.17 → 1.0.19
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/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/subagent/index.js +4 -4
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/chunks/{chunk-VE2HDCNB.js → chunk-2R3XTLE2.js} +1 -1
- package/dist/chunks/{chunk-B4VUX6SD.js → chunk-GZVSE44O.js} +500 -129
- package/dist/chunks/{chunk-4MIOCDBV.js → chunk-KZ54RSWP.js} +2 -2
- package/dist/chunks/{chunk-5DA2D3K2.js → chunk-Q2N6B5JN.js} +277 -0
- package/dist/chunks/{src-6WPNVGT2.js → src-7UV3KKGU.js} +2 -2
- package/dist/chunks/{test-stream-ZSKNLUEJ.js → test-stream-PP63L7MK.js} +1 -1
- package/dist/cli.js +37 -11
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
Agent
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2R3XTLE2.js";
|
|
5
5
|
import {
|
|
6
6
|
FileSystemGateway,
|
|
7
7
|
Type,
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
detectPlatform,
|
|
10
10
|
getFileSystem,
|
|
11
11
|
getPlatformConfig,
|
|
12
|
+
hashText,
|
|
12
13
|
streamSimple
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Q2N6B5JN.js";
|
|
14
15
|
import {
|
|
15
16
|
__require
|
|
16
17
|
} from "./chunk-TG2EQLX2.js";
|
|
@@ -1995,7 +1996,7 @@ function isTransientFileError(error) {
|
|
|
1995
1996
|
return /^(EPERM|EACCES|EBUSY|ENOTEMPTY)$/.test(code) || /\b(EPERM|EACCES|EBUSY|locked|busy)\b/i.test(message);
|
|
1996
1997
|
}
|
|
1997
1998
|
function sleep(ms) {
|
|
1998
|
-
return new Promise((
|
|
1999
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1999
2000
|
}
|
|
2000
2001
|
async function retryFileOperation(operation, retryDelaysMs) {
|
|
2001
2002
|
let lastError;
|
|
@@ -3038,15 +3039,15 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
3038
3039
|
}
|
|
3039
3040
|
ensurePendingWait() {
|
|
3040
3041
|
if (this.pendingWait) return;
|
|
3041
|
-
this.pendingWait = new Promise((
|
|
3042
|
-
this.resolvePendingWait =
|
|
3042
|
+
this.pendingWait = new Promise((resolve3) => {
|
|
3043
|
+
this.resolvePendingWait = resolve3;
|
|
3043
3044
|
});
|
|
3044
3045
|
}
|
|
3045
3046
|
resolvePending() {
|
|
3046
|
-
const
|
|
3047
|
+
const resolve3 = this.resolvePendingWait;
|
|
3047
3048
|
this.pendingWait = void 0;
|
|
3048
3049
|
this.resolvePendingWait = void 0;
|
|
3049
|
-
|
|
3050
|
+
resolve3?.();
|
|
3050
3051
|
}
|
|
3051
3052
|
};
|
|
3052
3053
|
|
|
@@ -3124,29 +3125,29 @@ var AgentSessionAutoContinueController = class {
|
|
|
3124
3125
|
this.options.emit({ type: "auto_continue_scheduled", reason });
|
|
3125
3126
|
this.options.agent.followUp(message);
|
|
3126
3127
|
const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout2;
|
|
3127
|
-
await new Promise((
|
|
3128
|
+
await new Promise((resolve3) => {
|
|
3128
3129
|
this.autoContinueTimer = setTimer(() => {
|
|
3129
3130
|
this.autoContinueTimer = void 0;
|
|
3130
3131
|
if (this.options.getDisposed()) {
|
|
3131
|
-
|
|
3132
|
+
resolve3();
|
|
3132
3133
|
return;
|
|
3133
3134
|
}
|
|
3134
3135
|
void this.options.continueTurn().catch(() => {
|
|
3135
|
-
}).finally(
|
|
3136
|
+
}).finally(resolve3);
|
|
3136
3137
|
}, 0);
|
|
3137
3138
|
});
|
|
3138
3139
|
}
|
|
3139
3140
|
ensurePending() {
|
|
3140
3141
|
if (this.pendingAutoContinue) return;
|
|
3141
|
-
this.pendingAutoContinue = new Promise((
|
|
3142
|
-
this.resolvePendingAutoContinue =
|
|
3142
|
+
this.pendingAutoContinue = new Promise((resolve3) => {
|
|
3143
|
+
this.resolvePendingAutoContinue = resolve3;
|
|
3143
3144
|
});
|
|
3144
3145
|
}
|
|
3145
3146
|
resolvePending() {
|
|
3146
|
-
const
|
|
3147
|
+
const resolve3 = this.resolvePendingAutoContinue;
|
|
3147
3148
|
this.pendingAutoContinue = void 0;
|
|
3148
3149
|
this.resolvePendingAutoContinue = void 0;
|
|
3149
|
-
|
|
3150
|
+
resolve3?.();
|
|
3150
3151
|
}
|
|
3151
3152
|
};
|
|
3152
3153
|
|
|
@@ -3555,15 +3556,15 @@ var AgentSessionQueueDispatcher = class {
|
|
|
3555
3556
|
}
|
|
3556
3557
|
ensurePending() {
|
|
3557
3558
|
if (this.pendingDispatch) return;
|
|
3558
|
-
this.pendingDispatch = new Promise((
|
|
3559
|
-
this.resolvePendingDispatch =
|
|
3559
|
+
this.pendingDispatch = new Promise((resolve3) => {
|
|
3560
|
+
this.resolvePendingDispatch = resolve3;
|
|
3560
3561
|
});
|
|
3561
3562
|
}
|
|
3562
3563
|
resolvePending() {
|
|
3563
|
-
const
|
|
3564
|
+
const resolve3 = this.resolvePendingDispatch;
|
|
3564
3565
|
this.pendingDispatch = void 0;
|
|
3565
3566
|
this.resolvePendingDispatch = void 0;
|
|
3566
|
-
|
|
3567
|
+
resolve3?.();
|
|
3567
3568
|
}
|
|
3568
3569
|
};
|
|
3569
3570
|
|
|
@@ -3717,15 +3718,15 @@ var AgentSessionRetryController = class {
|
|
|
3717
3718
|
if (this.pendingRetry) {
|
|
3718
3719
|
return;
|
|
3719
3720
|
}
|
|
3720
|
-
this.pendingRetry = new Promise((
|
|
3721
|
-
this.resolvePendingRetry =
|
|
3721
|
+
this.pendingRetry = new Promise((resolve3) => {
|
|
3722
|
+
this.resolvePendingRetry = resolve3;
|
|
3722
3723
|
});
|
|
3723
3724
|
}
|
|
3724
3725
|
resolvePending() {
|
|
3725
|
-
const
|
|
3726
|
+
const resolve3 = this.resolvePendingRetry;
|
|
3726
3727
|
this.pendingRetry = void 0;
|
|
3727
3728
|
this.resolvePendingRetry = void 0;
|
|
3728
|
-
|
|
3729
|
+
resolve3?.();
|
|
3729
3730
|
}
|
|
3730
3731
|
};
|
|
3731
3732
|
|
|
@@ -5443,9 +5444,98 @@ function truncateLine(line, maxChars = GREP_MAX_LINE_LENGTH) {
|
|
|
5443
5444
|
return { text: `${line.slice(0, maxChars)}... [truncated]`, wasTruncated: true };
|
|
5444
5445
|
}
|
|
5445
5446
|
|
|
5447
|
+
// ../../packages/shared-headless-capabilities/src/builtin/fs/file-observation-state.ts
|
|
5448
|
+
function snapshotsEqual(left, right) {
|
|
5449
|
+
return left.mtimeMs === right.mtimeMs && left.size === right.size && left.contentHash === right.contentHash;
|
|
5450
|
+
}
|
|
5451
|
+
function snapshotsHaveSameContent(left, right) {
|
|
5452
|
+
return left.size === right.size && left.contentHash === right.contentHash;
|
|
5453
|
+
}
|
|
5454
|
+
function snapshotFromStatAndContent(stat, content) {
|
|
5455
|
+
return {
|
|
5456
|
+
mtimeMs: stat.mtime.getTime(),
|
|
5457
|
+
size: stat.size,
|
|
5458
|
+
contentHash: hashText(content)
|
|
5459
|
+
};
|
|
5460
|
+
}
|
|
5461
|
+
function createFileObservationState(options = {}) {
|
|
5462
|
+
const files = /* @__PURE__ */ new Map();
|
|
5463
|
+
const maxFiles = Math.max(1, Math.floor(options.maxFiles ?? 100));
|
|
5464
|
+
const maxRangesPerFile = Math.max(0, Math.floor(options.maxRangesPerFile ?? 8));
|
|
5465
|
+
function touchFile(absolutePath, observed) {
|
|
5466
|
+
files.delete(absolutePath);
|
|
5467
|
+
files.set(absolutePath, observed);
|
|
5468
|
+
while (files.size > maxFiles) {
|
|
5469
|
+
const oldest = files.keys().next().value;
|
|
5470
|
+
if (oldest === void 0) break;
|
|
5471
|
+
files.delete(oldest);
|
|
5472
|
+
}
|
|
5473
|
+
}
|
|
5474
|
+
function touchRange(readRanges, rangeKey, range) {
|
|
5475
|
+
if (maxRangesPerFile === 0) {
|
|
5476
|
+
readRanges.clear();
|
|
5477
|
+
return;
|
|
5478
|
+
}
|
|
5479
|
+
readRanges.delete(rangeKey);
|
|
5480
|
+
readRanges.set(rangeKey, range);
|
|
5481
|
+
while (readRanges.size > maxRangesPerFile) {
|
|
5482
|
+
const oldest = readRanges.keys().next().value;
|
|
5483
|
+
if (oldest === void 0) break;
|
|
5484
|
+
readRanges.delete(oldest);
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
return {
|
|
5488
|
+
isUnchangedReadRange(absolutePath, rangeKey, snapshot, selectedContentHash) {
|
|
5489
|
+
const observed = files.get(absolutePath);
|
|
5490
|
+
const range = observed?.readRanges.get(rangeKey);
|
|
5491
|
+
if (!observed || !range) {
|
|
5492
|
+
return false;
|
|
5493
|
+
}
|
|
5494
|
+
const unchanged = snapshotsEqual(range.snapshot, snapshot) && range.selectedContentHash === selectedContentHash;
|
|
5495
|
+
touchRange(observed.readRanges, rangeKey, range);
|
|
5496
|
+
touchFile(absolutePath, observed);
|
|
5497
|
+
return unchanged;
|
|
5498
|
+
},
|
|
5499
|
+
recordTextRead(absolutePath, rangeKey, snapshot, selectedContentHash, completeTextKnown) {
|
|
5500
|
+
const observed = files.get(absolutePath) ?? {
|
|
5501
|
+
snapshot,
|
|
5502
|
+
completeTextKnown: false,
|
|
5503
|
+
readRanges: /* @__PURE__ */ new Map()
|
|
5504
|
+
};
|
|
5505
|
+
const preservesFreshCompleteRead = observed.completeTextKnown && snapshotsHaveSameContent(observed.snapshot, snapshot);
|
|
5506
|
+
observed.snapshot = snapshot;
|
|
5507
|
+
observed.completeTextKnown = completeTextKnown || preservesFreshCompleteRead;
|
|
5508
|
+
touchRange(observed.readRanges, rangeKey, { snapshot, selectedContentHash });
|
|
5509
|
+
touchFile(absolutePath, observed);
|
|
5510
|
+
},
|
|
5511
|
+
getCompleteTextSnapshot(absolutePath) {
|
|
5512
|
+
const observed = files.get(absolutePath);
|
|
5513
|
+
if (!observed?.completeTextKnown) {
|
|
5514
|
+
return void 0;
|
|
5515
|
+
}
|
|
5516
|
+
touchFile(absolutePath, observed);
|
|
5517
|
+
return observed.snapshot;
|
|
5518
|
+
},
|
|
5519
|
+
recordCompleteTextWrite(absolutePath, snapshot) {
|
|
5520
|
+
touchFile(absolutePath, {
|
|
5521
|
+
snapshot,
|
|
5522
|
+
completeTextKnown: true,
|
|
5523
|
+
readRanges: /* @__PURE__ */ new Map()
|
|
5524
|
+
});
|
|
5525
|
+
},
|
|
5526
|
+
invalidate(absolutePath) {
|
|
5527
|
+
files.delete(absolutePath);
|
|
5528
|
+
},
|
|
5529
|
+
clear() {
|
|
5530
|
+
files.clear();
|
|
5531
|
+
}
|
|
5532
|
+
};
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5446
5535
|
// ../../packages/shared-headless-capabilities/src/builtin/fs/read.ts
|
|
5447
5536
|
function createReadTool(cwd, options) {
|
|
5448
5537
|
const fs2 = getFileSystem();
|
|
5538
|
+
const fileObservationState = options?.fileObservationState ?? createFileObservationState();
|
|
5449
5539
|
const rootSchema = buildRootParameterSchema(cwd, options);
|
|
5450
5540
|
const readSchema = Type.Object({
|
|
5451
5541
|
path: Type.String({ description: "Path to the file to read. Example: 'Scripts/PlayerController.cs' or 'Assets/Gen/config.json'." }),
|
|
@@ -5464,7 +5554,7 @@ function createReadTool(cwd, options) {
|
|
|
5464
5554
|
const { path: path3, root, offset, limit, question } = params;
|
|
5465
5555
|
const absolutePath = resolveReadPath(path3, cwd, options, root);
|
|
5466
5556
|
return new Promise(
|
|
5467
|
-
(
|
|
5557
|
+
(resolve3, reject) => {
|
|
5468
5558
|
if (signal?.aborted) {
|
|
5469
5559
|
reject(new Error("Operation aborted"));
|
|
5470
5560
|
return;
|
|
@@ -5480,11 +5570,18 @@ function createReadTool(cwd, options) {
|
|
|
5480
5570
|
(async () => {
|
|
5481
5571
|
try {
|
|
5482
5572
|
await fs2.access(absolutePath);
|
|
5573
|
+
if (isSpecialDevicePath(absolutePath)) {
|
|
5574
|
+
throw new Error(`Refusing to read special device path: ${absolutePath}`);
|
|
5575
|
+
}
|
|
5576
|
+
const stat = await fs2.stat(absolutePath);
|
|
5577
|
+
if (!stat.isFile) {
|
|
5578
|
+
throw new Error(`Refusing to read non-regular file: ${absolutePath}`);
|
|
5579
|
+
}
|
|
5483
5580
|
if (aborted) return;
|
|
5484
5581
|
const imageInfo = await readImageInfo(absolutePath, fs2);
|
|
5485
5582
|
if (imageInfo) {
|
|
5486
5583
|
if (question && options?.understandFile) {
|
|
5487
|
-
|
|
5584
|
+
resolve3(await options.understandFile({
|
|
5488
5585
|
absolutePath,
|
|
5489
5586
|
displayPath: path3,
|
|
5490
5587
|
mimeType: imageInfo.mimeType,
|
|
@@ -5496,7 +5593,7 @@ function createReadTool(cwd, options) {
|
|
|
5496
5593
|
return;
|
|
5497
5594
|
}
|
|
5498
5595
|
const dimensionText = imageInfo.width && imageInfo.height ? `, ${imageInfo.width}x${imageInfo.height}` : "";
|
|
5499
|
-
|
|
5596
|
+
resolve3({
|
|
5500
5597
|
content: [
|
|
5501
5598
|
{ type: "text", text: `Read image file [${imageInfo.mimeType}${dimensionText}]. Original image attached as an image block; for very large images, prefer a smaller crop or resized copy before rereading.` },
|
|
5502
5599
|
{ type: "image", data: imageInfo.base64, mimeType: imageInfo.mimeType }
|
|
@@ -5513,7 +5610,7 @@ function createReadTool(cwd, options) {
|
|
|
5513
5610
|
}
|
|
5514
5611
|
const mediaInfo = detectUnderstandingKind(absolutePath);
|
|
5515
5612
|
if (mediaInfo && options?.understandFile) {
|
|
5516
|
-
|
|
5613
|
+
resolve3(await options.understandFile({
|
|
5517
5614
|
absolutePath,
|
|
5518
5615
|
displayPath: path3,
|
|
5519
5616
|
mimeType: mediaInfo.mimeType,
|
|
@@ -5524,7 +5621,7 @@ function createReadTool(cwd, options) {
|
|
|
5524
5621
|
return;
|
|
5525
5622
|
}
|
|
5526
5623
|
if (mediaInfo) {
|
|
5527
|
-
|
|
5624
|
+
resolve3({
|
|
5528
5625
|
content: [{
|
|
5529
5626
|
type: "text",
|
|
5530
5627
|
text: `Cannot read ${mediaInfo.kind} file ${path3} semantically: no media understanding handler is configured for ${mediaInfo.mimeType}.`
|
|
@@ -5540,24 +5637,62 @@ function createReadTool(cwd, options) {
|
|
|
5540
5637
|
});
|
|
5541
5638
|
return;
|
|
5542
5639
|
}
|
|
5543
|
-
const content = String(await fs2.readFile(absolutePath, "utf-8"));
|
|
5544
|
-
const allLines = content.split("\n");
|
|
5545
|
-
const totalFileLines = allLines.length;
|
|
5546
5640
|
const startLine = offset ? Math.max(0, offset - 1) : 0;
|
|
5547
5641
|
const startLineDisplay = startLine + 1;
|
|
5548
|
-
|
|
5549
|
-
|
|
5642
|
+
const range = await fs2.readTextRange(absolutePath, {
|
|
5643
|
+
offsetLine: startLine,
|
|
5644
|
+
limitLines: limit,
|
|
5645
|
+
maxOutputLines: DEFAULT_MAX_LINES,
|
|
5646
|
+
maxOutputBytes: DEFAULT_MAX_BYTES
|
|
5647
|
+
}, signal);
|
|
5648
|
+
const snapshot = {
|
|
5649
|
+
mtimeMs: range.mtimeMs,
|
|
5650
|
+
size: range.size,
|
|
5651
|
+
contentHash: range.contentHash
|
|
5652
|
+
};
|
|
5653
|
+
const totalFileLines = range.totalLines;
|
|
5654
|
+
if (startLine >= range.totalLines) {
|
|
5655
|
+
throw new Error(`Offset ${offset} is beyond end of file (${range.totalLines} lines total)`);
|
|
5550
5656
|
}
|
|
5551
|
-
let selectedContent;
|
|
5552
5657
|
let userLimitedLines;
|
|
5553
5658
|
if (limit !== void 0) {
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5659
|
+
userLimitedLines = range.selectedLines;
|
|
5660
|
+
}
|
|
5661
|
+
const rangeKey = `${startLineDisplay}:${limit === void 0 ? "all" : limit}`;
|
|
5662
|
+
const truncation = {
|
|
5663
|
+
content: range.content,
|
|
5664
|
+
truncated: range.truncated,
|
|
5665
|
+
truncatedBy: range.truncatedBy,
|
|
5666
|
+
totalLines: range.selectedLines,
|
|
5667
|
+
totalBytes: range.selectedBytes,
|
|
5668
|
+
outputLines: range.outputLines,
|
|
5669
|
+
outputBytes: range.outputBytes,
|
|
5670
|
+
lastLinePartial: false,
|
|
5671
|
+
firstLineExceedsLimit: range.firstLineExceedsLimit,
|
|
5672
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
5673
|
+
maxBytes: DEFAULT_MAX_BYTES
|
|
5674
|
+
};
|
|
5675
|
+
const readStartsAtBeginning = offset === void 0 || offset === 1;
|
|
5676
|
+
const readReachedEndOfFile = startLine + range.selectedLines >= range.totalLines;
|
|
5677
|
+
const isCompleteTextKnown = readStartsAtBeginning && readReachedEndOfFile && !truncation.truncated && !truncation.firstLineExceedsLimit;
|
|
5678
|
+
if (fileObservationState.isUnchangedReadRange(absolutePath, rangeKey, snapshot, range.selectedContentHash)) {
|
|
5679
|
+
if (signal) {
|
|
5680
|
+
signal.removeEventListener("abort", onAbort);
|
|
5681
|
+
}
|
|
5682
|
+
resolve3({
|
|
5683
|
+
content: [{
|
|
5684
|
+
type: "text",
|
|
5685
|
+
text: "File unchanged since previous read of this range. Use a different offset/limit if you need another section."
|
|
5686
|
+
}],
|
|
5687
|
+
details: {
|
|
5688
|
+
unchangedSincePreviousRead: true,
|
|
5689
|
+
path: absolutePath,
|
|
5690
|
+
offset: startLineDisplay,
|
|
5691
|
+
limit
|
|
5692
|
+
}
|
|
5693
|
+
});
|
|
5694
|
+
return;
|
|
5559
5695
|
}
|
|
5560
|
-
const truncation = truncateHead(selectedContent);
|
|
5561
5696
|
let outputText;
|
|
5562
5697
|
let details;
|
|
5563
5698
|
const sourceHeader = `File: ${absolutePath}
|
|
@@ -5565,7 +5700,7 @@ Base directory: ${nodePath.dirname(absolutePath)}
|
|
|
5565
5700
|
|
|
5566
5701
|
`;
|
|
5567
5702
|
if (truncation.firstLineExceedsLimit) {
|
|
5568
|
-
const firstLineSize = formatSize(
|
|
5703
|
+
const firstLineSize = formatSize(range.firstLineBytes);
|
|
5569
5704
|
outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit.]`;
|
|
5570
5705
|
details = { truncation };
|
|
5571
5706
|
} else if (truncation.truncated) {
|
|
@@ -5582,8 +5717,8 @@ Base directory: ${nodePath.dirname(absolutePath)}
|
|
|
5582
5717
|
[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
|
|
5583
5718
|
}
|
|
5584
5719
|
details = { truncation };
|
|
5585
|
-
} else if (userLimitedLines !== void 0 && startLine + userLimitedLines <
|
|
5586
|
-
const remaining =
|
|
5720
|
+
} else if (userLimitedLines !== void 0 && startLine + userLimitedLines < range.totalLines) {
|
|
5721
|
+
const remaining = range.totalLines - (startLine + userLimitedLines);
|
|
5587
5722
|
const nextOffset = startLine + userLimitedLines + 1;
|
|
5588
5723
|
outputText = truncation.content;
|
|
5589
5724
|
outputText += `
|
|
@@ -5592,13 +5727,20 @@ Base directory: ${nodePath.dirname(absolutePath)}
|
|
|
5592
5727
|
} else {
|
|
5593
5728
|
outputText = truncation.content;
|
|
5594
5729
|
}
|
|
5730
|
+
fileObservationState.recordTextRead(
|
|
5731
|
+
absolutePath,
|
|
5732
|
+
rangeKey,
|
|
5733
|
+
snapshot,
|
|
5734
|
+
range.selectedContentHash,
|
|
5735
|
+
isCompleteTextKnown
|
|
5736
|
+
);
|
|
5595
5737
|
outputText = sourceHeader + outputText;
|
|
5596
5738
|
const textContent = [{ type: "text", text: outputText }];
|
|
5597
5739
|
if (aborted) return;
|
|
5598
5740
|
if (signal) {
|
|
5599
5741
|
signal.removeEventListener("abort", onAbort);
|
|
5600
5742
|
}
|
|
5601
|
-
|
|
5743
|
+
resolve3({ content: textContent, details });
|
|
5602
5744
|
} catch (error) {
|
|
5603
5745
|
if (signal) {
|
|
5604
5746
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -5613,6 +5755,10 @@ Base directory: ${nodePath.dirname(absolutePath)}
|
|
|
5613
5755
|
}
|
|
5614
5756
|
};
|
|
5615
5757
|
}
|
|
5758
|
+
function isSpecialDevicePath(path3) {
|
|
5759
|
+
const normalized = nodePath.resolve(path3).replace(/\\/g, "/");
|
|
5760
|
+
return /^\/dev\/(?:zero|random|urandom|full|stdin|stdout|stderr|tty|console)$/.test(normalized) || /^\/dev\/fd\/[0-2]$/.test(normalized) || /^\/proc\/(?:self|\d+)\/fd\/[0-2]$/.test(normalized);
|
|
5761
|
+
}
|
|
5616
5762
|
function detectUnderstandingKind(filePath) {
|
|
5617
5763
|
const lower = filePath.toLowerCase();
|
|
5618
5764
|
if (/\.(mp4|mov|m4v|webm|avi|mkv)$/.test(lower)) {
|
|
@@ -5734,6 +5880,8 @@ function parseWebpDimensions(bytes) {
|
|
|
5734
5880
|
// ../../packages/shared-headless-capabilities/src/builtin/fs/write.ts
|
|
5735
5881
|
function createWriteTool(cwd, options) {
|
|
5736
5882
|
const fs2 = getFileSystem();
|
|
5883
|
+
const usesExternalFileObservationState = !!options?.fileObservationState;
|
|
5884
|
+
const fileObservationState = options?.fileObservationState ?? createFileObservationState();
|
|
5737
5885
|
const rootSchema = buildRootParameterSchema(cwd, options);
|
|
5738
5886
|
const writeSchema = Type.Object({
|
|
5739
5887
|
path: Type.String({ description: "Path to the file to write." }),
|
|
@@ -5744,14 +5892,14 @@ function createWriteTool(cwd, options) {
|
|
|
5744
5892
|
return {
|
|
5745
5893
|
name: "write",
|
|
5746
5894
|
label: "write",
|
|
5747
|
-
description: `Write
|
|
5895
|
+
description: `Write complete file contents. Use this to create a new file, or to intentionally replace an entire existing file after reading the latest full file with read_file. Do not use for targeted edits to existing files; use edit_file instead. For existing files, omitted content will be deleted. Automatically creates parent directories. ${pathResolutionDescription}`,
|
|
5748
5896
|
parameters: writeSchema,
|
|
5749
5897
|
execute: async (_toolCallId, params, signal) => {
|
|
5750
5898
|
const { path: filePath, root, content } = params;
|
|
5751
5899
|
const absolutePath = resolveToCwd(filePath, cwd, options, root);
|
|
5752
5900
|
const dir = fs2.dirname(absolutePath);
|
|
5753
5901
|
return new Promise(
|
|
5754
|
-
(
|
|
5902
|
+
(resolve3, reject) => {
|
|
5755
5903
|
if (signal?.aborted) {
|
|
5756
5904
|
reject(new Error("Operation aborted"));
|
|
5757
5905
|
return;
|
|
@@ -5768,12 +5916,38 @@ function createWriteTool(cwd, options) {
|
|
|
5768
5916
|
try {
|
|
5769
5917
|
await fs2.mkdir(dir, { recursive: true });
|
|
5770
5918
|
if (aborted) return;
|
|
5919
|
+
let fileExists = false;
|
|
5920
|
+
try {
|
|
5921
|
+
const stat = await fs2.stat(absolutePath);
|
|
5922
|
+
fileExists = stat.isFile;
|
|
5923
|
+
} catch {
|
|
5924
|
+
fileExists = false;
|
|
5925
|
+
}
|
|
5926
|
+
if (fileExists) {
|
|
5927
|
+
const currentContent = String(await fs2.readFile(absolutePath, "utf-8"));
|
|
5928
|
+
const currentStat = await fs2.stat(absolutePath);
|
|
5929
|
+
const currentSnapshot = snapshotFromStatAndContent(currentStat, currentContent);
|
|
5930
|
+
const observedSnapshot = fileObservationState.getCompleteTextSnapshot(absolutePath);
|
|
5931
|
+
if (!observedSnapshot) {
|
|
5932
|
+
throw new Error(
|
|
5933
|
+
usesExternalFileObservationState ? "Read the current full file with read_file before overwriting it, or use edit_file for targeted changes." : "Existing-file overwrite requires write_file to share a fileObservationState with read_file; use createSharedFileSystemTools or pass the same fileObservationState to both tools."
|
|
5934
|
+
);
|
|
5935
|
+
}
|
|
5936
|
+
if (!snapshotsHaveSameContent(observedSnapshot, currentSnapshot)) {
|
|
5937
|
+
throw new Error("File changed since it was read. Read the latest file contents before overwriting it.");
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5771
5940
|
await fs2.writeFile(absolutePath, content, "utf-8");
|
|
5941
|
+
const writtenStat = await fs2.stat(absolutePath);
|
|
5942
|
+
fileObservationState.recordCompleteTextWrite(
|
|
5943
|
+
absolutePath,
|
|
5944
|
+
snapshotFromStatAndContent(writtenStat, content)
|
|
5945
|
+
);
|
|
5772
5946
|
if (aborted) return;
|
|
5773
5947
|
if (signal) {
|
|
5774
5948
|
signal.removeEventListener("abort", onAbort);
|
|
5775
5949
|
}
|
|
5776
|
-
|
|
5950
|
+
resolve3({
|
|
5777
5951
|
content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${filePath}` }],
|
|
5778
5952
|
details: void 0
|
|
5779
5953
|
});
|
|
@@ -5839,28 +6013,30 @@ function buildFuzzyIndexMap(text) {
|
|
|
5839
6013
|
}
|
|
5840
6014
|
return { normalized, originalStartByNormalizedIndex, originalEndByNormalizedIndex };
|
|
5841
6015
|
}
|
|
5842
|
-
function
|
|
5843
|
-
if (needle.length === 0) return
|
|
5844
|
-
|
|
6016
|
+
function collectMatches(content, needle) {
|
|
6017
|
+
if (needle.length === 0) return [];
|
|
6018
|
+
const matches = [];
|
|
5845
6019
|
let index = 0;
|
|
5846
6020
|
while (true) {
|
|
5847
6021
|
const nextIndex = content.indexOf(needle, index);
|
|
5848
6022
|
if (nextIndex === -1) break;
|
|
5849
|
-
|
|
6023
|
+
matches.push({ index: nextIndex, matchLength: needle.length });
|
|
5850
6024
|
index = nextIndex + needle.length;
|
|
5851
6025
|
}
|
|
5852
|
-
return
|
|
6026
|
+
return matches;
|
|
5853
6027
|
}
|
|
5854
6028
|
function fuzzyFindText(content, oldText) {
|
|
5855
6029
|
const exactIndex = content.indexOf(oldText);
|
|
5856
6030
|
if (exactIndex !== -1) {
|
|
6031
|
+
const matches2 = collectMatches(content, oldText);
|
|
5857
6032
|
return {
|
|
5858
6033
|
found: true,
|
|
5859
6034
|
index: exactIndex,
|
|
5860
6035
|
matchLength: oldText.length,
|
|
5861
6036
|
usedFuzzyMatch: false,
|
|
5862
6037
|
contentForReplacement: content,
|
|
5863
|
-
occurrences:
|
|
6038
|
+
occurrences: matches2.length,
|
|
6039
|
+
matches: matches2,
|
|
5864
6040
|
matchKind: "exact"
|
|
5865
6041
|
};
|
|
5866
6042
|
}
|
|
@@ -5875,6 +6051,7 @@ function fuzzyFindText(content, oldText) {
|
|
|
5875
6051
|
usedFuzzyMatch: false,
|
|
5876
6052
|
contentForReplacement: content,
|
|
5877
6053
|
occurrences: 0,
|
|
6054
|
+
matches: [],
|
|
5878
6055
|
matchKind: "none"
|
|
5879
6056
|
};
|
|
5880
6057
|
}
|
|
@@ -5887,19 +6064,26 @@ function fuzzyFindText(content, oldText) {
|
|
|
5887
6064
|
usedFuzzyMatch: false,
|
|
5888
6065
|
contentForReplacement: content,
|
|
5889
6066
|
occurrences: 0,
|
|
6067
|
+
matches: [],
|
|
5890
6068
|
matchKind: "none"
|
|
5891
6069
|
};
|
|
5892
6070
|
}
|
|
5893
|
-
const
|
|
5894
|
-
const
|
|
5895
|
-
|
|
6071
|
+
const fuzzyMatches = collectMatches(fuzzyContent, fuzzyOldText);
|
|
6072
|
+
const matches = fuzzyMatches.map((match) => {
|
|
6073
|
+
const endFuzzyIndex = match.index + match.matchLength - 1;
|
|
6074
|
+
const originalStart = fuzzyMap.originalStartByNormalizedIndex[match.index] ?? match.index;
|
|
6075
|
+
const originalEnd = fuzzyMap.originalEndByNormalizedIndex[endFuzzyIndex] ?? originalStart + match.matchLength;
|
|
6076
|
+
return { index: originalStart, matchLength: originalEnd - originalStart };
|
|
6077
|
+
});
|
|
6078
|
+
const firstMatch = matches[0] ?? { index: -1, matchLength: 0 };
|
|
5896
6079
|
return {
|
|
5897
6080
|
found: true,
|
|
5898
|
-
index:
|
|
5899
|
-
matchLength:
|
|
6081
|
+
index: firstMatch.index,
|
|
6082
|
+
matchLength: firstMatch.matchLength,
|
|
5900
6083
|
usedFuzzyMatch: true,
|
|
5901
6084
|
contentForReplacement: content,
|
|
5902
|
-
occurrences:
|
|
6085
|
+
occurrences: matches.length,
|
|
6086
|
+
matches,
|
|
5903
6087
|
matchKind: "fuzzy"
|
|
5904
6088
|
};
|
|
5905
6089
|
}
|
|
@@ -5947,7 +6131,7 @@ function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
5947
6131
|
}
|
|
5948
6132
|
|
|
5949
6133
|
// ../../packages/shared-headless-capabilities/src/builtin/fs/edit.ts
|
|
5950
|
-
function validateEditArgs(path3, oldText, newText) {
|
|
6134
|
+
function validateEditArgs(path3, oldText, newText, replaceAll) {
|
|
5951
6135
|
const filePath = typeof path3 === "string" ? path3.trim() : "";
|
|
5952
6136
|
if (!filePath) {
|
|
5953
6137
|
throw new Error("Invalid edit_file arguments: path cannot be empty. Pass the file path in path and the exact text to replace in oldText.");
|
|
@@ -5961,11 +6145,14 @@ function validateEditArgs(path3, oldText, newText) {
|
|
|
5961
6145
|
if (oldText === newText) {
|
|
5962
6146
|
throw new Error("Invalid edit_file arguments: oldText and newText are identical, so the edit would make no change.");
|
|
5963
6147
|
}
|
|
6148
|
+
if (replaceAll !== void 0 && typeof replaceAll !== "boolean") {
|
|
6149
|
+
throw new Error("Invalid edit_file arguments: replaceAll must be a boolean when provided.");
|
|
6150
|
+
}
|
|
5964
6151
|
if (filePath.includes("<|tool") || oldText.includes("<|tool") || newText.includes("<|tool")) {
|
|
5965
6152
|
throw new Error("Invalid edit_file arguments: path, oldText, and newText must be real edit values, not tool-call markup.");
|
|
5966
6153
|
}
|
|
5967
6154
|
}
|
|
5968
|
-
function
|
|
6155
|
+
function countOccurrences(content, needle) {
|
|
5969
6156
|
if (needle.length === 0) return 0;
|
|
5970
6157
|
let count = 0;
|
|
5971
6158
|
let index = 0;
|
|
@@ -5992,6 +6179,8 @@ function createEditErrorResult(params) {
|
|
|
5992
6179
|
diff: "",
|
|
5993
6180
|
oldTextOccurrences: params.oldTextOccurrences,
|
|
5994
6181
|
newTextOccurrences: params.newTextOccurrences,
|
|
6182
|
+
replaceAll: params.replaceAll,
|
|
6183
|
+
replacedOccurrences: 0,
|
|
5995
6184
|
errorReason: params.errorReason,
|
|
5996
6185
|
recoveryHint: params.recoveryHint,
|
|
5997
6186
|
staleBaseLikely: params.staleBaseLikely,
|
|
@@ -6002,12 +6191,14 @@ function createEditErrorResult(params) {
|
|
|
6002
6191
|
}
|
|
6003
6192
|
function createEditTool(cwd, options) {
|
|
6004
6193
|
const fs2 = getFileSystem();
|
|
6194
|
+
const fileObservationState = options?.fileObservationState ?? createFileObservationState();
|
|
6005
6195
|
const rootSchema = buildRootParameterSchema(cwd, options);
|
|
6006
6196
|
const editSchema = Type.Object({
|
|
6007
6197
|
path: Type.String({ description: "Path to the file to edit." }),
|
|
6008
6198
|
...rootSchema ? { root: rootSchema } : {},
|
|
6009
6199
|
oldText: Type.String({ description: "Exact text to find and replace (must match exactly)" }),
|
|
6010
|
-
newText: Type.String({ description: "New text to replace the old text with" })
|
|
6200
|
+
newText: Type.String({ description: "New text to replace the old text with" }),
|
|
6201
|
+
replaceAll: Type.Optional(Type.Boolean({ description: "Replace every occurrence of oldText. Only set this to true when every matching occurrence should be replaced." }))
|
|
6011
6202
|
});
|
|
6012
6203
|
const pathResolutionDescription = buildPathResolutionDescription(cwd, options);
|
|
6013
6204
|
return {
|
|
@@ -6016,10 +6207,11 @@ function createEditTool(cwd, options) {
|
|
|
6016
6207
|
description: `Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits. ${pathResolutionDescription}`,
|
|
6017
6208
|
parameters: editSchema,
|
|
6018
6209
|
execute: async (_toolCallId, params, signal) => {
|
|
6019
|
-
const { path: path3, root, oldText, newText } = params;
|
|
6020
|
-
validateEditArgs(path3, oldText, newText);
|
|
6210
|
+
const { path: path3, root, oldText, newText, replaceAll } = params;
|
|
6211
|
+
validateEditArgs(path3, oldText, newText, replaceAll);
|
|
6212
|
+
const shouldReplaceAll = replaceAll === true;
|
|
6021
6213
|
const absolutePath = resolveToCwd(path3, cwd, options, root);
|
|
6022
|
-
return new Promise((
|
|
6214
|
+
return new Promise((resolve3, reject) => {
|
|
6023
6215
|
if (signal?.aborted) {
|
|
6024
6216
|
reject(new Error("Operation aborted"));
|
|
6025
6217
|
return;
|
|
@@ -6049,50 +6241,57 @@ function createEditTool(cwd, options) {
|
|
|
6049
6241
|
const normalizedContent = normalizeToLF(content);
|
|
6050
6242
|
const normalizedOldText = normalizeToLF(oldText);
|
|
6051
6243
|
const normalizedNewText = normalizeToLF(newText);
|
|
6052
|
-
const newTextOccurrences =
|
|
6244
|
+
const newTextOccurrences = countOccurrences(normalizedContent, normalizedNewText);
|
|
6053
6245
|
const matchResult = fuzzyFindText(normalizedContent, normalizedOldText);
|
|
6054
6246
|
if (!matchResult.found) {
|
|
6055
6247
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6056
|
-
|
|
6248
|
+
resolve3(
|
|
6057
6249
|
createEditErrorResult({
|
|
6058
6250
|
message: `Could not find oldText in ${path3}.`,
|
|
6059
6251
|
matchKind: "none",
|
|
6060
6252
|
errorReason: "old_text_not_found",
|
|
6061
6253
|
oldTextOccurrences: 0,
|
|
6062
6254
|
newTextOccurrences,
|
|
6255
|
+
replaceAll: shouldReplaceAll,
|
|
6063
6256
|
staleBaseLikely: newTextOccurrences > 0,
|
|
6064
6257
|
alreadyAppliedCandidate: newTextOccurrences > 0,
|
|
6065
|
-
recoveryHint: newTextOccurrences > 0 ? "The replacement text already appears in the current file. Read the current file before retrying; this may be a stale oldText from an earlier edit in the same turn." : "Read the current file and retry with
|
|
6258
|
+
recoveryHint: newTextOccurrences > 0 ? "The replacement text already appears in the current file. Read the current file before retrying; this may be a stale oldText from an earlier edit in the same turn." : "Read the current file and retry with exact text copied from the latest file contents."
|
|
6066
6259
|
})
|
|
6067
6260
|
);
|
|
6068
6261
|
return;
|
|
6069
6262
|
}
|
|
6070
|
-
if (matchResult.occurrences > 1) {
|
|
6263
|
+
if (matchResult.occurrences > 1 && !shouldReplaceAll) {
|
|
6071
6264
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6072
|
-
|
|
6265
|
+
resolve3(
|
|
6073
6266
|
createEditErrorResult({
|
|
6074
6267
|
message: `Found ${matchResult.occurrences} occurrences of oldText in ${path3}. The text must be unique.`,
|
|
6075
6268
|
matchKind: matchResult.matchKind,
|
|
6076
6269
|
errorReason: "old_text_not_unique",
|
|
6077
6270
|
oldTextOccurrences: matchResult.occurrences,
|
|
6078
6271
|
newTextOccurrences,
|
|
6079
|
-
|
|
6272
|
+
replaceAll: shouldReplaceAll,
|
|
6273
|
+
recoveryHint: "Read the current file and retry with a larger exact block copied from the latest file contents, or set replaceAll to true if every occurrence should be replaced."
|
|
6080
6274
|
})
|
|
6081
6275
|
);
|
|
6082
6276
|
return;
|
|
6083
6277
|
}
|
|
6084
6278
|
if (aborted) return;
|
|
6085
|
-
const baseContent =
|
|
6086
|
-
const
|
|
6279
|
+
const baseContent = matchResult.contentForReplacement;
|
|
6280
|
+
const matchesToReplace = shouldReplaceAll ? matchResult.matches : matchResult.matches.slice(0, 1);
|
|
6281
|
+
let newContent = baseContent;
|
|
6282
|
+
for (const match of [...matchesToReplace].sort((left, right) => right.index - left.index)) {
|
|
6283
|
+
newContent = newContent.substring(0, match.index) + normalizedNewText + newContent.substring(match.index + match.matchLength);
|
|
6284
|
+
}
|
|
6087
6285
|
if (baseContent === newContent) {
|
|
6088
6286
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
6089
|
-
|
|
6287
|
+
resolve3(
|
|
6090
6288
|
createEditErrorResult({
|
|
6091
6289
|
message: `No changes made to ${path3}. The replacement produced identical content.`,
|
|
6092
6290
|
matchKind: matchResult.matchKind,
|
|
6093
6291
|
errorReason: "replacement_no_change",
|
|
6094
6292
|
oldTextOccurrences: matchResult.occurrences,
|
|
6095
6293
|
newTextOccurrences,
|
|
6294
|
+
replaceAll: shouldReplaceAll,
|
|
6096
6295
|
recoveryHint: "Read the current file and verify whether the intended edit is already present or whether oldText/newText need a larger exact block.",
|
|
6097
6296
|
alreadyAppliedCandidate: newTextOccurrences > 0
|
|
6098
6297
|
})
|
|
@@ -6101,16 +6300,17 @@ function createEditTool(cwd, options) {
|
|
|
6101
6300
|
}
|
|
6102
6301
|
const finalContent = bom + restoreLineEndings(newContent, originalEnding);
|
|
6103
6302
|
await fs2.writeFile(absolutePath, finalContent, "utf-8");
|
|
6303
|
+
fileObservationState.invalidate(absolutePath);
|
|
6104
6304
|
if (aborted) return;
|
|
6105
6305
|
if (signal) {
|
|
6106
6306
|
signal.removeEventListener("abort", onAbort);
|
|
6107
6307
|
}
|
|
6108
6308
|
const diffResult = generateDiffString(content, restoreLineEndings(newContent, originalEnding));
|
|
6109
|
-
|
|
6309
|
+
resolve3({
|
|
6110
6310
|
content: [
|
|
6111
6311
|
{
|
|
6112
6312
|
type: "text",
|
|
6113
|
-
text: `Successfully replaced text in ${path3} using ${matchResult.matchKind} match.`
|
|
6313
|
+
text: `Successfully replaced text in ${path3} using ${matchResult.matchKind} match (${matchesToReplace.length} occurrence${matchesToReplace.length === 1 ? "" : "s"}).`
|
|
6114
6314
|
},
|
|
6115
6315
|
{ type: "text", text: diffResult.diff }
|
|
6116
6316
|
],
|
|
@@ -6121,7 +6321,9 @@ function createEditTool(cwd, options) {
|
|
|
6121
6321
|
diff: diffResult.diff,
|
|
6122
6322
|
firstChangedLine: diffResult.firstChangedLine,
|
|
6123
6323
|
oldTextOccurrences: matchResult.occurrences,
|
|
6124
|
-
newTextOccurrences
|
|
6324
|
+
newTextOccurrences,
|
|
6325
|
+
replaceAll: shouldReplaceAll,
|
|
6326
|
+
replacedOccurrences: matchesToReplace.length
|
|
6125
6327
|
}
|
|
6126
6328
|
});
|
|
6127
6329
|
} catch (error) {
|
|
@@ -6155,7 +6357,7 @@ function createLsTool(cwd, options) {
|
|
|
6155
6357
|
description: `List directory contents. Use this to inspect folders before reading or writing files. Example: list_dir path='Scripts' or list_dir path='Data'. ${pathResolutionDescription} Returns entries sorted alphabetically, with '/' suffix for directories. Includes dotfiles. Output is truncated to ${DEFAULT_LIMIT} entries or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first).`,
|
|
6156
6358
|
parameters: lsSchema,
|
|
6157
6359
|
execute: async (_toolCallId, params, signal) => {
|
|
6158
|
-
return new Promise((
|
|
6360
|
+
return new Promise((resolve3, reject) => {
|
|
6159
6361
|
if (signal?.aborted) {
|
|
6160
6362
|
reject(new Error("Operation aborted"));
|
|
6161
6363
|
return;
|
|
@@ -6205,7 +6407,7 @@ function createLsTool(cwd, options) {
|
|
|
6205
6407
|
}
|
|
6206
6408
|
signal?.removeEventListener("abort", onAbort);
|
|
6207
6409
|
if (results.length === 0) {
|
|
6208
|
-
|
|
6410
|
+
resolve3({ content: [{ type: "text", text: "(empty directory)" }], details: void 0 });
|
|
6209
6411
|
return;
|
|
6210
6412
|
}
|
|
6211
6413
|
const rawOutput = results.join("\n");
|
|
@@ -6226,7 +6428,7 @@ function createLsTool(cwd, options) {
|
|
|
6226
6428
|
|
|
6227
6429
|
[${notices.join(". ")}]`;
|
|
6228
6430
|
}
|
|
6229
|
-
|
|
6431
|
+
resolve3({
|
|
6230
6432
|
content: [{ type: "text", text: output }],
|
|
6231
6433
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
6232
6434
|
});
|
|
@@ -6402,11 +6604,11 @@ function waitForFileCompletion(requestId) {
|
|
|
6402
6604
|
if (existing) {
|
|
6403
6605
|
return Promise.resolve(existing);
|
|
6404
6606
|
}
|
|
6405
|
-
return new Promise((
|
|
6406
|
-
store.waiters.set(requestId,
|
|
6607
|
+
return new Promise((resolve3) => {
|
|
6608
|
+
store.waiters.set(requestId, resolve3);
|
|
6407
6609
|
});
|
|
6408
6610
|
}
|
|
6409
|
-
async function runNativeGrep(searchPath, pattern, globPattern, ignoreCase, literal, contextLines, limit, signal) {
|
|
6611
|
+
async function runNativeGrep(searchPath, pattern, globPattern, ignoreCase, literal, contextLines, limit, outputMode, offset, signal) {
|
|
6410
6612
|
const bridge = getNativeFileBridge();
|
|
6411
6613
|
if (!bridge) {
|
|
6412
6614
|
throw new Error("CS.Pie.PieFileBridge not available");
|
|
@@ -6418,7 +6620,9 @@ async function runNativeGrep(searchPath, pattern, globPattern, ignoreCase, liter
|
|
|
6418
6620
|
!!ignoreCase,
|
|
6419
6621
|
!!literal,
|
|
6420
6622
|
contextLines,
|
|
6421
|
-
limit
|
|
6623
|
+
limit,
|
|
6624
|
+
outputMode,
|
|
6625
|
+
offset
|
|
6422
6626
|
);
|
|
6423
6627
|
const onAbort = () => bridge.CancelRequest(requestId);
|
|
6424
6628
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -6443,6 +6647,45 @@ async function runNativeGrep(searchPath, pattern, globPattern, ignoreCase, liter
|
|
|
6443
6647
|
bridge.ReleaseRequest(requestId);
|
|
6444
6648
|
}
|
|
6445
6649
|
}
|
|
6650
|
+
function normalizeOutputMode(value) {
|
|
6651
|
+
if (value === void 0 || value === null || value === "content") return "content";
|
|
6652
|
+
if (value === "files_with_matches" || value === "count") return value;
|
|
6653
|
+
throw new Error("Invalid grep_text arguments: outputMode must be one of content, files_with_matches, or count.");
|
|
6654
|
+
}
|
|
6655
|
+
function normalizeNonNegativeInteger(value, fallback, name) {
|
|
6656
|
+
if (value === void 0 || value === null) return fallback;
|
|
6657
|
+
const numberValue = Number(value);
|
|
6658
|
+
if (!Number.isInteger(numberValue) || numberValue < 0) {
|
|
6659
|
+
throw new Error(`Invalid grep_text arguments: ${name} must be a non-negative integer.`);
|
|
6660
|
+
}
|
|
6661
|
+
return numberValue;
|
|
6662
|
+
}
|
|
6663
|
+
function formatContentMatches(matches, contextLines) {
|
|
6664
|
+
const outputLines = [];
|
|
6665
|
+
let linesTruncated = false;
|
|
6666
|
+
for (const match of matches) {
|
|
6667
|
+
const start = contextLines > 0 ? Math.max(0, match.lineIndex - contextLines) : match.lineIndex;
|
|
6668
|
+
const end = contextLines > 0 ? Math.min(match.lines.length - 1, match.lineIndex + contextLines) : match.lineIndex;
|
|
6669
|
+
for (let c = start; c <= end; c++) {
|
|
6670
|
+
const lineText = match.lines[c];
|
|
6671
|
+
const { text: truncatedText, wasTruncated } = truncateLine(lineText);
|
|
6672
|
+
if (wasTruncated) linesTruncated = true;
|
|
6673
|
+
const currentLineNum = c + 1;
|
|
6674
|
+
if (c === match.lineIndex) {
|
|
6675
|
+
outputLines.push(`${match.relativePath}:${currentLineNum}: ${truncatedText}`);
|
|
6676
|
+
} else {
|
|
6677
|
+
outputLines.push(`${match.relativePath}-${currentLineNum}- ${truncatedText}`);
|
|
6678
|
+
}
|
|
6679
|
+
}
|
|
6680
|
+
}
|
|
6681
|
+
return { lines: outputLines, linesTruncated };
|
|
6682
|
+
}
|
|
6683
|
+
function sortFileSummariesByMtime(files) {
|
|
6684
|
+
return files.sort((left, right) => {
|
|
6685
|
+
if (right.mtimeMs !== left.mtimeMs) return right.mtimeMs - left.mtimeMs;
|
|
6686
|
+
return left.relativePath.localeCompare(right.relativePath);
|
|
6687
|
+
});
|
|
6688
|
+
}
|
|
6446
6689
|
async function collectFiles(dirPath, globPattern, fs2, isGitignored = () => false) {
|
|
6447
6690
|
const files = [];
|
|
6448
6691
|
const queue = [{ dir: dirPath, relPrefix: "" }];
|
|
@@ -6502,13 +6745,19 @@ function createGrepTextTool(cwd, options) {
|
|
|
6502
6745
|
context: Type.Optional(
|
|
6503
6746
|
Type.Number({ description: "Number of lines to show before and after each match (default: 0)" })
|
|
6504
6747
|
),
|
|
6748
|
+
outputMode: Type.Optional(Type.Union([
|
|
6749
|
+
Type.Literal("content"),
|
|
6750
|
+
Type.Literal("files_with_matches"),
|
|
6751
|
+
Type.Literal("count")
|
|
6752
|
+
], { description: "Result mode: content returns matching lines, files_with_matches returns matching file paths, count returns per-file match counts." })),
|
|
6753
|
+
offset: Type.Optional(Type.Number({ description: "Number of results to skip before returning output (default: 0)." })),
|
|
6505
6754
|
limit: Type.Optional(Type.Number({ description: "Maximum number of matches to return (default: 100)" }))
|
|
6506
6755
|
});
|
|
6507
6756
|
const pathResolutionDescription = buildPathResolutionDescription(cwd, options);
|
|
6508
6757
|
return {
|
|
6509
6758
|
name: "grep_text",
|
|
6510
6759
|
label: "grep_text",
|
|
6511
|
-
description: `Search file contents for a pattern. ${pathResolutionDescription}
|
|
6760
|
+
description: `Search file contents for a pattern. ${pathResolutionDescription} outputMode='content' returns matching lines with file paths and line numbers, outputMode='files_with_matches' returns matching file paths, and outputMode='count' returns per-file match counts. Output is truncated to ${DEFAULT_LIMIT2} results or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Long lines are truncated to ${GREP_MAX_LINE_LENGTH} chars.`,
|
|
6512
6761
|
parameters: grepSchema,
|
|
6513
6762
|
execute: async (_toolCallId, params, signal) => {
|
|
6514
6763
|
const {
|
|
@@ -6519,9 +6768,11 @@ function createGrepTextTool(cwd, options) {
|
|
|
6519
6768
|
ignoreCase,
|
|
6520
6769
|
literal,
|
|
6521
6770
|
context: contextLines,
|
|
6771
|
+
outputMode: rawOutputMode,
|
|
6772
|
+
offset: rawOffset,
|
|
6522
6773
|
limit
|
|
6523
6774
|
} = params;
|
|
6524
|
-
return new Promise((
|
|
6775
|
+
return new Promise((resolve3, reject) => {
|
|
6525
6776
|
if (signal?.aborted) {
|
|
6526
6777
|
reject(new Error("Operation aborted"));
|
|
6527
6778
|
return;
|
|
@@ -6531,6 +6782,8 @@ function createGrepTextTool(cwd, options) {
|
|
|
6531
6782
|
(async () => {
|
|
6532
6783
|
try {
|
|
6533
6784
|
const searchPath = resolveToCwd(searchDir || ".", cwd, options, root);
|
|
6785
|
+
const outputMode = normalizeOutputMode(rawOutputMode);
|
|
6786
|
+
const offset = normalizeNonNegativeInteger(rawOffset, 0, "offset");
|
|
6534
6787
|
const effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT2);
|
|
6535
6788
|
const ctx = contextLines && contextLines > 0 ? contextLines : 0;
|
|
6536
6789
|
let isDir;
|
|
@@ -6558,6 +6811,8 @@ function createGrepTextTool(cwd, options) {
|
|
|
6558
6811
|
let matchCount = 0;
|
|
6559
6812
|
let matchLimitReached = false;
|
|
6560
6813
|
let linesTruncated = false;
|
|
6814
|
+
let filesScanned = 0;
|
|
6815
|
+
let totalFilesWithMatches = 0;
|
|
6561
6816
|
const isGitignored = await loadGitignoreMatcher(searchPath, fs2);
|
|
6562
6817
|
if (platform === "puerts" && getNativeFileBridge()) {
|
|
6563
6818
|
const nativeResult = await runNativeGrep(
|
|
@@ -6568,12 +6823,16 @@ function createGrepTextTool(cwd, options) {
|
|
|
6568
6823
|
!!literal,
|
|
6569
6824
|
ctx,
|
|
6570
6825
|
effectiveLimit,
|
|
6826
|
+
outputMode,
|
|
6827
|
+
offset,
|
|
6571
6828
|
signal
|
|
6572
6829
|
);
|
|
6573
6830
|
outputLines = nativeResult.Lines ?? [];
|
|
6574
|
-
matchCount = nativeResult.MatchCount ?? 0;
|
|
6831
|
+
matchCount = nativeResult.TotalMatches ?? nativeResult.MatchCount ?? 0;
|
|
6575
6832
|
matchLimitReached = !!nativeResult.MatchLimitReached;
|
|
6576
6833
|
linesTruncated = !!nativeResult.LinesTruncated;
|
|
6834
|
+
filesScanned = nativeResult.FilesScanned ?? 0;
|
|
6835
|
+
totalFilesWithMatches = nativeResult.TotalFilesWithMatches ?? 0;
|
|
6577
6836
|
globalThis.pieBridge?.log?.(
|
|
6578
6837
|
"info",
|
|
6579
6838
|
`[grep_text] path=${searchPath} pattern=${pattern} matches=${matchCount} files=${nativeResult.FilesScanned ?? 0}`
|
|
@@ -6586,11 +6845,9 @@ function createGrepTextTool(cwd, options) {
|
|
|
6586
6845
|
} else {
|
|
6587
6846
|
filePaths = [searchPath];
|
|
6588
6847
|
}
|
|
6848
|
+
const contentMatches = [];
|
|
6849
|
+
const fileSummaries = /* @__PURE__ */ new Map();
|
|
6589
6850
|
for (const filePath of filePaths) {
|
|
6590
|
-
if (matchCount >= effectiveLimit) {
|
|
6591
|
-
matchLimitReached = true;
|
|
6592
|
-
break;
|
|
6593
|
-
}
|
|
6594
6851
|
if (signal?.aborted) {
|
|
6595
6852
|
reject(new Error("Operation aborted"));
|
|
6596
6853
|
return;
|
|
@@ -6603,36 +6860,65 @@ function createGrepTextTool(cwd, options) {
|
|
|
6603
6860
|
}
|
|
6604
6861
|
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
6605
6862
|
const relativePath = isDir ? fs2.relative(searchPath, filePath) : fs2.basename(filePath);
|
|
6863
|
+
filesScanned++;
|
|
6864
|
+
let fileMatchCount = 0;
|
|
6606
6865
|
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
6607
|
-
if (matchCount >= effectiveLimit) {
|
|
6608
|
-
matchLimitReached = true;
|
|
6609
|
-
break;
|
|
6610
|
-
}
|
|
6611
6866
|
regex.lastIndex = 0;
|
|
6612
6867
|
if (regex.test(lines[lineIdx])) {
|
|
6613
6868
|
matchCount++;
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
const lineText = lines[c];
|
|
6618
|
-
const { text: truncatedText, wasTruncated } = truncateLine(lineText);
|
|
6619
|
-
if (wasTruncated) linesTruncated = true;
|
|
6620
|
-
const currentLineNum = c + 1;
|
|
6621
|
-
if (c === lineIdx) {
|
|
6622
|
-
outputLines.push(`${relativePath}:${currentLineNum}: ${truncatedText}`);
|
|
6623
|
-
} else {
|
|
6624
|
-
outputLines.push(`${relativePath}-${currentLineNum}- ${truncatedText}`);
|
|
6625
|
-
}
|
|
6869
|
+
fileMatchCount++;
|
|
6870
|
+
if (outputMode === "content" && matchCount > offset && matchCount <= offset + effectiveLimit) {
|
|
6871
|
+
contentMatches.push({ filePath, relativePath, lineIndex: lineIdx, lines });
|
|
6626
6872
|
}
|
|
6627
6873
|
}
|
|
6628
6874
|
}
|
|
6875
|
+
if (fileMatchCount > 0) {
|
|
6876
|
+
const fileStat = await fs2.stat(filePath);
|
|
6877
|
+
fileSummaries.set(filePath, {
|
|
6878
|
+
filePath,
|
|
6879
|
+
relativePath,
|
|
6880
|
+
mtimeMs: fileStat.mtime.getTime(),
|
|
6881
|
+
matchCount: fileMatchCount
|
|
6882
|
+
});
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
totalFilesWithMatches = fileSummaries.size;
|
|
6886
|
+
if (outputMode === "content") {
|
|
6887
|
+
matchLimitReached = matchCount > offset + effectiveLimit;
|
|
6888
|
+
const formatted = formatContentMatches(contentMatches.slice(0, effectiveLimit), ctx);
|
|
6889
|
+
outputLines = formatted.lines;
|
|
6890
|
+
linesTruncated = formatted.linesTruncated;
|
|
6891
|
+
} else {
|
|
6892
|
+
const sortedFiles = sortFileSummariesByMtime([...fileSummaries.values()]);
|
|
6893
|
+
const page = sortedFiles.slice(offset, offset + effectiveLimit);
|
|
6894
|
+
if (offset + effectiveLimit < sortedFiles.length) {
|
|
6895
|
+
matchLimitReached = true;
|
|
6896
|
+
}
|
|
6897
|
+
if (outputMode === "files_with_matches") {
|
|
6898
|
+
outputLines = page.map((file) => file.relativePath);
|
|
6899
|
+
} else {
|
|
6900
|
+
outputLines = [
|
|
6901
|
+
`Total matches: ${matchCount}`,
|
|
6902
|
+
`Files with matches: ${sortedFiles.length}`,
|
|
6903
|
+
...page.map((file) => `${file.relativePath}: ${file.matchCount}`)
|
|
6904
|
+
];
|
|
6905
|
+
}
|
|
6629
6906
|
}
|
|
6630
6907
|
}
|
|
6631
6908
|
signal?.removeEventListener("abort", onAbort);
|
|
6632
6909
|
if (matchCount === 0) {
|
|
6633
|
-
|
|
6910
|
+
resolve3({
|
|
6634
6911
|
content: [{ type: "text", text: "No matches found" }],
|
|
6635
|
-
details: {
|
|
6912
|
+
details: {
|
|
6913
|
+
noMatches: true,
|
|
6914
|
+
outputMode,
|
|
6915
|
+
totalMatches: 0,
|
|
6916
|
+
totalFilesWithMatches: 0,
|
|
6917
|
+
filesScanned,
|
|
6918
|
+
offset,
|
|
6919
|
+
limit: effectiveLimit,
|
|
6920
|
+
resultLimitReached: false
|
|
6921
|
+
}
|
|
6636
6922
|
});
|
|
6637
6923
|
return;
|
|
6638
6924
|
}
|
|
@@ -6640,10 +6926,17 @@ function createGrepTextTool(cwd, options) {
|
|
|
6640
6926
|
const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
6641
6927
|
let output = truncation.content;
|
|
6642
6928
|
const details = {};
|
|
6929
|
+
details.outputMode = outputMode;
|
|
6930
|
+
details.totalMatches = matchCount;
|
|
6931
|
+
details.totalFilesWithMatches = totalFilesWithMatches;
|
|
6932
|
+
details.filesScanned = filesScanned;
|
|
6933
|
+
details.offset = offset;
|
|
6934
|
+
details.limit = effectiveLimit;
|
|
6935
|
+
details.resultLimitReached = matchLimitReached;
|
|
6643
6936
|
const notices = [];
|
|
6644
6937
|
if (matchLimitReached) {
|
|
6645
6938
|
notices.push(
|
|
6646
|
-
`${effectiveLimit} matches limit reached. Use
|
|
6939
|
+
outputMode === "content" ? `${effectiveLimit} matches limit reached. Use offset=${offset + effectiveLimit} for more, increase limit, or refine pattern` : `${effectiveLimit} results limit reached. Use offset=${offset + effectiveLimit} for more, increase limit, or refine pattern`
|
|
6647
6940
|
);
|
|
6648
6941
|
details.matchLimitReached = effectiveLimit;
|
|
6649
6942
|
}
|
|
@@ -6662,7 +6955,7 @@ function createGrepTextTool(cwd, options) {
|
|
|
6662
6955
|
|
|
6663
6956
|
[${notices.join(". ")}]`;
|
|
6664
6957
|
}
|
|
6665
|
-
|
|
6958
|
+
resolve3({
|
|
6666
6959
|
content: [{ type: "text", text: output }],
|
|
6667
6960
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
6668
6961
|
});
|
|
@@ -6745,8 +7038,8 @@ function waitForFileCompletion2(requestId) {
|
|
|
6745
7038
|
if (existing) {
|
|
6746
7039
|
return Promise.resolve(existing);
|
|
6747
7040
|
}
|
|
6748
|
-
return new Promise((
|
|
6749
|
-
store.waiters.set(requestId,
|
|
7041
|
+
return new Promise((resolve3) => {
|
|
7042
|
+
store.waiters.set(requestId, resolve3);
|
|
6750
7043
|
});
|
|
6751
7044
|
}
|
|
6752
7045
|
async function runNativeFind(searchPath, pattern, limit, signal) {
|
|
@@ -6836,7 +7129,7 @@ function createFindFilesTool(cwd, options) {
|
|
|
6836
7129
|
parameters: findSchema,
|
|
6837
7130
|
execute: async (_toolCallId, params, signal) => {
|
|
6838
7131
|
const { pattern, path: searchDir, root, limit } = params;
|
|
6839
|
-
return new Promise((
|
|
7132
|
+
return new Promise((resolve3, reject) => {
|
|
6840
7133
|
if (signal?.aborted) {
|
|
6841
7134
|
reject(new Error("Operation aborted"));
|
|
6842
7135
|
return;
|
|
@@ -6852,10 +7145,12 @@ function createFindFilesTool(cwd, options) {
|
|
|
6852
7145
|
return;
|
|
6853
7146
|
}
|
|
6854
7147
|
let results;
|
|
7148
|
+
let resultLimitReached = false;
|
|
6855
7149
|
const isGitignored = await loadGitignoreMatcher(searchPath, fs2);
|
|
6856
7150
|
if (platform === "puerts" && getNativeFileBridge2()) {
|
|
6857
7151
|
const nativeResult = await runNativeFind(searchPath, pattern, effectiveLimit, signal);
|
|
6858
7152
|
results = (nativeResult.Results ?? []).filter((filePath) => !isGitignored(filePath, false));
|
|
7153
|
+
resultLimitReached = !!nativeResult.LimitReached && results.length >= effectiveLimit;
|
|
6859
7154
|
globalThis.pieBridge?.log?.(
|
|
6860
7155
|
"info",
|
|
6861
7156
|
`[find_files] path=${searchPath} pattern=${pattern} matches=${results.length} dirs=${nativeResult.ScannedDirectories ?? 0} files=${nativeResult.ScannedFiles ?? 0}`
|
|
@@ -6864,28 +7159,33 @@ function createFindFilesTool(cwd, options) {
|
|
|
6864
7159
|
try {
|
|
6865
7160
|
const _require = globalThis.require || __require;
|
|
6866
7161
|
const { globSync } = _require("glob");
|
|
6867
|
-
|
|
7162
|
+
const allResults = globSync(pattern, {
|
|
6868
7163
|
cwd: searchPath,
|
|
6869
7164
|
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
6870
7165
|
nodir: false,
|
|
6871
7166
|
absolute: false,
|
|
6872
7167
|
dot: true
|
|
6873
|
-
}).filter((filePath) => !isGitignored(filePath, false))
|
|
7168
|
+
}).filter((filePath) => !isGitignored(filePath, false));
|
|
7169
|
+
resultLimitReached = allResults.length > effectiveLimit;
|
|
7170
|
+
results = allResults.slice(0, effectiveLimit);
|
|
6874
7171
|
} catch {
|
|
6875
|
-
|
|
7172
|
+
const allResults = await globFiles(searchPath, pattern, fs2, effectiveLimit + 1, signal, isGitignored);
|
|
7173
|
+
resultLimitReached = allResults.length > effectiveLimit;
|
|
7174
|
+
results = allResults.slice(0, effectiveLimit);
|
|
6876
7175
|
}
|
|
6877
7176
|
} else {
|
|
6878
|
-
|
|
7177
|
+
const allResults = await globFiles(searchPath, pattern, fs2, effectiveLimit + 1, signal, isGitignored);
|
|
7178
|
+
resultLimitReached = allResults.length > effectiveLimit;
|
|
7179
|
+
results = allResults.slice(0, effectiveLimit);
|
|
6879
7180
|
}
|
|
6880
7181
|
signal?.removeEventListener("abort", onAbort);
|
|
6881
7182
|
if (results.length === 0) {
|
|
6882
|
-
|
|
7183
|
+
resolve3({
|
|
6883
7184
|
content: [{ type: "text", text: "No files found matching pattern" }],
|
|
6884
7185
|
details: { noMatches: true }
|
|
6885
7186
|
});
|
|
6886
7187
|
return;
|
|
6887
7188
|
}
|
|
6888
|
-
const resultLimitReached = results.length >= effectiveLimit;
|
|
6889
7189
|
const rawOutput = results.join("\n");
|
|
6890
7190
|
const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
|
|
6891
7191
|
let resultOutput = truncation.content;
|
|
@@ -6902,7 +7202,7 @@ function createFindFilesTool(cwd, options) {
|
|
|
6902
7202
|
if (notices.length > 0) {
|
|
6903
7203
|
resultOutput += "\n\n[" + notices.join(". ") + "]";
|
|
6904
7204
|
}
|
|
6905
|
-
|
|
7205
|
+
resolve3({
|
|
6906
7206
|
content: [{ type: "text", text: resultOutput }],
|
|
6907
7207
|
details: Object.keys(details).length > 0 ? details : void 0
|
|
6908
7208
|
});
|
|
@@ -6918,10 +7218,12 @@ function createFindFilesTool(cwd, options) {
|
|
|
6918
7218
|
|
|
6919
7219
|
// ../../packages/shared-headless-capabilities/src/builtin/fs/index.ts
|
|
6920
7220
|
function createSharedFileSystemTools(sandboxRoot, options) {
|
|
7221
|
+
const fileObservationState = options?.fileObservationState ?? createFileObservationState();
|
|
7222
|
+
const sharedOptions = { ...options, fileObservationState };
|
|
6921
7223
|
return [
|
|
6922
|
-
createReadTool(sandboxRoot,
|
|
6923
|
-
createWriteTool(sandboxRoot,
|
|
6924
|
-
createEditTool(sandboxRoot,
|
|
7224
|
+
createReadTool(sandboxRoot, sharedOptions),
|
|
7225
|
+
createWriteTool(sandboxRoot, sharedOptions),
|
|
7226
|
+
createEditTool(sandboxRoot, sharedOptions),
|
|
6925
7227
|
createLsTool(sandboxRoot, options),
|
|
6926
7228
|
createGrepTextTool(sandboxRoot, options),
|
|
6927
7229
|
createFindFilesTool(sandboxRoot, options)
|
|
@@ -7207,6 +7509,70 @@ function summarizeToolResults(value) {
|
|
|
7207
7509
|
}
|
|
7208
7510
|
return { count: value.length, errors, toolNames };
|
|
7209
7511
|
}
|
|
7512
|
+
function collectTextLength(content) {
|
|
7513
|
+
if (typeof content === "string") return content.length;
|
|
7514
|
+
if (!Array.isArray(content)) return 0;
|
|
7515
|
+
let length = 0;
|
|
7516
|
+
for (const block of content) {
|
|
7517
|
+
if (!isRecord(block)) continue;
|
|
7518
|
+
if (typeof block.text === "string") length += block.text.length;
|
|
7519
|
+
}
|
|
7520
|
+
return length;
|
|
7521
|
+
}
|
|
7522
|
+
function collectArgumentLength(content) {
|
|
7523
|
+
if (!Array.isArray(content)) return 0;
|
|
7524
|
+
let length = 0;
|
|
7525
|
+
for (const block of content) {
|
|
7526
|
+
if (!isRecord(block) || !("arguments" in block)) continue;
|
|
7527
|
+
if (typeof block.arguments === "string") {
|
|
7528
|
+
length += block.arguments.length;
|
|
7529
|
+
continue;
|
|
7530
|
+
}
|
|
7531
|
+
try {
|
|
7532
|
+
length += JSON.stringify(block.arguments).length;
|
|
7533
|
+
} catch {
|
|
7534
|
+
length += String(block.arguments).length;
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
return length;
|
|
7538
|
+
}
|
|
7539
|
+
function summarizeMessages(value) {
|
|
7540
|
+
if (!Array.isArray(value)) return typeof value;
|
|
7541
|
+
const roles = [];
|
|
7542
|
+
const toolNames = [];
|
|
7543
|
+
const stopReasons = [];
|
|
7544
|
+
let errorCount = 0;
|
|
7545
|
+
let contentBlocks = 0;
|
|
7546
|
+
let textLength = 0;
|
|
7547
|
+
let argumentLength = 0;
|
|
7548
|
+
for (const message of value) {
|
|
7549
|
+
if (!isRecord(message)) continue;
|
|
7550
|
+
const role = typeof message.role === "string" ? message.role : void 0;
|
|
7551
|
+
if (role && roles.length < 20) roles.push(role);
|
|
7552
|
+
if (typeof message.stopReason === "string" && stopReasons.length < 20) stopReasons.push(message.stopReason);
|
|
7553
|
+
if (typeof message.errorMessage === "string" && message.errorMessage) errorCount++;
|
|
7554
|
+
if (typeof message.toolName === "string" && toolNames.length < 20) toolNames.push(message.toolName);
|
|
7555
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
7556
|
+
contentBlocks += content.length;
|
|
7557
|
+
textLength += collectTextLength(message.content);
|
|
7558
|
+
argumentLength += collectArgumentLength(message.content);
|
|
7559
|
+
for (const block of content) {
|
|
7560
|
+
if (isRecord(block) && typeof block.name === "string" && toolNames.length < 20) {
|
|
7561
|
+
toolNames.push(block.name);
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
return {
|
|
7566
|
+
count: value.length,
|
|
7567
|
+
roles,
|
|
7568
|
+
toolNames: [...new Set(toolNames)].slice(0, 20),
|
|
7569
|
+
stopReasons: [...new Set(stopReasons)].slice(0, 20),
|
|
7570
|
+
errorCount,
|
|
7571
|
+
contentBlocks,
|
|
7572
|
+
textLength,
|
|
7573
|
+
argumentLength
|
|
7574
|
+
};
|
|
7575
|
+
}
|
|
7210
7576
|
function summarizeFailure(value) {
|
|
7211
7577
|
if (!isRecord(value)) return typeof value;
|
|
7212
7578
|
return {
|
|
@@ -7259,6 +7625,10 @@ function summarizeMeta(meta) {
|
|
|
7259
7625
|
summary[key] = Array.isArray(value) ? { count: value.length } : typeof value;
|
|
7260
7626
|
continue;
|
|
7261
7627
|
}
|
|
7628
|
+
if (key === "addedMessages") {
|
|
7629
|
+
summary[key] = summarizeMessages(value);
|
|
7630
|
+
continue;
|
|
7631
|
+
}
|
|
7262
7632
|
if (key === "message") {
|
|
7263
7633
|
summary[key] = summarizeMessage(value);
|
|
7264
7634
|
continue;
|
|
@@ -8458,8 +8828,8 @@ async function runQueuedSearch(model, fn) {
|
|
|
8458
8828
|
const key = `${model.provider}/${model.id}`;
|
|
8459
8829
|
const previous = SEARCH_PROVIDER_QUEUE.get(key) ?? Promise.resolve();
|
|
8460
8830
|
let release;
|
|
8461
|
-
const next = new Promise((
|
|
8462
|
-
release =
|
|
8831
|
+
const next = new Promise((resolve3) => {
|
|
8832
|
+
release = resolve3;
|
|
8463
8833
|
});
|
|
8464
8834
|
const queued = previous.catch(() => void 0).then(() => next);
|
|
8465
8835
|
SEARCH_PROVIDER_QUEUE.set(key, queued);
|
|
@@ -8830,10 +9200,10 @@ function assistantText(message) {
|
|
|
8830
9200
|
async function withAbort(promise, signal) {
|
|
8831
9201
|
if (!signal) return promise;
|
|
8832
9202
|
if (signal.aborted) throw signal.reason ?? new Error("operation aborted");
|
|
8833
|
-
return await new Promise((
|
|
9203
|
+
return await new Promise((resolve3, reject) => {
|
|
8834
9204
|
const onAbort = () => reject(signal.reason ?? new Error("operation aborted"));
|
|
8835
9205
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
8836
|
-
promise.then(
|
|
9206
|
+
promise.then(resolve3, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
8837
9207
|
});
|
|
8838
9208
|
}
|
|
8839
9209
|
async function applyPromptWithModel(deps, prompt, url, content, signal) {
|
|
@@ -9334,13 +9704,13 @@ async function runWithDeadline(operation, timeoutMs, message, parentSignal) {
|
|
|
9334
9704
|
const child = createChildSignal(parentSignal);
|
|
9335
9705
|
let timer;
|
|
9336
9706
|
try {
|
|
9337
|
-
return await new Promise((
|
|
9707
|
+
return await new Promise((resolve3, reject) => {
|
|
9338
9708
|
timer = setTimeout(() => {
|
|
9339
9709
|
const error = new Error(message);
|
|
9340
9710
|
child.abort(error);
|
|
9341
9711
|
reject(error);
|
|
9342
9712
|
}, timeoutMs);
|
|
9343
|
-
operation(child.signal).then(
|
|
9713
|
+
operation(child.signal).then(resolve3, reject);
|
|
9344
9714
|
});
|
|
9345
9715
|
} finally {
|
|
9346
9716
|
if (timer) clearTimeout(timer);
|
|
@@ -9904,6 +10274,7 @@ export {
|
|
|
9904
10274
|
buildProjectContextSection,
|
|
9905
10275
|
createAskUserCapability,
|
|
9906
10276
|
createPolicyEnforcedTools,
|
|
10277
|
+
createFileObservationState,
|
|
9907
10278
|
createSharedFileSystemTools,
|
|
9908
10279
|
buildToolsPromptSection,
|
|
9909
10280
|
createSharedWebSearchTool,
|