pi-readseek 0.12.3 → 0.12.5

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 (3) hide show
  1. package/README.md +4 -0
  2. package/dist/index.ts +45 -15
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -27,6 +27,10 @@ attachments without a separate extraction call. Reading never runs local inferen
27
27
  captioning, detection, and OCR require their explicit tools. Legacy tool names and
28
28
  selectable inspection facets are removed.
29
29
 
30
+ Image attachments share a 32 MiB base64-data budget per tool result. Images that
31
+ would exceed the remaining budget are omitted with a notice and their file path;
32
+ smaller images later in the result can still be attached.
33
+
30
34
  The four `show` tools display readable summaries and grouped details rather than
31
35
  JSON dumps. Expand with Ctrl+O to reveal content; long lines wrap on resize.
32
36
  Source anchors, selected document nodes, asset paths, and continuation notices
package/dist/index.ts CHANGED
@@ -5374,7 +5374,9 @@ function registerDefTool(pi, options = {}) {
5374
5374
  // src/access.ts
5375
5375
  import { Text as Text7, wrapTextWithAnsi as wrapTextWithAnsi2 } from "@earendil-works/pi-tui";
5376
5376
  import { Type as Type11 } from "@sinclair/typebox";
5377
- import { readFile as readFile2 } from "node:fs/promises";
5377
+ import { createReadStream } from "node:fs";
5378
+ import { stat } from "node:fs/promises";
5379
+ var MAX_IMAGE_ATTACHMENT_BYTES = 32 * 1024 * 1024;
5378
5380
  var operations = {
5379
5381
  show: readSeekShow,
5380
5382
  tree: readSeekTree,
@@ -5386,22 +5388,25 @@ var operations = {
5386
5388
  objects: readSeekObjects,
5387
5389
  ocr: readSeekOcr
5388
5390
  };
5389
- function resolveNonShowSummary(tool, envelope) {
5390
- if (tool === "syntax") {
5391
- const errorCount = envelope?.diagnostics?.error_count ?? 0;
5391
+ function resolveNonShowSummary(value) {
5392
+ if (!value)
5393
+ return "complete";
5394
+ if (value.tool === "syntax") {
5395
+ const errorCount = value.envelope.errorCount + value.envelope.missingCount;
5392
5396
  return errorCount === 0 ? "clean" : `${errorCount} syntax error${errorCount === 1 ? "" : "s"}`;
5393
5397
  }
5394
- if (tool === "check") {
5395
- const violations = Array.isArray(envelope?.violations) ? envelope.violations.length : 0;
5398
+ if (value.tool === "check") {
5399
+ const violations = value.envelope.violation_count;
5396
5400
  return violations === 0 ? "clean" : `${violations} rule violation${violations === 1 ? "" : "s"}`;
5397
5401
  }
5398
- if (tool === "objects") {
5399
- const objects = Array.isArray(envelope?.objects) ? envelope.objects.length : 0;
5402
+ if (value.tool === "objects") {
5403
+ const envelope = value.envelope;
5404
+ const objects = "vision" in envelope ? envelope.vision.reduce((count, vision) => count + (vision.objects?.length ?? 0), 0) : ("objects" in envelope.content) ? envelope.content.objects?.length ?? 0 : 0;
5400
5405
  return `${objects} object${objects === 1 ? "" : "s"}`;
5401
5406
  }
5402
- if (tool === "caption")
5407
+ if (value.tool === "caption")
5403
5408
  return "caption generated";
5404
- if (tool === "ocr")
5409
+ if (value.tool === "ocr")
5405
5410
  return "text recognized";
5406
5411
  return "complete";
5407
5412
  }
@@ -5492,13 +5497,38 @@ async function executeAccess(opts) {
5492
5497
  { type: "text", text: presentation ? formatShowResult(presentation) : JSON.stringify(envelope) }
5493
5498
  ];
5494
5499
  if (opts.tool === "show" || opts.tool === "assets") {
5500
+ let remainingImageBytes = MAX_IMAGE_ATTACHMENT_BYTES;
5495
5501
  for (const [imagePath, mimeType] of imagePaths(envelope)) {
5502
+ opts.signal?.throwIfAborted();
5496
5503
  if (opts.imageMode === "off")
5497
5504
  content.push({ type: "text", text: `Image available at ${imagePath}; image attachments are disabled by readseek.imageMode.` });
5498
- else if (opts.modelSupportsImages)
5499
- content.push({ type: "image", data: (await readFile2(imagePath)).toString("base64"), mimeType });
5500
- else
5505
+ else if (!opts.modelSupportsImages)
5501
5506
  content.push({ type: "text", text: `Image available at ${imagePath}; this host model does not support image attachments.` });
5507
+ else {
5508
+ const maxSourceBytes = Math.floor(remainingImageBytes / 4) * 3;
5509
+ let data;
5510
+ if (maxSourceBytes > 0 && (await stat(imagePath)).size <= maxSourceBytes) {
5511
+ const chunks = [];
5512
+ let sourceBytes = 0;
5513
+ const stream = createReadStream(imagePath, { end: maxSourceBytes, signal: opts.signal });
5514
+ for await (const chunk of stream) {
5515
+ sourceBytes += chunk.length;
5516
+ if (sourceBytes > maxSourceBytes)
5517
+ break;
5518
+ chunks.push(chunk);
5519
+ }
5520
+ opts.signal?.throwIfAborted();
5521
+ if (sourceBytes <= maxSourceBytes)
5522
+ data = Buffer.concat(chunks, sourceBytes).toString("base64");
5523
+ }
5524
+ opts.signal?.throwIfAborted();
5525
+ if (data === undefined)
5526
+ content.push({ type: "text", text: `Image available at ${imagePath}; attachment omitted because it would exceed the 32 MiB image attachment budget for this result.` });
5527
+ else {
5528
+ remainingImageBytes -= data.length;
5529
+ content.push({ type: "image", data, mimeType });
5530
+ }
5531
+ }
5502
5532
  }
5503
5533
  }
5504
5534
  return { content, details: { readSeekValue: { tool: opts.tool, ok: true, path: filePath, envelope, presentation } } };
@@ -5566,7 +5596,7 @@ function registerAccessTools(pi, options = {}) {
5566
5596
  const summary = summaryLine(presentation.summary, { hidden: hasExpandable && !expanded, theme, style: hasWarning ? "warning" : "success" });
5567
5597
  return renderShowPresentation(summary, presentation.body, extras, expanded, theme, width);
5568
5598
  }
5569
- const nonShowSummary = resolveNonShowSummary(name, envelope);
5599
+ const nonShowSummary = resolveNonShowSummary(result.details?.readSeekValue);
5570
5600
  const summary = summaryLine(nonShowSummary, { hidden: !!text && !expanded, theme, style: "success" });
5571
5601
  return new Text7(clampLinesToWidth((expanded ? `${summary}
5572
5602
  ${text}` : summary).split(`
@@ -5650,7 +5680,7 @@ function piReadSeekExtension(pi) {
5650
5680
  onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
5651
5681
  name: readName,
5652
5682
  toolAliases,
5653
- imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
5683
+ imageMode: resolveReadSeekImageMode()
5654
5684
  });
5655
5685
  registerWriteTool(pi, {
5656
5686
  onFileAnchored: (absolutePath) => sessionAnchors.markAnchored(absolutePath),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-readseek",
3
- "version": "0.12.3",
3
+ "version": "0.12.5",
4
4
  "description": "Pi extension for LINE:HASH-anchored file operations and structural code navigation",
5
5
  "type": "module",
6
6
  "exports": {
@@ -39,8 +39,8 @@
39
39
  "node": ">=20.0.0"
40
40
  },
41
41
  "dependencies": {
42
- "@jarkkojs/readseek": "^0.12.3",
43
- "@jarkkojs/readseek-api": "^0.12.3",
42
+ "@jarkkojs/readseek": "^0.12.5",
43
+ "@jarkkojs/readseek-api": "^0.12.5",
44
44
  "diff": "^9.0.0",
45
45
  "xxhash-wasm": "^1.1.0"
46
46
  },