@lucid-agents/taskmarket 1.17.2 → 1.18.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.
- package/dist/THIRD_PARTY_NOTICES.md +54 -27
- package/dist/agent-compatibility.json +1 -1
- package/dist/{budget-view-XKZC3GY5.js → budget-view-JPZ7HWR2.js} +1 -1
- package/dist/{channels-JF4Q5KG3.js → channels-KCDZ5REF.js} +4 -3
- package/dist/chunk-3FF6JAMK.js +868 -0
- package/dist/{chunk-52Q2DCZN.js → chunk-3LNZ766H.js} +23 -23
- package/dist/chunk-5VMWXDNO.js +7518 -0
- package/dist/{chunk-73RXOYME.js → chunk-EB6OJBF5.js} +5 -0
- package/dist/chunk-HEGWADBV.js +447 -0
- package/dist/{chunk-GJQUALXN.js → chunk-LEVAZYR5.js} +106 -29
- package/dist/{launch-2U3KFTFD.js → chunk-ONYTKAHC.js} +4977 -2385
- package/dist/chunk-RHUDL5WA.js +351 -0
- package/dist/chunk-RQTJZYFV.js +831 -0
- package/dist/chunk-YB2B5DPR.js +10320 -0
- package/dist/chunk-ZIC2YCLV.js +65 -0
- package/dist/{doctor-7TWLIJWW.js → doctor-TB3KKAZB.js} +13 -10
- package/dist/index.js +2065 -712
- package/dist/launch-ZV6OLBFM.js +6364 -0
- package/dist/pi-extensions/remote-tools.cjs +74 -3
- package/dist/pi-extensions/xgate.cjs +1039 -25
- package/dist/{status-5IMULBQQ.js → prompter-LSICEYSQ.js} +3 -12
- package/dist/reconcile-UXL65RRT.js +194 -0
- package/dist/status-FHZ3UGEB.js +28 -0
- package/dist/sync-pull-YECZ4PJA.js +409 -0
- package/package.json +1 -1
- package/dist/chunk-FRV3ZHUR.js +0 -16141
- package/dist/chunk-LDCFKFF6.js +0 -154
- package/dist/chunk-XXD7TWLP.js +0 -181
- package/dist/reconcile-XZR2UYSW.js +0 -83
|
@@ -73,6 +73,11 @@ function statusBarText(view) {
|
|
|
73
73
|
parts.push(
|
|
74
74
|
open ? `budget ${formatUsdc(remainingBudget(view))} left` : `channel ${active[0].state.replace("_", " ")}`
|
|
75
75
|
);
|
|
76
|
+
const depositing = active.reduce(
|
|
77
|
+
(sum, channel) => sum + BigInt(channel.amounts.depositing),
|
|
78
|
+
0n
|
|
79
|
+
);
|
|
80
|
+
if (depositing > 0n) parts.push(`deposit ${formatUsdc(depositing)} confirming`);
|
|
76
81
|
}
|
|
77
82
|
const compute = view.compute;
|
|
78
83
|
if (compute?.configured && compute.availableCredits !== null) {
|
|
@@ -0,0 +1,447 @@
|
|
|
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
|
+
x402FeePost
|
|
4
|
+
} from "./chunk-3LNZ766H.js";
|
|
5
|
+
import {
|
|
6
|
+
createWalletAccountFromKeystore,
|
|
7
|
+
signMessage
|
|
8
|
+
} from "./chunk-5VMWXDNO.js";
|
|
9
|
+
import {
|
|
10
|
+
READ_AUTH_ADDRESS_HEADER,
|
|
11
|
+
READ_AUTH_SIGNATURE_HEADER,
|
|
12
|
+
TASK_ACCESS_GRANT_HEADER,
|
|
13
|
+
apiGet,
|
|
14
|
+
apiPost,
|
|
15
|
+
buildClaimMessage,
|
|
16
|
+
buildReadAuthMessage,
|
|
17
|
+
buildSubmitMessage,
|
|
18
|
+
getTaskmarketHome,
|
|
19
|
+
isRecordingPendingResponse,
|
|
20
|
+
loadKeystore
|
|
21
|
+
} from "./chunk-YB2B5DPR.js";
|
|
22
|
+
|
|
23
|
+
// src/lib/read-auth.ts
|
|
24
|
+
async function signReadAuth() {
|
|
25
|
+
let keystore;
|
|
26
|
+
try {
|
|
27
|
+
keystore = await loadKeystore();
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const account = await createWalletAccountFromKeystore(keystore);
|
|
33
|
+
const signature = await account.signMessage({
|
|
34
|
+
message: buildReadAuthMessage(keystore.walletAddress)
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
walletAddress: keystore.walletAddress,
|
|
38
|
+
headers: {
|
|
39
|
+
[READ_AUTH_ADDRESS_HEADER]: keystore.walletAddress,
|
|
40
|
+
[READ_AUTH_SIGNATURE_HEADER]: signature
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
} catch {
|
|
44
|
+
return { walletAddress: keystore.walletAddress, headers: {} };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/lib/task-access-grants.ts
|
|
49
|
+
import { promises as fs } from "fs";
|
|
50
|
+
import path from "path";
|
|
51
|
+
function getGrantsPath() {
|
|
52
|
+
return path.join(getTaskmarketHome(), "task-access-grants.json");
|
|
53
|
+
}
|
|
54
|
+
async function readGrantsFile() {
|
|
55
|
+
try {
|
|
56
|
+
const raw = await fs.readFile(getGrantsPath(), "utf-8");
|
|
57
|
+
return JSON.parse(raw);
|
|
58
|
+
} catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function loadTaskAccessGrant(taskId) {
|
|
63
|
+
const grants = await readGrantsFile();
|
|
64
|
+
const record = grants[taskId];
|
|
65
|
+
if (!record) return null;
|
|
66
|
+
if (new Date(record.expiresAt).getTime() <= Date.now()) return null;
|
|
67
|
+
return record.grant;
|
|
68
|
+
}
|
|
69
|
+
async function taskAccessGrantHeaders(taskId) {
|
|
70
|
+
const grant = await loadTaskAccessGrant(taskId);
|
|
71
|
+
return grant ? { [TASK_ACCESS_GRANT_HEADER]: grant } : {};
|
|
72
|
+
}
|
|
73
|
+
async function saveTaskAccessGrant(taskId, grant, expiresAt) {
|
|
74
|
+
const grants = await readGrantsFile();
|
|
75
|
+
grants[taskId] = { grant, expiresAt };
|
|
76
|
+
const dir = path.dirname(getGrantsPath());
|
|
77
|
+
await fs.mkdir(dir, { recursive: true });
|
|
78
|
+
await fs.writeFile(getGrantsPath(), JSON.stringify(grants, null, 2), {
|
|
79
|
+
mode: 384
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/sdk/artifact-upload.ts
|
|
84
|
+
import { extname } from "path";
|
|
85
|
+
import { Readable } from "stream";
|
|
86
|
+
import https from "https";
|
|
87
|
+
import http from "http";
|
|
88
|
+
function mimeTypeForPath(filePath) {
|
|
89
|
+
switch (extname(filePath).toLowerCase()) {
|
|
90
|
+
case ".png":
|
|
91
|
+
return "image/png";
|
|
92
|
+
case ".jpg":
|
|
93
|
+
case ".jpeg":
|
|
94
|
+
return "image/jpeg";
|
|
95
|
+
case ".webp":
|
|
96
|
+
return "image/webp";
|
|
97
|
+
case ".gif":
|
|
98
|
+
return "image/gif";
|
|
99
|
+
case ".svg":
|
|
100
|
+
return "image/svg+xml";
|
|
101
|
+
case ".pdf":
|
|
102
|
+
return "application/pdf";
|
|
103
|
+
case ".mp4":
|
|
104
|
+
return "video/mp4";
|
|
105
|
+
case ".webm":
|
|
106
|
+
return "video/webm";
|
|
107
|
+
case ".mp3":
|
|
108
|
+
return "audio/mpeg";
|
|
109
|
+
case ".wav":
|
|
110
|
+
return "audio/wav";
|
|
111
|
+
case ".md":
|
|
112
|
+
return "text/markdown";
|
|
113
|
+
case ".txt":
|
|
114
|
+
return "text/plain";
|
|
115
|
+
case ".json":
|
|
116
|
+
return "application/json";
|
|
117
|
+
case ".zip":
|
|
118
|
+
return "application/zip";
|
|
119
|
+
default:
|
|
120
|
+
return "application/octet-stream";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function putArtifactBytes(uploadUrl, data, mimeType, onProgress) {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
const url = new URL(uploadUrl);
|
|
126
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
127
|
+
const req = transport.request(
|
|
128
|
+
url,
|
|
129
|
+
{
|
|
130
|
+
method: "PUT",
|
|
131
|
+
headers: {
|
|
132
|
+
"Content-Type": mimeType,
|
|
133
|
+
"Content-Length": String(data.length)
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
(res) => {
|
|
137
|
+
const chunks = [];
|
|
138
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
139
|
+
res.on("end", () => {
|
|
140
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
141
|
+
resolve();
|
|
142
|
+
} else {
|
|
143
|
+
const body = Buffer.concat(chunks).toString("utf8").slice(0, 500);
|
|
144
|
+
reject(
|
|
145
|
+
new Error(
|
|
146
|
+
`Upload failed (${res.statusCode ?? "unknown"}): ${body || "no response body"}`
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
req.on("error", reject);
|
|
154
|
+
let uploaded = 0;
|
|
155
|
+
const stream = Readable.from([data]);
|
|
156
|
+
stream.on("data", (chunk) => {
|
|
157
|
+
uploaded += chunk.length;
|
|
158
|
+
onProgress?.(Math.round(uploaded / data.length * 100));
|
|
159
|
+
});
|
|
160
|
+
stream.on("error", reject);
|
|
161
|
+
stream.pipe(req);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/lib/task-sdk.ts
|
|
166
|
+
async function putWithProgress(upload) {
|
|
167
|
+
process.stderr.write(` Uploading ${upload.fileName}...
|
|
168
|
+
`);
|
|
169
|
+
await putArtifactBytes(upload.uploadUrl, upload.data, upload.mimeType, (percent) => {
|
|
170
|
+
process.stderr.write(`\r ${upload.fileName}: ${percent}%`);
|
|
171
|
+
});
|
|
172
|
+
process.stderr.write("\n");
|
|
173
|
+
}
|
|
174
|
+
var cliTaskTransport = {
|
|
175
|
+
get: (path2, headers) => apiGet(path2, { headers }),
|
|
176
|
+
// A write the caller gave no key is sent exactly as before the SDK existed, with no options,
|
|
177
|
+
// so `apiPost` resolves its key itself (explicit, then TASKMARKET_IDEMPOTENCY_KEY, then fresh).
|
|
178
|
+
post: (path2, body, options) => options === void 0 ? apiPost(path2, body) : apiPost(path2, body, options),
|
|
179
|
+
feePost: (path2, body, options) => x402FeePost(path2, body, options),
|
|
180
|
+
put: putWithProgress
|
|
181
|
+
};
|
|
182
|
+
function keystoreTaskSigner(keystore) {
|
|
183
|
+
return {
|
|
184
|
+
address: keystore.walletAddress,
|
|
185
|
+
signMessage: (message) => signMessage(message, keystore)
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function cliTaskReadContext() {
|
|
189
|
+
return {
|
|
190
|
+
transport: cliTaskTransport,
|
|
191
|
+
readAuth: signReadAuth,
|
|
192
|
+
accessGrantHeaders: taskAccessGrantHeaders
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function cliTaskWriteContext(keystore) {
|
|
196
|
+
return { ...cliTaskReadContext(), signer: keystoreTaskSigner(keystore) };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/sdk/tasks.ts
|
|
200
|
+
import { promises as fsPromises } from "fs";
|
|
201
|
+
import { basename } from "path";
|
|
202
|
+
import { createHash } from "crypto";
|
|
203
|
+
import { keccak256 } from "viem";
|
|
204
|
+
var TaskOperationError = class extends Error {
|
|
205
|
+
code;
|
|
206
|
+
constructor(code, message) {
|
|
207
|
+
super(message);
|
|
208
|
+
this.name = "TaskOperationError";
|
|
209
|
+
this.code = code;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
function withChainQuery(path2, chainId) {
|
|
213
|
+
if (chainId === void 0) return path2;
|
|
214
|
+
return `${path2}${path2.includes("?") ? "&" : "?"}chainId=${chainId}`;
|
|
215
|
+
}
|
|
216
|
+
function withChainBody(body, chainId) {
|
|
217
|
+
return chainId === void 0 ? body : { ...body, chainId };
|
|
218
|
+
}
|
|
219
|
+
function writeOptions(idempotencyKey) {
|
|
220
|
+
return idempotencyKey === void 0 ? void 0 : { idempotencyKey };
|
|
221
|
+
}
|
|
222
|
+
async function readAuthHeaders(ctx) {
|
|
223
|
+
return (await ctx.readAuth?.())?.headers ?? {};
|
|
224
|
+
}
|
|
225
|
+
async function getTask(ctx, ref) {
|
|
226
|
+
const headers = {
|
|
227
|
+
...await readAuthHeaders(ctx),
|
|
228
|
+
...await ctx.accessGrantHeaders?.(ref.taskId) ?? {}
|
|
229
|
+
};
|
|
230
|
+
const task = await ctx.transport.get(
|
|
231
|
+
withChainQuery(`/api/tasks/${ref.taskId}`, ref.chainId),
|
|
232
|
+
headers
|
|
233
|
+
);
|
|
234
|
+
return task ?? null;
|
|
235
|
+
}
|
|
236
|
+
async function getTaskTerms(ctx, ref) {
|
|
237
|
+
const headers = {
|
|
238
|
+
...await readAuthHeaders(ctx),
|
|
239
|
+
...await ctx.accessGrantHeaders?.(ref.taskId) ?? {}
|
|
240
|
+
};
|
|
241
|
+
const detail = await ctx.transport.get(
|
|
242
|
+
withChainQuery(`/api/tasks/${ref.taskId}/terms`, ref.chainId),
|
|
243
|
+
headers
|
|
244
|
+
);
|
|
245
|
+
return detail ?? null;
|
|
246
|
+
}
|
|
247
|
+
async function listTasks(ctx, filter = {}) {
|
|
248
|
+
const params = new URLSearchParams();
|
|
249
|
+
if (filter.chainId !== void 0) params.set("chainId", String(filter.chainId));
|
|
250
|
+
if (filter.q) params.set("q", filter.q);
|
|
251
|
+
if (filter.sort) params.set("sort", filter.sort);
|
|
252
|
+
if (filter.status) params.set("status", filter.status);
|
|
253
|
+
if (filter.phase) params.set("phase", filter.phase);
|
|
254
|
+
if (filter.mode) params.set("mode", filter.mode);
|
|
255
|
+
if (filter.auctionType) params.set("auctionType", filter.auctionType);
|
|
256
|
+
if (filter.tags) params.set("tags", filter.tags);
|
|
257
|
+
if (filter.minReward) params.set("minReward", filter.minReward);
|
|
258
|
+
if (filter.maxReward) params.set("maxReward", filter.maxReward);
|
|
259
|
+
if (filter.deadlineHours) params.set("deadlineHours", filter.deadlineHours);
|
|
260
|
+
if (filter.limit) params.set("limit", filter.limit);
|
|
261
|
+
if (filter.cursor) params.set("cursor", filter.cursor);
|
|
262
|
+
const result = await ctx.transport.get(
|
|
263
|
+
`/api/tasks?${params.toString()}`,
|
|
264
|
+
await readAuthHeaders(ctx)
|
|
265
|
+
);
|
|
266
|
+
return { tasks: result.tasks, hasMore: result.hasMore, nextCursor: result.nextCursor };
|
|
267
|
+
}
|
|
268
|
+
async function listTaskSubmissions(ctx, ref) {
|
|
269
|
+
const headers = await readAuthHeaders(ctx);
|
|
270
|
+
return await ctx.transport.get(
|
|
271
|
+
withChainQuery(`/api/tasks/${ref.taskId}/submissions`, ref.chainId),
|
|
272
|
+
headers
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
async function listWorkerSubmissions(ctx, options = {}) {
|
|
276
|
+
const auth = await ctx.readAuth?.();
|
|
277
|
+
const address = options.address ?? auth?.walletAddress;
|
|
278
|
+
if (!address) {
|
|
279
|
+
throw new TaskOperationError(
|
|
280
|
+
"no_address",
|
|
281
|
+
"No worker address was given and there is no reading wallet to default to"
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
return ctx.transport.get(`/api/submissions/mine?workerAddress=${encodeURIComponent(address)}`, {
|
|
285
|
+
...auth?.headers ?? {}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async function getWorkerInbox(ctx) {
|
|
289
|
+
const auth = await ctx.readAuth?.();
|
|
290
|
+
if (!auth?.walletAddress) {
|
|
291
|
+
throw new TaskOperationError("no_address", "There is no reading wallet to ask about");
|
|
292
|
+
}
|
|
293
|
+
const raw = await ctx.transport.get(
|
|
294
|
+
`/api/agents/inbox?address=${encodeURIComponent(auth.walletAddress)}`,
|
|
295
|
+
auth.headers
|
|
296
|
+
);
|
|
297
|
+
return {
|
|
298
|
+
address: auth.walletAddress,
|
|
299
|
+
proven: Object.keys(auth.headers).length > 0,
|
|
300
|
+
inbox: {
|
|
301
|
+
asRequester: raw?.asRequester ?? [],
|
|
302
|
+
asWorker: raw?.asWorker ?? [],
|
|
303
|
+
invitedPrivateTasks: raw?.invitedPrivateTasks ?? []
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async function getAgentStats(ctx, options) {
|
|
308
|
+
return await ctx.transport.get(
|
|
309
|
+
withChainQuery(
|
|
310
|
+
`/api/agents/stats?address=${encodeURIComponent(options.address)}`,
|
|
311
|
+
options.chainId
|
|
312
|
+
),
|
|
313
|
+
{}
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
async function claimTask(ctx, ref, options = {}) {
|
|
317
|
+
const signature = await ctx.signer.signMessage(buildClaimMessage(ref.taskId));
|
|
318
|
+
const { data, idempotencyKey } = await ctx.transport.post(
|
|
319
|
+
`/api/tasks/${ref.taskId}/claim`,
|
|
320
|
+
withChainBody({ workerAddress: ctx.signer.address, signature }, ref.chainId),
|
|
321
|
+
writeOptions(options.idempotencyKey)
|
|
322
|
+
);
|
|
323
|
+
return { claimId: data.claimId, idempotencyKey };
|
|
324
|
+
}
|
|
325
|
+
async function authorizeUploads(ctx, ref) {
|
|
326
|
+
return { signature: await ctx.signer.signMessage(buildSubmitMessage(ref.taskId)) };
|
|
327
|
+
}
|
|
328
|
+
async function readArtifactFile(file) {
|
|
329
|
+
const fileName = file.fileName ?? basename(file.path);
|
|
330
|
+
const data = await fsPromises.readFile(file.path);
|
|
331
|
+
if (data.length === 0) {
|
|
332
|
+
throw new TaskOperationError("empty_artifact", `File is empty: ${fileName}`);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
data,
|
|
336
|
+
artifact: {
|
|
337
|
+
fileName,
|
|
338
|
+
mimeType: file.mimeType ?? mimeTypeForPath(file.path),
|
|
339
|
+
role: file.role,
|
|
340
|
+
sizeBytes: data.length,
|
|
341
|
+
sha256Hash: createHash("sha256").update(data).digest("hex"),
|
|
342
|
+
keccak256Hash: keccak256(new Uint8Array(data))
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
async function requestArtifactUpload(ctx, ref, artifact, authorization) {
|
|
347
|
+
const {
|
|
348
|
+
data: { uploadUrl, artifactKey }
|
|
349
|
+
} = await ctx.transport.post(
|
|
350
|
+
`/api/tasks/${ref.taskId}/submissions/request-upload-url`,
|
|
351
|
+
withChainBody(
|
|
352
|
+
{
|
|
353
|
+
taskId: ref.taskId,
|
|
354
|
+
workerAddress: ctx.signer.address,
|
|
355
|
+
signature: authorization.signature,
|
|
356
|
+
fileName: artifact.fileName,
|
|
357
|
+
mimeType: artifact.mimeType,
|
|
358
|
+
role: artifact.role,
|
|
359
|
+
sizeBytes: artifact.sizeBytes
|
|
360
|
+
},
|
|
361
|
+
ref.chainId
|
|
362
|
+
)
|
|
363
|
+
);
|
|
364
|
+
return { uploadUrl, artifactKey };
|
|
365
|
+
}
|
|
366
|
+
async function uploadArtifact(ctx, ref, file, authorization) {
|
|
367
|
+
const { data, artifact } = await readArtifactFile(file);
|
|
368
|
+
const { uploadUrl, artifactKey } = await requestArtifactUpload(ctx, ref, artifact, authorization);
|
|
369
|
+
await ctx.transport.put({
|
|
370
|
+
uploadUrl,
|
|
371
|
+
data,
|
|
372
|
+
mimeType: artifact.mimeType,
|
|
373
|
+
fileName: artifact.fileName
|
|
374
|
+
});
|
|
375
|
+
return { artifactKey, ...artifact };
|
|
376
|
+
}
|
|
377
|
+
async function submitUploadedArtifacts(ctx, ref, artifacts, options = {}) {
|
|
378
|
+
if (artifacts.length === 0) {
|
|
379
|
+
throw new TaskOperationError("no_artifacts", "At least one artifact is required");
|
|
380
|
+
}
|
|
381
|
+
const signature = await ctx.signer.signMessage(
|
|
382
|
+
buildSubmitMessage(
|
|
383
|
+
ref.taskId,
|
|
384
|
+
artifacts.map((artifact) => artifact.artifactKey)
|
|
385
|
+
)
|
|
386
|
+
);
|
|
387
|
+
const { data, idempotencyKey } = await ctx.transport.feePost(
|
|
388
|
+
`/api/tasks/${ref.taskId}/submissions/from-keys`,
|
|
389
|
+
withChainBody(
|
|
390
|
+
{
|
|
391
|
+
taskId: ref.taskId,
|
|
392
|
+
workerAddress: ctx.signer.address,
|
|
393
|
+
artifacts,
|
|
394
|
+
signature
|
|
395
|
+
},
|
|
396
|
+
ref.chainId
|
|
397
|
+
),
|
|
398
|
+
options
|
|
399
|
+
);
|
|
400
|
+
if (isRecordingPendingResponse(data)) {
|
|
401
|
+
return {
|
|
402
|
+
submissionId: data.submissionId,
|
|
403
|
+
idempotencyKey,
|
|
404
|
+
recordingPending: {
|
|
405
|
+
...typeof data.intentId === "string" ? { intentId: data.intentId } : {},
|
|
406
|
+
...typeof data.txHash === "string" ? { txHash: data.txHash } : {}
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return { submissionId: data.submissionId, idempotencyKey };
|
|
411
|
+
}
|
|
412
|
+
async function submitTaskFiles(ctx, ref, files, options = {}) {
|
|
413
|
+
if (files.length === 0) {
|
|
414
|
+
throw new TaskOperationError("no_artifacts", "At least one file is required");
|
|
415
|
+
}
|
|
416
|
+
const authorization = await authorizeUploads(ctx, ref);
|
|
417
|
+
const artifacts = await Promise.all(
|
|
418
|
+
files.map((file) => uploadArtifact(ctx, ref, file, authorization))
|
|
419
|
+
);
|
|
420
|
+
return submitUploadedArtifacts(ctx, ref, artifacts, options);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export {
|
|
424
|
+
signReadAuth,
|
|
425
|
+
taskAccessGrantHeaders,
|
|
426
|
+
saveTaskAccessGrant,
|
|
427
|
+
mimeTypeForPath,
|
|
428
|
+
putArtifactBytes,
|
|
429
|
+
cliTaskTransport,
|
|
430
|
+
keystoreTaskSigner,
|
|
431
|
+
cliTaskReadContext,
|
|
432
|
+
cliTaskWriteContext,
|
|
433
|
+
TaskOperationError,
|
|
434
|
+
getTask,
|
|
435
|
+
getTaskTerms,
|
|
436
|
+
listTasks,
|
|
437
|
+
listTaskSubmissions,
|
|
438
|
+
listWorkerSubmissions,
|
|
439
|
+
getWorkerInbox,
|
|
440
|
+
getAgentStats,
|
|
441
|
+
claimTask,
|
|
442
|
+
authorizeUploads,
|
|
443
|
+
readArtifactFile,
|
|
444
|
+
requestArtifactUpload,
|
|
445
|
+
submitUploadedArtifacts,
|
|
446
|
+
submitTaskFiles
|
|
447
|
+
};
|
|
@@ -6,21 +6,23 @@ import {
|
|
|
6
6
|
createBaseChannelRpcReader,
|
|
7
7
|
createWalletAccountFromKeystore,
|
|
8
8
|
createX402Allocations,
|
|
9
|
-
getTaskmarketHome,
|
|
10
9
|
getX402JournalPath,
|
|
11
10
|
getX402PolicyPath,
|
|
12
|
-
keystoreExists,
|
|
13
11
|
listX402Payments,
|
|
14
|
-
loadKeystore,
|
|
15
12
|
loadX402Policy,
|
|
16
13
|
rpcUrlForNetwork
|
|
17
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-5VMWXDNO.js";
|
|
18
15
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
getTaskmarketHome,
|
|
17
|
+
keystoreExists,
|
|
18
|
+
loadKeystore
|
|
19
|
+
} from "./chunk-YB2B5DPR.js";
|
|
21
20
|
import {
|
|
22
21
|
formatUsdc
|
|
23
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-EB6OJBF5.js";
|
|
23
|
+
import {
|
|
24
|
+
AgentLaunchError
|
|
25
|
+
} from "./chunk-YM4S6IE4.js";
|
|
24
26
|
|
|
25
27
|
// src/lib/agent/channels.ts
|
|
26
28
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -334,7 +336,11 @@ async function createAgentRails(options) {
|
|
|
334
336
|
budgetAmount: rail.limits.total,
|
|
335
337
|
depositAmount: rail.limits.deposit,
|
|
336
338
|
policyRuleId: rail.policyRuleId,
|
|
337
|
-
...options.fetch ? { fetch: options.fetch } : {}
|
|
339
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
340
|
+
confirmationWait: {
|
|
341
|
+
...options.confirmationWait,
|
|
342
|
+
...options.onConfirmationWait ? { onWait: (progress) => options.onConfirmationWait(rail, progress) } : {}
|
|
343
|
+
}
|
|
338
344
|
});
|
|
339
345
|
const transport = createTaskMarketTransport({
|
|
340
346
|
payer,
|
|
@@ -438,10 +444,12 @@ function createAgentTransport(rails) {
|
|
|
438
444
|
};
|
|
439
445
|
}
|
|
440
446
|
async function reconcileLeftoverOperations(rails, log = () => void 0) {
|
|
441
|
-
|
|
447
|
+
const usable = [];
|
|
448
|
+
const blocked = [];
|
|
449
|
+
for (const entry of rails) {
|
|
450
|
+
const { rail, payer } = entry;
|
|
442
451
|
let pending = (await payer.channelStatus()).pendingOperation;
|
|
443
|
-
if (
|
|
444
|
-
if (pending.kind === "payment") {
|
|
452
|
+
if (pending?.kind === "payment") {
|
|
445
453
|
log(`Reconciling payment ${pending.id} left on rail ${rail.id}`);
|
|
446
454
|
try {
|
|
447
455
|
await payer.reconcile(pending.id);
|
|
@@ -451,13 +459,22 @@ async function reconcileLeftoverOperations(rails, log = () => void 0) {
|
|
|
451
459
|
);
|
|
452
460
|
}
|
|
453
461
|
pending = (await payer.channelStatus()).pendingOperation;
|
|
454
|
-
if (!pending) continue;
|
|
455
462
|
}
|
|
463
|
+
if (!pending) {
|
|
464
|
+
usable.push(entry);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
const reason = `The ${rail.id} channel has an unresolved ${pending.kind} operation ${pending.id} from an earlier session; it must be reconciled (taskmarket agent reconcile ${pending.id}) before anything else is bought on that channel.`;
|
|
468
|
+
log(`${reason} Its models are left out of this session.`);
|
|
469
|
+
blocked.push(reason);
|
|
470
|
+
}
|
|
471
|
+
if (usable.length === 0 && blocked.length > 0) {
|
|
456
472
|
throw new AgentLaunchError(
|
|
457
473
|
"channel_operation_pending",
|
|
458
|
-
|
|
474
|
+
`${blocked.join(" ")} Try again later.`
|
|
459
475
|
);
|
|
460
476
|
}
|
|
477
|
+
return usable;
|
|
461
478
|
}
|
|
462
479
|
|
|
463
480
|
// src/lib/agent/policy.ts
|
|
@@ -469,7 +486,7 @@ var XGATE_ROUTES = [
|
|
|
469
486
|
"/providers/anthropic/v1/messages",
|
|
470
487
|
"/providers/openai/v1/responses"
|
|
471
488
|
];
|
|
472
|
-
var WORKSPACE_CONSENT_VERSION = "boat-eu-2026-10";
|
|
489
|
+
var WORKSPACE_CONSENT_VERSION = "boat-eu-2026-10-sync";
|
|
473
490
|
var address = z.string().regex(/^0x[0-9a-fA-F]{40}$/, "expected a 0x-prefixed 20-byte address");
|
|
474
491
|
var atomic2 = z.string().regex(/^(0|[1-9][0-9]{0,30})$/, "expected an integer amount string");
|
|
475
492
|
var positiveAtomic = atomic2.refine((value) => BigInt(value) > 0n, "expected an amount above 0");
|
|
@@ -669,15 +686,31 @@ async function assessAgentPolicy(policy, options) {
|
|
|
669
686
|
}
|
|
670
687
|
return { reasons, usage };
|
|
671
688
|
}
|
|
672
|
-
function
|
|
673
|
-
if (requested
|
|
674
|
-
if (!policy.models.includes(requested)) {
|
|
689
|
+
function requireApprovedModel(policy, requested) {
|
|
690
|
+
if (requested !== void 0 && !policy.models.includes(requested)) {
|
|
675
691
|
throw new AgentLaunchError(
|
|
676
692
|
"model_not_allowed",
|
|
677
693
|
`Model ${requested} is not in the approved spending policy (${policy.models.join(", ")})`
|
|
678
694
|
);
|
|
679
695
|
}
|
|
680
|
-
|
|
696
|
+
}
|
|
697
|
+
function planSessionModels(options) {
|
|
698
|
+
const { approved, offered, requested, previous } = options;
|
|
699
|
+
const unavailable = approved.filter((model) => !offered.includes(model));
|
|
700
|
+
if (offered.length === 0) {
|
|
701
|
+
throw new AgentLaunchError(
|
|
702
|
+
"no_qualified_models",
|
|
703
|
+
`None of the approved models (${approved.join(", ")}) is qualified by xgate on a route the policy pays for`
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
if (requested !== void 0 && !offered.includes(requested)) {
|
|
707
|
+
throw new AgentLaunchError(
|
|
708
|
+
"model_not_offered",
|
|
709
|
+
`Model ${requested} is approved, but xgate does not offer it on a route a usable rail of the policy pays for, so this session cannot buy it. This session offers ${offered.join(", ")}`
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
const start = requested ?? (previous !== void 0 && offered.includes(previous) ? previous : offered[0]);
|
|
713
|
+
return { start, unavailable };
|
|
681
714
|
}
|
|
682
715
|
|
|
683
716
|
// src/lib/agent/retired.ts
|
|
@@ -738,7 +771,9 @@ async function openAgentChannels(options = {}) {
|
|
|
738
771
|
models: options.models ? options.models(policy) : [...policy.models],
|
|
739
772
|
walletAddress: keystore.walletAddress,
|
|
740
773
|
getAccount,
|
|
741
|
-
...options.fetch ? { fetch: options.fetch } : {}
|
|
774
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
775
|
+
...options.onConfirmationWait ? { onConfirmationWait: options.onConfirmationWait } : {},
|
|
776
|
+
...options.confirmationWait ? { confirmationWait: options.confirmationWait } : {}
|
|
742
777
|
};
|
|
743
778
|
const rails = await createAgentRails(common);
|
|
744
779
|
const retired = await createAgentRails({
|
|
@@ -863,9 +898,7 @@ async function uncertainPurchases(rails, statuses = /* @__PURE__ */ new Map()) {
|
|
|
863
898
|
for (const record2 of records) {
|
|
864
899
|
if (!record2.invocationId || record2.rail?.id !== BASE_CHANNEL_RAIL_ID) continue;
|
|
865
900
|
if (!UNDECIDED.has(record2.state)) continue;
|
|
866
|
-
const rail = rails.find(
|
|
867
|
-
(r) => record2.origin === r.rail.origin && record2.pathname === r.rail.route
|
|
868
|
-
);
|
|
901
|
+
const rail = rails.find((r) => record2.funding?.sessionKey === r.payer.sessionKey);
|
|
869
902
|
if (!rail) continue;
|
|
870
903
|
const lease = statuses.get(rail.rail.id)?.pendingOperation;
|
|
871
904
|
found.push({
|
|
@@ -955,6 +988,34 @@ function outcomeOf(operation) {
|
|
|
955
988
|
if (operation.state === "failed") return "failed";
|
|
956
989
|
return "pending";
|
|
957
990
|
}
|
|
991
|
+
var REFUND_WAIT_TIMEOUT_MS = 9e4;
|
|
992
|
+
var REFUND_WAIT_INTERVAL_MS = 3e3;
|
|
993
|
+
async function awaitRefund(rail, operation, wait = {}) {
|
|
994
|
+
const timeoutMs = wait.timeoutMs ?? REFUND_WAIT_TIMEOUT_MS;
|
|
995
|
+
const intervalMs = Math.max(1, wait.intervalMs ?? REFUND_WAIT_INTERVAL_MS);
|
|
996
|
+
const sleep = wait.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
997
|
+
let current = operation;
|
|
998
|
+
let waitedMs = 0;
|
|
999
|
+
while ((current.state === "dispatched" || current.state === "unknown") && current.transaction && waitedMs < timeoutMs) {
|
|
1000
|
+
try {
|
|
1001
|
+
wait.onWait?.({
|
|
1002
|
+
transaction: current.transaction,
|
|
1003
|
+
amount: current.amount,
|
|
1004
|
+
waitedMs,
|
|
1005
|
+
timeoutMs
|
|
1006
|
+
});
|
|
1007
|
+
} catch {
|
|
1008
|
+
}
|
|
1009
|
+
const step = Math.min(intervalMs, timeoutMs - waitedMs);
|
|
1010
|
+
await sleep(step);
|
|
1011
|
+
waitedMs += step;
|
|
1012
|
+
try {
|
|
1013
|
+
current = await rail.payer.reconcileOperation(current.operationId);
|
|
1014
|
+
} catch {
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return current;
|
|
1018
|
+
}
|
|
958
1019
|
async function closeSession(allocationId2) {
|
|
959
1020
|
try {
|
|
960
1021
|
await allocationsReader().close(allocationId2);
|
|
@@ -975,7 +1036,11 @@ function closeLines(channel) {
|
|
|
975
1036
|
}
|
|
976
1037
|
async function closeChannel(rail, confirm, options = {}) {
|
|
977
1038
|
const retired = options.retired ?? false;
|
|
978
|
-
|
|
1039
|
+
let status = await rail.payer.channelStatus();
|
|
1040
|
+
if (status.pendingOperation?.kind === "payment" && options.reconcilePurchase) {
|
|
1041
|
+
await options.reconcilePurchase(status.pendingOperation.id);
|
|
1042
|
+
status = await rail.payer.channelStatus();
|
|
1043
|
+
}
|
|
979
1044
|
const channel = await channelView(rail, status, { retired });
|
|
980
1045
|
const result = async (outcome, operation2, reconciled) => {
|
|
981
1046
|
const after = operation2 ? await channelView(rail, void 0, { retired }) : channel;
|
|
@@ -1002,7 +1067,11 @@ async function closeChannel(rail, confirm, options = {}) {
|
|
|
1002
1067
|
if (status.closed) return result("closed", null, false);
|
|
1003
1068
|
const lease = status.pendingOperation;
|
|
1004
1069
|
if (lease?.kind === "lifecycle") {
|
|
1005
|
-
const operation2 = await
|
|
1070
|
+
const operation2 = await awaitRefund(
|
|
1071
|
+
rail,
|
|
1072
|
+
await rail.payer.reconcileOperation(lease.id),
|
|
1073
|
+
options.wait
|
|
1074
|
+
);
|
|
1006
1075
|
return result(outcomeOf(operation2), operation2, true);
|
|
1007
1076
|
}
|
|
1008
1077
|
if (lease?.kind === "payment") {
|
|
@@ -1026,10 +1095,17 @@ async function closeChannel(rail, confirm, options = {}) {
|
|
|
1026
1095
|
if (!await confirm(closeLines(channel))) {
|
|
1027
1096
|
throw new AgentLaunchError("not_approved", "The channel was not closed");
|
|
1028
1097
|
}
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1098
|
+
const operationId = `agent-close-${randomUUID2()}`;
|
|
1099
|
+
let operation;
|
|
1100
|
+
try {
|
|
1101
|
+
operation = await rail.payer.refund({ operationId, close: true });
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
if (error.code !== "outcome_uncertain") throw error;
|
|
1104
|
+
const recorded = await rail.payer.reconcileOperation(operationId).catch(() => void 0);
|
|
1105
|
+
if (!recorded || recorded.state !== "dispatched" && recorded.state !== "unknown") throw error;
|
|
1106
|
+
operation = recorded;
|
|
1107
|
+
}
|
|
1108
|
+
operation = await awaitRefund(rail, operation, options.wait);
|
|
1033
1109
|
return result(outcomeOf(operation), operation, false);
|
|
1034
1110
|
}
|
|
1035
1111
|
|
|
@@ -1047,7 +1123,8 @@ export {
|
|
|
1047
1123
|
loadAgentPolicy,
|
|
1048
1124
|
saveAgentPolicy,
|
|
1049
1125
|
assessAgentPolicy,
|
|
1050
|
-
|
|
1126
|
+
requireApprovedModel,
|
|
1127
|
+
planSessionModels,
|
|
1051
1128
|
settledRetiredRails,
|
|
1052
1129
|
openAgentChannels,
|
|
1053
1130
|
selectChannel,
|