@cydm/pie 1.0.23 → 1.0.25

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 (28) hide show
  1. package/README.md +51 -5
  2. package/dist/builtin/extensions/ask-user/index.js +3 -3
  3. package/dist/builtin/extensions/ask-user/package.json +16 -0
  4. package/dist/builtin/extensions/changelog/package.json +11 -0
  5. package/dist/builtin/extensions/deploy/package.json +11 -0
  6. package/dist/builtin/extensions/document-attachments/package.json +11 -0
  7. package/dist/builtin/extensions/files/package.json +14 -0
  8. package/dist/builtin/extensions/init/package.json +11 -0
  9. package/dist/builtin/extensions/kimi-attachments/package.json +11 -0
  10. package/dist/builtin/extensions/plan-mode/index.js +3 -3
  11. package/dist/builtin/extensions/plan-mode/package.json +11 -0
  12. package/dist/builtin/extensions/subagent/index.js +4 -4
  13. package/dist/builtin/extensions/subagent/package.json +14 -0
  14. package/dist/builtin/extensions/todo/index.js +3 -3
  15. package/dist/builtin/extensions/todo/package.json +11 -0
  16. package/dist/builtin/skills/browser-tools/README.md +1 -1
  17. package/dist/builtin/skills/browser-tools/SKILL.md +3 -3
  18. package/dist/builtin/skills/browser-tools/playwright-cli.js +22 -1
  19. package/dist/chunks/{chunk-PUZP2Q3F.js → chunk-4B42ZU4B.js} +1 -1
  20. package/dist/chunks/{chunk-7ABSJC2D.js → chunk-64XJIKVK.js} +371 -120
  21. package/dist/chunks/{chunk-4WU5VAX4.js → chunk-BEWVRLCP.js} +387 -230
  22. package/dist/chunks/{chunk-AFTBFF6J.js → chunk-O4DPFYYP.js} +3425 -416
  23. package/dist/chunks/{src-UCXJ4C6P.js → src-27UD2ZBZ.js} +2 -2
  24. package/dist/chunks/{test-stream-UKULOPZB.js → test-stream-ZNGAUUVH.js} +6 -2
  25. package/dist/cli.js +3574 -2050
  26. package/dist/{chunks/typescript-GSKWJIO4.js → code-intel-worker.cjs} +2507 -2031
  27. package/models.schema.json +38 -13
  28. package/package.json +11 -7
@@ -3270,7 +3270,8 @@ function convertStats(stats) {
3270
3270
  isDirectory: stats.isDirectory(),
3271
3271
  isFile: stats.isFile(),
3272
3272
  size: stats.size,
3273
- mtime: stats.mtime
3273
+ mtime: stats.mtime,
3274
+ isSymbolicLink: stats.isSymbolicLink()
3274
3275
  };
3275
3276
  }
3276
3277
  function byteLength(text) {
@@ -3284,6 +3285,40 @@ function normalizeRangeOptions(options) {
3284
3285
  maxOutputBytes: Math.max(0, Math.floor(options?.maxOutputBytes ?? Number.POSITIVE_INFINITY))
3285
3286
  };
3286
3287
  }
