opencrowd 0.15.0 → 0.15.1

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 (2) hide show
  1. package/bundle/opencrowd.js +162 -29
  2. package/package.json +1 -1
@@ -286,13 +286,13 @@ async function listArtifacts(state, prefix = "") {
286
286
  const { readdir: readdir3 } = await import("node:fs/promises");
287
287
  const start = await safeArtifactPath(state, prefix || ".");
288
288
  const files = [];
289
- await walk3(start, files, state.artifactsDir);
289
+ await walk4(start, files, state.artifactsDir);
290
290
  return files.sort();
291
- async function walk3(dir, out, root) {
291
+ async function walk4(dir, out, root) {
292
292
  for (const entry of await readdir3(dir, { withFileTypes: true })) {
293
293
  const full = join3(dir, entry.name);
294
294
  if (entry.isDirectory()) {
295
- await walk3(full, out, root);
295
+ await walk4(full, out, root);
296
296
  } else if (entry.isFile()) {
297
297
  out.push(relative2(root, full));
298
298
  }
@@ -15903,18 +15903,45 @@ var init_redact = __esm({
15903
15903
  }
15904
15904
  });
15905
15905
 
15906
+ // ../../packages/protocol/dist/limits.js
15907
+ function capStrings(value, maxChars = MAX_EXTERNAL_STRING_CHARS, depth = 0) {
15908
+ if (typeof value === "string")
15909
+ return capString(value, maxChars);
15910
+ if (depth > 30 || !value || typeof value !== "object")
15911
+ return value;
15912
+ if (Array.isArray(value))
15913
+ return value.map((item) => capStrings(item, maxChars, depth + 1));
15914
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, capStrings(item, maxChars, depth + 1)]));
15915
+ }
15916
+ function capString(value, maxChars = MAX_EXTERNAL_STRING_CHARS) {
15917
+ return value.length <= maxChars ? value : `${value.slice(0, maxChars)}[truncated ${value.length - maxChars} chars]`;
15918
+ }
15919
+ var MAX_EXTERNAL_STRING_CHARS;
15920
+ var init_limits = __esm({
15921
+ "../../packages/protocol/dist/limits.js"() {
15922
+ "use strict";
15923
+ MAX_EXTERNAL_STRING_CHARS = 128 * 1024;
15924
+ }
15925
+ });
15926
+
15906
15927
  // ../../packages/protocol/dist/telemetry.js
