@lucid-agents/taskmarket 1.18.1 → 1.19.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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "taskmarket": "1.18.1",
2
+ "taskmarket": "1.19.1",
3
3
  "manifestVersion": 1,
4
4
  "pi": {
5
5
  "package": "@earendil-works/pi-coding-agent",
@@ -35,6 +35,16 @@
35
35
  "version": "0.85.1"
36
36
  }
37
37
  ],
38
+ "piVendored": [
39
+ {
40
+ "package": "@silvia-odwyer/photon-node",
41
+ "version": "0.3.4"
42
+ },
43
+ {
44
+ "package": "jiti",
45
+ "version": "2.7.0"
46
+ }
47
+ ],
38
48
  "support": [
39
49
  {
40
50
  "package": "ssh2",
@@ -15,7 +15,7 @@ import {
15
15
  statusBarText,
16
16
  uncertainPurchaseMessage,
17
17
  usdcToAtomic
18
- } from "./chunk-EB6OJBF5.js";
18
+ } from "./chunk-XJOB4NMV.js";
19
19
  import "./chunk-S7SJMJ2I.js";
20
20
  export {
21
21
  AGENT_VIEW_VERSION,
@@ -10,10 +10,10 @@ import {
10
10
  selectRail,
11
11
  uncertainPurchases,
12
12
  withdrawalHatch
13
- } from "./chunk-LEVAZYR5.js";
14
- import "./chunk-5VMWXDNO.js";
15
- import "./chunk-YB2B5DPR.js";
16
- import "./chunk-EB6OJBF5.js";
13
+ } from "./chunk-MZSOLQZB.js";
14
+ import "./chunk-NRKTRQEA.js";
15
+ import "./chunk-X7AUWPZV.js";
16
+ import "./chunk-XJOB4NMV.js";
17
17
  import "./chunk-YM4S6IE4.js";
18
18
  import "./chunk-S7SJMJ2I.js";
19
19
  export {
@@ -24,6 +24,15 @@ var AGENT_COMPATIBILITY = {
24
24
  { package: "@earendil-works/chord", version: "0.85.1" },
25
25
  { package: "@earendil-works/pi-telemetry", version: "0.85.1" }
26
26
  ],
27
+ /**
28
+ * The packages Pi's published bundle still loads from npm, copied as published into
29
+ * `dist/pi/vendor`. Pi takes them at a range, so a lockfile bump could otherwise ship another
30
+ * release; the build also checks every copied file's hash (scripts/bundle-agent-runtime.ts).
31
+ */
32
+ piVendored: [
33
+ { package: "@silvia-odwyer/photon-node", version: "0.3.4" },
34
+ { package: "jiti", version: "2.7.0" }
35
+ ],
27
36
  /** What the remote tool extension and the broker connector are built with besides Pi. */
28
37
  support: [{ package: "ssh2", version: "1.17.0" }],
29
38
  /**
@@ -76,9 +85,14 @@ function findInstalledPackage(name, fromDir) {
76
85
  }
77
86
  }
78
87
  var here = path.dirname(fileURLToPath(import.meta.url));
79
- function piRuntime(fromDir = here) {
88
+ var runningFromSource = fileURLToPath(import.meta.url).endsWith(".ts");
89
+ function piRuntime(fromDir = here, options = {}) {
80
90
  const staged = path.join(fromDir, "pi");
81
- const packageDir = existsSync(path.join(staged, "package.json")) ? staged : findInstalledPackage(AGENT_COMPATIBILITY.pi.package, fromDir)?.dir;
91
+ let packageDir;
92
+ if (existsSync(path.join(staged, "package.json"))) packageDir = staged;
93
+ else if (options.fromSource ?? runningFromSource) {
94
+ packageDir = findInstalledPackage(AGENT_COMPATIBILITY.pi.package, fromDir)?.dir;
95
+ }
82
96
  if (!packageDir) return void 0;
83
97
  return { packageDir, cliPath: path.join(packageDir, "dist", "bundle", "cli.js") };
84
98
  }
@@ -92,11 +106,11 @@ function assertAgentCompatibility(options = {}) {
92
106
  );
93
107
  }
94
108
  const fromDir = options.fromDir ?? here;
95
- const runtime = piRuntime(fromDir);
109
+ const runtime = piRuntime(fromDir, { fromSource: options.fromSource });
96
110
  if (!runtime || !existsSync(runtime.cliPath)) {
97
111
  throw new AgentLaunchError(
98
112
  "incompatible_install",
99
- `Pi, which taskmarket agent runs, is missing from this install; ${reinstall}`
113
+ `This install is damaged: the Pi bundled with it (${path.join(fromDir, "pi")}) is missing or incomplete, and taskmarket agent runs no other Pi; ${reinstall}`
100
114
  );
101
115
  }
102
116
  const pi = JSON.parse(readFileSync(path.join(runtime.packageDir, "package.json"), "utf8"));
@@ -22,7 +22,7 @@ import {
22
22
  unresolvedCount,
23
23
  updateSyncState,
24
24
  uploadWorkspace
25
- } from "./chunk-ONYTKAHC.js";
25
+ } from "./chunk-ATHBKQT4.js";
26
26
  import {
27
27
  AgentLaunchError
28
28
  } from "./chunk-YM4S6IE4.js";
@@ -1,7 +1,7 @@
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
  getTaskmarketHome
4
- } from "./chunk-YB2B5DPR.js";
4
+ } from "./chunk-X7AUWPZV.js";
5
5
  import {
6
6
  AgentLaunchError
7
7
  } from "./chunk-YM4S6IE4.js";
@@ -23778,6 +23778,7 @@ function hostKeyBlob(pinned) {
23778
23778
  var KILL_SETTLE_MS = 5e3;
23779
23779
  var KILL_RETRY_MS = [1e3, 3e3];
23780
23780
  var EXIT_DRAIN_MS = 150;
23781
+ var UNTIL_CLOSE_MS = 6e4;
23781
23782
  var earlyExits = /* @__PURE__ */ new WeakMap();
23782
23783
  var SshConnection = class _SshConnection {
23783
23784
  client;
@@ -24076,8 +24077,8 @@ var SshConnection = class _SshConnection {
24076
24077
  exitSignal = signal;
24077
24078
  drainTimer = setTimeout(() => {
24078
24079
  channel.close();
24079
- finish();
24080
- }, EXIT_DRAIN_MS);
24080
+ finish(options.untilClose ? new Error("The sandbox did not finish sending the command output") : void 0);
24081
+ }, options.untilClose ? UNTIL_CLOSE_MS : EXIT_DRAIN_MS);
24081
24082
  };
24082
24083
  channel.on("exit", onExit);
24083
24084
  const early = earlyExits.get(channel);
@@ -24528,7 +24529,8 @@ var SshSandboxBackend = class {
24528
24529
  },
24529
24530
  signal,
24530
24531
  maxStreamBytes: this.maxStreamBytes,
24531
- killCommand: stopScopeCommand(unit)
24532
+ killCommand: stopScopeCommand(unit),
24533
+ untilClose: true
24532
24534
  });
