@lucid-agents/taskmarket 1.17.1 → 1.18.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.
@@ -115,31 +115,32 @@ CLI uses. Each is distributed under the licence named; licence texts follow the
115
115
  - partial-json 0.1.7: MIT, text 44
116
116
  - path-expression-matcher 1.5.0: MIT
117
117
  - path-key 3.1.1: MIT, text 43
118
+ - picomatch 4.0.4: MIT, text 45
118
119
  - proper-lockfile 4.1.2: MIT, text 19
119
120
  - protobufjs 7.6.5: BSD-3-Clause
120
- - retry 0.12.0: MIT, text 45
121
- - retry 0.13.1: MIT, text 45
122
- - safe-buffer 5.2.1: MIT, text 46
123
- - safer-buffer 2.1.2: MIT, text 47
121
+ - retry 0.12.0: MIT, text 46
122
+ - retry 0.13.1: MIT, text 46
123
+ - safe-buffer 5.2.1: MIT, text 47
124
+ - safer-buffer 2.1.2: MIT, text 48
124
125
  - semver 7.8.0: ISC, text 32
125
- - shebang-command 2.0.0: MIT, text 48
126
+ - shebang-command 2.0.0: MIT, text 49
126
127
  - shebang-regex 3.0.0: MIT, text 43
127
- - signal-exit 3.0.7: ISC, text 49
128
- - ssh2 1.17.0: MIT, text 50
128
+ - signal-exit 3.0.7: ISC, text 50
129
+ - ssh2 1.17.0: MIT, text 51
129
130
  - standardwebhooks 1.1.1: MIT
130
131
  - strnum 2.3.0: MIT
131
132
  - ts-algebra 2.0.0: MIT, text 35
132
- - tslib 2.8.1: 0BSD, text 51
133
- - tsup 8.5.1: MIT, text 52
133
+ - tslib 2.8.1: 0BSD, text 52
134
+ - tsup 8.5.1: MIT, text 53
134
135
  - tweetnacl 0.14.5: Unlicense, text 25
135
- - typebox 1.3.7: MIT, text 53
136
- - undici-types 6.21.0: MIT, text 54
137
- - undici 8.9.0: MIT, text 54
138
- - web-streams-polyfill 3.3.3: MIT, text 55
136
+ - typebox 1.3.7: MIT, text 54
137
+ - undici-types 6.21.0: MIT, text 55
138
+ - undici 8.9.0: MIT, text 55
139
+ - web-streams-polyfill 3.3.3: MIT, text 56
139
140
  - which 2.0.2: ISC, text 32
140
- - ws 8.21.0: MIT, text 56
141
+ - ws 8.21.0: MIT, text 57
141
142
  - xml-naming 0.1.0: MIT
142
- - yaml 2.9.0: ISC, text 57
143
+ - yaml 2.9.0: ISC, text 58
143
144
 
144
145
  ## Licence text 1
145
146
 
@@ -2805,6 +2806,32 @@ SOFTWARE.
2805
2806
 
2806
2807
  ## Licence text 45
2807
2808
 
2809
+ ```
2810
+ The MIT License (MIT)
2811
+
2812
+ Copyright (c) 2017-present, Jon Schlinkert.
2813
+
2814
+ Permission is hereby granted, free of charge, to any person obtaining a copy
2815
+ of this software and associated documentation files (the "Software"), to deal
2816
+ in the Software without restriction, including without limitation the rights
2817
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2818
+ copies of the Software, and to permit persons to whom the Software is
2819
+ furnished to do so, subject to the following conditions:
2820
+
2821
+ The above copyright notice and this permission notice shall be included in
2822
+ all copies or substantial portions of the Software.
2823
+
2824
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2825
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2826
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2827
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2828
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2829
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2830
+ THE SOFTWARE.
2831
+ ```
2832
+
2833
+ ## Licence text 46
2834
+
2808
2835
  ```
2809
2836
  Copyright (c) 2011:
2810
2837
  Tim Koschützki (tim@debuggable.com)
@@ -2829,7 +2856,7 @@ Felix Geisendörfer (felix@debuggable.com)
2829
2856
  THE SOFTWARE.
2830
2857
  ```
2831
2858
 
2832
- ## Licence text 46
2859
+ ## Licence text 47
2833
2860
 
