@poe-platform/safe-bash 0.1.758 → 0.1.760

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 (33) hide show
  1. package/dist/image-ast/index.browser.js +41 -16
  2. package/dist/image-ast/index.js +41 -16
  3. package/dist/pdf-ast/ast.d.ts +8 -3
  4. package/dist/pdf-ast/index.js +45 -19
  5. package/dist/safe-bash/chunks/{chunk-WNWIBBJZ.js → chunk-CRQDMBQA.js} +130 -9
  6. package/dist/safe-bash/command-chunks/{chunk-L2C5TIII.js → chunk-MC4XXPQJ.js} +2 -2
  7. package/dist/safe-bash/command-chunks/{chunk-TCAGGM44.js → chunk-S3SCPBHC.js} +41 -17
  8. package/dist/safe-bash/command-chunks/{chunk-XYCWDGOG.js → chunk-UCI6ZQFB.js} +1 -1
  9. package/dist/safe-bash/commands/python/index.browser.js +1 -1
  10. package/dist/safe-bash/commands/python/index.d.ts +1 -1
  11. package/dist/safe-bash/commands/python/llm-capability.d.ts +7 -3
  12. package/dist/safe-bash/commands/python/llm-capability.js +149 -10
  13. package/dist/safe-bash/commands/python/shell-capability.js +10 -2
  14. package/dist/safe-bash/core.browser.js +1 -1
  15. package/dist/safe-bash-command-ffmpeg/index.js +3513 -3500
  16. package/dist/safe-bash-command-gh/index.js +128 -27
  17. package/dist/safe-bash-command-imagemagick/index.js +2 -2
  18. package/dist/safe-bash-command-pandoc/command.js +3 -3
  19. package/dist/safe-bash-command-pandoc/index.js +3 -3
  20. package/dist/safe-bash-command-pdfimages/index.js +41 -16
  21. package/dist/safe-bash-command-pdfinfo/index.js +45 -19
  22. package/dist/safe-bash-command-pdftk/index.js +41 -16
  23. package/dist/safe-bash-command-pdftoppm/index.js +45 -19
  24. package/dist/safe-bash-command-pdftotext/index.js +41 -16
  25. package/dist/safe-bash-command-qpdf/index.js +41 -16
  26. package/dist/safe-bash-command-sips/index.js +2 -2
  27. package/dist/safe-bash-command-soffice/index.js +1 -1
  28. package/dist/safe-bash-command-ssconvert/index.js +63 -38
  29. package/dist/safe-bash-command-wkhtmltopdf/index.js +42 -16
  30. package/dist/safe-bash-io-engine/internal.d.ts +2 -2
  31. package/dist/safe-bash-mcp/index.d.ts +1 -1
  32. package/package.json +2 -2
  33. package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +22 -0