15907
15928
  function scrubText(text) {
15908
15929
  return text.replace(/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/g, "[REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+\/-]+=*/gi, "Bearer [REDACTED]").replace(/\b(?:sk-[A-Za-z0-9_-]{16,}|ph[csx]_[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)\b/g, "[REDACTED]").replace(/\b((?:[A-Z_]*(?:SECRET|PASSWORD|API_KEY|PRIVATE_KEY|ACCESS_TOKEN)[A-Z_]*|authorization|cookie|signature|x-amz-signature)\s*[=:]\s*)[^\s&,;]+/gi, "$1[REDACTED]").replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/g, "$1[REDACTED]@");
15909
15930
  }
15910
15931
  function captureDetail(input2, maxBytes = 96 * 1024) {
15911
15932
  let redacted = false;
15933
+ let spent = 0, truncatedStrings = false;
15934
+ const budget = maxBytes * 8;
15912
15935
  const clean3 = (value2, depth = 0) => {
15913
15936
  if (depth > 30)
15914
15937
  return "[depth limit]";
15915
15938
  if (typeof value2 === "string") {
15916
- const s3 = scrubText(value2);
15917
- redacted ||= s3 !== value2;
15939
+ const limit = Math.min(maxBytes, Math.max(0, budget - spent));
15940
+ truncatedStrings ||= value2.length > limit;
15941
+ const capped = capString(value2, limit);
15942
+ spent += capped.length;
15943
+ const s3 = scrubText(capped);
15944
+ redacted ||= s3 !== capped;
15918
15945
  return s3;
15919
15946
  }
15920
15947
  if (Array.isArray(value2))
@@ -15934,13 +15961,14 @@ function captureDetail(input2, maxBytes = 96 * 1024) {
15934
15961
  return {
15935
15962
  value: bytes > maxBytes ? { preview: json3.slice(0, Math.floor(maxBytes / 6)) } : value,
15936
15963
  redacted,
15937
- truncated: bytes > maxBytes,
15964
+ truncated: bytes > maxBytes || truncatedStrings,
15938
15965
  bytes
15939
15966
  };
15940
15967
  }
15941
15968
  var init_telemetry = __esm({
15942
15969
  "../../packages/protocol/dist/telemetry.js"() {
15943
15970
  "use strict";
15971
+ init_limits();
15944
15972
  init_redact();
15945
15973
  }
15946
15974
  });
@@ -16209,6 +16237,7 @@ var init_dist = __esm({
16209
16237
  init_commands();
16210
16238
  init_events();
16211
16239
  init_redact();
16240
+ init_limits();
16212
16241
  init_telemetry();
16213
16242
  init_seq();
16214
16243
  init_codec();
@@ -58334,30 +58363,110 @@ var init_crowdcode_preferences = __esm({
58334
58363
  // ../../packages/economy/dist/media.js
58335
58364
  import { createHash as createHash2 } from "node:crypto";
58336
58365
  async function extractMedia(session, value, depth = 0) {
58337
- if (depth > 20 || !value || typeof value !== "object")
58366
+ return walk3(session, value, void 0, void 0, depth);
58367
+ }
58368
+ async function walk3(session, value, key, siblings, depth) {
58369
+ if (depth > 20 || !value)
58370
+ return value;
58371
+ if (typeof value === "string")
58372
+ return await extractString(session, value, key, siblings) ?? value;
58373
+ if (typeof value !== "object")
58338
58374
  return value;
58339
58375
  if (Array.isArray(value))
58340
- return Promise.all(value.map((item) => extractMedia(session, item, depth + 1)));
58376
+ return Promise.all(value.map((item) => walk3(session, item, key, siblings, depth + 1)));
58341
58377
  const source = value;
58342
- const extensions = { "video/mp4": "mp4", "video/webm": "webm", "image/png": "png", "image/jpeg": "jpg", "image/webp": "webp", "audio/mpeg": "mp3", "audio/wav": "wav" };
58343
- const mime = String(source.media_type ?? source.mime_type ?? "image/png");
58344
- if (typeof source.b64_json === "string" && extensions[mime] && /^[A-Za-z0-9+/\r\n]*={0,2}$/.test(source.b64_json)) {
58345
- const bytes = Buffer.from(source.b64_json, "base64");
58346
- if (bytes.length > 24 * 1024 * 1024) {
58347
- const { b64_json: _inline2, ...metadata2 } = source;
58348
- return { ...metadata2, media_error: "Generated media exceeds 24 MiB artifact limit", bytes: bytes.length };
58349
- }
58350
- const name = createHash2("sha256").update(bytes).digest("hex").slice(0, 20);
58351
- const artifact = await saveArtifact(session, `generated/${name}.${extensions[mime]}`, bytes, { media_type: mime });
58352
- const { b64_json: _inline, ...metadata } = source;
58353
- return { ...metadata, artifact_path: artifact.path, bytes: bytes.length };
58378
+ const entries = await Promise.all(Object.entries(source).map(async ([name, item]) => [name, await walk3(session, item, name, source, depth + 1)]));
58379
+ const openAiStyle = entries.find(([name, item]) => name === "b64_json" && isReplacement(item));
58380
+ if (!openAiStyle)
58381
+ return Object.fromEntries(entries);
58382
+ return { ...Object.fromEntries(entries.filter(([name]) => name !== "b64_json")), ...openAiStyle[1] };
58383
+ }
58384
+ function isReplacement(value) {
58385
+ return Boolean(value && typeof value === "object" && ("artifact_path" in value || "media_error" in value));
58386
+ }
58387
+ async function extractString(session, value, key, siblings) {
58388
+ const payload = dataUriPayload(value) ?? { base64: value, declared: declaredMediaType(key, siblings) };
58389
+ if (payload.base64.length < MIN_BASE64_CHARS || !looksLikeBase64(payload.base64))
58390
+ return void 0;
58391
+ const mediaType = sniff(Buffer.from(payload.base64.slice(0, SNIFF_CHARS), "base64")) ?? payload.declared;
58392
+ if (!mediaType || !EXTENSIONS[mediaType] || !isCompleteBase64(payload.base64))
58393
+ return void 0;
58394
+ const bytes = Buffer.from(payload.base64, "base64");
58395
+ if (bytes.length > MAX_ARTIFACT_BYTES) {
58396
+ return { media_error: `Generated media exceeds ${MAX_ARTIFACT_BYTES / (1024 * 1024)} MiB artifact limit`, bytes: bytes.length, media_type: mediaType };
58354
58397
  }
58355
- return Object.fromEntries(await Promise.all(Object.entries(source).map(async ([key, item]) => [key, await extractMedia(session, item, depth + 1)])));
58398
+ const name = createHash2("sha256").update(bytes).digest("hex").slice(0, 20);
58399
+ const artifact = await saveArtifact(session, `generated/${name}.${EXTENSIONS[mediaType]}`, bytes, { media_type: mediaType });
58400
+ return { artifact_path: artifact.path, bytes: bytes.length, media_type: mediaType };
58401
+ }
58402
+ function declaredMediaType(key, siblings) {
58403
+ if (key !== "b64_json")
58404
+ return void 0;
58405
+ const declared = siblings?.media_type ?? siblings?.mime_type;
58406
+ return typeof declared === "string" ? declared : "image/png";
58407
+ }
58408
+ function dataUriPayload(value) {
58409
+ const match = /^data:([a-z]+\/[a-z0-9.+-]+)?(?:;[a-z-]+=[^;,]*)*;base64,/i.exec(value);
58410
+ return match ? { base64: value.slice(match[0].length), declared: match[1]?.toLowerCase() } : void 0;
58411
+ }
58412
+ function looksLikeBase64(value) {
58413
+ return /^[A-Za-z0-9+/_-]{64}/.test(value);
58414
+ }
58415
+ function isCompleteBase64(value) {
58416
+ return /^[A-Za-z0-9+/_\-\s]*={0,2}$/.test(value);
58417
+ }
58418
+ function sniff(bytes) {
58419
+ const ascii = (at, text) => bytes.length >= at + text.length && bytes.subarray(at, at + text.length).toString("latin1") === text;
58420
+ if (ascii(0, "RIFF"))
58421
+ return ascii(8, "WAVE") ? "audio/wav" : ascii(8, "WEBP") ? "image/webp" : ascii(8, "AVI ") ? "video/x-msvideo" : void 0;
58422
+ if (ascii(0, "ID3"))
58423
+ return "audio/mpeg";
58424
+ if (ascii(0, "OggS"))
58425
+ return "audio/ogg";
58426
+ if (ascii(0, "fLaC"))
58427
+ return "audio/flac";
58428
+ if (ascii(0, "%PDF-"))
58429
+ return "application/pdf";
58430
+ if (ascii(0, "GIF87a") || ascii(0, "GIF89a"))
58431
+ return "image/gif";
58432
+ if (bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])))
58433
+ return "image/png";
58434
+ if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255)
58435
+ return "image/jpeg";
58436
+ if (bytes.length >= 4 && bytes.subarray(0, 4).equals(Buffer.from([26, 69, 223, 163])))
58437
+ return "video/webm";
58438
+ if (ascii(4, "ftyp")) {
58439
+ const brand = bytes.subarray(8, 12).toString("latin1");
58440
+ return brand.startsWith("qt") ? "video/quicktime" : /^(M4A|M4B|mp4a)/.test(brand) ? "audio/mp4" : "video/mp4";
58441
+ }
58442
+ if (bytes.length >= 4 && bytes[0] === 255 && (bytes[1] & 224) === 224 && (bytes[1] & 24) !== 8 && (bytes[1] & 6) !== 0 && (bytes[2] & 240) !== 240 && (bytes[2] & 240) !== 0)
58443
+ return "audio/mpeg";
58444
+ return void 0;
58356
58445
  }
58446
+ var MAX_ARTIFACT_BYTES, MIN_BASE64_CHARS, SNIFF_CHARS, EXTENSIONS;
58357
58447
  var init_media = __esm({
58358
58448
  "../../packages/economy/dist/media.js"() {
58359
58449
  "use strict";
58360
58450
  init_dist2();
58451
+ MAX_ARTIFACT_BYTES = 24 * 1024 * 1024;
58452
+ MIN_BASE64_CHARS = 1024;
58453
+ SNIFF_CHARS = 64;
58454
+ EXTENSIONS = {
58455
+ "video/mp4": "mp4",
58456
+ "video/webm": "webm",
58457
+ "video/quicktime": "mov",
58458
+ "video/x-msvideo": "avi",
58459
+ "image/png": "png",
58460
+ "image/jpeg": "jpg",
58461
+ "image/gif": "gif",
58462
+ "image/webp": "webp",
58463
+ "audio/mpeg": "mp3",
58464
+ "audio/wav": "wav",
58465
+ "audio/ogg": "ogg",
58466
+ "audio/flac": "flac",
58467
+ "audio/mp4": "m4a",
58468
+ "application/pdf": "pdf"
58469
+ };
58361
58470
  }
58362
58471
  });
58363
58472
 
@@ -58474,6 +58583,9 @@ var init_purchases = __esm({
58474
58583
  // ../../packages/economy/dist/gateway.js
58475
58584
  import { readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
58476
58585
  import { join as join12 } from "node:path";
58586
+ async function boundResponse(session, data) {
58587
+ return capStrings(await extractMedia(session, data), MAX_EXTERNAL_STRING_CHARS);
58588
+ }
58477
58589
  function requestFor(endpoint, method, body) {
58478
58590
  if (method !== "GET" && method !== "HEAD")
58479
58591
  return { url: endpoint, body };
@@ -58568,6 +58680,7 @@ var init_gateway = __esm({
58568
58680
  "use strict";
58569
58681
  init_crowdcode_preferences();
58570
58682
  init_media();
58683
+ init_dist();
58571
58684
  init_dist2();
58572
58685
  init_offers();
58573
58686
  init_approvals();
@@ -58651,7 +58764,7 @@ var init_gateway = __esm({
58651
58764
  case "review_paid_service":
58652
58765
  return await this.reviewPaidService(args);
58653
58766
  case "read_service":
58654
- return this.options.agentcash.read ? await this.options.agentcash.read(String(args.url ?? "")) : { ok: false, error: "Authenticated reads are unavailable" };
58767
+ return await this.readService(args);
58655
58768
  case "bridge_usdc":
58656
58769
  return await this.bridgeUsdc(args, operationId);
58657
58770
  default:
@@ -58695,6 +58808,15 @@ var init_gateway = __esm({
58695
58808
  async serviceEvidence(endpoint) {
58696
58809
  return await this.crowdcodeEnabled() ? this.options.crowdcode.getServiceScore({ apiEndpoint: endpoint }) : { ok: true, unproven: true, summary: "CrowdCode is off; reputation checks and submissions are disabled." };
58697
58810
  }
58811
+ /** An unpaid read is still an external body: bound it like a paid one. */
58812
+ async readService(args) {
58813
+ if (!this.options.agentcash.read)
58814
+ return { ok: false, error: "Authenticated reads are unavailable" };
58815
+ const result = await this.options.agentcash.read(String(args.url ?? ""));
58816
+ if (!result.ok || result.data === void 0)
58817
+ return result;
58818
+ return { ...result, data: await boundResponse(this.options.session, result.data) };
58819
+ }
58698
58820
  async getWalletStatus() {
58699
58821
  const result = await this.options.agentcash.getBalance();
58700
58822
  if (!result.ok) {
@@ -58851,7 +58973,7 @@ var init_gateway = __esm({
58851
58973
  const paid = outcome === "paid_success" || outcome === "paid_failure";
58852
58974
  const chargedCents = result.ambiguous ? quotedCostCents : paid ? Math.max(1, Math.round((result.payment?.paidUsd ?? quotedCostCents / 100) * 100)) : 0;
58853
58975
  if (result.data)
58854
- result.data = await extractMedia(session, result.data);
58976
+ result.data = await boundResponse(session, result.data);
58855
58977
  const artifact = result.ambiguityReason === "transport" ? void 0 : await saveArtifact(session, `service-calls/${Date.now()}-${slugUrl(endpoint)}.json`, JSON.stringify({ status: result.status, body: result.data }, null, 2), { endpoint, method });
58856
58978
  const purchaseId = newPurchaseId();
58857
58979
  const railUsed = result.payment?.rail ?? inspection.rail;
@@ -59369,6 +59491,9 @@ var init_mocks = __esm({
59369
59491
  }
59370
59492
  };
59371
59493
  }
59494
+ async read(url3) {
59495
+ return url3 in (this.options.readResults ?? {}) ? { ok: true, data: this.options.readResults[url3] } : { ok: false, error: `no scripted read for ${url3}` };
59496
+ }
59372
59497
  async bridge(input2) {
59373
59498
  return { ok: true, data: { bridged: input2.amountUsd, from: input2.from, to: input2.to, mock: true } };
59374
59499
  }
@@ -59483,6 +59608,7 @@ var init_dist4 = __esm({
59483
59608
  init_crowdcode();
59484
59609
  init_gateway();
59485
59610
  init_mcp();
59611
+ init_media();
59486
59612
  init_monad();
59487
59613
  init_mocks();
59488
59614
  init_purchases();
@@ -87058,7 +87184,9 @@ function createHostedToolExecutor(options) {
87058
87184
  }
87059
87185
  try {
87060
87186
  const result = await postHostedTool(options, name, { ...args, ...source === void 0 ? {} : { source } }, { signal: context.signal });
87061
- return result.ok ? { ok: true, data: result.data } : { ok: false, error: result.error ?? "tool failed" };
87187
+ if (result.ok)
87188
+ return { ok: true, data: await boundResponse(context.session, result.data) };
87189
+ return { ok: false, error: result.error ?? "tool failed" };
87062
87190
  } catch (error53) {
87063
87191
  if (context.signal?.aborted)
87064
87192
  throw error53;
@@ -87074,6 +87202,7 @@ var init_hosted_provider = __esm({
87074
87202
  "use strict";
87075
87203
  init_dist();
87076
87204
  init_dist2();
87205
+ init_dist4();
87077
87206
  init_worker();
87078
87207
  HostedRequestError = class extends Error {
87079
87208
  code;
@@ -87974,7 +88103,7 @@ var init_worker = __esm({
87974
88103
  try {
87975
88104
  if (saved?.approvalPending && name === "call_paid_service" && economy)
87976
88105
  await economy.execute("inspect_paid_service", { url: args.url, method: args.method, sample_body: args.body });
87977
- output3 = await invoke();
88106
+ output3 = capToolResult(await invoke());
87978
88107
  if (financialState && name === "get_budget_status")
87979
88108
  output3 = { ok: true, data: latestFinancialState };
87980
88109
  if (financialState && name === "complete_session" && output3.ok && output3.data && typeof output3.data === "object")
@@ -88417,7 +88546,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
88417
88546
  "You can use new services without prior CrowdCode reviews. Payable: x402 USDC on Base or Solana and MPP USDC.e on Tempo; the hosted gateway pays the cheapest offered network your balance on that network covers. Other networks, including Monad, are not payable here. Payments stay within the user's configured limits; over-limit quotes request approval. Use read_service for authenticated job polling and never resubmit a pending generation job.",
88418
88547
  "funding_required means your USDC is on the wrong network, not that you are out of money, and a wallet with money in it finishes the job. First go back to find_paid_service and look for an equivalent service sold on a network you already hold USDC on \u2014 every result lists its `networks`, and paying from an existing balance costs no transfer fee. If none fits, move funds with bridge_usdc and retry the same call: aim for about $1 so one fee funds many later calls, and when the wallet holds less than that, move what the call needs plus the fee, as long as the fee stays under about a fifth of what moves (roughly $0.15 and up at a $0.03 fee). A move inside that rule is ordinary work, not a decision for the owner: make it and retry, exactly as you would pay for the call itself. The spending guard applies the owner's approval mode to the fee by itself and pauses the run if it has to, so you never need permission before calling bridge_usdc. Stopping to ask whether you may spend three cents moving the owner's own money to the network a seller wants is the dead end this rule exists to prevent. Only below that threshold is a move not worth making on your own: quote_only it, then ask. When the owner asks you to move funds, quote first, state the fee, then execute and report both transaction ids and the amount received. Moves follow the owner's approval mode like payments; never move funds without a reason.",
88419
88548
  "When money is what blocks a task, say exactly that and nothing else: which network the service is sold on, what you hold where, and the single thing you need \u2014 permission for a move you have quoted, or USDC added on that network. Never dress a funding problem up as the service being unsuitable or unavailable, never imply your wallet is empty when it is not, and never offer a recorded request_service as though it were the answer the owner asked for."
88420
- ] : [], capabilityIndex ?? DEFAULT_CAPABILITY_INDEX, "When a task needs current web facts, search results, or unfamiliar page content, make one paid web search your FIRST move \u2014 do not serially guess URLs with curl; one paid search replaces minutes of blind fetching and costs less than the LLM turns it saves.", "Approval, budget, reputation, and payment rails are enforced in code \u2014 you cannot bypass them, so state costs plainly and never invent payment details.", "When the user says CrowdCode on/off, call set_crowdcode_enabled; default to session scope unless they ask for a persistent default. Use list_my_reviews and delete_my_review for user-directed review management, including while off. Before finishing a substantive task, consider concrete paid services that would fix real shortcomings and submit qualifying demand with request_service (input, output, problem, price_per_call), unless CrowdCode is off. Never invent a purchase or demand.", "Each tool result includes the budget before and after that tool call. Never ask for wallet private keys or secrets.", ...options.promptSections ?? []);
88549
+ ] : [], capabilityIndex ?? DEFAULT_CAPABILITY_INDEX, "When a task needs current web facts, search results, or unfamiliar page content, make one paid web search your FIRST move \u2014 do not serially guess URLs with curl; one paid search replaces minutes of blind fetching and costs less than the LLM turns it saves.", "Approval, budget, reputation, and payment rails are enforced in code \u2014 you cannot bypass them, so state costs plainly and never invent payment details.", "When the user says CrowdCode on/off, call set_crowdcode_enabled; default to session scope unless they ask for a persistent default. Use list_my_reviews and delete_my_review for user-directed review management, including while off. Before finishing a substantive task, consider concrete paid services that would fix real shortcomings and submit qualifying demand with request_service (input, output, problem, price_per_call), unless CrowdCode is off. Never invent a purchase or demand.", "Generated media never reaches you as base64 you can read: when a service returns audio, an image, video or a PDF, the runtime has already written the bytes to a file and handed you {artifact_path, bytes, media_type} instead. Work from that path with run_shell \u2014 ffmpeg, python3 and node are installed, so converting a wav to the mp3 the owner asked for is one command \u2014 and leave the deliverable under artifacts/ in the format they asked for. Never pay a second time for a file you already hold.", "Each tool result includes the budget before and after that tool call. Never ask for wallet private keys or secrets.", ...options.promptSections ?? []);
88421
88550
  } else {
88422
88551
  systemPromptParts.push("Paid external services are unavailable in this run; work with local tools only and say so if the task truly requires external capability.");
88423
88552
  }
@@ -88694,6 +88823,7 @@ ${outputContinuationPrefix}`);
88694
88823
  } else {
88695
88824
  result = { ok: false, error: `unknown tool: ${call2.name}` };
88696
88825
  }
88826
+ result = capToolResult(result);
88697
88827
  await refreshFinancialState();
88698
88828
  const budgetAfterToolCall = currentBudget();
88699
88829
  if (options.financialState && call2.name === "get_budget_status")
@@ -89062,6 +89192,9 @@ async function completeMockLlmCall(session, provider, messages, turn) {
89062
89192
  throw error53;
89063
89193
  }
89064
89194
  }
89195
+ function capToolResult(result) {
89196
+ return result.data === void 0 ? result : { ...result, data: capStrings(result.data, MAX_EXTERNAL_STRING_CHARS) };
89197
+ }
89065
89198
  function toolMessagePayload(payload) {
89066
89199
  return truncateForModel(pruneToolPayload(payload), 12e3);
89067
89200
  }
@@ -91630,17 +91763,17 @@ async function writeTree(dir, tree, meta4) {
91630
91763
  }
91631
91764
  async function readTree(dir) {
91632
91765
  const tree = {};
91633
- const walk3 = async (current) => {
91766
+ const walk4 = async (current) => {
91634
91767
  for (const entry of await readdir2(current, { withFileTypes: true })) {
91635
91768
  const path3 = join31(current, entry.name);
91636
91769
  if (entry.isDirectory()) {
91637
- await walk3(path3);
91770
+ await walk4(path3);
91638
91771
  } else if (entry.name.endsWith(".md")) {
91639
91772
  tree[relative4(dir, path3)] = await readFile22(path3, "utf8");
91640
91773
  }
91641
91774
  }
91642
91775
  };
91643
- await walk3(dir);
91776
+ await walk4(dir);
91644
91777
  let meta4;
91645
91778
  try {
91646
91779
  meta4 = JSON.parse(await readFile22(join31(dir, "meta.json"), "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrowd",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "A CLI agent with its own wallet — discovers x402 services, pays in USDC, and completes tasks under a local budget",
5
5
  "type": "module",
6
6
  "license": "MIT",