2834
2861
  ```
2835
2862
  The MIT License (MIT)
@@ -2855,7 +2882,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2855
2882
  THE SOFTWARE.
2856
2883
  ```
2857
2884
 
2858
- ## Licence text 47
2885
+ ## Licence text 48
2859
2886
 
2860
2887
  ```
2861
2888
  MIT License
@@ -2881,7 +2908,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2881
2908
  SOFTWARE.
2882
2909
  ```
2883
2910
 
2884
- ## Licence text 48
2911
+ ## Licence text 49
2885
2912
 
2886
2913
  ```
2887
2914
  MIT License
@@ -2895,7 +2922,7 @@ The above copyright notice and this permission notice shall be included in all c
2895
2922
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2896
2923
  ```
2897
2924
 
2898
- ## Licence text 49
2925
+ ## Licence text 50
2899
2926
 
2900
2927
  ```
2901
2928
  The ISC License
@@ -2916,7 +2943,7 @@ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2916
2943
  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2917
2944
  ```
2918
2945
 
2919
- ## Licence text 50
2946
+ ## Licence text 51
2920
2947
 
2921
2948
  ```
2922
2949
  Copyright Brian White. All rights reserved.
@@ -2940,7 +2967,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2940
2967
  IN THE SOFTWARE.
2941
2968
  ```
2942
2969
 
2943
- ## Licence text 51
2970
+ ## Licence text 52
2944
2971
 
2945
2972
  ```
2946
2973
  Copyright (c) Microsoft Corporation.
@@ -2957,7 +2984,7 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2957
2984
  PERFORMANCE OF THIS SOFTWARE.
2958
2985
  ```
2959
2986
 
2960
- ## Licence text 52
2987
+ ## Licence text 53
2961
2988
 
2962
2989
  ```
2963
2990
  MIT License
@@ -2983,7 +3010,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2983
3010
  SOFTWARE.
2984
3011
  ```
2985
3012
 
2986
- ## Licence text 53
3013
+ ## Licence text 54
2987
3014
 
2988
3015
  ```
2989
3016
  TypeBox
@@ -3011,7 +3038,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
3011
3038
  THE SOFTWARE.
3012
3039
  ```
3013
3040
 
3014
- ## Licence text 54
3041
+ ## Licence text 55
3015
3042
 
3016
3043
  ```
3017
3044
  MIT License
@@ -3037,7 +3064,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3037
3064
  SOFTWARE.
3038
3065
  ```
3039
3066
 
3040
- ## Licence text 55
3067
+ ## Licence text 56
3041
3068
 
3042
3069
  ```
3043
3070
  The MIT License (MIT)
@@ -3064,7 +3091,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3064
3091
  SOFTWARE.
3065
3092
  ```
3066
3093
 
3067
- ## Licence text 56
3094
+ ## Licence text 57
3068
3095
 
3069
3096
  ```
3070
3097
  Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
@@ -3089,7 +3116,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3089
3116
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3090
3117
  ```
3091
3118
 
3092
- ## Licence text 57
3119
+ ## Licence text 58
3093
3120
 