@@ -1626,9 +1626,14 @@ function createPythonShellCapability(context, options = {}) {
1626
1626
  const stdout = [];
1627
1627
  const stderr = [];
1628
1628
  let captured = 0;
1629
+ let outputFailure;
1629
1630
  const sink = (target, type) => ({ async write(chunk) {
1630
1631
  childSignal.throwIfAborted();
1631
- if (chunk.length > outputLimit - captured) throw new RangeError("Python shell output limit exceeded");
1632
+ if (outputFailure) throw outputFailure;
1633
+ if (chunk.length > outputLimit - captured) {
1634
+ outputFailure = new RangeError("Python shell output limit exceeded");
1635
+ throw outputFailure;
1636
+ }
1632
1637
  captured += chunk.length;
1633
1638
  if (publish) {
1634
1639
  for (let offset = 0; offset < chunk.length; offset += chunkBytes) {
@@ -1652,6 +1657,7 @@ function createPythonShellCapability(context, options = {}) {
1652
1657
  externalInvocation: true
1653
1658
  });
1654
1659
  childSignal.throwIfAborted();
1660
+ if (outputFailure) throw outputFailure;
1655
1661
  return { stdout, stderr, exitCode: result.exitCode };
1656
1662
  } catch (error) {
1657
1663
  if (!publish && !signal.aborted && childSignal.aborted && childSignal.reason?.name === "TimeoutError") {
@@ -1732,9 +1738,90 @@ function record(value) {
1732
1738
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Expected a structured Python LLM request");
1733
1739
  return value;
1734
1740
  }
1741
+ function jsonBytes(value, limit) {
1742
+ if (limit === Infinity) return 0;
1743
+ let bytes = 0;
1744
+ const ancestors = /* @__PURE__ */ new Set();
1745
+ const add2 = (size) => {
1746
+ if (size > limit - bytes) throw new RangeError("Python LLM serialized response limit exceeded");
1747
+ bytes += size;
1748
+ };
1749
+ const string = (text2) => {
1750
+ add2(2);
1751
+ if (text2.length > limit - bytes) throw new RangeError("Python LLM serialized response limit exceeded");
1752
+ for (let i = 0; i < text2.length; i++) {
1753
+ const point = text2.codePointAt(i);
1754
+ if (point > 65535) {
1755
+ add2(4);
1756
+ i++;
1757
+ } else if (point === 34 || point === 92) add2(2);
1758
+ else if (point < 32) add2([8, 9, 10, 12, 13].includes(point) ? 2 : 6);
1759
+ else if (point >= 55296 && point <= 57343) add2(6);
1760
+ else add2(point < 128 ? 1 : point < 2048 ? 2 : 3);
1761
+ }
1762
+ };
1763
+ const visit = (item) => {
1764
+ if (typeof item === "string") {
1765
+ string(item);
1766
+ return;
1767
+ }
1768
+ if (item === null || typeof item === "boolean" || typeof item === "number" && Number.isFinite(item)) {
1769
+ add2(JSON.stringify(item).length);
1770
+ return;
1771
+ }
1772
+ if (!item || typeof item !== "object" || ancestors.has(item)) throw new TypeError("Python LLM response must contain acyclic data");
1773
+ ancestors.add(item);
1774
+ add2(2);
1775
+ let entries = 0;
1776
+ if (Array.isArray(item)) {
1777
+ if (item.length > limit - bytes) throw new RangeError("Python LLM serialized response limit exceeded");
1778
+ for (let i = 0; i < item.length; i++) {
1779
+ const descriptor = Object.getOwnPropertyDescriptor(item, String(i));
1780
+ if (!descriptor || !("value" in descriptor)) throw new TypeError("Python LLM response must contain data");
1781
+ if (entries++) add2(1);
1782
+ visit(descriptor.value);
1783
+ }
1784
+ } else {
1785
+ for (const key in item) {
1786
+ if (!Object.hasOwn(item, key)) continue;
1787
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
1788
+ if (!("value" in descriptor)) throw new TypeError("Python LLM response must contain data");
1789
+ if (entries++) add2(1);
1790
+ string(key);
1791
+ add2(1);
1792
+ visit(descriptor.value);
1793
+ }
1794
+ }
1795
+ ancestors.delete(item);
1796
+ };
1797
+ visit(value);
1798
+ return bytes;
1799
+ }
1800
+ function* textFragments(text2, limit) {
1801
+ let start = 0, bytes = 0;
1802
+ for (let i = 0; i < text2.length; ) {
1803
+ const point = text2.codePointAt(i);
1804
+ const width = point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
1805
+ if (width > limit) throw new RangeError("Python LLM stream chunk limit cannot fit a Unicode scalar");
1806
+ if (width > limit - bytes) {
1807
+ yield text2.slice(start, i);
1808
+ start = i;
1809
+ bytes = 0;
1810
+ }
1811
+ bytes += width;
1812
+ i += point > 65535 ? 2 : 1;
1813
+ }
1814
+ if (start < text2.length || !text2.length) yield text2.slice(start);
1815
+ }
1735
1816
  function createPythonLlmCapability(context, service, options = {}) {
1736
1817
  const chunkBytes = options.maxStreamChunkBytes ?? 16384;
1737
1818
  if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 1) throw new RangeError("Invalid Python LLM stream chunk limit");
1819
+ const bufferedLimit = options.maxBufferedResponseBytes ?? Infinity;
1820
+ const eventLimit = options.maxBufferedEvents ?? bufferedLimit;
1821
+ const metadataLimit = options.maxMetadataBytes ?? bufferedLimit;
1822
+ for (const limit of [bufferedLimit, eventLimit, metadataLimit]) {
1823
+ if (limit !== Infinity && (!Number.isSafeInteger(limit) || limit < 1)) throw new RangeError("Invalid Python LLM host limit");
1824
+ }
1738
1825
  const prepare = async (value, signal) => {
1739
1826
  const payload = record(value);
1740
1827
  for (const key of ["template", "conversation"]) {
@@ -1791,7 +1878,15 @@ function createPythonLlmCapability(context, service, options = {}) {
1791
1878
  request.signal.throwIfAborted();
1792
1879
  yield { type: "bytes", data: Array.from(event.data.subarray(offset, offset + chunkBytes)) };
1793
1880
  }
1794
- } else yield event;
1881
+ } else if (event.type === "text") {
1882
+ for (const text2 of textFragments(event.text, chunkBytes)) {
1883
+ request.signal.throwIfAborted();
1884
+ yield { type: "text", text: text2 };
1885
+ }
1886
+ } else {
1887
+ jsonBytes(event.response, metadataLimit);
1888
+ yield event;
1889
+ }
1795
1890
  }
1796
1891
  };
1797
1892
  return {
@@ -1814,15 +1909,41 @@ function createPythonLlmCapability(context, service, options = {}) {
1814
1909
  });
1815
1910
  }
1816
1911
  if (operation.operation !== "complete") throw new TypeError("Unsupported Python LLM operation");
1817
- const text2 = [], data = [];
1912
+ const request = await prepare(payload, signal);
1913
+ const bounded = { ...request, maxOutputBytes: Math.min(request.maxOutputBytes ?? Infinity, bufferedLimit) };
1914
+ let text2 = "", textBytes = 0, dataBytes = 0, received = 0;
1915
+ const data = [];
1818
1916
  let response = {};
1819
- for await (const value2 of events(payload, { signal })) {
1820
- const event = record(value2);
1821
- if (event.type === "text") text2.push(event.text);
1822
- else if (event.type === "bytes") for (const byte of event.data) data.push(byte);
1823
- else if (event.type === "response") response = { ...record(event.response) };
1917
+ let envelopeBytes = jsonBytes({ text: "", data: [] }, bufferedLimit);
1918
+ const admit = (size) => {
1919
+ if (size > bufferedLimit - envelopeBytes - textBytes - dataBytes) throw new RangeError("Python LLM buffered response limit exceeded");
1920
+ };
1921
+ for await (const event of service.stream(bounded)) {
1922
+ bounded.signal.throwIfAborted();
1923
+ if (++received > eventLimit) throw new RangeError("Python LLM buffered event limit exceeded");
1924
+ if (event.type === "text") {
1925
+ const size = bufferedLimit === Infinity || !event.text ? 0 : jsonBytes(event.text, bufferedLimit - envelopeBytes - textBytes - dataBytes + 2) - 2;
1926
+ admit(size);
1927
+ textBytes += size;
1928
+ if (event.text) text2 += event.text;
1929
+ } else if (event.type === "bytes") {
1930
+ let size = 0;
1931
+ if (bufferedLimit !== Infinity) for (let i = 0; i < event.data.length; i++) {
1932
+ size += String(event.data[i]).length + (data.length || i ? 1 : 0);
1933
+ admit(size);
1934
+ }
1935
+ dataBytes += size;
1936
+ for (const byte of event.data) data.push(byte);
1937
+ } else {
1938
+ jsonBytes(event.response, metadataLimit);
1939
+ const details = record(event.response);
1940
+ const size = jsonBytes({ ...details, text: "", data: [] }, bufferedLimit);
1941
+ if (size > bufferedLimit - textBytes - dataBytes) throw new RangeError("Python LLM buffered response limit exceeded");
1942
+ envelopeBytes = size;
1943
+ response = { ...details };
1944
+ }
1824
1945
  }
1825
- return { ...response, text: text2.join(""), data };
1946
+ return { ...response, text: text2, data };
1826
1947
  },
1827
1948
  stream: events
1828
1949
  };
@@ -48,11 +48,11 @@ import {
48
48
  trimImage,
49
49
  unflattenImage,
50
50
  unpremultiplyRgbaImage
51
- } from "./chunk-XYCWDGOG.js";
51
+ } from "./chunk-UCI6ZQFB.js";
52
52
  import {
53
53
  PdfDocument,
54
54
  encodePng
55
- } from "./chunk-TCAGGM44.js";
55
+ } from "./chunk-S3SCPBHC.js";
56
56
  import {
57
57
  createLuaFilterCapability
58
58
  } from "./chunk-Y2KHC6TZ.js";
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // packages/pdf-ast/src/cos/filters.ts
6
6
  init_buffer();