24533
24535
  return { exitCode, stderr: Buffer.concat(stderr).toString("utf8").trim().slice(0, 2e3) };
24534
24536
  }
@@ -24609,7 +24611,16 @@ var SshSandboxBackend = class {
24609
24611
  } finally {
24610
24612
  options.signal?.removeEventListener("abort", onAbort);
24611
24613
  }
24612
- const parsed = parseManifestOutput(Buffer.concat(out));
24614
+ if (overLimit)
24615
+ throw new ManifestLimitError(options.limit);
24616
+ let parsed;
24617
+ try {
24618
+ parsed = parseManifestOutput(Buffer.concat(out));
24619
+ } catch (error) {
24620
+ if (entries >= (limit + 1) * 4)
24621
+ throw new ManifestLimitError(options.limit);
24622
+ throw error;
24623
+ }
24613
24624
  if (parsed.length > limit)
24614
24625
  throw new ManifestLimitError(options.limit);
24615
24626
  return parsed;
@@ -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-3LNZ766H.js";
4
+ } from "./chunk-M7WD6A2T.js";
5
5
  import {
6
6
  createWalletAccountFromKeystore,
7
7
  signMessage
8
- } from "./chunk-5VMWXDNO.js";
8
+ } from "./chunk-NRKTRQEA.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-YB2B5DPR.js";
22
+ } from "./chunk-X7AUWPZV.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,
@@ -6,14 +6,14 @@ import {
6
6
  loadDeliverableRecord,
7
7
  readComputeView,
8
8
  taskStage
9
- } from "./chunk-3FF6JAMK.js";
9
+ } from "./chunk-OTXEK6VM.js";
10
10
  import {
11
11
  activeClaim,
12
12
  captureProblem,
13
13
  killedSession,
14
14
  loadSyncState,
15
15
  orphanOf
16
- } from "./chunk-ONYTKAHC.js";
16
+ } from "./chunk-ATHBKQT4.js";
17
17
  import {
18
18
  allocationViews,
19
19
  channelView,
@@ -21,19 +21,19 @@ import {
21
21
  openAgentChannels,
22
22
  uncertainPurchases,
23
23
  withdrawalHatch
24
- } from "./chunk-LEVAZYR5.js";
24
+ } from "./chunk-MZSOLQZB.js";
25
25
  import {
26
26
  SandboxListOutputSchema,
27
27
  apiUrl,
28
28
  selectedNetwork
29
- } from "./chunk-YB2B5DPR.js";
29
+ } from "./chunk-X7AUWPZV.js";
30
30
  import {
31
31
  AGENT_VIEW_VERSION,
32
32
  blockingPurchases,
33
33
  channelLines,
34
34
  computeLines,
35
35
  formatUsdc
36
- } from "./chunk-EB6OJBF5.js";
36
+ } from "./chunk-XJOB4NMV.js";
37
37
  import {
38
38
  AgentLaunchError
39
39
  } from "./chunk-YM4S6IE4.js";
