@lucid-agents/taskmarket 1.19.0 → 1.20.0

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.
@@ -11,7 +11,7 @@ import {
11
11
  loadKeystore,
12
12
  mppAttributionMemo,
13
13
  parseMppChallenge
14
- } from "./chunk-PGXPZFVY.js";
14
+ } from "./chunk-F7HVOUHY.js";
15
15
 
16
16
  // ../../packages/x402-payer/dist/api.js
17
17
  var X402_PAYER_API_VERSION = 1;
@@ -4254,13 +4254,17 @@ async function runX402Exchange(runtime, input, options, context) {
4254
4254
  throw aborted();
4255
4255
  let response;
4256
4256
  try {
4257
- response = await fetchImpl(new Request(prepared.url, {
4258
- method: prepared.method,
4259
- headers: unpaidHeaders,
4260
- body: prepared.body,
4261
- redirect: "error",
4262
- signal
4263
- }));
4257
+ response = await fetchImpl(
4258
+ new Request(prepared.url, {
4259
+ method: prepared.method,
4260
+ headers: unpaidHeaders,
4261
+ body: prepared.body,
4262
+ redirect: "error",
4263
+ signal
4264
+ }),
4265
+ // See the paid request below: without it, a garbage collection can cut off the abort.
4266
+ { signal }
4267
+ );
4264
4268
  } catch (error) {
4265
4269
  if (signal.aborted)
4266
4270
  throw aborted();
@@ -4576,13 +4580,19 @@ async function runX402Exchange(runtime, input, options, context) {
4576
4580
  await transition({ state: "dispatched" });
4577
4581
  let paid;
4578
4582
  try {
4579
- paid = await fetchImpl(new Request(prepared.url, {
4580
- method: prepared.method,
4581
- headers: paidHeaders,
4582
- body: prepared.body,
4583
- redirect: "error",
4584
- signal
4585
- }));
4583
+ paid = await fetchImpl(
4584
+ new Request(prepared.url, {
4585
+ method: prepared.method,
4586
+ headers: paidHeaders,
4587
+ body: prepared.body,
4588
+ redirect: "error",
4589
+ signal
4590
+ }),
4591
+ // The signal again, so fetch follows it directly. A Request follows its signal through an
4592
+ // AbortController that only the Request holds strongly: once fetch has copied it, a garbage
4593
+ // collection drops that controller, and aborting `signal` no longer cancels the request.
4594
+ { signal }
4595
+ );
4586
4596
  } catch (error) {
4587
4597
  await transition({ state: "unknown", error: errorMessage(error) });
4588
4598
  throw fail("outcome_uncertain", X402_OUTCOME_UNKNOWN_MESSAGE, { cause: error, pending: true });
@@ -5464,7 +5474,7 @@ function createChannelRuntime(config) {
5464
5474
  const combined = anyX402Signal([deadline.signal, ...signal ? [signal] : []]);
5465
5475
  try {
5466
5476
  throwIfAborted(combined.signal);
5467
- const response = await (config.fetch ?? globalThis.fetch)(new Request(requestUrl, { ...init, redirect: "error", signal: combined.signal }));
5477
+ const response = await (config.fetch ?? globalThis.fetch)(new Request(requestUrl, { ...init, redirect: "error", signal: combined.signal }), { signal: combined.signal });
5468
5478
  const bytes = await readX402BoundedBody(response, 64 * 1024, combined.signal);
5469
5479
  let body;
5470
5480
  try {
@@ -22,7 +22,7 @@ import {
22
22
  unresolvedCount,
23
23
  updateSyncState,
24
24
  uploadWorkspace
25
- } from "./chunk-MJR2CZWZ.js";
25
+ } from "./chunk-RA6EZSII.js";
26
26
  import {
27
27
  AgentLaunchError
28
28
  } from "./chunk-YM4S6IE4.js";
@@ -1,15 +1,16 @@
1
1
  import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
2
  import {
3
3
  x402FeePost
4
- } from "./chunk-6KJ5EFBN.js";
4
+ } from "./chunk-2YFJNXXA.js";
5
5
  import {
6
6
  createWalletAccountFromKeystore,
7
7
  signMessage
8
- } from "./chunk-VMSV4EYS.js";
8
+ } from "./chunk-SCZPZWRF.js";
9
9
  import {
10
10
  READ_AUTH_ADDRESS_HEADER,
11
11
  READ_AUTH_SIGNATURE_HEADER,
12
12
  TASK_ACCESS_GRANT_HEADER,
13
+ TaskAwardSchema,
13
14
  apiGet,
14
15
  apiPost,
15
16
  buildClaimMessage,
@@ -18,7 +19,7 @@ import {
18
19
  getTaskmarketHome,
19
20
  isRecordingPendingResponse,
20
21
  loadKeystore
21
- } from "./chunk-PGXPZFVY.js";
22
+ } from "./chunk-F7HVOUHY.js";
22
23
 
23
24
  // src/lib/read-auth.ts
24
25
  async function signReadAuth() {
@@ -313,6 +314,16 @@ async function getAgentStats(ctx, options) {
313
314
  {}
314
315
  );
315
316
  }
317
+ function taskAwardsOf(task) {
318
+ const parsed = TaskAwardSchema.array().safeParse(task.awards ?? []);
319
+ if (!parsed.success) {
320
+ throw new TaskOperationError(
321
+ "malformed_response",
322
+ `Task ${task.id} returned awards that do not match the award schema`
323
+ );
324
+ }
325
+ return parsed.data;
326
+ }
316
327
  async function claimTask(ctx, ref, options = {}) {
317
328
  const signature = await ctx.signer.signMessage(buildClaimMessage(ref.taskId));
318
329
  const { data, idempotencyKey } = await ctx.transport.post(
@@ -438,6 +449,7 @@ export {
438
449
  listWorkerSubmissions,
439
450
  getWorkerInbox,
440
451
  getAgentStats,
452
+ taskAwardsOf,
441
453
  claimTask,
442
454
  authorizeUploads,
443
455
  readArtifactFile,
@@ -11,15 +11,19 @@ import {
11
11
  listX402Payments,
12
12
  loadX402Policy,
13
13
  rpcUrlForNetwork
14
- } from "./chunk-VMSV4EYS.js";
14
+ } from "./chunk-SCZPZWRF.js";
15
15
  import {
16
16
  getTaskmarketHome,
17
17
  keystoreExists,
18
18
  loadKeystore
19
- } from "./chunk-PGXPZFVY.js";
19
+ } from "./chunk-F7HVOUHY.js";
20
20
  import {
21
- formatUsdc
22
- } from "./chunk-EB6OJBF5.js";
21
+ failureDetail,
22
+ failureDetailOf,
23
+ formatUsdc,
24
+ responseFailureReason,
25
+ withFailureDetail
26
+ } from "./chunk-XJOB4NMV.js";
23
27
  import {
24
28
  AgentLaunchError
25
29
  } from "./chunk-YM4S6IE4.js";
@@ -299,6 +303,63 @@ function parse(bytes) {
299
303
  function routeOfCatalogRow(row) {
300
304
  return row?.owned_by === "anthropic" ? "/providers/anthropic/v1/messages" : "/providers/openai/v1/responses";
301
305
  }
306
+ var MAX_NOTED_FAILURES = 16;
307
+ var NETWORK_CODE = /^[A-Z][A-Z0-9_]{2,40}$/;
308
+ function networkCode(error) {
309
+ let current = error?.cause;
310
+ for (let depth = 0; depth < 5 && current && typeof current === "object"; depth++) {
311
+ const code = current.code;
312
+ if (typeof code === "string" && NETWORK_CODE.test(code)) return code;
313
+ current = current.cause;
314
+ }
315
+ return void 0;
316
+ }
317
+ function payerFailureDetail(error) {
318
+ const e = error;
319
+ const message = typeof e?.message === "string" && !(typeof e.code === "string" && e.message.includes(e.code)) ? e.message : void 0;
320
+ const code = networkCode(error);
321
+ return failureDetail({
322
+ status: e?.status ?? e?.response?.status,
323
+ reason: responseFailureReason(e?.response) ?? (code ? `${message ?? "network error"} (${code})` : message)
324
+ });
325
+ }
326
+ function recordingFailures(payer) {
327
+ const noted = /* @__PURE__ */ new Map();
328
+ const note = (invocationId, detail) => {
329
+ if (!detail) return;
330
+ noted.delete(invocationId);
331
+ noted.set(invocationId, detail);
332
+ if (noted.size > MAX_NOTED_FAILURES) noted.delete(noted.keys().next().value);
333
+ };
334
+ return {
335
+ payer: {
336
+ apiVersion: payer.apiVersion,
337
+ async request(input, callOptions) {
338
+ try {
339
+ const result = await payer.request(input, callOptions);
340
+ const status = result.response.status;
341
+ if (status < 200 || status > 299) {
342
+ note(
343
+ input.invocationId,
344
+ failureDetail({ status, reason: responseFailureReason(result.response) })
345
+ );
346
+ }
347
+ return result;
348
+ } catch (error) {
349
+ note(input.invocationId, payerFailureDetail(error));
350
+ throw error;
351
+ }
352
+ },
353
+ status: (invocationId) => payer.status(invocationId),
354
+ reconcile: (invocationId) => payer.reconcile(invocationId)
355
+ },
356
+ take(invocationId) {
357
+ const detail = noted.get(invocationId);
358
+ noted.delete(invocationId);
359
+ return detail;
360
+ }
361
+ };
362
+ }
302
363
  async function createAgentRails(options) {
303
364
  const journalPath = getX402JournalPath();
304
365
  const policyPath = getX402PolicyPath();
@@ -342,8 +403,9 @@ async function createAgentRails(options) {
342
403
  ...options.onConfirmationWait ? { onWait: (progress) => options.onConfirmationWait(rail, progress) } : {}
343
404
  }
344
405
  });
406
+ const recorder = recordingFailures(payer);
345
407
  const transport = createTaskMarketTransport({
346
- payer,
408
+ payer: recorder.payer,
347
409
  origin: rail.origin,
348
410
  allocationId: rail.allocationId,
349
411
  policyRuleId: rail.policyRuleId,
@@ -352,7 +414,7 @@ async function createAgentRails(options) {
352
414
  balances: () => allocations.balances(),
353
415
  ...options.fetch ? { fetch: options.fetch } : {}
354
416
  });
355
- return { rail, payer, transport };
417
+ return { rail, payer, transport, takeFailureDetail: recorder.take };
356
418
  });
357
419
  }
358
420
  function bodyModel(body) {
@@ -409,12 +471,16 @@ function createAgentTransport(rails) {
409
471
  }
410
472
  try {
411
473
  return await rail.transport.purchase(invocation, signal, onResponse);
412
- } catch (error) {
474
+ } catch (caught) {
475
+ const error = withFailureDetail(caught, rail.takeFailureDetail?.(invocation.id));
413
476
  if (error instanceof XgatePurchaseError && error.pending && await neverRecorded(rail.payer, invocation.id)) {
414
- throw new XgatePurchaseError(
415
- error.code === "outcome_uncertain" ? "channel_operation_pending" : error.code,
416
- invocation.id,
417
- false
477
+ throw withFailureDetail(
478
+ new XgatePurchaseError(
479
+ error.code === "outcome_uncertain" ? "channel_operation_pending" : error.code,
480
+ invocation.id,
481
+ false
482
+ ),
483
+ failureDetailOf(error)
418
484
  );
419
485
  }
420
486
  throw error;
@@ -1,5 +1,73 @@
1
1
  import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
2
 
3
+ // src/lib/agent/failure-detail.ts
4
+ var MAX_FAILURE_REASON_CHARS = 160;
5
+ var MAX_REASON_BODY_BYTES = 64 * 1024;
6
+ var REDACTIONS = [
7
+ [/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi, "[url]"],
8
+ [/\b(bearer|basic)\s+\S+/gi, "$1 [redacted]"],
9
+ [
10
+ /\b(authorization|cookie|token|secret|password|passphrase|signature|private[-_ ]?key|api[-_ ]?key)\b(\s*[:=]\s*)\S+/gi,
11
+ "$1$2[redacted]"
12
+ ],
13
+ [/\b0x[0-9a-f]{16,}\b/gi, "[hex]"],
14
+ [/\b[\w.+-]+@[\w-]+(\.[\w-]+)+\b/g, "[email]"],
15
+ [/[A-Za-z0-9+/_=-]{32,}/g, "[redacted]"]
16
+ ];
17
+ function sanitizeFailureReason(text) {
18
+ if (typeof text !== "string") return void 0;
19
+ const line = text.slice(0, 16 * MAX_FAILURE_REASON_CHARS).split(/[\r\n]+/).find((part) => part.trim());
20
+ if (!line) return void 0;
21
+ let reason = line.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069]/g, " ").replace(/\s+/g, " ").trim();
22
+ for (const [pattern, replacement] of REDACTIONS) reason = reason.replace(pattern, replacement);
23
+ if (!reason) return void 0;
24
+ return reason.length > MAX_FAILURE_REASON_CHARS ? `${reason.slice(0, MAX_FAILURE_REASON_CHARS - 3).trimEnd()}...` : reason;
25
+ }
26
+ function failureDetail(value) {
27
+ if (!value || typeof value !== "object") return void 0;
28
+ const { status, reason } = value;
29
+ const detail = {
30
+ ...Number.isInteger(status) && status >= 100 && status <= 599 ? { status } : {}
31
+ };
32
+ const text = sanitizeFailureReason(reason);
33
+ if (text) detail.reason = text;
34
+ return detail.status === void 0 && detail.reason === void 0 ? void 0 : detail;
35
+ }
36
+ function describeFailureDetail(detail) {
37
+ const status = detail.status !== void 0 ? `HTTP ${detail.status}` : "";
38
+ return status && detail.reason ? `${status}: ${detail.reason}` : status || detail.reason || "";
39
+ }
40
+ function responseFailureReason(response) {
41
+ const body = response?.body;
42
+ if (!(body instanceof Uint8Array) || body.byteLength > MAX_REASON_BODY_BYTES) return void 0;
43
+ let value;
44
+ try {
45
+ value = JSON.parse(new TextDecoder().decode(body));
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ const record = value ?? {};
50
+ const error = record.error;
51
+ for (const candidate of [
52
+ typeof error === "string" ? error : error?.message,
53
+ record.message,
54
+ record.detail
55
+ ]) {
56
+ const reason = sanitizeFailureReason(candidate);
57
+ if (reason) return reason;
58
+ }
59
+ return void 0;
60
+ }
61
+ function failureDetailOf(error) {
62
+ return error && typeof error === "object" ? failureDetail(error.detail) : void 0;
63
+ }
64
+ function withFailureDetail(error, detail) {
65
+ if (detail && error && typeof error === "object") {
66
+ error.detail = detail;
67
+ }
68
+ return error;
69
+ }
70
+
3
71
  // src/lib/agent/budget-view.ts
4
72
  var AGENT_VIEW_VERSION = 1;
5
73
  var USDC_DECIMALS = 6;
@@ -154,24 +222,38 @@ var CHARGED_UNDELIVERED = /* @__PURE__ */ new Set([
154
222
  "invalid_response",
155
223
  "response_hook_failed"
156
224
  ]);
225
+ var REQUEST_FAILED = /* @__PURE__ */ new Set(["request_failed", "request_rejected"]);
157
226
  function purchaseFailureNotice(failure) {
227
+ const detail = failureDetail(failure.detail);
228
+ const cause = detail ? `${failure.code}, ${describeFailureDetail(detail)}` : failure.code;
158
229
  if (failure.pending) {
159
230
  return {
160
231
  level: "warning",
161
- text: uncertainPurchaseMessage(failure.invocationId || "(unknown)")
232
+ text: `${uncertainPurchaseMessage(failure.invocationId || "(unknown)")}${detail ? ` Cause: ${describeFailureDetail(detail)}.` : ""}`
162
233
  };
163
234
  }
164
235
  const charged = failure.payment?.chargedAtomic;
165
236
  if (charged !== void 0 || CHARGED_UNDELIVERED.has(failure.code)) {
166
237
  return {
167
238
  level: "error",
168
- text: `This turn was paid${charged !== void 0 ? ` (${formatUsdc(charged)})` : ""} but its response was lost (${failure.code}). ${LOST_RESPONSE_TEXT}`
239
+ text: `This turn was paid${charged !== void 0 ? ` (${formatUsdc(charged)})` : ""} but its response was lost (${cause}). ${LOST_RESPONSE_TEXT}`
240
+ };
241
+ }
242
+ if (REQUEST_FAILED.has(failure.code)) {
243
+ return {
244
+ level: "error",
245
+ text: `This turn's request to xgate failed (${cause}). It is not retried automatically; send the prompt again to start a new turn.`
169
246
  };
170
247
  }
171
248
  return void 0;
172
249
  }
173
250
 
174
251
  export {
252
+ failureDetail,
253
+ describeFailureDetail,
254
+ responseFailureReason,
255
+ failureDetailOf,
256
+ withFailureDetail,
175
257
  AGENT_VIEW_VERSION,
176
258
  formatUsdc,
177
259
  formatCredits,
@@ -6,7 +6,7 @@ import {
6
6
  findInstalledPackage,
7
7
  nodeVersionSupported,
8
8
  piRuntime
9
- } from "./chunk-OKZ2NRYZ.js";
9
+ } from "./chunk-4ZC6SH7H.js";
10
10
  import "./chunk-YM4S6IE4.js";
11
11
  import "./chunk-S7SJMJ2I.js";
12
12
  export {
@@ -3,31 +3,31 @@ import {
3
3
  AGENT_COMPATIBILITY,
4
4
  assertAgentCompatibility,
5
5
  nodeVersionSupported
6
- } from "./chunk-OKZ2NRYZ.js";
6
+ } from "./chunk-4ZC6SH7H.js";
7
7
  import {
8
8
  listSandboxes
9
- } from "./chunk-U5F3Z2BR.js";
10
- import "./chunk-RKGRXJ5X.js";
11
- import "./chunk-MJR2CZWZ.js";
12
- import "./chunk-6KJ5EFBN.js";
9
+ } from "./chunk-LKO25UQ7.js";
10
+ import "./chunk-S62UQAAF.js";
11
+ import "./chunk-RA6EZSII.js";
12
+ import "./chunk-2YFJNXXA.js";
13
13
  import {
14
14
  channelView,
15
15
  loadAgentPolicy,
16
16
  openAgentChannels
17
- } from "./chunk-7Y7IOFKT.js";
17
+ } from "./chunk-W7JSBEGK.js";
18
18
  import {
19
19
  getX402PolicyPath,
20
20
  loadX402Policy,
21
21
  rpcUrlForNetwork
22
- } from "./chunk-VMSV4EYS.js";
22
+ } from "./chunk-SCZPZWRF.js";
23
23
  import {
24
24
  apiUrl,
25
25
  keystoreExists,
26
26
  loadKeystore
27
- } from "./chunk-PGXPZFVY.js";
27
+ } from "./chunk-F7HVOUHY.js";
28
28
  import {
29
29
  AGENT_VIEW_VERSION
30
- } from "./chunk-EB6OJBF5.js";
30
+ } from "./chunk-XJOB4NMV.js";
31
31
  import "./chunk-YM4S6IE4.js";
32
32
  import "./chunk-S7SJMJ2I.js";
33
33