7
- import { deflate, Inflate } from "pako";
7
+ import { deflate, Inflate, Z_BUF_ERROR } from "pako";
8
8
 
9
9
  // packages/pdf-ast/src/vendor/pdfjs-fonts.mjs
10
10
  init_buffer();
@@ -19365,22 +19365,31 @@ function decodeFlate(bytes, parms, maxDecodedBytes = DEFAULT_MAX_DECODED_BYTES)
19365
19365
  inflated = new BoundedFlateStream(bytes, maxDecodedBytes).getBytes();
19366
19366
  } catch (error) {
19367
19367
  if (error instanceof PdfError && error.code === "E_LIMIT") throw error;
19368
+ const wrapped = bytes[0] === 31 && bytes[1] === 139 || bytes.length >= 2 && (bytes[0] & 15) === 8 && bytes[0] >>> 4 <= 7 && (bytes[0] << 8 | bytes[1]) % 31 === 0;
19368
19369
  const decoder = new Inflate({
19369
- raw: !(bytes[0] === 31 && bytes[1] === 139),
19370
+ raw: !wrapped,
19370
19371
  chunkSize: Math.max(1, Math.min(64 * 1024, maxDecodedBytes + 1))
19371
19372
  });
19373
+ const chunks = [];
19372
19374
  let length = 0;
19373
19375
  decoder.onData = (chunk) => {
19374
19376
  length += chunk.length;
19375
19377
  if (length > maxDecodedBytes) {
19376
19378
  throw new PdfError("E_LIMIT", "FlateDecode output exceeds maximum decoded byte budget");
19377
19379
  }
19378
- decoder.chunks.push(chunk);
19380
+ chunks.push(chunk);
19379
19381
  };
19380
- if (!decoder.push(bytes, true) || decoder.err || !decoder.ended) {
19382
+ const complete = decoder.push(bytes, true);
19383
+ const truncated = wrapped && decoder.err === Z_BUF_ERROR && length > 0;
19384
+ if ((!complete || decoder.err || !decoder.ended) && !truncated) {
19381
19385
  throw new PdfError("E_CAPABILITY", "Invalid FlateDecode compressed stream");
19382
19386
  }
19383
- inflated = decoder.result;
19387
+ inflated = new Uint8Array(length);
19388
+ let offset = 0;
19389
+ for (const chunk of chunks) {
19390
+ inflated.set(chunk, offset);
19391
+ offset += chunk.length;
19392
+ }
19384
19393
  }
19385
19394
  return applyPredictor(inflated, parms);
19386
19395
  }