@@ -60,6 +60,7 @@ async function readTaskStatus(wallet) {
60
60
  } : null,
61
61
  deliverableId: record.deliverable?.deliverableId ?? record.submission?.deliverableId ?? null,
62
62
  submissionId: record.submission?.submissionId ?? null,
63
+ outcome: record.state === "submitted" ? record.outcome ?? null : null,
63
64
  updatedAt: record.updatedAt,
64
65
  source: "saved_state"
65
66
  };
@@ -289,6 +290,11 @@ var STAGES = {
289
290
  frozen: "frozen (validated, waiting for your approval to submit)",
290
291
  submitting: "submitting (sent; the next session looks it up before anything is sent again)",
291
292
  submitted: "submitted (not yet accepted or paid)",
293
+ awaiting_acceptance: "awaiting acceptance (submitted; the requester or evaluator decides)",
294
+ reward_pending: "reward pending (awarded; the payout is not verified on the chain yet)",
295
+ paid: "paid (the payout is verified on the chain)",
296
+ rejected: "rejected (no reward)",
297
+ expired: "expired (ended without an award; no reward)",
292
298
  stopped: "stopped"
293
299
  };
294
300
  function taskLines(task, error) {
@@ -304,6 +310,17 @@ function taskLines(task, error) {
304
310
  }
305
311
  if (task.deliverableId) lines.push(` Deliverable: ${task.deliverableId}`);
306
312
  if (task.submissionId) lines.push(` Submission: ${task.submissionId}`);
313
+ const outcome = task.outcome;
314
+ if (outcome?.payout) {
315
+ lines.push(
316
+ ` Paid: ${outcome.payout.amount} atomic units of ${outcome.payout.asset} in ${outcome.payout.settlementTxHash}`
317
+ );
318
+ } else if (outcome?.award) {
319
+ lines.push(
320
+ ` Award: ${outcome.award.workerPayment} atomic units of ${outcome.award.asset}, not verified (${outcome.unverified?.reason ?? "unverified"})`
321
+ );
322
+ }
323
+ if (outcome) lines.push(` Outcome observed ${outcome.observedAt}`);
307
324
  lines.push(` As recorded ${task.updatedAt}`);
308
325
  return lines;
309
326
  }