3288
+ var StreamingBinaryDetector = class {
3289
+ decoder = new TextDecoder("utf-8", { fatal: true });
3290
+ byteCount = 0;
3291
+ suspiciousControlCount = 0;
3292
+ invalidUtf8 = false;
3293
+ hasNul = false;
3294
+ update(bytes) {
3295
+ if (bytes.byteLength === 0) return;
3296
+ this.byteCount += bytes.byteLength;
3297
+ for (const byte of bytes) {
3298
+ if (byte === 0) this.hasNul = true;
3299
+ if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13 || byte === 127) {
3300
+ this.suspiciousControlCount++;
3301
+ }
3302
+ }
3303
+ if (!this.invalidUtf8) {
3304
+ try {
3305
+ this.decoder.decode(bytes, { stream: true });
3306
+ } catch {
3307
+ this.invalidUtf8 = true;
3308
+ }
3309
+ }
3310
+ }
3311
+ finish() {
3312
+ if (!this.invalidUtf8) {
3313
+ try {
3314
+ this.decoder.decode();
3315
+ } catch {
3316
+ this.invalidUtf8 = true;
3317
+ }
3318
+ }
3319
+ return this.invalidUtf8 || this.hasNul || this.byteCount > 0 && this.suspiciousControlCount / this.byteCount > 0.02;
3320
+ }
3321
+ };
3287
3322
  var NodeFileSystem = class {
3288
3323
  platform = "node";
3289
3324
  sep;
@@ -3298,6 +3333,18 @@ var NodeFileSystem = class {
3298
3333
  readFileSync(path, encoding = "utf-8") {
3299
3334
  return getNodeFs().readFileSync(path, encoding);
3300
3335
  }
3336
+ async readFilePrefix(path, maxBytes) {
3337
+ const length = Math.max(0, Math.floor(maxBytes));
3338
+ if (length === 0) return new Uint8Array();
3339
+ const handle = await getNodeFs().promises.open(path, "r");
3340
+ try {
3341
+ const buffer = Buffer.allocUnsafe(length);
3342
+ const { bytesRead } = await handle.read(buffer, 0, length, 0);
3343
+ return new Uint8Array(buffer.subarray(0, bytesRead));
3344
+ } finally {
3345
+ await handle.close();
3346
+ }
3347
+ }
3301
3348
  async readTextRange(path, options, signal) {
3302
3349
  const fs = getNodeFs();
3303
3350
  const range = normalizeRangeOptions(options);
@@ -3320,6 +3367,10 @@ var NodeFileSystem = class {
3320
3367
  let truncated = false;
3321
3368
  let truncatedBy = null;
3322
3369
  let firstLineExceedsLimit = false;
3370
+ const fileBinaryDetector = new StreamingBinaryDetector();
3371
+ const selectedBinaryDetector = new StreamingBinaryDetector();
3372
+ const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true });
3373
+ let rawLineIndex = 0;
3323
3374
  const selectedEndLine = Number.isFinite(range.limitLines) ? range.offsetLine + range.limitLines : Number.POSITIVE_INFINITY;
3324
3375
  const startLine = () => {
3325
3376
  if (lineStarted) return;
@@ -3395,23 +3446,41 @@ var NodeFileSystem = class {
3395
3446
  lineStarted = false;
3396
3447
  lineIndex++;
3397
3448
  };
3398
- const stream = fs.createReadStream(path, { encoding: "utf-8" });
3449
+ const scanRawBytes = (bytes) => {
3450
+ fileBinaryDetector.update(bytes);
3451
+ let segmentStart = 0;
3452
+ for (let index = 0; index < bytes.byteLength; index++) {
3453
+ if (bytes[index] !== 10) continue;
3454
+ if (rawLineIndex >= range.offsetLine && rawLineIndex < selectedEndLine) {
3455
+ selectedBinaryDetector.update(bytes.subarray(segmentStart, index + 1));
3456
+ }
3457
+ rawLineIndex++;
3458
+ segmentStart = index + 1;
3459
+ }
3460
+ if (segmentStart < bytes.byteLength && rawLineIndex >= range.offsetLine && rawLineIndex < selectedEndLine) {
3461
+ selectedBinaryDetector.update(bytes.subarray(segmentStart));
3462
+ }
3463
+ };
3464
+ const scanText = (text) => {
3465
+ if (!text) return;
3466
+ fullHasher.update(text);
3467
+ for (const char of text) {
3468
+ if (char === "\n") finishLine();
3469
+ else scanLineChar(char);
3470
+ }
3471
+ };
3472
+ const stream = fs.createReadStream(path);
3399
3473
  try {
3400
3474
  for await (const chunk of stream) {
3401
3475
  if (signal?.aborted) {
3402
3476
  stream.destroy();
3403
3477
  throw new Error("Operation aborted");
3404
3478
  }
3405
- const text = String(chunk);
3406
- fullHasher.update(text);
3407
- for (const char of text) {
3408
- if (char === "\n") {
3409
- finishLine();
3410
- } else {
3411
- scanLineChar(char);
3412
- }
3413
- }
3479
+ const bytes = chunk;
3480
+ scanRawBytes(bytes);
3481
+ scanText(textDecoder.decode(bytes, { stream: true }));
3414
3482
  }
3483
+ scanText(textDecoder.decode());
3415
3484
  finishLine();
3416
3485
  } finally {
3417
3486
  stream.destroy();
@@ -3430,6 +3499,8 @@ var NodeFileSystem = class {
3430
3499
  truncated,
3431
3500
  truncatedBy,
3432
3501
  firstLineExceedsLimit,
3502
+ selectedBytesLookBinary: selectedBinaryDetector.finish(),
3503
+ fileBytesLookBinary: fileBinaryDetector.finish(),
3433
3504
  mtimeMs: stat.mtime.getTime(),
3434
3505
  size: stat.size
3435
3506
  };
@@ -3450,6 +3521,28 @@ var NodeFileSystem = class {
3450
3521
  async readdir(path) {
3451
3522
  return getNodeFs().promises.readdir(path);
3452
3523
  }
3524
+ async readDirectoryBounded(path, limit, signal) {
3525
+ if (!Number.isSafeInteger(limit) || limit < 0) throw new Error("directory entry limit must be a non-negative safe integer");
3526
+ if (signal?.aborted) throw new Error("Operation aborted");
3527
+ const entries = [];
3528
+ const directory = await getNodeFs().promises.opendir(path);
3529
+ let limitReached = false;
3530
+ try {
3531
+ while (entries.length <= limit) {
3532
+ if (signal?.aborted) throw new Error("Operation aborted");
3533
+ const entry = await directory.read();
3534
+ if (!entry) break;
3535
+ if (entries.length === limit) {
3536
+ limitReached = true;
3537
+ break;
3538
+ }
3539
+ entries.push(entry.name);
3540
+ }
3541
+ } finally {
3542
+ await directory.close().catch(() => void 0);
3543
+ }
3544
+ return { entries, limitReached };
3545
+ }
3453
3546
  readdirSync(path) {
3454
3547
  return getNodeFs().readdirSync(path);
3455
3548
  }
@@ -3477,6 +3570,15 @@ var NodeFileSystem = class {
3477
3570
  entry.name = getNodePath().basename(path);
3478
3571
  return entry;
3479
3572
  }
3573
+ lstatSync(path) {
3574
+ const stats = getNodeFs().lstatSync(path);
3575
+ const entry = convertStats(stats);
3576
+ entry.name = getNodePath().basename(path);
3577
+ return entry;
3578
+ }
3579
+ realpathSync(path) {
3580
+ return getNodeFs().realpathSync(path);
3581
+ }
3480
3582
  async access(path, mode) {
3481
3583
  await getNodeFs().promises.access(path, mode);
3482
3584
  }
@@ -3537,6 +3639,35 @@ async function taskToPromise(task) {
3537
3639
  }
3538
3640
  return task;
3539
3641
  }
3642
+ function getFileCompletionStore() {
3643
+ const globalValue = globalThis;
3644
+ if (!globalValue.__pieFileCompletionStore) {
3645
+ globalValue.__pieFileCompletionStore = { completions: /* @__PURE__ */ new Map(), waiters: /* @__PURE__ */ new Map() };
3646
+ }
3647
+ if (!globalValue._pieFileComplete) {
3648
+ globalValue._pieFileComplete = (requestId, resultJson, error) => {
3649
+ const store = getFileCompletionStore();
3650
+ const completion = { resultJson, error };
3651
+ const waiter = store.waiters.get(requestId);
3652
+ if (waiter) {
3653
+ store.waiters.delete(requestId);
3654
+ waiter(completion);
3655
+ } else {
3656
+ store.completions.set(requestId, completion);
3657
+ }
3658
+ };
3659
+ }
3660
+ return globalValue.__pieFileCompletionStore;
3661
+ }
3662
+ function waitForFileCompletion(requestId) {
3663
+ const store = getFileCompletionStore();
3664
+ const existing = store.completions.get(requestId);
3665
+ if (existing) {
3666
+ store.completions.delete(requestId);
3667
+ return Promise.resolve(existing);
3668
+ }
3669
+ return new Promise((resolve) => store.waiters.set(requestId, resolve));
3670
+ }
3540
3671
  function toJsStringArray(value) {
3541
3672
  if (Array.isArray(value)) {
3542
3673
  return value.map((entry) => String(entry ?? ""));
@@ -3566,6 +3697,11 @@ function normalizeOptionalNumber(value, fallback) {
3566
3697
  }
3567
3698
  return Math.max(0, Math.floor(value));
3568
3699
  }
3700
+ function normalizeInt32(value, fallback) {
3701
+ const normalized = normalizeOptionalNumber(value, fallback);
3702
+ if (!Number.isSafeInteger(normalized) || normalized > 2147483647) throw new Error("Unity range-read arguments exceed Int32 bounds");
3703
+ return normalized;
3704
+ }
3569
3705
  function convertTextRangeResult(result) {
3570
3706
  return {
3571
3707
  content: String(result?.Content ?? ""),
@@ -3581,66 +3717,12 @@ function convertTextRangeResult(result) {
3581
3717
  truncated: !!result?.Truncated,
3582
3718
  truncatedBy: result?.TruncatedBy ? String(result.TruncatedBy) : null,
3583
3719
  firstLineExceedsLimit: !!result?.FirstLineExceedsLimit,
3720
+ ...typeof result?.SelectedBytesLookBinary === "boolean" ? { selectedBytesLookBinary: result.SelectedBytesLookBinary } : {},
3721
+ ...typeof result?.FileBytesLookBinary === "boolean" ? { fileBytesLookBinary: result.FileBytesLookBinary } : {},
3584
3722
  mtimeMs: (Number(result?.LastWriteTicksUtc || 0) - 621355968e9) / 1e4,
3585
3723
  size: Number(result?.Size ?? 0)
3586
3724
  };
3587
3725
  }
3588
- function byteLength2(text) {
3589
- if (typeof Buffer !== "undefined") {
3590
- return Buffer.byteLength(text, "utf-8");
3591
- }
3592
- return unescape(encodeURIComponent(text)).length;
3593
- }
3594
- function buildTextRangeFallback(content, stat, offsetLine, limitLines, maxOutputLines, maxOutputBytes) {
3595
- const lines = content.split("\n");
3596
- const endLine = Number.isFinite(limitLines) ? Math.min(lines.length, offsetLine + limitLines) : lines.length;
3597
- const selectedLinesArray = offsetLine >= lines.length ? [] : lines.slice(offsetLine, endLine);
3598
- const selectedContent = selectedLinesArray.join("\n");
3599
- const outputLines = [];
3600
- let outputBytes = 0;
3601
- let truncated = false;
3602
- let truncatedBy = null;
3603
- let firstLineExceedsLimit = false;
3604
- for (const line of selectedLinesArray) {
3605
- const lineBytes = byteLength2(line);
3606
- if (outputLines.length === 0 && lineBytes > maxOutputBytes) {
3607
- firstLineExceedsLimit = true;
3608
- truncated = true;
3609
- truncatedBy = "bytes";
3610
- break;
3611
- }
3612
- if (outputLines.length >= maxOutputLines) {
3613
- truncated = true;
3614
- truncatedBy = "lines";
3615
- break;
3616
- }
3617
- const candidateBytes = lineBytes + (outputLines.length > 0 ? 1 : 0);
3618
- if (outputBytes + candidateBytes > maxOutputBytes) {
3619
- truncated = true;
3620
- truncatedBy = "bytes";
3621
- break;
3622
- }
3623
- outputLines.push(line);
3624
- outputBytes += candidateBytes;
3625
- }
3626
- return {
3627
- content: outputLines.join("\n"),
3628
- contentHash: hashText(content),
3629
- selectedContentHash: hashText(selectedContent),
3630
- totalLines: lines.length,
3631
- totalBytes: stat.size,
3632
- selectedLines: selectedLinesArray.length,
3633
- selectedBytes: byteLength2(selectedContent),
3634
- firstLineBytes: selectedLinesArray.length > 0 ? byteLength2(selectedLinesArray[0]) : 0,
3635
- outputLines: outputLines.length,
3636
- outputBytes,
3637
- truncated,
3638
- truncatedBy,
3639
- firstLineExceedsLimit,
3640
- mtimeMs: stat.mtime.getTime(),
3641
- size: stat.size
3642
- };
3643
- }
3644
3726
  function convertDateTime(dateTime) {
3645
3727
  if (!dateTime) return /* @__PURE__ */ new Date(0);
3646
3728
  const ticks = dateTime.get_Ticks?.() || 0;
@@ -3667,6 +3749,9 @@ function convertFileSystemInfo(info) {
3667
3749
  mtime: info.LastWriteTimeUtc ? convertDateTime(info.LastWriteTimeUtc) : /* @__PURE__ */ new Date(0)
3668
3750
  };
3669
3751
  }
3752
+ function fileNameFromPath(IO, path) {
3753
+ return String(IO.Path.GetFileName(path) || IO.Path.GetPathRoot(path) || path);
3754
+ }
3670
3755
  var PuerTSFileSystem = class {
3671
3756
  platform = "puerts";
3672
3757
  sep = "/";
@@ -3706,32 +3791,62 @@ var PuerTSFileSystem = class {
3706
3791
  }
3707
3792
  return IO.File.ReadAllText(normalizedPath);
3708
3793
  }
3794
+ async readFilePrefix(path, maxBytes) {
3795
+ const IO = getSystemIO();
3796
+ const normalizedPath = this.normalizePath(path);
3797
+ if (!IO.File.Exists(normalizedPath)) {
3798
+ throw new Error(`ENOENT: no such file or directory, open '${path}'`);
3799
+ }
3800
+ const length = Math.max(0, Math.floor(maxBytes));
3801
+ if (length === 0) return new Uint8Array();
3802
+ const stream = IO.File.OpenRead(normalizedPath);
3803
+ try {
3804
+ const count = Math.min(length, Number(stream.Length));
3805
+ const buffer = CS.System.Array.CreateInstance(CS.System.Byte, count);
3806
+ const bytesRead = Number(stream.Read(buffer, 0, count));
3807
+ const result = new Uint8Array(bytesRead);
3808
+ for (let index = 0; index < bytesRead; index += 1) {
3809
+ result[index] = Number(buffer.GetValue(index));
3810
+ }
3811
+ return result;
3812
+ } finally {
3813
+ stream.Dispose();
3814
+ }
3815
+ }
3709
3816
  async readTextRange(path, options, signal) {
3710
3817
  if (signal?.aborted) {
3711
3818
  throw new Error("Operation aborted");
3712
3819
  }
3713
3820
  const bridge = getPieFileBridge();
3714
3821
  const normalizedPath = this.normalizePath(path);
3715
- if (typeof bridge.ReadTextRangeAsync !== "function") {
3716
- const content = await this.readFile(path, "utf-8");
3717
- const stat = await this.stat(path);
3718
- const offsetLine = normalizeOptionalNumber(options?.offsetLine, 0);
3719
- const limitLines = options?.limitLines === void 0 ? Number.POSITIVE_INFINITY : normalizeOptionalNumber(options.limitLines, 0);
3720
- const maxOutputLines = normalizeOptionalNumber(options?.maxOutputLines, Number.POSITIVE_INFINITY);
3721
- const maxOutputBytes = normalizeOptionalNumber(options?.maxOutputBytes, Number.POSITIVE_INFINITY);
3722
- return buildTextRangeFallback(content, stat, offsetLine, limitLines, maxOutputLines, maxOutputBytes);
3723
- }
3724
- const payload = await taskToPromise(bridge.ReadTextRangeAsync(
3822
+ if (typeof bridge.StartReadTextRange !== "function" || typeof bridge.CancelRequest !== "function" || typeof bridge.ReleaseRequest !== "function") {
3823
+ throw new Error("The Unity host does not provide the cancellable bounded range-read bridge required by read_file.");
3824
+ }
3825
+ const requestId = Number(bridge.StartReadTextRange(
3725
3826
  normalizedPath,
3726
- normalizeOptionalNumber(options?.offsetLine, 0),
3727
- options?.limitLines === void 0 ? -1 : normalizeOptionalNumber(options.limitLines, 0),
3728
- normalizeOptionalNumber(options?.maxOutputLines, -1),
3729
- normalizeOptionalNumber(options?.maxOutputBytes, -1)
3827
+ normalizeInt32(options?.offsetLine, 0),
3828
+ options?.limitLines === void 0 ? -1 : normalizeInt32(options.limitLines, 0),
3829
+ normalizeInt32(options?.maxOutputLines, -1),
3830
+ normalizeInt32(options?.maxOutputBytes, -1)
3730
3831
  ));
3731
- if (signal?.aborted) {
3732
- throw new Error("Operation aborted");
3832
+ if (!Number.isSafeInteger(requestId) || requestId <= 0) {
3833
+ throw new Error("The Unity range-read bridge returned an invalid request id.");
3834
+ }
3835
+ const onAbort = () => bridge.CancelRequest(requestId);
3836
+ signal?.addEventListener("abort", onAbort, { once: true });
3837
+ try {
3838
+ if (signal?.aborted) bridge.CancelRequest(requestId);
3839
+ const completion = await waitForFileCompletion(requestId);
3840
+ if (completion.error) throw new Error(completion.error);
3841
+ if (signal?.aborted) throw new Error("Operation aborted");
3842
+ return convertTextRangeResult(JSON.parse(String(completion.resultJson ?? "{}")));
3843
+ } finally {
3844
+ const store = getFileCompletionStore();
3845
+ store.completions.delete(requestId);
3846
+ store.waiters.delete(requestId);
3847
+ signal?.removeEventListener("abort", onAbort);
3848
+ bridge.ReleaseRequest(requestId);
3733
3849
  }
3734
- return convertTextRangeResult(JSON.parse(String(payload ?? "{}")));
3735
3850
  }
3736
3851
  async writeFile(path, data, encoding = "utf-8") {
3737
3852
  const bridge = getPieFileBridge();
@@ -3789,6 +3904,32 @@ var PuerTSFileSystem = class {
3789
3904
  const entries = await taskToPromise(bridge.ReadDirectoryAsync(normalizedPath));
3790
3905
  return toJsStringArray(entries);
3791
3906
  }
3907
+ async readDirectoryBounded(path, limit, signal) {
3908
+ if (!Number.isSafeInteger(limit) || limit < 0 || limit > 2147483647) throw new Error("directory entry limit must be between 0 and 2147483647");
3909
+ if (signal?.aborted) throw new Error("Operation aborted");
3910
+ const IO = getSystemIO();
3911
+ const bridge = getPieFileBridge();
3912
+ const normalizedPath = this.normalizePath(path);
3913
+ if (!IO.Directory.Exists(normalizedPath)) throw new Error(`ENOENT: no such file or directory, scandir '${path}'`);
3914
+ if (typeof bridge.StartReadDirectoryBounded !== "function") throw new Error("The Unity host does not provide bounded cancellable directory enumeration.");
3915
+ const requestId = Number(bridge.StartReadDirectoryBounded(normalizedPath, limit));
3916
+ const onAbort = () => bridge.CancelRequest(requestId);
3917
+ signal?.addEventListener("abort", onAbort, { once: true });
3918
+ try {
3919
+ if (signal?.aborted) bridge.CancelRequest(requestId);
3920
+ const completion = await waitForFileCompletion(requestId);
3921
+ if (completion.error) throw new Error(completion.error);
3922
+ if (signal?.aborted) throw new Error("Operation aborted");
3923
+ const result = JSON.parse(String(completion.resultJson ?? "{}"));
3924
+ return { entries: toJsStringArray(result?.Entries), limitReached: !!result?.LimitReached };
3925
+ } finally {
3926
+ const store = getFileCompletionStore();
3927
+ store.completions.delete(requestId);
3928
+ store.waiters.delete(requestId);
3929
+ signal?.removeEventListener("abort", onAbort);
3930
+ bridge.ReleaseRequest(requestId);
3931
+ }
3932
+ }
3792
3933
  readdirSync(path) {
3793
3934
  const IO = getSystemIO();
3794
3935
  const normalizedPath = this.normalizePath(path);
@@ -3840,6 +3981,49 @@ var PuerTSFileSystem = class {
3840
3981
  }
3841
3982
  throw new Error(`ENOENT: no such file or directory, stat '${path}'`);
3842
3983
  }
3984
+ lstatSync(path) {
3985
+ const IO = getSystemIO();
3986
+ const normalizedPath = this.normalizePath(path);
3987
+ let attributes;
3988
+ try {
3989
+ attributes = Number(IO.File.GetAttributes(normalizedPath));
3990
+ } catch {
3991
+ throw new Error(`ENOENT: no such file or directory, lstat '${path}'`);
3992
+ }
3993
+ const isSymbolicLink = (attributes & Number(IO.FileAttributes.ReparsePoint)) !== 0;
3994
+ if (!isSymbolicLink) {
3995
+ return this.statSync(path);
3996
+ }
3997
+ return {
3998
+ name: fileNameFromPath(IO, normalizedPath),
3999
+ isDirectory: false,
4000
+ isFile: false,
4001
+ isSymbolicLink: true,
4002
+ size: 0,
4003
+ mtime: /* @__PURE__ */ new Date(0)
4004
+ };
4005
+ }
4006
+ realpathSync(path) {
4007
+ const IO = getSystemIO();
4008
+ const fullPath = this.normalizePath(String(IO.Path.GetFullPath(this.normalizePath(path))));
4009
+ const root = this.normalizePath(String(IO.Path.GetPathRoot(fullPath) || ""));
4010
+ const relative = fullPath.slice(root.length);
4011
+ const parts = relative.split("/").filter(Boolean);
4012
+ let cursor = root;
4013
+ for (const part of parts) {
4014
+ cursor = cursor ? this.normalizePath(String(IO.Path.Combine(cursor, part))) : part;
4015
+ let attributes;
4016
+ try {
4017
+ attributes = Number(IO.File.GetAttributes(cursor));
4018
+ } catch {
4019
+ throw new Error(`ENOENT: no such file or directory, realpath '${path}'`);
4020
+ }
4021
+ if ((attributes & Number(IO.FileAttributes.ReparsePoint)) !== 0) {
4022
+ throw new Error(`Symbolic links are not supported by the Unity filesystem sandbox: ${cursor}`);
4023
+ }
4024
+ }
4025
+ return fullPath;
4026
+ }
3843
4027
  async access(path, mode) {
3844
4028
  const IO = getSystemIO();
3845
4029
  const normalizedPath = this.normalizePath(path);
@@ -5369,6 +5553,54 @@ function logEmptyAssistantContentError(error, model, options) {
5369
5553
  }
5370
5554
 
5371
5555
  // ../../packages/ai/src/providers/openai-compat.ts
5556
+ function normalizeTokenCount(value) {
5557
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
5558
+ return void 0;
5559
+ }
5560
+ return Math.floor(value);
5561
+ }
5562
+ function mergeOpenAIUsage(previous, usage) {
5563
+ return {
5564
+ promptTokens: normalizeTokenCount(usage?.prompt_tokens) ?? previous.promptTokens,
5565
+ completionTokens: normalizeTokenCount(usage?.completion_tokens) ?? previous.completionTokens,
5566
+ cachedTokens: normalizeTokenCount(usage?.prompt_tokens_details?.cached_tokens) ?? previous.cachedTokens,
5567
+ reasoningTokens: normalizeTokenCount(usage?.completion_tokens_details?.reasoning_tokens) ?? previous.reasoningTokens,
5568
+ totalTokens: normalizeTokenCount(usage?.total_tokens) ?? previous.totalTokens
5569
+ };
5570
+ }
5571
+ function normalizeOpenAIUsage(usage) {
5572
+ const reportedPromptTokens = usage.promptTokens;
5573
+ const reportedCachedTokens = usage.cachedTokens;
5574
+ const reportedCompletionTokens = usage.completionTokens;
5575
+ const reportedReasoningTokens = usage.reasoningTokens;
5576
+ const reportedTotalTokens = usage.totalTokens;
5577
+ const cachedTokens = reportedCachedTokens ?? 0;
5578
+ const promptTokens = reportedPromptTokens ?? (reportedTotalTokens !== void 0 && reportedCompletionTokens !== void 0 ? Math.max(cachedTokens, reportedTotalTokens - reportedCompletionTokens) : cachedTokens);
5579
+ const cacheRead = Math.min(cachedTokens, promptTokens);
5580
+ const input = promptTokens - cacheRead;
5581
+ const output = reportedCompletionTokens ?? (reportedTotalTokens !== void 0 ? Math.max(reportedReasoningTokens ?? 0, reportedTotalTokens - promptTokens) : reportedReasoningTokens ?? 0);
5582
+ const partitionTotal = promptTokens + output;
5583
+ return {
5584
+ input,
5585
+ output,
5586
+ cacheRead,
5587
+ totalTokens: Math.max(reportedTotalTokens ?? 0, partitionTotal)
5588
+ };
5589
+ }
5590
+ function formatOpenAIStreamError(error) {
5591
+ if (!error || typeof error !== "object") {
5592
+ return typeof error === "string" ? error : "OpenAI-compatible stream returned an error";
5593
+ }
5594
+ const message = typeof error.message === "string" && error.message.length > 0 ? error.message : "OpenAI-compatible stream returned an error";
5595
+ const metadata = [];
5596
+ if (typeof error.type === "string" && error.type.length > 0) {
5597
+ metadata.push(`type=${error.type}`);
5598
+ }
5599
+ if (typeof error.code === "string" && error.code.length > 0) {
5600
+ metadata.push(`code=${error.code}`);
5601
+ }
5602
+ return metadata.length > 0 ? `${message} (${metadata.join(", ")})` : message;
5603
+ }
5372
5604
  var streamOpenAICompletions = (model, context, options) => {
5373
5605
  const stream = new AssistantMessageEventStream();
5374
5606
  (async () => {
@@ -5418,6 +5650,7 @@ var streamOpenAICompletions = (model, context, options) => {
5418
5650
  let currentBlock = null;
5419
5651
  const blocks = output.content;
5420
5652
  const blockIndex = () => blocks.length - 1;
5653
+ let usageSnapshot = {};
5421
5654
  const finishCurrentBlock = (block) => {
5422
5655
  if (block) {
5423
5656
  if (block.type === "text") {
@@ -5462,26 +5695,28 @@ var streamOpenAICompletions = (model, context, options) => {
5462
5695
  } catch {
5463
5696
  continue;
5464
5697
  }
5698
+ if (chunk?.error) {
5699
+ throw new Error(formatOpenAIStreamError(chunk.error));
5700
+ }
5465
5701
  if (chunk.usage) {
5466
- const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens || 0;
5467
- const reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens || 0;
5468
- const input = (chunk.usage.prompt_tokens || 0) - cachedTokens;
5469
- const outputTokens = (chunk.usage.completion_tokens || 0) + reasoningTokens;
5702
+ usageSnapshot = mergeOpenAIUsage(usageSnapshot, chunk.usage);
5703
+ const normalizedUsage = normalizeOpenAIUsage(usageSnapshot);
5470
5704
  output.usage = {
5471
- input,
5472
- output: outputTokens,
5473
- cacheRead: cachedTokens,
5705
+ input: normalizedUsage.input,
5706
+ output: normalizedUsage.output,
5707
+ cacheRead: normalizedUsage.cacheRead,
5474
5708
  cacheWrite: 0,
5475
- totalTokens: input + outputTokens + cachedTokens,
5709
+ totalTokens: normalizedUsage.totalTokens,
5476
5710
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
5477
5711
  };
5478
- calculateCost(model, output.usage);
5712
+ output.usage.cost = calculateCost(model, output.usage);
5479
5713
  if (process.env.PIE_DEBUG_CACHE) {
5714
+ const normalizedPromptTokens = normalizedUsage.input + normalizedUsage.cacheRead;
5480
5715
  console.error("[CACHE DEBUG] Response usage:", JSON.stringify({
5481
- input_tokens: input,
5482
- output_tokens: outputTokens,
5483
- cached_tokens: cachedTokens,
5484
- cache_hit_rate: chunk.usage.prompt_tokens ? `${(cachedTokens / chunk.usage.prompt_tokens * 100).toFixed(1)}%` : "N/A"
5716
+ input_tokens: normalizedUsage.input,
5717
+ output_tokens: normalizedUsage.output,
5718
+ cached_tokens: normalizedUsage.cacheRead,
5719
+ cache_hit_rate: normalizedPromptTokens > 0 ? `${(normalizedUsage.cacheRead / normalizedPromptTokens * 100).toFixed(1)}%` : "N/A"
5485
5720
  }, null, 2));
5486
5721
  }
5487
5722
  }
@@ -5634,6 +5869,36 @@ import Anthropic from "@anthropic-ai/sdk";
5634
5869
  function sanitizeSurrogates(text) {
5635
5870
  return text.replace(/[\uD800-\uDFFF]/g, "");
5636
5871
  }
5872
+ var ANTHROPIC_PROVIDER_HEADERS = {
5873
+ accept: "application/json",
5874
+ "anthropic-dangerous-direct-browser-access": "true",
5875
+ "anthropic-version": "2023-06-01"
5876
+ };
5877
+ function buildAnthropicHeaders(modelHeaders, runtimeHeaders, includeLongCacheBeta) {
5878
+ const merged = /* @__PURE__ */ new Map();
5879
+ for (const layer of [ANTHROPIC_PROVIDER_HEADERS, modelHeaders, runtimeHeaders]) {
5880
+ for (const [key, value] of Object.entries(layer ?? {})) {
5881
+ const normalizedKey = key.toLowerCase();
5882
+ if (normalizedKey && normalizedKey !== "anthropic-beta") {
5883
+ merged.set(normalizedKey, value);
5884
+ }
5885
+ }
5886
+ }
5887
+ const betaFeatures = includeLongCacheBeta ? ["extended-cache-ttl-2025-04-11"] : [];
5888
+ for (const layer of [modelHeaders, runtimeHeaders]) {
5889
+ for (const [key, value] of Object.entries(layer ?? {})) {
5890
+ if (key.toLowerCase() !== "anthropic-beta") continue;
5891
+ for (const feature of value.split(",")) {
5892
+ const trimmed = feature.trim();
5893
+ if (trimmed) betaFeatures.push(trimmed);
5894
+ }
5895
+ }
5896
+ }
5897
+ if (betaFeatures.length > 0) {
5898
+ merged.set("anthropic-beta", [...new Set(betaFeatures)].join(","));
5899
+ }
5900
+ return Object.fromEntries(merged);
5901
+ }
5637
5902
  function normalizeToolCallId(id) {
5638
5903
  return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
5639
5904
  }
@@ -5838,31 +6103,18 @@ var streamAnthropic = (model, context, options) => {
5838
6103
  };
5839
6104
  try {
5840
6105
  const apiKey = options?.apiKey ?? getEnvApiKey(model.provider, model.apiKeyEnv) ?? "";
5841
- const modelHeaders = model.headers || {};
5842
- const otherModelHeaders = {};
5843
- let modelBetaValue;
5844
- for (const [key, value] of Object.entries(modelHeaders)) {
5845
- if (key.toLowerCase() === "anthropic-beta") {
5846
- modelBetaValue = value;
5847
- } else {
5848
- otherModelHeaders[key] = value;
5849
- }
5850
- }
5851
- const betaFlags = [
5852
- ...options?.cacheRetention === "long" ? ["extended-cache-ttl-2025-04-11"] : [],
5853
- ...modelBetaValue?.split(",") ?? []
5854
- ].map((flag) => flag.trim()).filter(Boolean);
6106
+ const cacheCapability = getModelCacheCapability(model);
6107
+ const promptCacheSupported = cacheCapability.supported && isPromptCacheCapability(cacheCapability);
6108
+ const headers = buildAnthropicHeaders(
6109
+ model.headers,
6110
+ options?.headers,
6111
+ promptCacheSupported && options?.cacheRetention === "long"
6112
+ );
5855
6113
  const client = new Anthropic({
5856
6114
  apiKey,
5857
6115
  baseURL: model.baseUrl,
5858
6116
  dangerouslyAllowBrowser: true,
5859
- defaultHeaders: {
5860
- accept: "application/json",
5861
- "anthropic-dangerous-direct-browser-access": "true",
5862
- "anthropic-version": "2023-06-01",
5863
- ...otherModelHeaders,
5864
- ...betaFlags.length > 0 ? { "anthropic-beta": [...new Set(betaFlags)].join(",") } : {}
5865
- }
6117
+ defaultHeaders: headers
5866
6118
  });
5867
6119
  const params = {
5868
6120
  model: model.id,
@@ -5870,7 +6122,6 @@ var streamAnthropic = (model, context, options) => {
5870
6122
  max_tokens: options?.maxTokens || Math.floor(model.maxTokens / 3) || 1024,
5871
6123
  stream: true
5872
6124
  };
5873
- const cacheCapability = model.cacheCapability ?? getModelCacheCapability(model) ?? ANTHROPIC_PROMPT_CACHE_CAPABILITY;
5874
6125
  if (context.systemPrompt) {
5875
6126
  params.system = [{ type: "text", text: sanitizeSurrogates(context.systemPrompt) }];
5876
6127
  }
@@ -5893,7 +6144,7 @@ var streamAnthropic = (model, context, options) => {
5893
6144
  params.tool_choice = options.toolChoice;
5894
6145
  }
5895
6146
  }
5896
- if (isPromptCacheCapability(cacheCapability)) {
6147
+ if (promptCacheSupported) {
5897
6148
  applyCacheBreakpoints(params, options?.cacheRetention);
5898
6149
  }
5899
6150
  options?.onPayload?.(params);