@@ -30960,7 +30969,7 @@ function resolvePageFonts(doc, resourcesDict) {
30960
30969
  const glyphNames = buildFontEncodingGlyphNamesMap(encNode);
30961
30970
  const widths = /* @__PURE__ */ new Map();
30962
30971
  let defaultWidth = 556;
30963
- const isTwoByteCid = subtype === "Type0" || Boolean(cmap?.isTwoByte);
30972
+ const isTwoByteCid = subtype === "Type0";
30964
30973
  let fontMatrix;
30965
30974
  let charProcs;
30966
30975
  let fontResources;
@@ -32413,7 +32422,7 @@ function evaluateContentStreamToDisplayList(params) {
32413
32422
  const [x, y] = transformPoint(st.ctm, seg.x, seg.y);
32414
32423
  transformedSegments.push({ kind: "cubic", x1, y1, x2, y2, x, y });
32415
32424
  } else if (seg.kind === "rect") {
32416
- if (hasRotOrShear) {
32425
+ if (hasRotOrShear || seg.width * st.ctm[0] < 0 || seg.height * st.ctm[3] < 0) {
32417
32426
  const [p0x, p0y] = transformPoint(st.ctm, seg.x, seg.y);
32418
32427
  const [p1x, p1y] = transformPoint(st.ctm, seg.x + seg.width, seg.y);
32419
32428
  const [p2x, p2y] = transformPoint(st.ctm, seg.x + seg.width, seg.y + seg.height);
@@ -32446,7 +32455,12 @@ function evaluateContentStreamToDisplayList(params) {
32446
32455
  transformedSegments.push({ kind: "close" });
32447
32456
  }
32448
32457
  }
32449
- if (node.clip && transformedSegments.length > 0) {
32458
+ const applyClip = () => {
32459
+ if (!node.clip) return;
32460
+ st.clipPaths = [...st.clipPaths ?? [], {
32461
+ segments: transformedSegments,
32462
+ fillRule: node.clip === "W*" ? "evenodd" : "nonzero"
32463
+ }];
32450
32464
  let cMinX = Infinity, cMinY = Infinity, cMaxX = -Infinity, cMaxY = -Infinity;
32451
32465
  for (const s of transformedSegments) {
32452
32466
  if (s.kind === "move" || s.kind === "line") {
@@ -32474,8 +32488,11 @@ function evaluateContentStreamToDisplayList(params) {
32474
32488
  Math.min(st.clipRect[3], cMaxY)
32475
32489
  ] : [cMinX, cMinY, cMaxX, cMaxY];
32476
32490
  }
32491
+ };
32492
+ if (node.paint === "n") {
32493
+ applyClip();
32494
+ break;
32477
32495
  }
32478
- if (node.paint === "n") break;
32479
32496
  const isFill = ["f", "F", "f*", "B", "B*", "b", "b*"].includes(node.paint);
32480
32497
  const isStroke = ["S", "s", "B", "B*", "b", "b*"].includes(node.paint);
32481
32498
  let evaluatedFillPattern = false;
@@ -32608,7 +32625,10 @@ function evaluateContentStreamToDisplayList(params) {
32608
32625
  }
32609
32626
  }
32610
32627
  }
32611
- if (evaluatedFillPattern && !isStroke) break;
32628
+ if (evaluatedFillPattern && !isStroke) {
32629
+ applyClip();
32630
+ break;
32631
+ }
32612
32632
  const fillRule = node.paint.includes("*") ? "evenodd" : "nonzero";
32613
32633
  const ctmScale = Math.max(1e-6, Math.hypot(st.ctm[0], st.ctm[1]));
32614
32634
  const scaledStrokeWidth = st.strokeWidth === 0 ? 0 : st.strokeWidth * ctmScale;
@@ -32628,6 +32648,7 @@ function evaluateContentStreamToDisplayList(params) {
32628
32648
  ...st.dashPhase !== void 0 ? { dashPhase: st.dashPhase } : {},
32629
32649
  ...st.clipRect ? { clipRect: [...st.clipRect] } : {}
32630
32650
  } });
32651
+ applyClip();
32631
32652
  break;
32632
32653
  }
