@lucid-agents/taskmarket 1.17.0 → 1.17.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/agent-compatibility.json +1 -1
- package/dist/budget-view-XKZC3GY5.js +36 -0
- package/dist/channels-Q7XMSUSO.js +29 -0
- package/dist/{chunk-3XPF2O6L.js → chunk-3S63AB3C.js} +3285 -4277
- package/dist/chunk-73RXOYME.js +185 -0
- package/dist/chunk-EIHZYRIU.js +1099 -0
- package/dist/chunk-F5SS4NUF.js +1062 -0
- package/dist/chunk-JAZ5BWPB.js +181 -0
- package/dist/chunk-MI2GPMCZ.js +154 -0
- package/dist/{compatibility-F3RC2GX7.js → chunk-OKZ2NRYZ.js} +5 -5
- package/dist/{chunk-O27J7ZPH.js → chunk-YM4S6IE4.js} +2 -1
- package/dist/compatibility-KUYGZSUC.js +19 -0
- package/dist/doctor-BY2PBHS7.js +216 -0
- package/dist/{errors-F3EGNA4F.js → errors-2L357HGA.js} +1 -1
- package/dist/index.js +264 -128
- package/dist/{launch-EZJGJJ5Y.js → launch-DE65XO5C.js} +1414 -700
- package/dist/pi-extensions/xgate.cjs +310 -5
- package/dist/reconcile-VEMPNIAO.js +83 -0
- package/dist/status-3Q55PBFC.js +18 -0
- package/package.json +3 -3
|
@@ -2006,11 +2006,293 @@ init_cjs_shims();
|
|
|
2006
2006
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
2007
2007
|
var import_pi_coding_agent = require("@earendil-works/pi-coding-agent");
|
|
2008
2008
|
|
|
2009
|
+
// src/lib/agent/budget-ui.ts
|
|
2010
|
+
init_cjs_shims();
|
|
2011
|
+
|
|
2012
|
+
// src/lib/agent/budget-view.ts
|
|
2013
|
+
init_cjs_shims();
|
|
2014
|
+
var USDC_DECIMALS = 6;
|
|
2015
|
+
function formatUsdc(atomic2) {
|
|
2016
|
+
let value;
|
|
2017
|
+
try {
|
|
2018
|
+
value = typeof atomic2 === "bigint" ? atomic2 : BigInt(atomic2);
|
|
2019
|
+
} catch {
|
|
2020
|
+
return "$?";
|
|
2021
|
+
}
|
|
2022
|
+
const negative = value < 0n;
|
|
2023
|
+
const abs = negative ? -value : value;
|
|
2024
|
+
const scale = 10n ** BigInt(USDC_DECIMALS);
|
|
2025
|
+
const fraction = (abs % scale).toString().padStart(USDC_DECIMALS, "0").replace(/0+$/, "");
|
|
2026
|
+
return `${negative ? "-" : ""}$${abs / scale}.${fraction.padEnd(2, "0")}`;
|
|
2027
|
+
}
|
|
2028
|
+
function formatCredits(value) {
|
|
2029
|
+
const number = Number(value);
|
|
2030
|
+
if (!Number.isFinite(number)) return value;
|
|
2031
|
+
return number >= 100 ? number.toFixed(0) : number.toFixed(1);
|
|
2032
|
+
}
|
|
2033
|
+
function remainingBudget(view) {
|
|
2034
|
+
const channels = view.channels.filter((channel) => channel.state === "open" && !channel.retired).reduce(
|
|
2035
|
+
(sum, channel) => sum + BigInt(channel.amounts.refundable) + BigInt(channel.amounts.lifetimeRoom),
|
|
2036
|
+
0n
|
|
2037
|
+
);
|
|
2038
|
+
if (!view.budget) return channels;
|
|
2039
|
+
const left = BigInt(view.budget.cap) - BigInt(view.budget.used);
|
|
2040
|
+
const budget = left > 0n ? left : 0n;
|
|
2041
|
+
return channels < budget ? channels : budget;
|
|
2042
|
+
}
|
|
2043
|
+
function blockingPurchases(view) {
|
|
2044
|
+
const ids = new Set(view.unresolved);
|
|
2045
|
+
for (const channel of view.channels) {
|
|
2046
|
+
if (channel.pendingOperation?.kind === "payment") ids.add(channel.pendingOperation.id);
|
|
2047
|
+
}
|
|
2048
|
+
return [...ids];
|
|
2049
|
+
}
|
|
2050
|
+
function statusBarText(view) {
|
|
2051
|
+
const parts = [];
|
|
2052
|
+
parts.push(view.lastTurn ? `turn ${formatUsdc(view.lastTurn.chargedAtomic)}` : "turn -");
|
|
2053
|
+
const active = view.channels.filter((channel) => !channel.retired);
|
|
2054
|
+
if (active.length > 0) {
|
|
2055
|
+
const open = active.some((channel) => channel.state === "open");
|
|
2056
|
+
parts.push(
|
|
2057
|
+
open ? `budget ${formatUsdc(remainingBudget(view))} left` : `channel ${active[0].state.replace("_", " ")}`
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
const compute = view.compute;
|
|
2061
|
+
if (compute?.configured && compute.availableCredits !== null) {
|
|
2062
|
+
const burn = compute.burnCreditsPerHour !== null ? ` at ${formatCredits(compute.burnCreditsPerHour)}/h` : "";
|
|
2063
|
+
parts.push(
|
|
2064
|
+
`compute ${formatCredits(compute.availableCredits)} cr${burn}${compute.low ? " LOW: /credits" : ""}`
|
|
2065
|
+
);
|
|
2066
|
+
} else if (compute && !compute.configured) {
|
|
2067
|
+
parts.push("compute: not set up");
|
|
2068
|
+
}
|
|
2069
|
+
const blocking = blockingPurchases(view);
|
|
2070
|
+
if (blocking.length > 0) {
|
|
2071
|
+
const id = blocking[0].length > 12 ? `${blocking[0].slice(0, 12)}...` : blocking[0];
|
|
2072
|
+
parts.push(`purchase ${id} unresolved: new turns blocked, see /budget`);
|
|
2073
|
+
}
|
|
2074
|
+
return parts.join(" | ");
|
|
2075
|
+
}
|
|
2076
|
+
function channelLines(channel) {
|
|
2077
|
+
const a = channel.amounts;
|
|
2078
|
+
return [
|
|
2079
|
+
`Channel ${channel.rail}${channel.retired ? " (retired)" : ""} ${channel.channelId} (${channel.network}, ${channel.route}): ${channel.state.replace("_", " ")}`,
|
|
2080
|
+
` Locked ${formatUsdc(a.locked)} (deposited, not spent); charged ${formatUsdc(a.charged)}; pending vouchers ${formatUsdc(a.pending)}; refunded ${formatUsdc(a.refunded)}`,
|
|
2081
|
+
` Refundable ${formatUsdc(a.refundable)}: what further turns can use and a close returns`,
|
|
2082
|
+
` Budget ${formatUsdc(a.budget)}; ${formatUsdc(a.lifetimeRoom)} may still be deposited; next deposit ${formatUsdc(a.depositTarget)}${Number(a.depositing) > 0 ? `; ${formatUsdc(a.depositing)} being deposited` : ""}`,
|
|
2083
|
+
...channel.pendingOperation ? [
|
|
2084
|
+
channel.pendingOperation.kind === "payment" ? ` Purchase ${channel.pendingOperation.id} is unresolved and blocks new turns: taskmarket agent reconcile ${channel.pendingOperation.id}` : ` A refund or withdrawal (${channel.pendingOperation.id}) is unresolved: taskmarket agent close checks it again`
|
|
2085
|
+
] : [],
|
|
2086
|
+
...channel.blockingDivergences.length > 0 ? [
|
|
2087
|
+
` Blocked: the journal, the router and the chain disagree (${channel.blockingDivergences.join(", ")})`
|
|
2088
|
+
] : []
|
|
2089
|
+
];
|
|
2090
|
+
}
|
|
2091
|
+
function budgetLines(view) {
|
|
2092
|
+
const lines2 = [
|
|
2093
|
+
`This session: ${view.session.turns} paid turn${view.session.turns === 1 ? "" : "s"}, ${formatUsdc(view.session.chargedAtomic)}${view.lastTurn ? `; last turn ${formatUsdc(view.lastTurn.chargedAtomic)}` : ""}`
|
|
2094
|
+
];
|
|
2095
|
+
for (const channel of view.channels) lines2.push(...channelLines(channel));
|
|
2096
|
+
for (const allocation of view.allocations) {
|
|
2097
|
+
lines2.push(
|
|
2098
|
+
`Allocation ${allocation.allocationId} (${allocation.exposure})${allocation.closed ? ", closed" : ""}: used ${formatUsdc(allocation.used.total)} of ${formatUsdc(allocation.caps.amount)}${allocation.caps.perDay ? `; today ${formatUsdc(allocation.used.today)} of ${formatUsdc(allocation.caps.perDay)}` : ""}`
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
for (const id of blockingPurchases(view)) {
|
|
2102
|
+
lines2.push(uncertainPurchaseMessage(id));
|
|
2103
|
+
}
|
|
2104
|
+
const compute = view.compute;
|
|
2105
|
+
if (compute) lines2.push(...computeLines(compute));
|
|
2106
|
+
return lines2;
|
|
2107
|
+
}
|
|
2108
|
+
function computeLines(compute) {
|
|
2109
|
+
if (!compute.configured) {
|
|
2110
|
+
return ["Compute credit: not set up; run taskmarket agent credits setup"];
|
|
2111
|
+
}
|
|
2112
|
+
if (compute.error !== void 0 || compute.availableCredits === null) {
|
|
2113
|
+
return [`Compute credit: unavailable (${compute.error ?? "not read"})`];
|
|
2114
|
+
}
|
|
2115
|
+
return [
|
|
2116
|
+
`Compute credit: ${formatCredits(compute.availableCredits)} available${compute.burnCreditsPerHour !== null ? `, burning ${formatCredits(compute.burnCreditsPerHour)} an hour while the sandbox runs` : ""}${compute.low ? ". Low: buy more with /credits" : ""}`,
|
|
2117
|
+
...compute.pendingPurchases > 0 ? [
|
|
2118
|
+
` ${compute.pendingPurchases} purchase(s) not settled yet: taskmarket agent credits reconcile`
|
|
2119
|
+
] : [],
|
|
2120
|
+
...compute.autoTopUp ? [
|
|
2121
|
+
` Auto-top-up: $${compute.autoTopUp.amountUsd} below ${compute.autoTopUp.thresholdCredits} credits, at most ${compute.autoTopUp.maxChunksPerDay} a day`
|
|
2122
|
+
] : []
|
|
2123
|
+
];
|
|
2124
|
+
}
|
|
2125
|
+
var LOST_RESPONSE_TEXT = "It was charged, and the response cannot be recovered or regenerated: xgate delivers it live only and keeps no copy. Asking again is a new paid turn.";
|
|
2126
|
+
function uncertainPurchaseMessage(invocationId2) {
|
|
2127
|
+
return `The outcome of purchase ${invocationId2} is unknown. It is never sent again, and new turns on its channel are blocked until it is reconciled: taskmarket agent reconcile ${invocationId2}. If it was charged, its response cannot be recovered or regenerated.`;
|
|
2128
|
+
}
|
|
2129
|
+
var CHARGED_UNDELIVERED = /* @__PURE__ */ new Set([
|
|
2130
|
+
"result_too_large",
|
|
2131
|
+
"response_rejected",
|
|
2132
|
+
"invalid_response",
|
|
2133
|
+
"response_hook_failed"
|
|
2134
|
+
]);
|
|
2135
|
+
function purchaseFailureNotice(failure) {
|
|
2136
|
+
if (failure.pending) {
|
|
2137
|
+
return {
|
|
2138
|
+
level: "warning",
|
|
2139
|
+
text: uncertainPurchaseMessage(failure.invocationId || "(unknown)")
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
const charged = failure.payment?.chargedAtomic;
|
|
2143
|
+
if (charged !== void 0 || CHARGED_UNDELIVERED.has(failure.code)) {
|
|
2144
|
+
return {
|
|
2145
|
+
level: "error",
|
|
2146
|
+
text: `This turn was paid${charged !== void 0 ? ` (${formatUsdc(charged)})` : ""} but its response was lost (${failure.code}). ${LOST_RESPONSE_TEXT}`
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
return void 0;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
// src/lib/agent/budget-ui.ts
|
|
2153
|
+
var BUDGET_STATUS_KEY = "xgate-taskmarket";
|
|
2154
|
+
function isBudgetView(value) {
|
|
2155
|
+
const view = value;
|
|
2156
|
+
return !!view && view.version === 1 && Array.isArray(view.channels) && Array.isArray(view.unresolved) && Array.isArray(view.allocations) && typeof view.session === "object";
|
|
2157
|
+
}
|
|
2158
|
+
function errorCode(error) {
|
|
2159
|
+
const code = error?.code;
|
|
2160
|
+
return typeof code === "string" ? code : "controller_unavailable";
|
|
2161
|
+
}
|
|
2162
|
+
function withPurchaseNotices(transport, notify) {
|
|
2163
|
+
const wrapped = {
|
|
2164
|
+
models: (signal) => transport.models(signal),
|
|
2165
|
+
async purchase(invocation, signal, onResponse) {
|
|
2166
|
+
try {
|
|
2167
|
+
return await transport.purchase(invocation, signal, onResponse);
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
const failure = error;
|
|
2170
|
+
if (typeof failure?.pending === "boolean" && typeof failure.code === "string") {
|
|
2171
|
+
const notice = purchaseFailureNotice({
|
|
2172
|
+
code: failure.code,
|
|
2173
|
+
invocationId: typeof failure.invocationId === "string" && failure.invocationId ? failure.invocationId : invocation.id,
|
|
2174
|
+
pending: failure.pending,
|
|
2175
|
+
...failure.payment ? { payment: failure.payment } : {}
|
|
2176
|
+
});
|
|
2177
|
+
if (notice) notify(notice.text, notice.level);
|
|
2178
|
+
}
|
|
2179
|
+
throw error;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
if (transport.status) wrapped.status = (signal) => transport.status(signal);
|
|
2184
|
+
if (transport.receipts) wrapped.receipts = (signal) => transport.receipts(signal);
|
|
2185
|
+
if (transport.reconcile) {
|
|
2186
|
+
wrapped.reconcile = (id, signal) => transport.reconcile(id, signal);
|
|
2187
|
+
}
|
|
2188
|
+
return wrapped;
|
|
2189
|
+
}
|
|
2190
|
+
function isOffer(value) {
|
|
2191
|
+
const offer = value;
|
|
2192
|
+
return !!offer && typeof offer.usdPerCredit === "string" && Array.isArray(offer.denominations) && offer.denominations.every(
|
|
2193
|
+
(entry) => Number.isSafeInteger(entry?.amountUsd) && typeof entry?.credits === "string"
|
|
2194
|
+
);
|
|
2195
|
+
}
|
|
2196
|
+
function registerBudgetUi(pi2, channel) {
|
|
2197
|
+
let context;
|
|
2198
|
+
let inFlight;
|
|
2199
|
+
const read = async () => {
|
|
2200
|
+
if (!channel) throw Object.assign(new Error("unavailable"), { code: "controller_unavailable" });
|
|
2201
|
+
const value = await channel.budget();
|
|
2202
|
+
if (!isBudgetView(value)) throw Object.assign(new Error("invalid"), { code: "invalid_budget" });
|
|
2203
|
+
return value;
|
|
2204
|
+
};
|
|
2205
|
+
const refresh = (ctx) => {
|
|
2206
|
+
if (ctx) context = ctx;
|
|
2207
|
+
inFlight ??= read().then((view) => {
|
|
2208
|
+
context?.ui.setStatus(BUDGET_STATUS_KEY, statusBarText(view));
|
|
2209
|
+
return view;
|
|
2210
|
+
}).catch(() => {
|
|
2211
|
+
context?.ui.setStatus(BUDGET_STATUS_KEY, "budget unavailable");
|
|
2212
|
+
return void 0;
|
|
2213
|
+
}).finally(() => {
|
|
2214
|
+
inFlight = void 0;
|
|
2215
|
+
});
|
|
2216
|
+
return inFlight;
|
|
2217
|
+
};
|
|
2218
|
+
pi2.on("session_start", async (_event, ctx) => {
|
|
2219
|
+
await refresh(ctx);
|
|
2220
|
+
});
|
|
2221
|
+
pi2.on("turn_end", async (_event, ctx) => {
|
|
2222
|
+
await refresh(ctx);
|
|
2223
|
+
});
|
|
2224
|
+
pi2.registerCommand("budget", {
|
|
2225
|
+
description: "Show what this session cost, the channel (locked, charged, pending vouchers, refundable), allocations, unresolved purchases and compute credit",
|
|
2226
|
+
handler: async (_args, ctx) => {
|
|
2227
|
+
context = ctx;
|
|
2228
|
+
try {
|
|
2229
|
+
const view = await read();
|
|
2230
|
+
ctx.ui.setStatus(BUDGET_STATUS_KEY, statusBarText(view));
|
|
2231
|
+
ctx.ui.notify(budgetLines(view).join("\n"), "info");
|
|
2232
|
+
} catch (error) {
|
|
2233
|
+
ctx.ui.notify(`The budget is unavailable (${errorCode(error)})`, "error");
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
});
|
|
2237
|
+
pi2.registerCommand("credits", {
|
|
2238
|
+
description: "Show compute credit and buy a chunk from your compute allocation (asks first; /credits <usd> picks the size)",
|
|
2239
|
+
handler: async (args, ctx) => {
|
|
2240
|
+
context = ctx;
|
|
2241
|
+
if (!channel) {
|
|
2242
|
+
ctx.ui.notify("Compute credit is unavailable (controller_unavailable)", "error");
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
try {
|
|
2246
|
+
const view = await read();
|
|
2247
|
+
if (view.compute) ctx.ui.notify(computeLines(view.compute).join("\n"), "info");
|
|
2248
|
+
if (view.compute && !view.compute.configured) return;
|
|
2249
|
+
const offer = await channel.creditOffer();
|
|
2250
|
+
if (!isOffer(offer) || offer.denominations.length === 0) {
|
|
2251
|
+
ctx.ui.notify("Compute credit is not on sale right now", "error");
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
const requested = args.trim().replace(/^\$/, "");
|
|
2255
|
+
const size = requested ? offer.denominations.find((entry) => String(entry.amountUsd) === requested) : offer.denominations.find((entry) => entry.default) ?? offer.denominations[0];
|
|
2256
|
+
if (!size) {
|
|
2257
|
+
ctx.ui.notify(
|
|
2258
|
+
`Compute credit is sold in $${offer.denominations.map((entry) => entry.amountUsd).join(", $")} chunks`,
|
|
2259
|
+
"error"
|
|
2260
|
+
);
|
|
2261
|
+
return;
|
|
2262
|
+
}
|
|
2263
|
+
if (!ctx.hasUI) {
|
|
2264
|
+
ctx.ui.notify("Buying compute credit needs the interactive session", "error");
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
const approved = await ctx.ui.confirm(
|
|
2268
|
+
"Buy compute credit",
|
|
2269
|
+
`Buy $${size.amountUsd} of compute credit (${size.credits} credits at $${offer.usdPerCredit} each) in USDC from your compute allocation? Its daily and monthly caps apply.`
|
|
2270
|
+
);
|
|
2271
|
+
if (!approved) return;
|
|
2272
|
+
const bought = await channel.buyCredits(size.amountUsd);
|
|
2273
|
+
ctx.ui.notify(
|
|
2274
|
+
`Bought ${bought?.purchase?.credits ?? size.credits} credits; ${bought?.balance?.availableCredits ?? "?"} available`,
|
|
2275
|
+
"info"
|
|
2276
|
+
);
|
|
2277
|
+
await refresh(ctx);
|
|
2278
|
+
} catch (error) {
|
|
2279
|
+
const code = errorCode(error);
|
|
2280
|
+
ctx.ui.notify(
|
|
2281
|
+
code === "purchase_pending" ? "Nothing was bought: an earlier compute purchase may have been paid and is not settled yet. Run taskmarket agent credits reconcile; it never pays again." : code === "controller_unavailable" || code === "aborted" ? "The controller did not answer, so the purchase may still complete. It is never repeated automatically; check with /budget or taskmarket agent credits status." : `Nothing was bought (${code})`,
|
|
2282
|
+
"error"
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
2287
|
+
return { refresh };
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2009
2290
|
// src/lib/agent/inference-channel.ts
|
|
2010
2291
|
init_cjs_shims();
|
|
2011
2292
|
var import_net = __toESM(require("net"), 1);
|
|
2012
2293
|
var MAX_MESSAGE_BYTES = 48 * 1024 * 1024;
|
|
2013
2294
|
var VIEW_TIMEOUT_MS = 6e4;
|
|
2295
|
+
var BUY_TIMEOUT_MS = 5 * 6e4;
|
|
2014
2296
|
var CODE = /^[a-z0-9_]{1,64}$/;
|
|
2015
2297
|
function lines(stream, onLine, onOverflow, maxBytes) {
|
|
2016
2298
|
let chunks = [];
|
|
@@ -2147,8 +2429,8 @@ var ChannelXgateTransport = class _ChannelXgateTransport {
|
|
|
2147
2429
|
this.requests.write(line);
|
|
2148
2430
|
return { id, reply };
|
|
2149
2431
|
}
|
|
2150
|
-
async view(body, signal) {
|
|
2151
|
-
const sent = this.send(body, { timeoutMs
|
|
2432
|
+
async view(body, signal, timeoutMs = VIEW_TIMEOUT_MS) {
|
|
2433
|
+
const sent = this.send(body, { timeoutMs, signal });
|
|
2152
2434
|
if (sent === "too_large") throw new XgateError("message_too_large");
|
|
2153
2435
|
if (!sent) throw new XgateError("controller_unavailable");
|
|
2154
2436
|
let reply;
|
|
@@ -2211,6 +2493,25 @@ var ChannelXgateTransport = class _ChannelXgateTransport {
|
|
|
2211
2493
|
async reconcile(invocationId2, signal) {
|
|
2212
2494
|
return (await this.view({ op: "reconcile", invocationId: invocationId2 }, signal)).value;
|
|
2213
2495
|
}
|
|
2496
|
+
/** The controller's budget view (`BudgetView`) for the status bar and `/budget`. */
|
|
2497
|
+
async budget(signal) {
|
|
2498
|
+
return (await this.view({ op: "budget" }, signal)).value;
|
|
2499
|
+
}
|
|
2500
|
+
/** The compute credit sizes and price the controller can buy, for `/credits`. */
|
|
2501
|
+
async creditOffer(signal) {
|
|
2502
|
+
return (await this.view({ op: "credit_offer" }, signal)).value;
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* `/credits`: ask the controller to buy one chunk of compute credit. The controller's purchase
|
|
2506
|
+
* is journaled and capped; a reply lost here never makes it buy again.
|
|
2507
|
+
*/
|
|
2508
|
+
async buyCredits(amountUsd, signal) {
|
|
2509
|
+
return (await this.view(
|
|
2510
|
+
{ op: "buy_credits", ...amountUsd !== void 0 ? { amountUsd } : {} },
|
|
2511
|
+
signal,
|
|
2512
|
+
BUY_TIMEOUT_MS
|
|
2513
|
+
)).value;
|
|
2514
|
+
}
|
|
2214
2515
|
};
|
|
2215
2516
|
function unavailableTransport() {
|
|
2216
2517
|
return {
|
|
@@ -2236,20 +2537,24 @@ function openTransport() {
|
|
|
2236
2537
|
}
|
|
2237
2538
|
async function taskmarketXgate(pi2) {
|
|
2238
2539
|
const transport = openTransport();
|
|
2239
|
-
|
|
2540
|
+
const channel = transport instanceof ChannelXgateTransport ? transport : void 0;
|
|
2240
2541
|
let context;
|
|
2241
2542
|
let controllerGone = false;
|
|
2242
2543
|
pi2.on("session_start", async (_event, ctx) => {
|
|
2243
2544
|
context = ctx;
|
|
2244
2545
|
if (controllerGone) ctx.shutdown();
|
|
2245
2546
|
});
|
|
2246
|
-
if (
|
|
2247
|
-
|
|
2547
|
+
if (channel) {
|
|
2548
|
+
channel.onClose((reason) => {
|
|
2248
2549
|
log(`The taskmarket controller is gone (${reason}); ending the session`);
|
|
2249
2550
|
controllerGone = true;
|
|
2250
2551
|
context?.shutdown();
|
|
2251
2552
|
});
|
|
2252
2553
|
}
|
|
2554
|
+
await createXgateExtension({
|
|
2555
|
+
transport: withPurchaseNotices(transport, (text2, level) => context?.ui.notify(text2, level))
|
|
2556
|
+
})(pi2);
|
|
2557
|
+
registerBudgetUi(pi2, channel);
|
|
2253
2558
|
pi2.on("model_select", async (event, ctx) => {
|
|
2254
2559
|
if (event.model.provider === "xgate") return;
|
|
2255
2560
|
const fallback = event.previousModel?.provider === "xgate" ? event.previousModel : ctx.modelRegistry.getAvailable().find((model) => model.provider === "xgate");
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
openAgentChannels
|
|
4
|
+
} from "./chunk-F5SS4NUF.js";
|
|
5
|
+
import {
|
|
6
|
+
X402_INVOCATION_ID_PATTERN
|
|
7
|
+
} from "./chunk-3S63AB3C.js";
|
|
8
|
+
import {
|
|
9
|
+
AgentLaunchError
|
|
10
|
+
} from "./chunk-YM4S6IE4.js";
|
|
11
|
+
import {
|
|
12
|
+
AGENT_VIEW_VERSION,
|
|
13
|
+
LOST_RESPONSE_TEXT,
|
|
14
|
+
formatUsdc
|
|
15
|
+
} from "./chunk-73RXOYME.js";
|
|
16
|
+
import "./chunk-S7SJMJ2I.js";
|
|
17
|
+
|
|
18
|
+
// src/lib/agent/reconcile.ts
|
|
19
|
+
function outcomeOf(status) {
|
|
20
|
+
if (status.state === "in_progress") return "in_progress";
|
|
21
|
+
if (status.charge === "manually_resolved") return "resolved_manually";
|
|
22
|
+
if (status.uncertain) return "uncertain";
|
|
23
|
+
if (status.charge === "settled") return "charged";
|
|
24
|
+
if (status.charge === "none" || status.charge === "released") return "not_charged";
|
|
25
|
+
return "uncertain";
|
|
26
|
+
}
|
|
27
|
+
function messageFor(outcome, status) {
|
|
28
|
+
const charged = status.payment?.settledAmount;
|
|
29
|
+
switch (outcome) {
|
|
30
|
+
case "charged":
|
|
31
|
+
return `Charged ${charged !== void 0 ? formatUsdc(charged) : "an amount the router committed"}. If its response did not reach you: ${LOST_RESPONSE_TEXT}`;
|
|
32
|
+
case "not_charged":
|
|
33
|
+
return "Nothing was charged. The request is not sent again; ask again in the agent if you still want an answer.";
|
|
34
|
+
case "in_progress":
|
|
35
|
+
return "It is still being sent by another process; reconcile it again once that finishes.";
|
|
36
|
+
case "resolved_manually":
|
|
37
|
+
return "It was resolved by hand; its recorded amount stands.";
|
|
38
|
+
default:
|
|
39
|
+
return "Its outcome is still unknown. It keeps its maximum counted and blocks new turns on its channel; reconcile it again later. It is never sent again.";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function railFor(rails, invocationId) {
|
|
43
|
+
for (const rail of rails) {
|
|
44
|
+
try {
|
|
45
|
+
await rail.payer.status(invocationId);
|
|
46
|
+
return rail;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (error.code !== "invocation_not_found") throw error;
|
|
49
|
+
}
|
|
50
|
+
const lease = (await rail.payer.channelStatus()).pendingOperation;
|
|
51
|
+
if (lease?.kind === "payment" && lease.id === invocationId) return rail;
|
|
52
|
+
}
|
|
53
|
+
throw new AgentLaunchError(
|
|
54
|
+
"invocation_not_found",
|
|
55
|
+
`No agent purchase ${invocationId} is recorded on this device`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
async function reconcileInvocation(invocationId, deps = {}) {
|
|
59
|
+
if (!X402_INVOCATION_ID_PATTERN.test(invocationId)) {
|
|
60
|
+
throw new AgentLaunchError("invalid_invocation_id", `Not an invocation ID: ${invocationId}`);
|
|
61
|
+
}
|
|
62
|
+
const opened = await (deps.openChannels ?? openAgentChannels)();
|
|
63
|
+
const rail = await railFor([...opened.rails, ...opened.retired ?? []], invocationId);
|
|
64
|
+
const status = await rail.payer.reconcile(invocationId);
|
|
65
|
+
const lease = (await rail.payer.channelStatus()).pendingOperation;
|
|
66
|
+
const outcome = outcomeOf(status);
|
|
67
|
+
return {
|
|
68
|
+
version: AGENT_VIEW_VERSION,
|
|
69
|
+
kind: "taskmarket.agent.reconcile",
|
|
70
|
+
invocationId,
|
|
71
|
+
rail: rail.rail.id,
|
|
72
|
+
outcome,
|
|
73
|
+
state: status.state,
|
|
74
|
+
charge: status.charge,
|
|
75
|
+
authorizedAtomic: status.payment?.authorizedAmount ?? null,
|
|
76
|
+
chargedAtomic: status.payment?.settledAmount ?? null,
|
|
77
|
+
blocksChannel: lease?.kind === "payment" && lease.id === invocationId,
|
|
78
|
+
message: messageFor(outcome, status)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
reconcileInvocation
|
|
83
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
collectAgentStatus,
|
|
4
|
+
listSandboxes,
|
|
5
|
+
statusLines
|
|
6
|
+
} from "./chunk-JAZ5BWPB.js";
|
|
7
|
+
import "./chunk-MI2GPMCZ.js";
|
|
8
|
+
import "./chunk-EIHZYRIU.js";
|
|
9
|
+
import "./chunk-F5SS4NUF.js";
|
|
10
|
+
import "./chunk-3S63AB3C.js";
|
|
11
|
+
import "./chunk-YM4S6IE4.js";
|
|
12
|
+
import "./chunk-73RXOYME.js";
|
|
13
|
+
import "./chunk-S7SJMJ2I.js";
|
|
14
|
+
export {
|
|
15
|
+
collectAgentStatus,
|
|
16
|
+
listSandboxes,
|
|
17
|
+
statusLines
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucid-agents/taskmarket",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"taskmarket": "./dist/index.js"
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"tsx": "^4.7.0",
|
|
29
29
|
"typescript": "^5.3.3",
|
|
30
30
|
"vitest": "^2.0.0",
|
|
31
|
+
"@taskmarket/agent-host": "0.0.1",
|
|
31
32
|
"@taskmarket/contracts": "0.0.0",
|
|
32
|
-
"@taskmarket/prettier-config": "0.0.0",
|
|
33
33
|
"@taskmarket/eslint-config": "0.0.0",
|
|
34
|
+
"@taskmarket/prettier-config": "0.0.0",
|
|
34
35
|
"@taskmarket/remote-tools": "0.0.1",
|
|
35
|
-
"@taskmarket/agent-host": "0.0.1",
|
|
36
36
|
"@taskmarket/shared": "2.0.0",
|
|
37
37
|
"@taskmarket/x402-payer": "0.1.0"
|
|
38
38
|
},
|