opencrowd 0.15.1 → 0.15.3

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 +228 -62
  2. package/package.json +1 -1
@@ -15926,7 +15926,7 @@ var init_limits = __esm({
15926
15926
 
15927
15927
  // ../../packages/protocol/dist/telemetry.js
15928
15928
  function scrubText(text) {
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]@");
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(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi, "$1[REDACTED]@");
15930
15930
  }
15931
15931
  function captureDetail(input2, maxBytes = 96 * 1024) {
15932
15932
  let redacted = false;
@@ -16111,7 +16111,7 @@ function isContextWindowError(value) {
16111
16111
  return true;
16112
16112
  return error53.error !== value && !!error53.error && isContextWindowError(typeof error53.error === "string" ? { message: error53.error } : error53.error);
16113
16113
  }
16114
- var MODEL_REQUEST_MAX_BYTES, MODEL_MAX_MESSAGES, MODEL_GATEWAY_TIMEOUT_MS, MODEL_BRIDGE_TIMEOUT_MS, SERVICE_GATEWAY_TIMEOUT_MS, SERVICE_BRIDGE_TIMEOUT_MS, SERVICE_RESPONSE_MAX_BYTES, DEFAULT_CONTEXT_WINDOW, ContextWindowExceeded;
16114
+ var MODEL_REQUEST_MAX_BYTES, MODEL_MAX_MESSAGES, MODEL_GATEWAY_TIMEOUT_MS, MODEL_BRIDGE_TIMEOUT_MS, SERVICE_GATEWAY_TIMEOUT_MS, SERVICE_BRIDGE_TIMEOUT_MS, HOSTED_TOOL_BRIDGE_TIMEOUT_MS, HOSTED_CONTROL_TOOL_TIMEOUT_MS, SERVICE_RESPONSE_MAX_BYTES, DEFAULT_CONTEXT_WINDOW, ContextWindowExceeded;
16115
16115
  var init_context = __esm({
16116
16116
  "../../packages/protocol/dist/context.js"() {
16117
16117
  "use strict";
@@ -16121,6 +16121,8 @@ var init_context = __esm({
16121
16121
  MODEL_BRIDGE_TIMEOUT_MS = 2 * MODEL_GATEWAY_TIMEOUT_MS + 3e4;
16122
16122
  SERVICE_GATEWAY_TIMEOUT_MS = 33e4;
16123
16123
  SERVICE_BRIDGE_TIMEOUT_MS = MODEL_GATEWAY_TIMEOUT_MS + SERVICE_GATEWAY_TIMEOUT_MS + 3e4;
16124
+ HOSTED_TOOL_BRIDGE_TIMEOUT_MS = 9e4;
16125
+ HOSTED_CONTROL_TOOL_TIMEOUT_MS = 185e3;
16124
16126
  SERVICE_RESPONSE_MAX_BYTES = 34 * 1024 * 1024;
16125
16127
  DEFAULT_CONTEXT_WINDOW = 64e3;
16126
16128
  ContextWindowExceeded = class extends Error {
@@ -16129,6 +16131,44 @@ var init_context = __esm({
16129
16131
  }
16130
16132
  });
16131
16133
 
16134
+ // ../../packages/protocol/dist/deadlines.js
16135
+ function withDeadline(work, timeoutMs, stage) {
16136
+ let timer;
16137
+ const started = work();
16138
+ started.catch(() => void 0);
16139
+ const expiry = new Promise((_resolve, reject) => {
16140
+ timer = setTimeout(() => reject(new DeadlineExceeded(stage, timeoutMs)), timeoutMs);
16141
+ });
16142
+ return Promise.race([started, expiry]).finally(() => clearTimeout(timer));
16143
+ }
16144
+ function deadline(totalMs, now = Date.now) {
16145
+ const expiresAt = now() + totalMs;
16146
+ return {
16147
+ /** Milliseconds left, never negative. */
16148
+ remaining: () => Math.max(0, expiresAt - now()),
16149
+ expired: () => now() >= expiresAt,
16150
+ /** The smaller of what is left and one operation's own ceiling. */
16151
+ slice: (operationMs) => Math.min(operationMs, Math.max(0, expiresAt - now()))
16152
+ };
16153
+ }
16154
+ var DeadlineExceeded;
16155
+ var init_deadlines = __esm({
16156
+ "../../packages/protocol/dist/deadlines.js"() {
16157
+ "use strict";
16158
+ DeadlineExceeded = class extends Error {
16159
+ stage;
16160
+ timeoutMs;
16161
+ code = "deadline_exceeded";
16162
+ constructor(stage, timeoutMs) {
16163
+ super(`${stage} did not finish within ${Math.round(timeoutMs / 1e3)}s`);
16164
+ this.stage = stage;
16165
+ this.timeoutMs = timeoutMs;
16166
+ this.name = "DeadlineExceeded";
16167
+ }
16168
+ };
16169
+ }
16170
+ });
16171
+
16132
16172
  // ../../packages/protocol/dist/spending.js
16133
16173
  function spendingDecision(input2) {
16134
16174
  const amount = BigInt(input2.amount), committed = BigInt(input2.queryCommitted);
@@ -16242,6 +16282,7 @@ var init_dist = __esm({
16242
16282
  init_seq();
16243
16283
  init_codec();
16244
16284
  init_context();
16285
+ init_deadlines();
16245
16286
  init_spending();
16246
16287
  init_financial_state();
16247
16288
  }
@@ -57687,6 +57728,16 @@ var init_offers = __esm({
57687
57728
  });
57688
57729
 
57689
57730
  // ../../packages/economy/dist/discovery.js
57731
+ function bounded(work, timeoutMs, stage) {
57732
+ return withDeadline(work, timeoutMs, stage).catch((error53) => ({ ok: false, error: error53.message }));
57733
+ }
57734
+ function originOf(url3) {
57735
+ try {
57736
+ return new URL(url3).origin;
57737
+ } catch {
57738
+ return url3;
57739
+ }
57740
+ }
57690
57741
  function searchResults(raw) {
57691
57742
  const body = raw?.results?.results ? raw.results : raw?.data?.results ? raw.data : raw;
57692
57743
  return Array.isArray(body?.results) ? body.results : Array.isArray(body?.services) ? body.services : [];
@@ -57728,12 +57779,16 @@ function ranked(ranking, schema) {
57728
57779
  sources: ranking.sources
57729
57780
  };
57730
57781
  }
57731
- var RANKED_NOTE, BaseServiceDiscovery;
57782
+ var PROBE_DEADLINE_MS, VERIFY_SWEEP_DEADLINE_MS, CATALOG_DEADLINE_MS, RANKED_NOTE, BaseServiceDiscovery;
57732
57783
  var init_discovery = __esm({
57733
57784
  "../../packages/economy/dist/discovery.js"() {
57734
57785
  "use strict";
57735
57786
  init_dist3();
57787
+ init_dist();
57736
57788
  init_offers();
57789
+ PROBE_DEADLINE_MS = 15e3;
57790
+ VERIFY_SWEEP_DEADLINE_MS = 6e4;
57791
+ CATALOG_DEADLINE_MS = 45e3;
57737
57792
  RANKED_NOTE = "Ranked by CrowdCode across Base, Solana and Tempo (AgentCash, Coinbase's x402 catalog, MPP catalog and CrowdCode reviews): quality first, then effective price on best_network (including about $0.03 to move USDC between your own networks when that network's balance is short), then your balance there. Prefer the highest-ranked service that fits the task, whatever its network. Each result's `networks` are the networks it can be paid on; between results that fit the task equally well, one you can pay from a balance you already hold beats one that needs a transfer first. transfer_needed means your balance on best_network is short: move USDC between your own networks first if you have a tool for that; otherwise say which network needs funds. New/unreviewed services are allowed. Inspect the chosen endpoint before paying.";
57738
57793
  BaseServiceDiscovery = class {
57739
57794
  searchApi;
@@ -57745,17 +57800,26 @@ var init_discovery = __esm({
57745
57800
  this.options = options;
57746
57801
  this.rails = options.rails ?? localRails(options.paymentNetwork);
57747
57802
  }
57748
- async inspect(input2) {
57803
+ async inspect(input2, budgetMs = PROBE_DEADLINE_MS) {
57749
57804
  const key = `${input2.method ?? "POST"} ${input2.url}`;
57750
57805
  const cached3 = this.cache.get(key);
57751
57806
  if (input2.body === void 0 && cached3 && cached3.until > Date.now())
57752
57807
  return { ok: true, data: cached3.data };
57753
- const result = await checkEndpointSchema({
57754
- url: input2.url,
57755
- probe: true,
57756
- sampleInputBody: input2.body && typeof input2.body === "object" ? input2.body : void 0,
57757
- signal: AbortSignal.timeout(1e4)
57758
- });
57808
+ if (budgetMs <= 0)
57809
+ return { ok: false, error: "Endpoint inspection ran out of time before it started" };
57810
+ let result;
57811
+ try {
57812
+ result = await withDeadline(() => checkEndpointSchema({
57813
+ url: input2.url,
57814
+ probe: true,
57815
+ sampleInputBody: input2.body && typeof input2.body === "object" ? input2.body : void 0,
57816
+ signal: AbortSignal.timeout(Math.min(1e4, budgetMs))
57817
+ }), budgetMs, `probe of ${originOf(input2.url)}`);
57818
+ } catch (error53) {
57819
+ if (!(error53 instanceof DeadlineExceeded))
57820
+ throw error53;
57821
+ return { ok: false, error: `${originOf(input2.url)} did not answer the inspection probe within ${Math.round(budgetMs / 1e3)}s` };
57822
+ }
57759
57823
  if (!result.found)
57760
57824
  return { ok: false, error: result.message ?? "Endpoint inspection failed" };
57761
57825
  const advisory = result.advisories.find((a) => a.method === (input2.method ?? "POST").toUpperCase());
@@ -57813,17 +57877,31 @@ var init_discovery = __esm({
57813
57877
  const reason = payableNetworks(offers, this.rails).includes(preferred) ? `your ${preferred} balance does not cover it` : `${preferred} is not offered`;
57814
57878
  return `Pays ${networkLabel(offer)}${offer.network_name === preferred ? "" : ` (${reason})`}. ${quote}`;
57815
57879
  }
57816
- /** Probe candidates in order and keep the first `limit` this payer can settle. */
57817
- async verified(candidates, limit) {
57880
+ /**
57881
+ * Probe candidates in order and keep the first `limit` this payer can
57882
+ * settle, within one shared time budget.
57883
+ *
57884
+ * `incomplete` is true when the budget ran out with candidates left. The
57885
+ * caller says so in the note: a short list of real, payable services is a
57886
+ * usable answer, and far better than the nine-minute silence this sweep used
57887
+ * to be capable of. Returning nothing at all would not be.
57888
+ */
57889
+ async verified(candidates, limit, budgetMs = VERIFY_SWEEP_DEADLINE_MS) {
57818
57890
  const services = [];
57891
+ const budget = deadline(budgetMs);
57892
+ let incomplete = false;
57819
57893
  for (let i = 0; i < candidates.length && services.length < limit; i += 4) {
57894
+ if (budget.expired()) {
57895
+ incomplete = true;
57896
+ break;
57897
+ }
57820
57898
  const batch = await Promise.all(candidates.slice(i, i + 4).map(async (c) => {
57821
57899
  try {
57822
57900
  const endpoint = c.endpoint ?? new URL(c.path, typeof c.origin === "string" ? c.origin : c.origin?.url).href;
57823
57901
  let method = c.method ?? "POST";
57824
- let inspection = await this.inspect({ url: endpoint, method });
57902
+ let inspection = await this.inspect({ url: endpoint, method }, budget.slice(PROBE_DEADLINE_MS));
57825
57903
  if (!inspection.ok && !c.method)
57826
- inspection = await this.inspect({ url: endpoint, method: method = "GET" });
57904
+ inspection = await this.inspect({ url: endpoint, method: method = "GET" }, budget.slice(PROBE_DEADLINE_MS));
57827
57905
  const schema = inspection.data;
57828
57906
  if (!inspection.ok || !schema?.payable)
57829
57907
  return void 0;
@@ -57845,7 +57923,13 @@ var init_discovery = __esm({
57845
57923
  }));
57846
57924
  services.push(...batch.filter(Boolean));
57847
57925
  }
57848
- return services.slice(0, limit);
57926
+ if (budget.expired() && services.length < limit)
57927
+ incomplete = true;
57928
+ return { services: services.slice(0, limit), incomplete };
57929
+ }
57930
+ /** What the model is told when the probe budget cut a candidate list short. */
57931
+ partialNote(incomplete) {
57932
+ return incomplete ? " Some candidates were still being probed when the search's time budget ran out, so this list is partial; search again or narrow the query if nothing here fits." : "";
57849
57933
  }
57850
57934
  /** The networks this payer settles, as CrowdCode names them. */
57851
57935
  networks() {
@@ -57861,38 +57945,48 @@ var init_discovery = __esm({
57861
57945
  const limit = Math.min(20, Math.max(1, options.limit ?? 8));
57862
57946
  const { rank, ...searchOptions } = options;
57863
57947
  const [result, balances] = await Promise.all([
57864
- this.searchApi(query, { ...searchOptions, limit: Math.min(50, limit * 3) }).catch((error53) => ({ ok: false, error: error53.message })),
57865
- rank && this.options.balances ? this.options.balances().catch(() => void 0) : Promise.resolve(void 0)
57948
+ bounded(() => this.searchApi(query, { ...searchOptions, limit: Math.min(50, limit * 3) }), CATALOG_DEADLINE_MS, "AgentCash search"),
57949
+ rank && this.options.balances ? withDeadline(() => this.options.balances(), CATALOG_DEADLINE_MS, "wallet balance read").catch(() => void 0) : Promise.resolve(void 0)
57866
57950
  ]);
57867
57951
  const candidates = result.ok ? searchResults(result.data) : [];
57868
57952
  if (rank) {
57869
- const ranking = await rank({
57953
+ const ranking = await bounded(() => rank({
57870
57954
  query,
57871
57955
  networks: this.networks(),
57872
57956
  ...balances ? { balances } : {},
57873
57957
  candidates: candidates.map(agentCashCandidate).filter((candidate) => candidate !== void 0),
57874
57958
  limit: Math.min(25, limit * 2)
57875
- }).catch((error53) => ({ ok: false, error: error53.message }));
57959
+ }), CATALOG_DEADLINE_MS, "CrowdCode ranking");
57876
57960
  const services = ranking.data?.services;
57877
57961
  if (ranking.ok && Array.isArray(services)) {
57878
57962
  const ordered = services.filter((service) => typeof service?.endpoint === "string").map((service) => ({ endpoint: service.endpoint, method: service.method ?? void 0, name: service.name, ranking: service }));
57879
- return { ok: true, data: { services: await this.verified(ordered, limit), note: RANKED_NOTE } };
57963
+ const verified2 = await this.verified(ordered, limit);
57964
+ return { ok: true, data: { services: verified2.services, note: `${RANKED_NOTE}${this.partialNote(verified2.incomplete)}` } };
57880
57965
  }
57881
57966
  }
57882
57967
  if (!result.ok)
57883
57968
  return result;
57969
+ const verified = await this.verified(candidates, limit);
57884
57970
  return { ok: true, data: {
57885
- services: await this.verified(candidates, limit),
57886
- note: `AgentCash discovery${rank ? " (CrowdCode ranking unavailable)" : ""}, verified ${describeRails(this.rails)}. New/unreviewed services are allowed. Inspect the chosen endpoint before paying.`
57971
+ services: verified.services,
57972
+ note: `AgentCash discovery${rank ? " (CrowdCode ranking unavailable)" : ""}, verified ${describeRails(this.rails)}. New/unreviewed services are allowed. Inspect the chosen endpoint before paying.${this.partialNote(verified.incomplete)}`
57887
57973
  } };
57888
57974
  }
57889
57975
  async discoverEndpoints(origin) {
57890
- const result = await discoverOriginSchema({ target: origin, signal: AbortSignal.timeout(1e4) });
57976
+ let result;
57977
+ try {
57978
+ result = await withDeadline(() => discoverOriginSchema({ target: origin, signal: AbortSignal.timeout(1e4) }), PROBE_DEADLINE_MS, `discovery of ${origin}`);
57979
+ } catch (error53) {
57980
+ if (!(error53 instanceof DeadlineExceeded))
57981
+ throw error53;
57982
+ return { ok: false, error: `${origin} did not answer its discovery document within ${Math.round(PROBE_DEADLINE_MS / 1e3)}s` };
57983
+ }
57891
57984
  if (!result.found)
57892
57985
  return { ok: false, error: result.message ?? "Provider discovery failed" };
57893
57986
  const candidates = result.endpoints.filter((e6) => e6.protocols?.some((protocol) => protocol === "x402" || protocol === "mpp")).map((e6) => ({ ...e6, origin: result.origin }));
57987
+ const verified = await this.verified(candidates, 50);
57894
57988
  return { ok: true, data: {
57895
- services: await this.verified(candidates, 50),
57989
+ services: verified.services,
57896
57990
  guidance: result.guidance,
57897
57991
  reads: result.endpoints.filter((e6) => ["siwx", "unprotected"].includes(e6.authMode ?? "")),
57898
57992
  note: "Use read_service to poll job URLs. Do not buy the same generation twice."
@@ -58621,7 +58715,7 @@ function requestFor(endpoint, method, body) {
58621
58715
  function inspectionKey(endpoint, method) {
58622
58716
  return `${method} ${endpoint}`;
58623
58717
  }
58624
- function originOf(endpoint) {
58718
+ function originOf2(endpoint) {
58625
58719
  try {
58626
58720
  return new URL(endpoint).origin;
58627
58721
  } catch {
@@ -59053,7 +59147,7 @@ var init_gateway = __esm({
59053
59147
  };
59054
59148
  }
59055
59149
  async resolveApproval(endpoint, method, quotedCostCents, evidence) {
59056
- const origin = originOf(endpoint);
59150
+ const origin = originOf2(endpoint);
59057
59151
  const rules = await loadApprovalRules(this.options.approvalRulesPath);
59058
59152
  const rule = matchApprovalRule(rules, endpoint, origin);
59059
59153
  if (rule?.decision === "block") {
@@ -59108,9 +59202,9 @@ var init_gateway = __esm({
59108
59202
  return `quoted cost ${quotedCostCents}c exceeds the per-call cap ${caps.maxCostCents}c for ${endpoint}`;
59109
59203
  }
59110
59204
  if (caps.sessionMaxCents !== void 0) {
59111
- const spent = (await listPurchases(this.options.session)).filter((state) => originOf(state.record.endpoint) === originOf(endpoint)).reduce((total, state) => total + state.record.charged_cost_cents, 0);
59205
+ const spent = (await listPurchases(this.options.session)).filter((state) => originOf2(state.record.endpoint) === originOf2(endpoint)).reduce((total, state) => total + state.record.charged_cost_cents, 0);
59112
59206
  if (spent + quotedCostCents > caps.sessionMaxCents) {
59113
- return `quoted cost would exceed the session cap ${caps.sessionMaxCents}c for ${originOf(endpoint)} (already ${spent}c)`;
59207
+ return `quoted cost would exceed the session cap ${caps.sessionMaxCents}c for ${originOf2(endpoint)} (already ${spent}c)`;
59114
59208
  }
59115
59209
  }
59116
59210
  return void 0;
@@ -62532,9 +62626,9 @@ ${value.slice(-(scanLimit - prefixLength))}`;
62532
62626
  );
62533
62627
  }
62534
62628
  const pollUrl = this.absolute(submitData.poll_url);
62535
- const deadline = Date.now() + budgetMs;
62629
+ const deadline2 = Date.now() + budgetMs;
62536
62630
  let lastStatus = submitData.status || "queued";
62537
- while (Date.now() < deadline) {
62631
+ while (Date.now() < deadline2) {
62538
62632
  await sleep(intervalMs);
62539
62633
  const pollResp = await this.fetchWithTimeout(pollUrl, {
62540
62634
  method: "GET",
@@ -64270,7 +64364,7 @@ var init_chunk_GPMYUGHX = __esm({
64270
64364
  });
64271
64365
 
64272
64366
  // ../../node_modules/@x402/evm/dist/esm/chunk-X4BYNI7R.mjs
64273
- async function signEip2612Permit(signer, tokenAddress, tokenName, tokenVersion, chainId, deadline, permittedAmount) {
64367
+ async function signEip2612Permit(signer, tokenAddress, tokenName, tokenVersion, chainId, deadline2, permittedAmount) {
64274
64368
  const owner = signer.address;
64275
64369
  const spender = getAddress(PERMIT2_ADDRESS);
64276
64370
  const nonce = await signer.readContract({
@@ -64291,7 +64385,7 @@ async function signEip2612Permit(signer, tokenAddress, tokenName, tokenVersion,
64291
64385
  spender,
64292
64386
  value: approvalAmount,
64293
64387
  nonce,
64294
- deadline: BigInt(deadline)
64388
+ deadline: BigInt(deadline2)
64295
64389
  };
64296
64390
  const signature = await signer.signTypedData({
64297
64391
  domain: domain3,
@@ -64305,7 +64399,7 @@ async function signEip2612Permit(signer, tokenAddress, tokenName, tokenVersion,
64305
64399
  spender,
64306
64400
  amount: approvalAmount.toString(),
64307
64401
  nonce: nonce.toString(),
64308
- deadline,
64402
+ deadline: deadline2,
64309
64403
  signature,
64310
64404
  version: "1"
64311
64405
  };
@@ -64432,7 +64526,7 @@ async function trySignEip2612PermitExtension(signer, options, requirements, resu
64432
64526
  } catch {
64433
64527
  }
64434
64528
  const permit2Auth = result.payload?.permit2Authorization;
64435
- const deadline = permit2Auth?.deadline ?? Math.floor(Date.now() / 1e3 + requirements.maxTimeoutSeconds).toString();
64529
+ const deadline2 = permit2Auth?.deadline ?? Math.floor(Date.now() / 1e3 + requirements.maxTimeoutSeconds).toString();
64436
64530
  const info = await signEip2612Permit(
64437
64531
  {
64438
64532
  address: signer.address,
@@ -64443,7 +64537,7 @@ async function trySignEip2612PermitExtension(signer, options, requirements, resu
64443
64537
  tokenName,
64444
64538
  tokenVersion,
64445
64539
  chainId,
64446
- deadline,
64540
+ deadline2,
64447
64541
  requiredAllowance
64448
64542
  );
64449
64543
  return {
@@ -64861,10 +64955,10 @@ async function createUptoPermit2Payload(signer, x402Version2, paymentRequirement
64861
64955
  const now = Math.floor(Date.now() / 1e3);
64862
64956
  const nonce = createPermit2Nonce();
64863
64957
  const validAfter = "0";
64864
- const deadline = (now + paymentRequirements.maxTimeoutSeconds).toString();
64865
- if (BigInt(deadline) <= BigInt(validAfter)) {
64958
+ const deadline2 = (now + paymentRequirements.maxTimeoutSeconds).toString();
64959
+ if (BigInt(deadline2) <= BigInt(validAfter)) {
64866
64960
  throw new Error(
64867
- `Invalid time window: deadline (${deadline}) must be after validAfter (${validAfter}). Check that maxTimeoutSeconds (${paymentRequirements.maxTimeoutSeconds}) is positive.`
64961
+ `Invalid time window: deadline (${deadline2}) must be after validAfter (${validAfter}). Check that maxTimeoutSeconds (${paymentRequirements.maxTimeoutSeconds}) is positive.`
64868
64962
  );
64869
64963
  }
64870
64964
  const permit2Authorization = {
@@ -64875,7 +64969,7 @@ async function createUptoPermit2Payload(signer, x402Version2, paymentRequirement
64875
64969
  },
64876
64970
  spender: x402UptoPermit2ProxyAddress,
64877
64971
  nonce,
64878
- deadline,
64972
+ deadline: deadline2,
64879
64973
  witness: {
64880
64974
  to: getAddress(paymentRequirements.payTo),
64881
64975
  facilitator: getAddress(facilitatorAddress),
@@ -87071,6 +87165,9 @@ function createHostedProvider(options) {
87071
87165
  });
87072
87166
  });
87073
87167
  req.setTimeout(MODEL_BRIDGE_TIMEOUT_MS, () => req.destroy(new Error("Hosted bridge timed out")));
87168
+ const total = setTimeout(() => req.destroy(new Error("Hosted bridge exceeded its total deadline")), MODEL_BRIDGE_TIMEOUT_MS);
87169
+ total.unref?.();
87170
+ req.once("close", () => clearTimeout(total));
87074
87171
  req.on("error", reject);
87075
87172
  req.end(body);
87076
87173
  }).catch((error53) => {
@@ -87159,8 +87256,11 @@ async function postHostedTool(options, name, args, settings = {}) {
87159
87256
  }
87160
87257
  });
87161
87258
  });
87162
- const timeoutMs = settings.timeoutMs ?? SERVICE_BRIDGE_TIMEOUT_MS;
87259
+ const timeoutMs = settings.timeoutMs ?? HOSTED_TOOL_BRIDGE_TIMEOUT_MS;
87163
87260
  req.setTimeout(timeoutMs, () => req.destroy(new HostedToolTimeout(name, timeoutMs)));
87261
+ const total = setTimeout(() => req.destroy(new HostedToolTimeout(name, timeoutMs)), timeoutMs);
87262
+ total.unref?.();
87263
+ req.once("close", () => clearTimeout(total));
87164
87264
  req.on("error", reject);
87165
87265
  req.end(body);
87166
87266
  });
@@ -87183,7 +87283,8 @@ function createHostedToolExecutor(options) {
87183
87283
  }
87184
87284
  }
87185
87285
  try {
87186
- const result = await postHostedTool(options, name, { ...args, ...source === void 0 ? {} : { source } }, { signal: context.signal });
87286
+ const timeoutMs = ["deploy_service", "request_secret", "vault_request"].includes(name) ? HOSTED_CONTROL_TOOL_TIMEOUT_MS : void 0;
87287
+ const result = await postHostedTool(options, name, { ...args, ...source === void 0 ? {} : { source } }, { signal: context.signal, ...timeoutMs ? { timeoutMs } : {} });
87187
87288
  if (result.ok)
87188
87289
  return { ok: true, data: await boundResponse(context.session, result.data) };
87189
87290
  return { ok: false, error: result.error ?? "tool failed" };
@@ -87348,7 +87449,7 @@ function numberValue6(value) {
87348
87449
  }
87349
87450
  return void 0;
87350
87451
  }
87351
- var DEFAULT_CROWDCODE_BASE, LIST_CACHE_MS, FIND_TIMEOUT_MS, PAY_TIMEOUT_MS, RECONCILE_DELAY_MS, NOT_SENT_CODES, FREE_FETCH_TIMEOUT_MS, USDC_ATOMIC_PER_USD, TRANSFER_WAIT_MS, TRANSFER_POLL_MS, PREFERRED_MOVE_USD, MOVE_FEE_ALLOWANCE_USD, MOVE_FEE_ESTIMATE_USD, MAX_MOVE_FEE_SHARE, BASE_WORKING_RESERVE_USD, RAIL_NETWORK2, ServiceDirectory, HostedAgentCashAdapter, usd, isPending, HostedCrowdCodeAdapter, HOSTED_BRIDGE_TOOL;
87452
+ var DEFAULT_CROWDCODE_BASE, LIST_CACHE_MS, FIND_TIMEOUT_MS, DIRECTORY_TIMEOUT_MS, PAY_TIMEOUT_MS, RECONCILE_DELAY_MS, NOT_SENT_CODES, FREE_FETCH_TIMEOUT_MS, USDC_ATOMIC_PER_USD, TRANSFER_WAIT_MS, TRANSFER_POLL_MS, PREFERRED_MOVE_USD, MOVE_FEE_ALLOWANCE_USD, MOVE_FEE_ESTIMATE_USD, MAX_MOVE_FEE_SHARE, BASE_WORKING_RESERVE_USD, RAIL_NETWORK2, ServiceDirectory, HostedAgentCashAdapter, usd, isPending, HostedCrowdCodeAdapter, HOSTED_BRIDGE_TOOL;
87352
87453
  var init_hosted_economy = __esm({
87353
87454
  "../../packages/agent-runtime/dist/hosted-economy.js"() {
87354
87455
  "use strict";
@@ -87362,6 +87463,7 @@ var init_hosted_economy = __esm({
87362
87463
  DEFAULT_CROWDCODE_BASE = "https://crowdcode-backend.onrender.com";
87363
87464
  LIST_CACHE_MS = 6e4;
87364
87465
  FIND_TIMEOUT_MS = 2e4;
87466
+ DIRECTORY_TIMEOUT_MS = 2e4;
87365
87467
  PAY_TIMEOUT_MS = SERVICE_GATEWAY_TIMEOUT_MS + 3e4;
87366
87468
  RECONCILE_DELAY_MS = 2e3;
87367
87469
  NOT_SENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ECONNREFUSED", "EACCES", "ENOTDIR"]);
@@ -87388,11 +87490,11 @@ var init_hosted_economy = __esm({
87388
87490
  if (!force && this.cache && Date.now() - this.cache.fetchedAt < LIST_CACHE_MS) {
87389
87491
  return this.cache.services;
87390
87492
  }
87391
- const response = await this.fetcher(`${this.base}/api/services`, { headers: { accept: "application/json" } });
87493
+ const response = await withDeadline(() => this.fetcher(`${this.base}/api/services`, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(DIRECTORY_TIMEOUT_MS) }), DIRECTORY_TIMEOUT_MS, "CrowdCode service list");
87392
87494
  if (!response.ok) {
87393
87495
  throw new Error(`CrowdCode service list unavailable (HTTP ${response.status})`);
87394
87496
  }
87395
- const payload = await response.json();
87497
+ const payload = await withDeadline(() => response.json(), DIRECTORY_TIMEOUT_MS, "CrowdCode service list body");
87396
87498
  const rows = Array.isArray(payload) ? payload : objectValue5(payload)?.services;
87397
87499
  const services = (Array.isArray(rows) ? rows : []).map(parseListedService).filter((row) => row !== void 0);
87398
87500
  for (const service of services) {
@@ -87412,8 +87514,8 @@ var init_hosted_economy = __esm({
87412
87514
  return this.idsByEndpoint.get(key);
87413
87515
  }
87414
87516
  async detail(serviceId) {
87415
- const response = await this.fetcher(`${this.base}/api/services/${encodeURIComponent(serviceId)}`, { headers: { accept: "application/json" } });
87416
- const body = objectValue5(await response.json().catch(() => void 0));
87517
+ const response = await withDeadline(() => this.fetcher(`${this.base}/api/services/${encodeURIComponent(serviceId)}`, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(DIRECTORY_TIMEOUT_MS) }), DIRECTORY_TIMEOUT_MS, "CrowdCode service detail");
87518
+ const body = objectValue5(await withDeadline(() => response.json(), DIRECTORY_TIMEOUT_MS, "CrowdCode service detail body").catch(() => void 0));
87417
87519
  return { status: response.status, body };
87418
87520
  }
87419
87521
  };
@@ -87594,13 +87696,13 @@ var init_hosted_economy = __esm({
87594
87696
  /** The supervisor found no 402: the endpoint is free, so the worker fetches it itself. */
87595
87697
  async fetchUnpaid(url3, method, body, headers) {
87596
87698
  try {
87597
- const response = await this.fetcher(url3, {
87699
+ const response = await withDeadline(() => this.fetcher(url3, {
87598
87700
  method,
87599
87701
  signal: AbortSignal.timeout(FREE_FETCH_TIMEOUT_MS),
87600
87702
  headers: { accept: "application/json", ...body !== void 0 ? { "content-type": "application/json" } : {}, ...headers ?? {} },
87601
87703
  ...body !== void 0 && method !== "GET" ? { body } : {}
87602
- });
87603
- const text = await response.text();
87704
+ }), FREE_FETCH_TIMEOUT_MS, "unpaid request");
87705
+ const text = await withDeadline(() => response.text(), FREE_FETCH_TIMEOUT_MS, "unpaid response body");
87604
87706
  return {
87605
87707
  ok: response.status < 400,
87606
87708
  ambiguous: false,
@@ -87691,8 +87793,8 @@ var init_hosted_economy = __esm({
87691
87793
  if (!reply.ok)
87692
87794
  return { ok: false, error: describeTransferError(reply) };
87693
87795
  let transfer2 = objectValue5(objectValue5(reply.data)?.transfer) ?? {};
87694
- const deadline = Date.now() + this.transferWait.totalMs;
87695
- while (isPending(transfer2) && stringValue5(transfer2.id) && Date.now() < deadline) {
87796
+ const deadline2 = Date.now() + this.transferWait.totalMs;
87797
+ while (isPending(transfer2) && stringValue5(transfer2.id) && Date.now() < deadline2) {
87696
87798
  await new Promise((resolve9) => setTimeout(resolve9, this.transferWait.pollMs));
87697
87799
  const next = await postHostedTool(this.socket, "economy.transfer", { action: "status", transfer_id: transfer2.id }, { timeoutMs: 6e4 }).catch(() => void 0);
87698
87800
  if (next?.ok)
@@ -87723,13 +87825,13 @@ var init_hosted_economy = __esm({
87723
87825
  /** CrowdCode's cross-chain search and ranking over its public API. */
87724
87826
  async findServices(search) {
87725
87827
  try {
87726
- const response = await this.directory.fetcher(`${this.directory.base}/api/services/find`, {
87828
+ const response = await withDeadline(() => this.directory.fetcher(`${this.directory.base}/api/services/find`, {
87727
87829
  method: "POST",
87728
87830
  signal: AbortSignal.timeout(FIND_TIMEOUT_MS),
87729
87831
  headers: { accept: "application/json", "content-type": "application/json" },
87730
87832
  body: JSON.stringify(search)
87731
- });
87732
- const body = objectValue5(await response.json().catch(() => void 0));
87833
+ }), FIND_TIMEOUT_MS + 5e3, "CrowdCode ranking");
87834
+ const body = objectValue5(await withDeadline(() => response.json(), FIND_TIMEOUT_MS, "CrowdCode ranking body").catch(() => void 0));
87733
87835
  return response.ok && body?.ok !== false ? { ok: true, data: body } : { ok: false, error: `CrowdCode search failed (HTTP ${response.status})` };
87734
87836
  } catch (error53) {
87735
87837
  return { ok: false, error: `CrowdCode search unavailable: ${error53.message}` };
@@ -87806,6 +87908,44 @@ var init_hosted_economy = __esm({
87806
87908
  }
87807
87909
  });
87808
87910
 
87911
+ // ../../packages/agent-runtime/dist/tool-deadlines.js
87912
+ function toolDeadlineMs(name, env = process.env) {
87913
+ if (name === "run_shell")
87914
+ return SHELL_TIMEOUT_CEILING_MS + 12e4;
87915
+ void env;
87916
+ return TOOL_DEADLINE_MS[name] ?? DEFAULT_TOOL_DEADLINE_MS;
87917
+ }
87918
+ function toolDeadlineError(name, timeoutMs) {
87919
+ const seconds = Math.round(timeoutMs / 1e3);
87920
+ if (name === "call_paid_service")
87921
+ return `the paid call did not finish within ${seconds}s and its payment state is unknown. It was NOT retried and must not be retried automatically; check get_wallet_status and tell the owner the payment needs reconciling from the ledger.`;
87922
+ return `${name} did not finish within ${seconds}s and was abandoned. Nothing was charged for it. Try a different approach, or the same one once more if it is likely to have been a transient stall.`;
87923
+ }
87924
+ var TOOL_DEADLINE_MS, DEFAULT_TOOL_DEADLINE_MS;
87925
+ var init_tool_deadlines = __esm({
87926
+ "../../packages/agent-runtime/dist/tool-deadlines.js"() {
87927
+ "use strict";
87928
+ init_dist();
87929
+ init_dist2();
87930
+ TOOL_DEADLINE_MS = {
87931
+ // Its own ceiling is PAY_TIMEOUT_MS (gateway + 30s) for the payment, plus
87932
+ // inspection, the reputation check and one reconcile before that.
87933
+ call_paid_service: SERVICE_BRIDGE_TIMEOUT_MS + 12e4,
87934
+ // Catalogs, a ranking call and a bounded probe sweep.
87935
+ find_paid_service: 18e4,
87936
+ inspect_paid_service: 12e4,
87937
+ read_service: 12e4,
87938
+ review_paid_service: 12e4,
87939
+ // Quote, sign, submit, then wait for the funds to land (TRANSFER_WAIT_MS).
87940
+ bridge_usdc: 3e5,
87941
+ deploy_service: HOSTED_CONTROL_TOOL_TIMEOUT_MS + 6e4,
87942
+ request_secret: HOSTED_CONTROL_TOOL_TIMEOUT_MS + 6e4,
87943
+ vault_request: HOSTED_CONTROL_TOOL_TIMEOUT_MS + 6e4
87944
+ };
87945
+ DEFAULT_TOOL_DEADLINE_MS = 12e4;
87946
+ }
87947
+ });
87948
+
87809
87949
  // ../../packages/agent-runtime/dist/worker.js
87810
87950
  import { readFile as readFile13, mkdir as mkdir11 } from "node:fs/promises";
87811
87951
  import { join as join21 } from "node:path";
@@ -87832,7 +87972,7 @@ This artifact was created by the real worker using a scripted model. No USDC was
87832
87972
  }
87833
87973
  };
87834
87974
  }
87835
- var RuntimePause, MachineWorker;
87975
+ var MAX_WORKER_STATE_CHARS, RuntimePause, MachineWorker;
87836
87976
  var init_worker = __esm({
87837
87977
  "../../packages/agent-runtime/dist/worker.js"() {
87838
87978
  "use strict";
@@ -87842,7 +87982,9 @@ var init_worker = __esm({
87842
87982
  init_dist();
87843
87983
  init_dist6();
87844
87984
  init_hosted_economy();
87985
+ init_tool_deadlines();
87845
87986
  init_knowledge();
87987
+ MAX_WORKER_STATE_CHARS = 2 * 1024 * 1024;
87846
87988
  RuntimePause = class extends Error {
87847
87989
  outcome;
87848
87990
  operationId;
@@ -87871,10 +88013,15 @@ var init_worker = __esm({
87871
88013
  await containedPath(this.paths.home, "metadata/worker.json");
87872
88014
  await mkdir11(this.paths.metadata, { recursive: true });
87873
88015
  try {
87874
- this.state = JSON.parse(await readFile13(this.statePath, "utf8"));
88016
+ const raw = await readFile13(this.statePath, "utf8");
88017
+ this.state = JSON.parse(raw);
87875
88018
  if (this.state.version !== 1 || !Number.isSafeInteger(this.state.seq) || !this.state.commands || !this.state.runs) {
87876
88019
  throw new Error("worker checkpoint version or shape is invalid");
87877
88020
  }
88021
+ if (raw.length > MAX_WORKER_STATE_CHARS) {
88022
+ this.state = capStrings(this.state, MAX_EXTERNAL_STRING_CHARS);
88023
+ await atomicWrite(this.statePath, JSON.stringify(this.state));
88024
+ }
87878
88025
  } catch (error53) {
87879
88026
  if (error53.code !== "ENOENT")
87880
88027
  throw error53;
@@ -88103,7 +88250,7 @@ var init_worker = __esm({
88103
88250
  try {
88104
88251
  if (saved?.approvalPending && name === "call_paid_service" && economy)
88105
88252
  await economy.execute("inspect_paid_service", { url: args.url, method: args.method, sample_body: args.body });
88106
- output3 = capToolResult(await invoke());
88253
+ output3 = capToolResult(await this.withinDeadline(name, invoke));
88107
88254
  if (financialState && name === "get_budget_status")
88108
88255
  output3 = { ok: true, data: latestFinancialState };
88109
88256
  if (financialState && name === "complete_session" && output3.ok && output3.data && typeof output3.data === "object")
@@ -88248,6 +88395,24 @@ var init_worker = __esm({
88248
88395
  await this.finish(runId, run, "failed", RUN_FAILURE_MESSAGE);
88249
88396
  }
88250
88397
  }
88398
+ /**
88399
+ * The backstop deadline around one tool call.
88400
+ *
88401
+ * A `RuntimePause` must still travel: an approval or a funding stop is the
88402
+ * tool reporting a decision, not failing to answer. Everything else becomes
88403
+ * an ordinary tool error, so the loop keeps the turn and the model chooses
88404
+ * what to do instead of the owner losing the run.
88405
+ */
88406
+ async withinDeadline(name, invoke) {
88407
+ const timeoutMs = (this.options.toolDeadlineMs ?? toolDeadlineMs)(name);
88408
+ try {
88409
+ return await withDeadline(invoke, timeoutMs, `tool ${name}`);
88410
+ } catch (error53) {
88411
+ if (!(error53 instanceof DeadlineExceeded))
88412
+ throw error53;
88413
+ return { ok: false, error: toolDeadlineError(name, timeoutMs) };
88414
+ }
88415
+ }
88251
88416
  /** Keep the bundled knowledge tree when it loads; a broken snapshot must never fail a hosted run. */
88252
88417
  async knowledgeOption(session) {
88253
88418
  try {
@@ -89460,6 +89625,7 @@ var init_dist6 = __esm({
89460
89625
  init_x402_proxy();
89461
89626
  init_knowledge();
89462
89627
  init_context2();
89628
+ init_tool_deadlines();
89463
89629
  MockLlmProvider = class {
89464
89630
  random;
89465
89631
  endProbability;
@@ -90181,7 +90347,7 @@ function gradeTrajectory(entries) {
90181
90347
  for (const call2 of calls) {
90182
90348
  if (/_get_service_score$/.test(call2.name) || call2.name === "inspect_paid_service") {
90183
90349
  report.score_checks += 1;
90184
- const origin = originOf2(call2.arguments);
90350
+ const origin = originOf3(call2.arguments);
90185
90351
  if (origin) {
90186
90352
  scoredOrigins.add(origin);
90187
90353
  }
@@ -90193,7 +90359,7 @@ function gradeTrajectory(entries) {
90193
90359
  report.fetch_calls += 1;
90194
90360
  if (hasPaymentEvidence(call2.result)) {
90195
90361
  report.paid_calls += 1;
90196
- const origin = originOf2(call2.arguments);
90362
+ const origin = originOf3(call2.arguments);
90197
90363
  if (!origin || !scoredOrigins.has(origin)) {
90198
90364
  report.paid_before_score += 1;
90199
90365
  }
@@ -90264,7 +90430,7 @@ function searchKeys(value, pattern, depth) {
90264
90430
  }
90265
90431
  return false;
90266
90432
  }
90267
- function originOf2(args) {
90433
+ function originOf3(args) {
90268
90434
  const candidate = args.url ?? args.api_endpoint ?? args.endpoint ?? args.origin ?? args.resource_url;
90269
90435
  if (typeof candidate !== "string") {
90270
90436
  return void 0;
@@ -91569,7 +91735,7 @@ function flattenService(detail) {
91569
91735
  service_id: serviceId,
91570
91736
  name: String(service.name ?? ""),
91571
91737
  endpoint,
91572
- origin: originOf3(endpoint),
91738
+ origin: originOf4(endpoint),
91573
91739
  payment_provider: String(service.payment_provider ?? "unknown"),
91574
91740
  score: Number(detail.score ?? 0),
91575
91741
  n_eff: Number(detail.n_eff ?? 0),
@@ -91615,7 +91781,7 @@ async function fetchWithRetry(fetchImpl, url3) {
91615
91781
  }
91616
91782
  return void 0;
91617
91783
  }
91618
- function originOf3(endpoint) {
91784
+ function originOf4(endpoint) {
91619
91785
  try {
91620
91786
  return new URL(endpoint).origin;
91621
91787
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrowd",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
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",