@@ -16,7 +16,7 @@ import {
16
16
  readMppTempoChallenge,
17
17
  reconcileX402Payment,
18
18
  withX402FileLock
19
- } from "./chunk-5VMWXDNO.js";
19
+ } from "./chunk-NRKTRQEA.js";
20
20
  import {
21
21
  ApiError,
22
22
  ComputeCreditBuyOutputSchema,
@@ -38,7 +38,7 @@ import {
38
38
  resolveIdempotencyKey,
39
39
  selectedNetwork,
40
40
  withIdempotentWrite
41
- } from "./chunk-YB2B5DPR.js";
41
+ } from "./chunk-X7AUWPZV.js";
42
42
 
43
43
  // src/lib/x402-fee.ts
44
44
  import { randomUUID as randomUUID3 } from "crypto";
@@ -11,15 +11,19 @@ import {
11
11
  listX402Payments,
12
12
  loadX402Policy,
13
13
  rpcUrlForNetwork
14
- } from "./chunk-5VMWXDNO.js";
14
+ } from "./chunk-NRKTRQEA.js";
15
15
  import {
16
16
  getTaskmarketHome,
17
17
  keystoreExists,
18
18
  loadKeystore
19
- } from "./chunk-YB2B5DPR.js";
19
+ } from "./chunk-X7AUWPZV.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;
@@ -11,7 +11,7 @@ import {
11
11
  loadKeystore,
12
12
  mppAttributionMemo,
13
13
  parseMppChallenge
14
- } from "./chunk-YB2B5DPR.js";
14
+ } from "./chunk-X7AUWPZV.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 {
@@ -8,7 +8,7 @@ import {
8
8
  unsafeHostPath,
9
9
  withFileLock,
10
10
  writeAtomic
11
- } from "./chunk-ONYTKAHC.js";
11
+ } from "./chunk-ATHBKQT4.js";
12
12
  import {
13
13
  buyComputeCredit,
14
14
  fetchComputePricing,
@@ -16,14 +16,14 @@ import {
16
16
  loadComputeCreditConfig,
17
17
  onComputeBalanceObserved,
18
18
  readComputeCreditStatus
19
- } from "./chunk-3LNZ766H.js";
19
+ } from "./chunk-M7WD6A2T.js";
20
20
  import {
21
21
  getTaskmarketHome
22
- } from "./chunk-YB2B5DPR.js";
22
+ } from "./chunk-X7AUWPZV.js";
23
23
  import {
24
24
  burnRate,
25
25
  computeLow
26
- } from "./chunk-EB6OJBF5.js";
26
+ } from "./chunk-XJOB4NMV.js";
27
27
  import {
28
28
  AgentLaunchError
29
29
  } from "./chunk-YM4S6IE4.js";
@@ -34,6 +34,7 @@ import { promises as fs } from "fs";
34
34
  import path from "path";
35
35
  var ACTIVE_TASK_VERSION = 1;
36
36
  function taskStage(record) {
37
+ if (record.state === "submitted") return record.outcome?.state ?? "submitted";
37
38
  if (record.state !== "claimed") return record.state;
38
39
  if (record.deliverable) return "frozen";
39
40
  return record.workspace?.state === "ready" ? "working" : "claimed";