32633
32654
  case "xobject": {
@@ -34736,8 +34757,8 @@ function segmentsToScreenEdges(segments, pageHeight, scale, closeSubpaths = fals
34736
34757
  if (closeSubpaths && hasOpenSubpath && (curX !== startX || curY !== startY)) {
34737
34758
  edges.push({ x0: curX, y0: curY, x1: startX, y1: startY });
34738
34759
  }
34739
- const [rx0, ry0] = toScreen(seg.x, seg.y + seg.height);
34740
- const [rx1, ry1] = toScreen(seg.x + seg.width, seg.y);
34760
+ const [rx0, ry0] = toScreen(seg.x, seg.y);
34761
+ const [rx1, ry1] = toScreen(seg.x + seg.width, seg.y + seg.height);
34741
34762
  edges.push(
34742
34763
  { x0: rx0, y0: ry0, x1: rx1, y1: ry0 },
34743
34764
  { x0: rx1, y0: ry0, x1: rx1, y1: ry1 },
@@ -34880,21 +34901,24 @@ function renderDisplayListToBitmap(displayList, options = {}) {
34880
34901
  }
34881
34902
  const aaVec = options.antialiasVector !== false;
34882
34903
  const aaTxt = options.antialiasText !== false;
34883
- const clipMasks = /* @__PURE__ */ new Map();
34904
+ let cachedClips;
34905
+ let cachedClipMask;
34884
34906
  const imageClipMasks = /* @__PURE__ */ new Map();
34885
34907
  for (const original of paintOperations(displayList)) {
34886
34908
  if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
34887
34909
  const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
34888
34910
  const clips = original.value.clipPaths;
34889
- let clipMask = clips ? clipMasks.get(clips) : void 0;
34911
+ let clipMask = clips && clips === cachedClips ? cachedClipMask : void 0;
34890
34912
  if (clips && !clipMask) {
34891
34913
  clipMask = new Uint8Array(width * height * 4).fill(255);
34892
- for (const segments of clips) {
34914
+ for (const clip of clips) {
34915
+ const { segments, fillRule } = "segments" in clip ? clip : { segments: clip, fillRule: "nonzero" };
34893
34916
  const layer = new Uint8Array(width * height * 4);
34894
- fillEdgesScanline4x4(layer, width, height, segmentsToScreenEdges(segments, displayList.height, scale, true), { r: 1, g: 1, b: 1 });
34917
+ fillEdgesScanline4x4(layer, width, height, segmentsToScreenEdges(segments, displayList.height, scale, true), { r: 1, g: 1, b: 1 }, 1, fillRule);
34895
34918
  for (let i = 3; i < layer.length; i += 4) clipMask[i] = Math.round(clipMask[i] * layer[i] / 255);
34896
34919
  }
34897
- clipMasks.set(clips, clipMask);
34920
+ cachedClips = clips;
34921
+ cachedClipMask = clipMask;
34898
34922
  }
34899
34923
  const imageClips = original.value.clipImages;
34900
34924
  if (imageClips) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PdfDocument,
3
3
  renderPdfPageToPng
4
- } from "./chunk-TCAGGM44.js";
4
+ } from "./chunk-S3SCPBHC.js";
5
5
  import {
6
6
  init_buffer
7
7
  } from "./chunk-HX3SHTCE.js";
@@ -16,7 +16,7 @@ import {
16
16
  pythonCommands,
17
17
  pythonDocumentPackages,
18
18
  pythonHostFailureCode
19
- } from "../../chunks/chunk-WNWIBBJZ.js";
19
+ } from "../../chunks/chunk-CRQDMBQA.js";
20
20
  import {
21
21
  PythonFailure,
22
22
  inspectPythonCapabilities
@@ -4,7 +4,7 @@ import { type PythonDiagnosticObserver } from './diagnostics.js';
4
4
  import { createPythonHostBridge, type PythonHostCapability, type PythonHostBridgeOptions } from './host-capabilities.js';
5
5
  export * from './host-capabilities.js';
6
6
  export { createPythonShellCapability } from './shell-capability.js';
7
- export { createPythonLlmCapability } from './llm-capability.js';
7
+ export { createPythonLlmCapability, type PythonLlmCapabilityOptions } from './llm-capability.js';
8
8
  /** A dedicated interpreter worker. The service event loop must never block. */
9
9
  export interface PythonWorkerEndpoint {
10
10
  postMessage(value: unknown): void;
@@ -1,8 +1,12 @@
1
1
  import type { CommandContext } from '../../contracts/index.js';
2
2
  import type { LlmService } from '../llm/service.js';
3
3
  import type { PythonHostCapability } from './host-capabilities.js';
4
- /** Reuses the invocation's authorized service; Python receives only model data. */
5
- export declare function createPythonLlmCapability(context: Pick<CommandContext, 'fs' | 'cwd' | 'inputBudget'>, service: LlmService, options?: {
4
+ export interface PythonLlmCapabilityOptions {
6
5
  readonly maxStreamChunkBytes?: number;
7
- }): PythonHostCapability;
6
+ readonly maxBufferedResponseBytes?: number;
7
+ readonly maxBufferedEvents?: number;
8
+ readonly maxMetadataBytes?: number;
9
+ }
10
+ /** Reuses the invocation's authorized service; Python receives only model data. */
11
+ export declare function createPythonLlmCapability(context: Pick<CommandContext, 'fs' | 'cwd' | 'inputBudget'>, service: LlmService, options?: PythonLlmCapabilityOptions): PythonHostCapability;
8
12
  //# sourceMappingURL=llm-capability.d.ts.map
@@ -5,11 +5,112 @@ function record(value) {
5
5
  throw new TypeError('Expected a structured Python LLM request');
6
6
  return value;
7
7
  }
8
+ /** Count the wire representation before retaining/serializing host metadata. */
9
+ function jsonBytes(value, limit) {
10
+ if (limit === Infinity)
11
+ return 0;
12
+ let bytes = 0;
13
+ const ancestors = new Set();
14
+ const add = (size) => {
15
+ if (size > limit - bytes)
16
+ throw new RangeError('Python LLM serialized response limit exceeded');
17
+ bytes += size;
18
+ };
19
+ const string = (text) => {
20
+ add(2);
21
+ if (text.length > limit - bytes)
22
+ throw new RangeError('Python LLM serialized response limit exceeded');
23
+ for (let i = 0; i < text.length; i++) {
24
+ const point = text.codePointAt(i);
25
+ if (point > 65535) {
26
+ add(4);
27
+ i++;
28
+ }
29
+ else if (point === 34 || point === 92)
30
+ add(2);
31
+ else if (point < 32)
32
+ add([8, 9, 10, 12, 13].includes(point) ? 2 : 6);
33
+ else if (point >= 0xd800 && point <= 0xdfff)
34
+ add(6);
35
+ else
36
+ add(point < 128 ? 1 : point < 2048 ? 2 : 3);
37
+ }
38
+ };
39
+ const visit = (item) => {
40
+ if (typeof item === 'string') {
41
+ string(item);
42
+ return;
43
+ }
44
+ if (item === null || typeof item === 'boolean' || typeof item === 'number' && Number.isFinite(item)) {
45
+ add(JSON.stringify(item).length);
46
+ return;
47
+ }
48
+ if (!item || typeof item !== 'object' || ancestors.has(item))
49
+ throw new TypeError('Python LLM response must contain acyclic data');
50
+ ancestors.add(item);
51
+ add(2);
52
+ let entries = 0;
53
+ if (Array.isArray(item)) {
54
+ if (item.length > limit - bytes)
55
+ throw new RangeError('Python LLM serialized response limit exceeded');
56
+ for (let i = 0; i < item.length; i++) {
57
+ const descriptor = Object.getOwnPropertyDescriptor(item, String(i));
58
+ if (!descriptor || !('value' in descriptor))
59
+ throw new TypeError('Python LLM response must contain data');
60
+ if (entries++)
61
+ add(1);
62
+ visit(descriptor.value);
63
+ }
64
+ }
65
+ else {
66
+ for (const key in item) {
67
+ if (!Object.hasOwn(item, key))
68
+ continue;
69
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
70
+ if (!('value' in descriptor))
71
+ throw new TypeError('Python LLM response must contain data');
72
+ if (entries++)
73
+ add(1);
74
+ string(key);
75
+ add(1);
76
+ visit(descriptor.value);
77
+ }
78
+ }
79
+ ancestors.delete(item);
80
+ };
81
+ visit(value);
82
+ return bytes;
83
+ }
84
+ function* textFragments(text, limit) {
85
+ let start = 0, bytes = 0;
86
+ for (let i = 0; i < text.length;) {
87
+ const point = text.codePointAt(i);
88
+ const width = point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
89
+ if (width > limit)
90
+ throw new RangeError('Python LLM stream chunk limit cannot fit a Unicode scalar');
91
+ if (width > limit - bytes) {
92
+ yield text.slice(start, i);
93
+ start = i;
94
+ bytes = 0;
95
+ }
96
+ bytes += width;
97
+ i += point > 65535 ? 2 : 1;
98
+ }
99
+ if (start < text.length || !text.length)
100
+ yield text.slice(start);
101
+ }
8
102
  /** Reuses the invocation's authorized service; Python receives only model data. */
9
103
  export function createPythonLlmCapability(context, service, options = {}) {
10
104
  const chunkBytes = options.maxStreamChunkBytes ?? 16384;
11
105
  if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 1)
12
106
  throw new RangeError('Invalid Python LLM stream chunk limit');
107
+ const bufferedLimit = options.maxBufferedResponseBytes ?? Infinity;
108
+ const eventLimit = options.maxBufferedEvents ?? bufferedLimit;
109
+ const metadataLimit = options.maxMetadataBytes ?? bufferedLimit;
110
+ for (const limit of [bufferedLimit, eventLimit, metadataLimit]) {
111
+ if (limit !== Infinity && (!Number.isSafeInteger(limit) || limit < 1))
112
+ throw new RangeError('Invalid Python LLM host limit');
113
+ }
13
114
  const prepare = async (value, signal) => {
14
115
  const payload = record(value);
15
116
  for (const key of ['template', 'conversation']) {
@@ -73,8 +174,16 @@ export function createPythonLlmCapability(context, service, options = {}) {
73
174
  yield { type: 'bytes', data: Array.from(event.data.subarray(offset, offset + chunkBytes)) };
74
175
  }
75
176
  }
76
- else
177
+ else if (event.type === 'text') {
178
+ for (const text of textFragments(event.text, chunkBytes)) {
179
+ request.signal.throwIfAborted();
180
+ yield { type: 'text', text };
181
+ }
182
+ }
183
+ else {
184
+ jsonBytes(event.response, metadataLimit);
77
185
  yield event;
186
+ }
78
187
  }
79
188
  };
80
189
  return {
@@ -93,19 +202,49 @@ export function createPythonLlmCapability(context, service, options = {}) {
93
202
  }
94
203
  if (operation.operation !== 'complete')
95
204
  throw new TypeError('Unsupported Python LLM operation');
96
- const text = [], data = [];
205
+ const request = await prepare(payload, signal);
206
+ const bounded = { ...request, maxOutputBytes: Math.min(request.maxOutputBytes ?? Infinity, bufferedLimit) };
207
+ let text = '', textBytes = 0, dataBytes = 0, received = 0;
208
+ const data = [];
97
209
  let response = {};
98
- for await (const value of events(payload, { signal })) {
99
- const event = record(value);
100
- if (event.type === 'text')
101
- text.push(event.text);
102
- else if (event.type === 'bytes')
210
+ let envelopeBytes = jsonBytes({ text: '', data: [] }, bufferedLimit);
211
+ const admit = (size) => {
212
+ if (size > bufferedLimit - envelopeBytes - textBytes - dataBytes)
213
+ throw new RangeError('Python LLM buffered response limit exceeded');
214
+ };
215
+ for await (const event of service.stream(bounded)) {
216
+ bounded.signal.throwIfAborted();
217
+ if (++received > eventLimit)
218
+ throw new RangeError('Python LLM buffered event limit exceeded');
219
+ if (event.type === 'text') {
220
+ const size = bufferedLimit === Infinity || !event.text ? 0 : jsonBytes(event.text, bufferedLimit - envelopeBytes - textBytes - dataBytes + 2) - 2;
221
+ admit(size);
222
+ textBytes += size;
223
+ if (event.text)
224
+ text += event.text;
225
+ }
226
+ else if (event.type === 'bytes') {
227
+ let size = 0;
228
+ if (bufferedLimit !== Infinity)
229
+ for (let i = 0; i < event.data.length; i++) {
230
+ size += String(event.data[i]).length + (data.length || i ? 1 : 0);
231
+ admit(size);
232
+ }
233
+ dataBytes += size;
103
234
  for (const byte of event.data)
104
235
  data.push(byte);
105
- else if (event.type === 'response')
106
- response = { ...record(event.response) };
236
+ }
237
+ else {
238
+ jsonBytes(event.response, metadataLimit);
239
+ const details = record(event.response);
240
+ const size = jsonBytes({ ...details, text: '', data: [] }, bufferedLimit);
241
+ if (size > bufferedLimit - textBytes - dataBytes)
242
+ throw new RangeError('Python LLM buffered response limit exceeded');
243
+ envelopeBytes = size;
244
+ response = { ...details };
245
+ }
107
246
  }
108
- return { ...response, text: text.join(''), data };
247
+ return { ...response, text, data };
109
248
  },
110
249
  stream: events,
111
250
  };
@@ -73,10 +73,15 @@ export function createPythonShellCapability(context, options = {}) {
73
73
  const stdout = [];
74
74
  const stderr = [];
75
75
  let captured = 0;
76
+ let outputFailure;
76
77
  const sink = (target, type) => ({ async write(chunk) {
77
78
  childSignal.throwIfAborted();
78
- if (chunk.length > outputLimit - captured)
79
- throw new RangeError('Python shell output limit exceeded');
79
+ if (outputFailure)
80
+ throw outputFailure;
81
+ if (chunk.length > outputLimit - captured) {
82
+ outputFailure = new RangeError('Python shell output limit exceeded');
83
+ throw outputFailure;
84
+ }
80
85
  captured += chunk.length;
81
86
  if (publish) {
82
87
  for (let offset = 0; offset < chunk.length; offset += chunkBytes) {
@@ -97,6 +102,9 @@ export function createPythonShellCapability(context, options = {}) {
97
102
  stdin: childInput, stdout: sink(stdout, 'stdout'), stderr: sink(stderr, 'stderr'), externalInvocation: true,
98
103
  });
99
104
  childSignal.throwIfAborted();
105
+ // A command may catch sink rejection and return a status; the host limit still failed.
106
+ if (outputFailure)
107
+ throw outputFailure;
100
108
  return { stdout, stderr, exitCode: result.exitCode };
101
109
  }
102
110
  catch (error) {
@@ -158,7 +158,7 @@ import {
158
158
  sha256,
159
159
  sha384,
160
160
  sha512
161
- } from "./chunks/chunk-WNWIBBJZ.js";
161
+ } from "./chunks/chunk-CRQDMBQA.js";
162
162
  import {
163
163
  PythonFailure,
164
164
  inspectPythonCapabilities