opencrowd 0.6.0 → 0.6.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/bundle/opencrowd.js +202 -43
- package/package.json +1 -1
package/bundle/opencrowd.js
CHANGED
|
@@ -16114,6 +16114,41 @@ var init_spending = __esm({
|
|
|
16114
16114
|
}
|
|
16115
16115
|
});
|
|
16116
16116
|
|
|
16117
|
+
// ../../packages/protocol/dist/financial-state.js
|
|
16118
|
+
var usdc, FinancialStateSchema;
|
|
16119
|
+
var init_financial_state = __esm({
|
|
16120
|
+
"../../packages/protocol/dist/financial-state.js"() {
|
|
16121
|
+
"use strict";
|
|
16122
|
+
init_zod();
|
|
16123
|
+
usdc = external_exports.string().regex(/^\d+\.\d{6}$/);
|
|
16124
|
+
FinancialStateSchema = external_exports.discriminatedUnion("status", [
|
|
16125
|
+
external_exports.object({
|
|
16126
|
+
status: external_exports.literal("available"),
|
|
16127
|
+
run_id: external_exports.string().uuid(),
|
|
16128
|
+
observed_at: external_exports.string().datetime(),
|
|
16129
|
+
currency: external_exports.literal("USDC"),
|
|
16130
|
+
wallet: external_exports.object({
|
|
16131
|
+
balance_status: external_exports.enum(["available", "unavailable"]),
|
|
16132
|
+
balance_usdc: usdc.nullable(),
|
|
16133
|
+
reserved_usdc: usdc,
|
|
16134
|
+
available_usdc: usdc.nullable()
|
|
16135
|
+
}),
|
|
16136
|
+
current_run: external_exports.object({
|
|
16137
|
+
inference_spent_usdc: usdc,
|
|
16138
|
+
service_spent_usdc: usdc,
|
|
16139
|
+
total_spent_usdc: usdc,
|
|
16140
|
+
reserved_usdc: usdc,
|
|
16141
|
+
budget_limit_usdc: usdc,
|
|
16142
|
+
remaining_budget_usdc: usdc,
|
|
16143
|
+
per_call_approval_threshold_usdc: usdc,
|
|
16144
|
+
approval_mode: external_exports.enum(["auto", "ask", "off"])
|
|
16145
|
+
})
|
|
16146
|
+
}),
|
|
16147
|
+
external_exports.object({ status: external_exports.literal("unavailable"), reason: external_exports.string() })
|
|
16148
|
+
]);
|
|
16149
|
+
}
|
|
16150
|
+
});
|
|
16151
|
+
|
|
16117
16152
|
// ../../packages/protocol/dist/index.js
|
|
16118
16153
|
var init_dist = __esm({
|
|
16119
16154
|
"../../packages/protocol/dist/index.js"() {
|
|
@@ -16129,6 +16164,7 @@ var init_dist = __esm({
|
|
|
16129
16164
|
init_codec();
|
|
16130
16165
|
init_context();
|
|
16131
16166
|
init_spending();
|
|
16167
|
+
init_financial_state();
|
|
16132
16168
|
}
|
|
16133
16169
|
});
|
|
16134
16170
|
|
|
@@ -34182,6 +34218,7 @@ var init_gateway = __esm({
|
|
|
34182
34218
|
if (outcome === "unknown") {
|
|
34183
34219
|
const missingReceipt = result.ambiguityReason === "missing_receipt";
|
|
34184
34220
|
const unsupportedReceipt = result.ambiguityReason === "unsupported_receipt";
|
|
34221
|
+
const timedOut = result.ambiguityReason === "timeout";
|
|
34185
34222
|
return {
|
|
34186
34223
|
ok: false,
|
|
34187
34224
|
error: missingReceipt ? [
|
|
@@ -34190,6 +34227,9 @@ var init_gateway = __esm({
|
|
|
34190
34227
|
].join(" ") : unsupportedReceipt ? [
|
|
34191
34228
|
`the call to ${endpoint} returned a settlement receipt on an unsupported payment rail (recorded as purchase ${purchaseId}).`,
|
|
34192
34229
|
"It was NOT retried and must not be retried automatically; reconcile the payment manually before continuing."
|
|
34230
|
+
].join(" ") : timedOut ? [
|
|
34231
|
+
`the call to ${endpoint} did not answer within the wait limit and its payment state is unknown (recorded as purchase ${purchaseId}).`,
|
|
34232
|
+
"The stored purchase was checked once without a final result. It was NOT retried and must not be retried automatically; the owner can reconcile it from the payment ledger."
|
|
34193
34233
|
].join(" ") : [
|
|
34194
34234
|
`the call to ${endpoint} failed in transport and its payment state is unknown (recorded as purchase ${purchaseId}).`,
|
|
34195
34235
|
"It was NOT retried and must not be retried automatically; verify the service state before calling again."
|
|
@@ -80049,12 +80089,15 @@ function economyTools(economy, options = {}) {
|
|
|
80049
80089
|
execute: async (name, args, operationId) => {
|
|
80050
80090
|
if (options.resuming && name === "call_paid_service")
|
|
80051
80091
|
await economy.execute("inspect_paid_service", { url: args.url, method: args.method, sample_body: args.body });
|
|
80052
|
-
|
|
80092
|
+
const result = await economy.execute(name, args, operationId);
|
|
80093
|
+
if (name === "request_service" && result.ok)
|
|
80094
|
+
reflected = true;
|
|
80095
|
+
return result;
|
|
80053
80096
|
},
|
|
80054
|
-
completionGate: async () => {
|
|
80097
|
+
completionGate: async (alreadyNudged = false) => {
|
|
80055
80098
|
const pending = await economy.hasPendingRequiredReviews();
|
|
80056
80099
|
const review = pending ? "A paid purchase still needs its required review; submit it with review_paid_service." : "";
|
|
80057
|
-
if (reflected || !supportsReflection)
|
|
80100
|
+
if (reflected || alreadyNudged || !supportsReflection)
|
|
80058
80101
|
return review || void 0;
|
|
80059
80102
|
reflected = true;
|
|
80060
80103
|
const status = await economy.execute("crowdcode_status", {});
|
|
@@ -80174,7 +80217,7 @@ import { isAbsolute as isAbsolute3 } from "node:path";
|
|
|
80174
80217
|
function createHostedProvider(options) {
|
|
80175
80218
|
if (!isAbsolute3(options.socketPath))
|
|
80176
80219
|
throw new Error("Hosted bridge socket must be absolute");
|
|
80177
|
-
const tools = [...OPEN_CROWD_TOOLS.filter((tool) => !["spawn_subagent", "check_subagents"].includes(tool.name)), ...options.extraTools ?? []];
|
|
80220
|
+
const tools = [...OPEN_CROWD_TOOLS.filter((tool) => !["spawn_subagent", "check_subagents"].includes(tool.name)).map((tool) => tool.name === "get_budget_status" ? { ...tool, description: "Read authoritative hosted finances: live USDC wallet balance, settled inference and service spending, pending holds, and remaining authorized budget for this run." } : tool), ...options.extraTools ?? []];
|
|
80178
80221
|
return {
|
|
80179
80222
|
async complete(messages, context) {
|
|
80180
80223
|
const operationId = context?.operationId;
|
|
@@ -80254,6 +80297,16 @@ function rejectedRequest(status, text) {
|
|
|
80254
80297
|
return void 0;
|
|
80255
80298
|
}
|
|
80256
80299
|
}
|
|
80300
|
+
async function readHostedFinancialState(options) {
|
|
80301
|
+
try {
|
|
80302
|
+
const reply = await postHostedTool(options, "economy.balance", {}, { timeoutMs: 1e4 });
|
|
80303
|
+
const parsed = FinancialStateSchema.safeParse(reply.data?.financial_state);
|
|
80304
|
+
if (reply.ok && parsed.success && parsed.data.status === "available" && parsed.data.run_id === options.runId)
|
|
80305
|
+
return parsed.data;
|
|
80306
|
+
} catch {
|
|
80307
|
+
}
|
|
80308
|
+
return { status: "unavailable", reason: "Hosted financial state could not be read. Do not infer balance or spending from the local session budget." };
|
|
80309
|
+
}
|
|
80257
80310
|
async function postHostedTool(options, name, args, settings = {}) {
|
|
80258
80311
|
if (!isAbsolute3(options.socketPath))
|
|
80259
80312
|
throw new Error("Hosted bridge socket must be absolute");
|
|
@@ -80288,7 +80341,8 @@ async function postHostedTool(options, name, args, settings = {}) {
|
|
|
80288
80341
|
}
|
|
80289
80342
|
});
|
|
80290
80343
|
});
|
|
80291
|
-
|
|
80344
|
+
const timeoutMs = settings.timeoutMs ?? SERVICE_BRIDGE_TIMEOUT_MS;
|
|
80345
|
+
req.setTimeout(timeoutMs, () => req.destroy(new HostedToolTimeout(name, timeoutMs)));
|
|
80292
80346
|
req.on("error", reject);
|
|
80293
80347
|
req.end(body);
|
|
80294
80348
|
});
|
|
@@ -80322,7 +80376,7 @@ function createHostedToolExecutor(options) {
|
|
|
80322
80376
|
}
|
|
80323
80377
|
};
|
|
80324
80378
|
}
|
|
80325
|
-
var HostedRequestError, HOSTED_TOOL_REQUEST_BYTES;
|
|
80379
|
+
var HostedRequestError, HOSTED_TOOL_REQUEST_BYTES, HostedToolTimeout;
|
|
80326
80380
|
var init_hosted_provider = __esm({
|
|
80327
80381
|
"../../packages/agent-runtime/dist/hosted-provider.js"() {
|
|
80328
80382
|
"use strict";
|
|
@@ -80340,6 +80394,15 @@ var init_hosted_provider = __esm({
|
|
|
80340
80394
|
}
|
|
80341
80395
|
};
|
|
80342
80396
|
HOSTED_TOOL_REQUEST_BYTES = 1024 * 1024;
|
|
80397
|
+
HostedToolTimeout = class extends Error {
|
|
80398
|
+
timeoutMs;
|
|
80399
|
+
code = "hosted_tool_timeout";
|
|
80400
|
+
constructor(name, timeoutMs) {
|
|
80401
|
+
super(`Hosted tool ${name} timed out after ${Math.round(timeoutMs / 1e3)}s`);
|
|
80402
|
+
this.timeoutMs = timeoutMs;
|
|
80403
|
+
this.name = "HostedToolTimeout";
|
|
80404
|
+
}
|
|
80405
|
+
};
|
|
80343
80406
|
}
|
|
80344
80407
|
});
|
|
80345
80408
|
|
|
@@ -80352,7 +80415,7 @@ function createHostedEconomy(options) {
|
|
|
80352
80415
|
const directory = new ServiceDirectory(fetcher, (options.crowdcodeBase ?? DEFAULT_CROWDCODE_BASE).replace(/\/+$/, ""));
|
|
80353
80416
|
return new EconomyGateway({
|
|
80354
80417
|
session: options.session,
|
|
80355
|
-
agentcash: new HostedAgentCashAdapter(directory, socket, fetcher),
|
|
80418
|
+
agentcash: new HostedAgentCashAdapter(directory, socket, fetcher, options.reconcileDelayMs),
|
|
80356
80419
|
crowdcode: new HostedCrowdCodeAdapter(directory, socket),
|
|
80357
80420
|
approvalMode: "auto",
|
|
80358
80421
|
hostedSpending: true,
|
|
@@ -80362,7 +80425,8 @@ function createHostedEconomy(options) {
|
|
|
80362
80425
|
});
|
|
80363
80426
|
}
|
|
80364
80427
|
function hostedDynamicTools(economy) {
|
|
80365
|
-
|
|
80428
|
+
const tools = economyTools(economy, { hidden: HIDDEN_HOSTED_TOOLS });
|
|
80429
|
+
return { ...tools, definitions: tools.definitions.map((tool) => tool.name === "get_wallet_status" ? { ...tool, description: "Read this hosted agent's USDC wallet balance and authoritative current-run inference spending, service spending, payment holds and authorized budget. Read-only." } : tool) };
|
|
80366
80430
|
}
|
|
80367
80431
|
function parseListedService(raw) {
|
|
80368
80432
|
const row = objectValue4(raw);
|
|
@@ -80426,7 +80490,7 @@ function numberValue6(value) {
|
|
|
80426
80490
|
}
|
|
80427
80491
|
return void 0;
|
|
80428
80492
|
}
|
|
80429
|
-
var DEFAULT_CROWDCODE_BASE, LIST_CACHE_MS, PAY_TIMEOUT_MS, FREE_FETCH_TIMEOUT_MS, USDC_ATOMIC_PER_USD, HIDDEN_HOSTED_TOOLS, ServiceDirectory, HostedAgentCashAdapter, HostedCrowdCodeAdapter;
|
|
80493
|
+
var DEFAULT_CROWDCODE_BASE, LIST_CACHE_MS, PAY_TIMEOUT_MS, RECONCILE_DELAY_MS, NOT_SENT_CODES, FREE_FETCH_TIMEOUT_MS, USDC_ATOMIC_PER_USD, HIDDEN_HOSTED_TOOLS, ServiceDirectory, HostedAgentCashAdapter, HostedCrowdCodeAdapter;
|
|
80430
80494
|
var init_hosted_economy = __esm({
|
|
80431
80495
|
"../../packages/agent-runtime/dist/hosted-economy.js"() {
|
|
80432
80496
|
"use strict";
|
|
@@ -80434,14 +80498,17 @@ var init_hosted_economy = __esm({
|
|
|
80434
80498
|
init_worker();
|
|
80435
80499
|
init_dist2();
|
|
80436
80500
|
init_dist4();
|
|
80501
|
+
init_dist();
|
|
80437
80502
|
init_hosted_provider();
|
|
80438
80503
|
init_economy_tools();
|
|
80439
80504
|
DEFAULT_CROWDCODE_BASE = "https://crowdcode-backend.onrender.com";
|
|
80440
80505
|
LIST_CACHE_MS = 6e4;
|
|
80441
|
-
PAY_TIMEOUT_MS =
|
|
80506
|
+
PAY_TIMEOUT_MS = SERVICE_GATEWAY_TIMEOUT_MS + 3e4;
|
|
80507
|
+
RECONCILE_DELAY_MS = 2e3;
|
|
80508
|
+
NOT_SENT_CODES = /* @__PURE__ */ new Set(["ENOENT", "ECONNREFUSED", "EACCES", "ENOTDIR"]);
|
|
80442
80509
|
FREE_FETCH_TIMEOUT_MS = 3e4;
|
|
80443
80510
|
USDC_ATOMIC_PER_USD = 1e6;
|
|
80444
|
-
HIDDEN_HOSTED_TOOLS = /* @__PURE__ */ new Set(["bridge_usdc"
|
|
80511
|
+
HIDDEN_HOSTED_TOOLS = /* @__PURE__ */ new Set(["bridge_usdc"]);
|
|
80445
80512
|
ServiceDirectory = class {
|
|
80446
80513
|
fetcher;
|
|
80447
80514
|
base;
|
|
@@ -80488,12 +80555,14 @@ var init_hosted_economy = __esm({
|
|
|
80488
80555
|
directory;
|
|
80489
80556
|
socket;
|
|
80490
80557
|
fetcher;
|
|
80558
|
+
reconcileDelayMs;
|
|
80491
80559
|
/** Rails observed by inspection; `fetch` refuses anything but Base x402 regardless of the caller's rail. */
|
|
80492
80560
|
inspectedRails = /* @__PURE__ */ new Map();
|
|
80493
|
-
constructor(directory, socket, fetcher) {
|
|
80561
|
+
constructor(directory, socket, fetcher, reconcileDelayMs = RECONCILE_DELAY_MS) {
|
|
80494
80562
|
this.directory = directory;
|
|
80495
80563
|
this.socket = socket;
|
|
80496
80564
|
this.fetcher = fetcher;
|
|
80565
|
+
this.reconcileDelayMs = reconcileDelayMs;
|
|
80497
80566
|
this.discovery = new BaseServiceDiscovery(async (query, options) => {
|
|
80498
80567
|
const reply = await postHostedTool(this.socket, "economy.read", {
|
|
80499
80568
|
url: "https://agentcash.dev/api/search",
|
|
@@ -80549,24 +80618,31 @@ var init_hosted_economy = __esm({
|
|
|
80549
80618
|
const body = request2.body === void 0 ? void 0 : typeof request2.body === "string" ? request2.body : JSON.stringify(request2.body);
|
|
80550
80619
|
const hash5 = request2.operationId ? createHash4("sha256").update(`${this.socket.runId}:${request2.operationId}`).digest("hex") : void 0;
|
|
80551
80620
|
const purchaseId = hash5 ? `${hash5.slice(0, 8)}-${hash5.slice(8, 12)}-4${hash5.slice(13, 16)}-8${hash5.slice(17, 20)}-${hash5.slice(20, 32)}` : randomUUID7();
|
|
80552
|
-
|
|
80621
|
+
const payArgs = {
|
|
80622
|
+
purchase_request_id: purchaseId,
|
|
80623
|
+
service_id: serviceId,
|
|
80624
|
+
url: request2.url,
|
|
80625
|
+
method,
|
|
80626
|
+
body,
|
|
80627
|
+
max_cost_cents: Math.ceil(request2.maxAmountUsd * 100)
|
|
80628
|
+
};
|
|
80629
|
+
const timeoutMs = Math.min(request2.timeoutMs ?? PAY_TIMEOUT_MS, PAY_TIMEOUT_MS);
|
|
80630
|
+
let reply, timedOut = false;
|
|
80553
80631
|
try {
|
|
80554
|
-
reply = await postHostedTool(this.socket, "economy.pay", {
|
|
80555
|
-
purchase_request_id: purchaseId,
|
|
80556
|
-
service_id: serviceId,
|
|
80557
|
-
url: request2.url,
|
|
80558
|
-
method,
|
|
80559
|
-
body,
|
|
80560
|
-
max_cost_cents: Math.ceil(request2.maxAmountUsd * 100)
|
|
80561
|
-
}, { timeoutMs: request2.timeoutMs ?? PAY_TIMEOUT_MS });
|
|
80632
|
+
reply = await postHostedTool(this.socket, "economy.pay", payArgs, { timeoutMs });
|
|
80562
80633
|
} catch (error53) {
|
|
80563
|
-
|
|
80564
|
-
|
|
80565
|
-
|
|
80566
|
-
|
|
80567
|
-
|
|
80568
|
-
|
|
80569
|
-
|
|
80634
|
+
timedOut = error53 instanceof HostedToolTimeout;
|
|
80635
|
+
const recovered = await this.reconcile(error53, payArgs, timeoutMs);
|
|
80636
|
+
if (!recovered) {
|
|
80637
|
+
return {
|
|
80638
|
+
ok: false,
|
|
80639
|
+
ambiguous: true,
|
|
80640
|
+
ambiguityReason: timedOut ? "timeout" : "transport",
|
|
80641
|
+
data: void 0,
|
|
80642
|
+
error: timedOut ? `the paid call did not answer within ${Math.round(timeoutMs / 1e3)}s and its stored purchase has no final result yet` : `payment request to the supervisor failed in transport: ${error53.message}`
|
|
80643
|
+
};
|
|
80644
|
+
}
|
|
80645
|
+
reply = recovered;
|
|
80570
80646
|
}
|
|
80571
80647
|
if (reply.ok) {
|
|
80572
80648
|
const data = objectValue4(reply.data) ?? {};
|
|
@@ -80599,13 +80675,29 @@ var init_hosted_economy = __esm({
|
|
|
80599
80675
|
return {
|
|
80600
80676
|
ok: false,
|
|
80601
80677
|
ambiguous: true,
|
|
80602
|
-
ambiguityReason: "transport",
|
|
80678
|
+
ambiguityReason: timedOut ? "timeout" : "transport",
|
|
80603
80679
|
data: void 0,
|
|
80604
|
-
error: reply.error ?? "the supervisor could not determine whether the payment settled"
|
|
80680
|
+
error: timedOut ? `the paid call did not answer within ${Math.round(timeoutMs / 1e3)}s and its stored purchase has no final result yet` : reply.error ?? "the supervisor could not determine whether the payment settled"
|
|
80605
80681
|
};
|
|
80606
80682
|
}
|
|
80607
80683
|
return refuse(reply.code ? `${reply.error ?? "payment refused"} (${reply.code})` : reply.error ?? "payment refused");
|
|
80608
80684
|
}
|
|
80685
|
+
/**
|
|
80686
|
+
* Re-post the same purchase once. A finished attempt replays from the ledger;
|
|
80687
|
+
* an attempt still in flight answers payment_unknown; a request that never
|
|
80688
|
+
* reached the supervisor is not reconciled because nothing was started.
|
|
80689
|
+
*/
|
|
80690
|
+
async reconcile(error53, payArgs, timeoutMs) {
|
|
80691
|
+
const code = error53?.code;
|
|
80692
|
+
if (typeof code === "string" && NOT_SENT_CODES.has(code))
|
|
80693
|
+
return void 0;
|
|
80694
|
+
await new Promise((resolve9) => setTimeout(resolve9, this.reconcileDelayMs));
|
|
80695
|
+
try {
|
|
80696
|
+
return await postHostedTool(this.socket, "economy.pay", payArgs, { timeoutMs });
|
|
80697
|
+
} catch {
|
|
80698
|
+
return void 0;
|
|
80699
|
+
}
|
|
80700
|
+
}
|
|
80609
80701
|
/** The supervisor found no 402: the endpoint is free, so the worker fetches it itself. */
|
|
80610
80702
|
async fetchUnpaid(url3, method, body, headers) {
|
|
80611
80703
|
try {
|
|
@@ -80948,6 +81040,15 @@ var init_worker = __esm({
|
|
|
80948
81040
|
const hosted = this.options.hostedTools?.(run.start, session);
|
|
80949
81041
|
const economy = this.options.economy?.(run.start, session);
|
|
80950
81042
|
const dynamicTools = economy ? hostedDynamicTools(economy) : void 0;
|
|
81043
|
+
let latestFinancialState = { status: "unavailable", reason: "Hosted financial state has not been read." };
|
|
81044
|
+
const financialState = this.options.financialState ? async () => {
|
|
81045
|
+
try {
|
|
81046
|
+
latestFinancialState = await this.options.financialState(run.start, session);
|
|
81047
|
+
} catch {
|
|
81048
|
+
latestFinancialState = { status: "unavailable", reason: "Hosted financial state could not be read; do not use local budget figures." };
|
|
81049
|
+
}
|
|
81050
|
+
return latestFinancialState;
|
|
81051
|
+
} : void 0;
|
|
80951
81052
|
const record3 = async (name, args, invoke) => {
|
|
80952
81053
|
const call2 = run.checkpoint?.response?.toolCalls.find((c) => c.name === name && !run.checkpoint?.completedTools[c.id]);
|
|
80953
81054
|
const id = `${run.checkpoint?.turn ?? 0}:${call2?.id ?? randomUUID8()}`;
|
|
@@ -80971,6 +81072,10 @@ var init_worker = __esm({
|
|
|
80971
81072
|
if (saved?.approvalPending && name === "call_paid_service" && economy)
|
|
80972
81073
|
await economy.execute("inspect_paid_service", { url: args.url, method: args.method, sample_body: args.body });
|
|
80973
81074
|
output3 = await invoke();
|
|
81075
|
+
if (financialState && name === "get_budget_status")
|
|
81076
|
+
output3 = { ok: true, data: latestFinancialState };
|
|
81077
|
+
if (financialState && name === "complete_session" && output3.ok && output3.data && typeof output3.data === "object")
|
|
81078
|
+
output3 = { ...output3, data: { ...output3.data, budget: latestFinancialState } };
|
|
80974
81079
|
} catch (error53) {
|
|
80975
81080
|
if (error53 instanceof RuntimePause && error53.outcome === "waiting_for_approval")
|
|
80976
81081
|
await this.mutate(() => {
|
|
@@ -81066,10 +81171,11 @@ var init_worker = __esm({
|
|
|
81066
81171
|
contextWindowTokens: run.start.payload.modelPolicy.contextWindowTokens,
|
|
81067
81172
|
maxOutputTokens: run.start.payload.modelPolicy.maxOutputTokens,
|
|
81068
81173
|
provider,
|
|
81174
|
+
financialState,
|
|
81069
81175
|
history: run.checkpoint ? void 0 : await this.history(session),
|
|
81070
81176
|
...dynamicTools ? {
|
|
81071
81177
|
dynamicTools: { definitions: dynamicTools.definitions, execute: (name, args) => record3(name, args, () => dynamicTools.execute(name, args, `${run.checkpoint?.turn ?? 0}:${run.checkpoint?.response?.toolCalls.find((c) => c.name === name && !run.checkpoint?.completedTools[c.id])?.id}`)) },
|
|
81072
|
-
completionGate: () => dynamicTools.completionGate(),
|
|
81178
|
+
completionGate: (alreadyNudged) => dynamicTools.completionGate(alreadyNudged),
|
|
81073
81179
|
knowledge: await this.knowledgeOption(session)
|
|
81074
81180
|
} : {},
|
|
81075
81181
|
onCheckpoint: async (checkpoint) => {
|
|
@@ -81392,6 +81498,8 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
81392
81498
|
"Try to solve the user's task completely. Use local files and bash when they are sufficient, but remember they are bounded by the user's installed tools, credentials, network, open ports, and process lifetime.",
|
|
81393
81499
|
"Before repeatedly fighting the local environment, decide whether the task needs external capability such as hosting, persistent infrastructure, remote compute, specialized APIs, live data, or access the user's device does not have."
|
|
81394
81500
|
];
|
|
81501
|
+
if (options.hosted)
|
|
81502
|
+
systemPromptParts.push("You operate this service for its owner. Inference and paid tools debit its USDC wallet. When authorized to build or sell a service, take concrete steps within the owner's configured scope and budget. Ask only for missing information or permissions that materially block progress. Report actual costs and outcomes. Financial sustainability is the owner's business objective; respect their stop controls.", "Financial state: the current hosted financial snapshot is authoritative; historical local session budget figures are not wallet balances and may omit inference spending. Wallet balance, pending payment holds, settled inference spend, settled service spend, and remaining run budget are separate. Spending totals cover only the identified current run. A budget limit does not supply funds. An unavailable balance is unknown, not zero. The snapshot before a model call excludes that call's eventual charge; never claim a response was free or that you spent nothing merely because no paid tool ran. Use get_budget_status or get_wallet_status for refreshed financial state. Respect approval mode and per-call limits; over-limit requests require the owner's approval.");
|
|
81395
81503
|
if (options.dynamicTools) {
|
|
81396
81504
|
systemPromptParts.push("When the local computer is not the right environment, or after one clear local capability failure, buy external capability: find_paid_service to discover, inspect_paid_service to see the exact schema/price/reputation, call_paid_service to execute through the enforced purchase lifecycle, and review_paid_service for the required review after every confirmed paid call (success or failure). The same review tool accepts unpaid experiences; review relevant free results or failed attempts without inventing payment evidence. Distinguish provider faults from local errors and uncertain failures.", ...options.hosted ? ["Discover services through AgentCash search and provider catalogs. You can use new services without prior CrowdCode reviews. Only x402 USDC on Base is payable, within the user's configured limits; over-limit quotes request approval. Use read_service for authenticated job polling and never resubmit a pending generation job."] : [], capabilityIndex ?? DEFAULT_CAPABILITY_INDEX, "When a task needs current web facts, search results, or unfamiliar page content, make one paid web search your FIRST move \u2014 do not serially guess URLs with curl; one paid search replaces minutes of blind fetching and costs less than the LLM turns it saves.", "Approval, budget, reputation, and payment rails are enforced in code \u2014 you cannot bypass them, so state costs plainly and never invent payment details.", "When the user says CrowdCode on/off, call set_crowdcode_enabled; default to session scope unless they ask for a persistent default. Use list_my_reviews and delete_my_review for user-directed review management, including while off. Before finishing a substantive task, consider concrete paid services that would fix real shortcomings and submit qualifying demand with request_service, unless CrowdCode is off. Never invent a purchase or demand.", "Each tool result includes the budget before and after that tool call. Never ask for wallet private keys or secrets.", ...options.promptSections ?? []);
|
|
81397
81505
|
} else {
|
|
@@ -81413,11 +81521,24 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
81413
81521
|
...options.history ?? [],
|
|
81414
81522
|
{ role: "user", content: task }
|
|
81415
81523
|
];
|
|
81524
|
+
let financial = { status: "unavailable", reason: "Hosted financial state has not been read." };
|
|
81525
|
+
const refreshFinancialState = async () => {
|
|
81526
|
+
if (!options.financialState)
|
|
81527
|
+
return;
|
|
81528
|
+
try {
|
|
81529
|
+
financial = FinancialStateSchema.parse(await options.financialState());
|
|
81530
|
+
} catch {
|
|
81531
|
+
financial = { status: "unavailable", reason: "Hosted financial state could not be read; do not use local budget figures." };
|
|
81532
|
+
}
|
|
81533
|
+
};
|
|
81534
|
+
const currentBudget = () => options.financialState ? financial : budgetStatus(session);
|
|
81535
|
+
const financialPrefix = "Current hosted financial snapshot: ";
|
|
81416
81536
|
if (!options.resume)
|
|
81417
81537
|
await options.onMessage?.({ role: "user", content: task });
|
|
81418
81538
|
const maxTurns = options.maxTurns ?? 100;
|
|
81419
81539
|
const repeatedFailures = new Map(options.resume?.repeatedFailures);
|
|
81420
81540
|
let completionNudges = options.resume?.completionNudges ?? 0;
|
|
81541
|
+
let pendingFinalMessage = options.resume?.pendingFinalMessage;
|
|
81421
81542
|
let outputContinuationNudges = options.resume?.outputContinuationNudges ?? 0;
|
|
81422
81543
|
let outputContinuationPrefix = options.resume?.outputContinuationPrefix ?? "";
|
|
81423
81544
|
let serviceCallFailures = options.resume?.serviceCallFailures ?? 0;
|
|
@@ -81431,6 +81552,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
81431
81552
|
context: contextState,
|
|
81432
81553
|
deliveredMessageIds: [...deliveredMessageIds],
|
|
81433
81554
|
completionNudges,
|
|
81555
|
+
pendingFinalMessage,
|
|
81434
81556
|
outputContinuationNudges,
|
|
81435
81557
|
outputContinuationPrefix,
|
|
81436
81558
|
serviceCallFailures,
|
|
@@ -81444,8 +81566,11 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
81444
81566
|
messages.push({ role: "user", content: message.prompt });
|
|
81445
81567
|
deliveredMessageIds.add(message.id);
|
|
81446
81568
|
}
|
|
81447
|
-
if (fresh.length)
|
|
81569
|
+
if (fresh.length) {
|
|
81570
|
+
pendingFinalMessage = void 0;
|
|
81571
|
+
outputContinuationPrefix = "";
|
|
81448
81572
|
await checkpoint(turn);
|
|
81573
|
+
}
|
|
81449
81574
|
for (const message of fresh)
|
|
81450
81575
|
await options.onMessage?.({ role: "user", content: message.prompt });
|
|
81451
81576
|
if (pending.length)
|
|
@@ -81473,6 +81598,15 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
81473
81598
|
options.onProgress?.({ type: "calling_llm", message: `Calling LLM provider (turn ${turn + 1}/${maxTurns})` });
|
|
81474
81599
|
let response;
|
|
81475
81600
|
try {
|
|
81601
|
+
if (options.financialState && !restored?.response && !contextState.prepared) {
|
|
81602
|
+
await refreshFinancialState();
|
|
81603
|
+
const snapshot = { role: "system", content: financialPrefix + JSON.stringify(financial) };
|
|
81604
|
+
const index2 = messages.findIndex((m) => m.role === "system" && m.content.startsWith(financialPrefix));
|
|
81605
|
+
if (index2 === -1)
|
|
81606
|
+
messages.splice(1, 0, snapshot);
|
|
81607
|
+
else
|
|
81608
|
+
messages[index2] = snapshot;
|
|
81609
|
+
}
|
|
81476
81610
|
response = restored?.response ?? await completeWithContext(session, messages, {
|
|
81477
81611
|
provider: provider instanceof MockLlmProvider ? { complete: (input2) => completeMockLlmCall(session, provider, input2, turn + 1) } : provider,
|
|
81478
81612
|
tools: wireToolDefinitions(enabledTools, dynamicDefinitions),
|
|
@@ -81489,6 +81623,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
81489
81623
|
}
|
|
81490
81624
|
});
|
|
81491
81625
|
options.signal?.throwIfAborted();
|
|
81626
|
+
await refreshFinancialState();
|
|
81492
81627
|
} catch (error53) {
|
|
81493
81628
|
if (error53 instanceof BudgetExhaustedError) {
|
|
81494
81629
|
const summary2 = await completeSession(session, `Stopped: the session budget is exhausted (${error53.message}).`);
|
|
@@ -81522,7 +81657,10 @@ ${outputContinuationPrefix}`);
|
|
|
81522
81657
|
if (response.toolCalls.length === 0) {
|
|
81523
81658
|
if (await injectMessages(turn + 1))
|
|
81524
81659
|
continue;
|
|
81525
|
-
const
|
|
81660
|
+
const finalMessage = response.content.trim() ? outputContinuationPrefix + response.content : pendingFinalMessage ?? outputContinuationPrefix;
|
|
81661
|
+
if (finalMessage.trim())
|
|
81662
|
+
pendingFinalMessage = finalMessage;
|
|
81663
|
+
const blocker = await options.completionGate?.(completionNudges > 0);
|
|
81526
81664
|
if (blocker) {
|
|
81527
81665
|
if (completionNudges >= 1) {
|
|
81528
81666
|
const summary3 = await completeSession(session, `Stopped: ${blocker}`);
|
|
@@ -81532,9 +81670,12 @@ ${outputContinuationPrefix}`);
|
|
|
81532
81670
|
const nudge = { role: "user", content: `You cannot finish yet: ${blocker}` };
|
|
81533
81671
|
messages.push(nudge);
|
|
81534
81672
|
await options.onMessage?.(nudge);
|
|
81673
|
+
await checkpoint(turn + 1);
|
|
81535
81674
|
continue;
|
|
81536
81675
|
}
|
|
81537
|
-
|
|
81676
|
+
if (!pendingFinalMessage?.trim())
|
|
81677
|
+
throw new Error("The model ended without an answer.");
|
|
81678
|
+
const summary2 = await completeSession(session, pendingFinalMessage);
|
|
81538
81679
|
return { outcome: "completed", summary: summary2, turns: turn + 1 };
|
|
81539
81680
|
}
|
|
81540
81681
|
const spawnedThisTurn = /* @__PURE__ */ new Map();
|
|
@@ -81577,7 +81718,7 @@ ${outputContinuationPrefix}`);
|
|
|
81577
81718
|
message: `Tool call: ${summarizeToolCall(call2)}`,
|
|
81578
81719
|
data: { tool: call2.name, arguments: call2.arguments }
|
|
81579
81720
|
});
|
|
81580
|
-
const budgetBeforeToolCall =
|
|
81721
|
+
const budgetBeforeToolCall = currentBudget();
|
|
81581
81722
|
const callArguments = call2.name === "complete_session" && outputContinuationPrefix ? {
|
|
81582
81723
|
...call2.arguments,
|
|
81583
81724
|
final_message: outputContinuationPrefix + (typeof call2.arguments.final_message === "string" ? call2.arguments.final_message : "")
|
|
@@ -81615,7 +81756,13 @@ ${outputContinuationPrefix}`);
|
|
|
81615
81756
|
} else {
|
|
81616
81757
|
result = { ok: false, error: `unknown tool: ${call2.name}` };
|
|
81617
81758
|
}
|
|
81618
|
-
|
|
81759
|
+
await refreshFinancialState();
|
|
81760
|
+
const budgetAfterToolCall = currentBudget();
|
|
81761
|
+
if (options.financialState && call2.name === "get_budget_status")
|
|
81762
|
+
result = { ok: true, data: financial };
|
|
81763
|
+
if (options.financialState && call2.name === "complete_session" && result.ok && result.data && typeof result.data === "object") {
|
|
81764
|
+
result = { ...result, data: { ...result.data, budget: financial } };
|
|
81765
|
+
}
|
|
81619
81766
|
options.onProgress?.({
|
|
81620
81767
|
type: "tool_result",
|
|
81621
81768
|
message: `Tool result: ${summarizeToolResult(call2.name, result)}`,
|
|
@@ -81636,6 +81783,8 @@ ${outputContinuationPrefix}`);
|
|
|
81636
81783
|
await checkpoint(turn, response, completedTools);
|
|
81637
81784
|
await options.onMessage?.(toolMessage);
|
|
81638
81785
|
}
|
|
81786
|
+
if (call2.name === "call_paid_service" && result.ok)
|
|
81787
|
+
serviceCallFailures = 0;
|
|
81639
81788
|
if (!result.ok) {
|
|
81640
81789
|
if (call2.name === "call_paid_service") {
|
|
81641
81790
|
serviceCallFailures += 1;
|
|
@@ -81659,7 +81808,12 @@ ${outputContinuationPrefix}`);
|
|
|
81659
81808
|
const result = completedTools[call2.id];
|
|
81660
81809
|
if (await injectMessages(turn + 1))
|
|
81661
81810
|
continue;
|
|
81662
|
-
|
|
81811
|
+
if (!result.ok)
|
|
81812
|
+
continue;
|
|
81813
|
+
const finalMessage = result.data && typeof result.data === "object" ? result.data.final_message : void 0;
|
|
81814
|
+
if (typeof finalMessage === "string" && finalMessage.trim())
|
|
81815
|
+
pendingFinalMessage = finalMessage;
|
|
81816
|
+
const blocker = await options.completionGate?.(completionNudges > 0);
|
|
81663
81817
|
if (blocker) {
|
|
81664
81818
|
if (completionNudges >= 1) {
|
|
81665
81819
|
const summary3 = await completeSession(session, `Stopped: ${blocker}`);
|
|
@@ -81667,23 +81821,25 @@ ${outputContinuationPrefix}`);
|
|
|
81667
81821
|
}
|
|
81668
81822
|
completionNudges += 1;
|
|
81669
81823
|
const gateMessage = {
|
|
81670
|
-
role: "
|
|
81671
|
-
|
|
81672
|
-
content: JSON.stringify({ result: { ok: false, error: `cannot complete yet: ${blocker}` } })
|
|
81824
|
+
role: "user",
|
|
81825
|
+
content: `You cannot finish yet: ${blocker}`
|
|
81673
81826
|
};
|
|
81674
81827
|
messages.push(gateMessage);
|
|
81675
81828
|
await options.onMessage?.(gateMessage);
|
|
81829
|
+
await checkpoint(turn + 1);
|
|
81676
81830
|
continue;
|
|
81677
81831
|
}
|
|
81832
|
+
if (!pendingFinalMessage?.trim())
|
|
81833
|
+
throw new Error("The model ended without an answer.");
|
|
81678
81834
|
const backgroundResults = backgroundSubagents.size > 0 ? await drainBackground() : void 0;
|
|
81679
|
-
if (result.
|
|
81680
|
-
const summary3 = { ...result.data };
|
|
81835
|
+
if (result.data && typeof result.data === "object") {
|
|
81836
|
+
const summary3 = { ...result.data, final_message: pendingFinalMessage };
|
|
81681
81837
|
if (backgroundResults) {
|
|
81682
81838
|
summary3.background_subagents = backgroundResults;
|
|
81683
81839
|
}
|
|
81684
81840
|
return { outcome: "completed", summary: summary3, turns: turn + 1 };
|
|
81685
81841
|
}
|
|
81686
|
-
const summary2 = await completeSession(session,
|
|
81842
|
+
const summary2 = await completeSession(session, pendingFinalMessage);
|
|
81687
81843
|
if (backgroundResults) {
|
|
81688
81844
|
summary2.background_subagents = backgroundResults;
|
|
81689
81845
|
}
|
|
@@ -82175,6 +82331,8 @@ function summarizeToolResult(name, result) {
|
|
|
82175
82331
|
return `call_paid_service ${String(data.outcome ?? "?")} HTTP ${String(data.status ?? "?")}, charged ${formatCents(Number(data.charged_cost_cents ?? 0))}${data.artifact_path ? `, saved ${String(data.artifact_path)}` : ""}`;
|
|
82176
82332
|
}
|
|
82177
82333
|
if (name === "get_budget_status" && result.data && typeof result.data === "object") {
|
|
82334
|
+
if ("status" in result.data)
|
|
82335
|
+
return `hosted finances: ${String(result.data.status)}`;
|
|
82178
82336
|
const data = result.data;
|
|
82179
82337
|
return `remaining ${formatCents(Number(data.remaining_cents ?? 0))}`;
|
|
82180
82338
|
}
|
|
@@ -82629,6 +82787,7 @@ async function workerCommand(args) {
|
|
|
82629
82787
|
provider: (run, session, extraTools) => socketPath ? createHostedProvider({ socketPath, runId: run.runId, sessionId: session.sessionId, extraTools }) : createWorkerDemoProvider(),
|
|
82630
82788
|
hostedTools: socketPath ? (run, session) => createHostedToolExecutor({ socketPath, runId: run.runId, sessionId: session.sessionId }) : void 0,
|
|
82631
82789
|
economy: socketPath ? (run, session) => createHostedEconomy({ socketPath, runId: run.runId, sessionId: session.sessionId, session }) : void 0,
|
|
82790
|
+
financialState: socketPath ? (run, session) => readHostedFinancialState({ socketPath, runId: run.runId, sessionId: session.sessionId }) : void 0,
|
|
82632
82791
|
output: (line) => {
|
|
82633
82792
|
process.stdout.write(line);
|
|
82634
82793
|
}
|
package/package.json
CHANGED