3094
3121
  ```
3095
3122
  Copyright Eemeli Aro <eemeli@gmail.com>
@@ -1,5 +1,5 @@
1
1
  {
2
- "taskmarket": "1.17.1",
2
+ "taskmarket": "1.18.0",
3
3
  "manifestVersion": 1,
4
4
  "pi": {
5
5
  "package": "@earendil-works/pi-coding-agent",
@@ -15,7 +15,7 @@ import {
15
15
  statusBarText,
16
16
  uncertainPurchaseMessage,
17
17
  usdcToAtomic
18
- } from "./chunk-73RXOYME.js";
18
+ } from "./chunk-EB6OJBF5.js";
19
19
  import "./chunk-S7SJMJ2I.js";
20
20
  export {
21
21
  AGENT_VIEW_VERSION,
@@ -10,10 +10,11 @@ import {
10
10
  selectRail,
11
11
  uncertainPurchases,
12
12
  withdrawalHatch
13
- } from "./chunk-F5SS4NUF.js";
14
- import "./chunk-3S63AB3C.js";
13
+ } from "./chunk-MO5KMUTL.js";
14
+ import "./chunk-S4A6V3J3.js";
15
+ import "./chunk-75LS6XS6.js";
16
+ import "./chunk-EB6OJBF5.js";
15
17
  import "./chunk-YM4S6IE4.js";
16
- import "./chunk-73RXOYME.js";
17
18
  import "./chunk-S7SJMJ2I.js";
18
19
  export {
19
20
  allocationViews,
@@ -0,0 +1,351 @@
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
+ import {
3
+ attemptsUsed,
4
+ computeAllocationId,
5
+ fileActiveTaskStore,
6
+ loadDeliverableRecord,
7
+ readComputeView,
8
+ taskStage
9
+ } from "./chunk-L6ZVRENW.js";
10
+ import {
11
+ activeClaim,
12
+ captureProblem,
13
+ killedSession,
14
+ loadSyncState,
15
+ orphanOf
16
+ } from "./chunk-64HQ7ZF2.js";
17
+ import {
18
+ allocationViews,
19
+ channelView,
20
+ getAgentPolicyPath,
21
+ openAgentChannels,
22
+ uncertainPurchases,
23
+ withdrawalHatch
24
+ } from "./chunk-MO5KMUTL.js";
25
+ import {
26
+ SandboxListOutputSchema,
27
+ apiUrl,
28
+ selectedNetwork
29
+ } from "./chunk-75LS6XS6.js";
30
+ import {
31
+ AGENT_VIEW_VERSION,
32
+ blockingPurchases,
33
+ channelLines,
34
+ computeLines,
35
+ formatUsdc
36
+ } from "./chunk-EB6OJBF5.js";
37
+ import {
38
+ AgentLaunchError
39
+ } from "./chunk-YM4S6IE4.js";
40
+
41
+ // src/lib/agent/status.ts
42
+ import { promises as fs } from "fs";
43
+ async function readTaskStatus(wallet) {
44
+ const network = selectedNetwork();
45
+ const record = await fileActiveTaskStore(wallet, network.chainId).load();
46
+ if (!record) return null;
47
+ const spec = record.workspace?.spec;
48
+ const deliverable = spec ? await loadDeliverableRecord(record.taskId).catch(() => void 0) : void 0;
49
+ return {
50
+ taskId: record.taskId,
51
+ chainId: record.chainId,
52
+ network: network.name,
53
+ stage: taskStage(record),
54
+ claimId: record.claimId ?? null,
55
+ workspace: record.workspace?.state === "ready" ? record.workspace.path : null,
56
+ validation: spec ? {
57
+ attemptsUsed: deliverable ? attemptsUsed(deliverable) : 0,
58
+ maxAttempts: spec.maxAttempts,
59
+ deadline: spec.deadline
60
+ } : null,
61
+ deliverableId: record.deliverable?.deliverableId ?? record.submission?.deliverableId ?? null,
62
+ submissionId: record.submission?.submissionId ?? null,
63
+ updatedAt: record.updatedAt,
64
+ source: "saved_state"
65
+ };
66
+ }
67
+ async function readSyncStatus(cwd = process.cwd()) {
68
+ const view = {
69
+ project: null,
70
+ approved: false,
71
+ approvedAt: null,
72
+ sessionRunning: false,
73
+ lastUploadAt: null,
74
+ uploadedFiles: null,
75
+ lastPullAt: null,
76
+ pending: null,
77
+ captureFailed: null,
78
+ orphanSandboxId: null,
79
+ source: "saved_state"
80
+ };
81
+ let state;
82
+ try {
83
+ view.project = await fs.realpath(cwd);
84
+ state = await loadSyncState(view.project);
85
+ } catch {
86
+ return view;
87
+ }
88
+ view.approved = state.approval !== void 0;
89
+ view.approvedAt = state.approval?.approvedAt ?? null;
90
+ const session = state.session;
91
+ const claim = activeClaim(state);
92
+ view.sessionRunning = claim?.purpose === "session" || !!session && !session.endedAt && !killedSession(state);
93
+ if (session) {
94
+ view.lastUploadAt = session.lastUploadAt ?? null;
95
+ view.uploadedFiles = session.uploadedFiles ?? null;
96
+ view.lastPullAt = session.lastPullAt ?? null;
97
+ }
98
+ if (state.capture?.state === "saved") {
99
+ view.pending = { ...state.capture.pending, checkedAt: state.capture.capturedAt, saved: true };
100
+ } else if (session?.pending) {
101
+ view.pending = { ...session.pending, saved: false };
102
+ }
103
+ const problem = captureProblem(state);
104
+ view.orphanSandboxId = problem ? null : orphanOf(state)?.sandboxId ?? null;
105
+ if (problem) {
106
+ view.captureFailed = {
107
+ kind: problem.kind,
108
+ at: problem.at,
109
+ reason: problem.reason,
110
+ sandboxId: problem.sandboxId ?? null
111
+ };
112
+ }
113
+ return view;
114
+ }
115
+ async function listSandboxes(credentials, fetchImpl = fetch) {
116
+ const response = await fetchImpl(`${apiUrl().replace(/\/+$/, "")}/api/sandboxes/list`, {
117
+ method: "POST",
118
+ redirect: "error",
119
+ headers: { "content-type": "application/json" },
120
+ body: JSON.stringify(credentials),
121
+ signal: AbortSignal.timeout(15e3)
122
+ });
123
+ if (!response.ok) {
124
+ await response.body?.cancel().catch(() => void 0);
125
+ throw new Error(`the sandbox broker answered ${response.status}`);
126
+ }
127
+ const parsed = SandboxListOutputSchema.safeParse(await response.json());
128
+ if (!parsed.success) throw new Error("the sandbox list is malformed");
129
+ return parsed.data.sandboxes.map((s) => ({
130
+ sandboxId: s.sandboxId,
131
+ purpose: s.purpose,
132
+ state: s.state,
133
+ meteredSeconds: s.meteredSeconds,
134
+ lastActiveAt: s.lastActiveAt,
135
+ expiresAt: s.expiresAt,
136
+ creditWarning: s.creditWarning,
137
+ creditGraceUntil: s.creditGraceUntil
138
+ }));
139
+ }
140
+ function message(error) {
141
+ return error.code ?? error.message;
142
+ }
143
+ async function collectAgentStatus(deps = {}) {
144
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
145
+ const view = {
146
+ version: AGENT_VIEW_VERSION,
147
+ kind: "taskmarket.agent.status",
148
+ generatedAt: now().toISOString(),
149
+ wallet: null,
150
+ policy: { state: "missing", path: getAgentPolicyPath(), models: [] },
151
+ channels: [],
152
+ purchases: { uncertain: [] },
153
+ inference: { blocked: false, reasons: [] },
154
+ allocations: [],
155
+ sandbox: { state: "no_wallet", sandboxes: [] },
156
+ compute: {
157
+ configured: false,
158
+ availableCredits: null,
159
+ reservedCredits: null,
160
+ burnCreditsPerHour: null,
161
+ low: false,
162
+ warning: null,
163
+ pendingPurchases: 0,
164
+ autoTopUp: null
165
+ },
166
+ sync: {
167
+ project: null,
168
+ approved: false,
169
+ approvedAt: null,
170
+ sessionRunning: false,
171
+ lastUploadAt: null,
172
+ uploadedFiles: null,
173
+ lastPullAt: null,
174
+ pending: null,
175
+ captureFailed: null,
176
+ orphanSandboxId: null,
177
+ source: "saved_state"
178
+ },
179
+ task: null
180
+ };
181
+ const reasons = view.inference.reasons;
182
+ let credentials;
183
+ try {
184
+ const opened = await (deps.openChannels ?? openAgentChannels)();
185
+ view.wallet = { address: opened.keystore.walletAddress };
186
+ credentials = { deviceId: opened.keystore.deviceId, apiToken: opened.keystore.apiToken };
187
+ view.policy = { state: "approved", path: view.policy.path, models: [...opened.policy.models] };
188
+ const statuses = /* @__PURE__ */ new Map();
189
+ for (const rail of opened.rails) {
190
+ const status = await rail.payer.channelStatus();
191
+ statuses.set(rail.rail.id, status);
192
+ const channel = await channelView(rail, status);
193
+ view.channels.push({ ...channel, withdrawal: withdrawalHatch(channel) });
194
+ }
195
+ for (const rail of opened.retired ?? []) {
196
+ const channel = await channelView(rail, void 0, { retired: true });
197
+ view.channels.push({ ...channel, withdrawal: withdrawalHatch(channel) });
198
+ }
199
+ view.purchases.uncertain = [
200
+ ...await uncertainPurchases(opened.rails, statuses),
201
+ ...await uncertainPurchases(opened.retired ?? [])
202
+ ];
203
+ view.allocations = await allocationViews(opened.rails, [
204
+ await (deps.computeAllocation ?? computeAllocationId)()
205
+ ]);
206
+ } catch (error) {
207
+ if (error instanceof AgentLaunchError && error.code === "wallet_required") {
208
+ reasons.push("no wallet: run taskmarket wallet import");
209
+ } else if (error instanceof AgentLaunchError && error.code === "setup_required") {
210
+ reasons.push("no approved spending policy");
211
+ } else {
212
+ view.policy = { ...view.policy, state: "invalid", error: message(error) };
213
+ reasons.push(`the spending policy cannot be used: ${message(error)}`);
214
+ }
215
+ }
216
+ const active = view.channels.filter((channel) => !channel.retired);
217
+ for (const id of blockingPurchases({ channels: active, unresolved: [] })) {
218
+ reasons.push(`purchase ${id} is unresolved: taskmarket agent reconcile ${id}`);
219
+ }
220
+ for (const channel of active) {
221
+ if (channel.state !== "open") reasons.push(`channel ${channel.rail} is ${channel.state}`);
222
+ }
223
+ if (credentials) {
224
+ try {
225
+ view.sandbox = {
226
+ state: "ok",
227
+ sandboxes: await (deps.listSandboxes ?? listSandboxes)(credentials)
228
+ };
229
+ } catch (error) {
230
+ view.sandbox = { state: "unavailable", sandboxes: [], error: message(error) };
231
+ }
232
+ }
233
+ const warning = view.sandbox.sandboxes.find((s) => s.purpose === "interactive" && s.creditWarning)?.creditWarning ?? null;
234
+ view.compute = await (deps.readCompute ?? readComputeView)({ warning });
235
+ view.sync = await (deps.readSync ?? readSyncStatus)();
236
+ if (view.wallet) {
237
+ try {
238
+ view.task = await (deps.readTask ?? readTaskStatus)(view.wallet.address);
239
+ } catch (error) {
240
+ view.taskError = message(error);
241
+ }
242
+ }
243
+ view.inference.blocked = reasons.length > 0;
244
+ return view;
245
+ }
246
+ function statusLines(view) {
247
+ const lines = [
248
+ `taskmarket agent status (view v${view.version}, ${view.generatedAt})`,
249
+ view.wallet ? `Wallet: ${view.wallet.address}` : "Wallet: none",
250
+ `Spending policy: ${view.policy.state}${view.policy.models.length ? ` (${view.policy.models.join(", ")})` : ""}${view.policy.error ? `: ${view.policy.error}` : ""}`
251
+ ];
252
+ for (const channel of view.channels) {
253
+ lines.push(...channelLines(channel));
254
+ if (channel.state === "blocked" || channel.state === "withdrawal_pending") {
255
+ lines.push(` ${channel.withdrawal.note}`);
256
+ }
257
+ }
258
+ if (view.purchases.uncertain.length === 0) {
259
+ lines.push("Purchases with an unknown outcome: none");
260
+ } else {
261
+ lines.push("Purchases with an unknown outcome (never resent; reconcile them):");
262
+ for (const purchase of view.purchases.uncertain) {
263
+ lines.push(
264
+ ` ${purchase.invocationId} on ${purchase.rail}: ${purchase.state}, at most ${formatUsdc(purchase.authorizedAtomic)}${purchase.blocking ? ", blocks new turns" : ""}`
265
+ );
266
+ }
267
+ }
268
+ lines.push(
269
+ view.inference.blocked ? `New inference: blocked (${view.inference.reasons.join("; ")})` : "New inference: ready"
270
+ );
271
+ if (view.sandbox.state === "ok") {
272
+ lines.push(
273
+ view.sandbox.sandboxes.length === 0 ? "Sandbox: none" : `Sandbox: ${view.sandbox.sandboxes.map(
274
+ (s) => `${s.sandboxId} ${s.purpose} ${s.state}${s.creditWarning ? ` (${s.creditWarning})` : ""}`
275
+ ).join("; ")}`
276
+ );
277
+ } else if (view.sandbox.state === "unavailable") {
278
+ lines.push(`Sandbox: unavailable (${view.sandbox.error})`);
279
+ }
280
+ lines.push(...computeLines(view.compute));
281
+ lines.push(...syncLines(view.sync));
282
+ lines.push(...taskLines(view.task, view.taskError));
283
+ return lines;
284
+ }
285
+ var STAGES = {
286
+ claiming: "claiming (a claim may have been sent; the next session confirms it before anything else)",
287
+ claimed: "claimed (the task workspace is not set up yet)",
288
+ working: "working (workspace ready, not validated yet)",
289
+ frozen: "frozen (validated, waiting for your approval to submit)",
290
+ submitting: "submitting (sent; the next session looks it up before anything is sent again)",
291
+ submitted: "submitted (not yet accepted or paid)",
292
+ stopped: "stopped"
293
+ };
294
+ function taskLines(task, error) {
295
+ if (error) return [`Marketplace task: the run state cannot be read (${error})`];
296
+ if (!task) return ["Marketplace task: none"];
297
+ const lines = [`Marketplace task ${task.taskId} on ${task.network}: ${STAGES[task.stage]}`];
298
+ if (task.claimId) lines.push(` Claim: ${task.claimId}`);
299
+ if (task.workspace) lines.push(` Workspace (in the sandbox): ${task.workspace}`);
300
+ if (task.validation) {
301
+ lines.push(
302
+ ` Validation: ${task.validation.attemptsUsed} of ${task.validation.maxAttempts} attempt(s) used, until ${task.validation.deadline}`
303
+ );
304
+ }
305
+ if (task.deliverableId) lines.push(` Deliverable: ${task.deliverableId}`);
306
+ if (task.submissionId) lines.push(` Submission: ${task.submissionId}`);
307
+ lines.push(` As recorded ${task.updatedAt}`);
308
+ return lines;
309
+ }
310
+ function syncLines(sync) {
311
+ if (!sync.project) return ["Workspace sync: this directory cannot be read"];
312
+ const lines = [
313
+ `Workspace sync (${sync.project}): ${sync.approved ? `upload approved ${sync.approvedAt}` : "upload not approved yet"}${sync.sessionRunning ? "; a session is running here" : ""}`
314
+ ];
315
+ if (sync.lastUploadAt) {
316
+ lines.push(` Last upload: ${sync.lastUploadAt} (${sync.uploadedFiles ?? 0} file(s))`);
317
+ }
318
+ if (sync.lastPullAt) lines.push(` Last pull: ${sync.lastPullAt}`);
319
+ if (sync.pending) {
320
+ const { clean, conflict, blocked } = sync.pending;
321
+ const notSaved = sync.pending.notSaved ?? 0;
322
+ const tooDeep = sync.pending.tooDeep ?? 0;
323
+ const waiting = `${clean + conflict} change(s) to pull${conflict ? ` (${conflict} in conflict)` : ""}${notSaved ? `, ${notSaved} too large to have been saved` : ""}${tooDeep ? `, ${tooDeep} director(ies) nested too deep to have been saved (taskmarket agent pull --keep-local <dir> acknowledges one, --discard all)` : ""}${blocked ? `, ${blocked} blocked` : ""}`;
324
+ lines.push(
325
+ sync.pending.saved ? ` Saved from the last session: ${waiting}; review them with taskmarket agent pull` : ` In the sandbox: ${waiting}, as counted ${sync.pending.checkedAt} (not checked live, which would resume the sandbox)`
326
+ );
327
+ }
328
+ if (sync.orphanSandboxId) {
329
+ lines.push(
330
+ ` A killed --no-sync session left sandbox ${sync.orphanSandboxId}: taskmarket agent pull --recover brings back what the agent made there, until it stops once idle.`
331
+ );
332
+ }
333
+ if (sync.captureFailed) {
334
+ const failed = sync.captureFailed;
335
+ const where = failed.sandboxId ? ` (sandbox ${failed.sandboxId})` : "";
336
+ lines.push(
337
+ failed.kind === "interrupted" ? ` The last session ended at ${failed.at} before its changes were saved${where}: recover them with taskmarket agent pull --recover, or drop them with taskmarket agent pull --discard. New sessions wait until then.` : ` The last session's changes could not be saved${where}: ${failed.reason}. Recover them with taskmarket agent pull --recover, or drop them with taskmarket agent pull --discard. New sessions wait until then.`
338
+ );
339
+ }
340
+ return lines;
341
+ }
342
+
343
+ export {
344
+ readTaskStatus,
345
+ readSyncStatus,
346
+ listSandboxes,
347
+ collectAgentStatus,
348
+ statusLines,
349
+ taskLines,
350
+ syncLines
351
+ };