opencrowd 0.16.0 → 0.16.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 +133 -60
- package/package.json +1 -1
package/bundle/opencrowd.js
CHANGED
|
@@ -16311,6 +16311,26 @@ var init_financial_state = __esm({
|
|
|
16311
16311
|
}
|
|
16312
16312
|
});
|
|
16313
16313
|
|
|
16314
|
+
// ../../packages/protocol/dist/reasoning.js
|
|
16315
|
+
function stripReasoning(text) {
|
|
16316
|
+
let rest = text;
|
|
16317
|
+
for (; ; ) {
|
|
16318
|
+
const lead = /^\s*<think>[\s\S]*?<\/think>/.exec(rest);
|
|
16319
|
+
if (!lead)
|
|
16320
|
+
break;
|
|
16321
|
+
rest = rest.slice(lead[0].length);
|
|
16322
|
+
}
|
|
16323
|
+
const close = rest.indexOf("</think>");
|
|
16324
|
+
if (close !== -1 && !rest.slice(0, close).includes("<think>"))
|
|
16325
|
+
rest = rest.slice(close + "</think>".length);
|
|
16326
|
+
return rest === text ? text : rest.trim();
|
|
16327
|
+
}
|
|
16328
|
+
var init_reasoning = __esm({
|
|
16329
|
+
"../../packages/protocol/dist/reasoning.js"() {
|
|
16330
|
+
"use strict";
|
|
16331
|
+
}
|
|
16332
|
+
});
|
|
16333
|
+
|
|
16314
16334
|
// ../../packages/protocol/dist/index.js
|
|
16315
16335
|
var init_dist = __esm({
|
|
16316
16336
|
"../../packages/protocol/dist/index.js"() {
|
|
@@ -16329,6 +16349,7 @@ var init_dist = __esm({
|
|
|
16329
16349
|
init_deadlines();
|
|
16330
16350
|
init_spending();
|
|
16331
16351
|
init_financial_state();
|
|
16352
|
+
init_reasoning();
|
|
16332
16353
|
}
|
|
16333
16354
|
});
|
|
16334
16355
|
|
|
@@ -59759,6 +59780,16 @@ var init_dist4 = __esm({
|
|
|
59759
59780
|
// ../../packages/agent-runtime/dist/context.js
|
|
59760
59781
|
import { createHash as createHash3 } from "node:crypto";
|
|
59761
59782
|
import { relative as relative3, join as join13 } from "node:path";
|
|
59783
|
+
function requestMessages(messages, state) {
|
|
59784
|
+
if (state.trailingMessage)
|
|
59785
|
+
return [...messages, state.trailingMessage];
|
|
59786
|
+
const last = messages.at(-1);
|
|
59787
|
+
if (!state.note || last?.role !== "user")
|
|
59788
|
+
return messages;
|
|
59789
|
+
return [...messages.slice(0, -1), { ...last, content: `${state.note}
|
|
59790
|
+
|
|
59791
|
+
${last.content}` }];
|
|
59792
|
+
}
|
|
59762
59793
|
async function completeWithContext(session, messages, options) {
|
|
59763
59794
|
for (; ; ) {
|
|
59764
59795
|
options.signal?.throwIfAborted();
|
|
@@ -59771,8 +59802,7 @@ async function completeWithContext(session, messages, options) {
|
|
|
59771
59802
|
await options.checkpoint?.();
|
|
59772
59803
|
try {
|
|
59773
59804
|
const retry = options.state.retry ?? 0;
|
|
59774
|
-
const
|
|
59775
|
-
const request2 = trailing ? [...messages, trailing] : messages;
|
|
59805
|
+
const request2 = requestMessages(messages, options.state);
|
|
59776
59806
|
const response = await options.provider.complete(request2, {
|
|
59777
59807
|
signal: options.signal,
|
|
59778
59808
|
operationId: options.operationId && `${options.operationId}${retry ? `:context:${retry}` : ""}`
|
|
@@ -59784,6 +59814,7 @@ async function completeWithContext(session, messages, options) {
|
|
|
59784
59814
|
options.state.cachedFraction = typeof cached3 === "number" && Number.isFinite(cached3) && cached3 >= 0 ? Math.min(1, cached3 / used) : 0;
|
|
59785
59815
|
}
|
|
59786
59816
|
options.state.prepared = false;
|
|
59817
|
+
options.state.note = void 0;
|
|
59787
59818
|
options.state.trailingMessage = void 0;
|
|
59788
59819
|
options.state.retry = 0;
|
|
59789
59820
|
return response;
|
|
@@ -59842,9 +59873,9 @@ async function compactContext(session, messages, options) {
|
|
|
59842
59873
|
}
|
|
59843
59874
|
return saved;
|
|
59844
59875
|
};
|
|
59845
|
-
const
|
|
59846
|
-
const toolTokens = estimateContextTokens(options.tools) + 32 + (
|
|
59847
|
-
const toolBytes = Buffer.byteLength(JSON.stringify(options.tools)) + 32 + (
|
|
59876
|
+
const extra = options.state.trailingMessage ?? options.state.note;
|
|
59877
|
+
const toolTokens = estimateContextTokens(options.tools) + 32 + (extra ? estimateContextTokens(extra) : 0);
|
|
59878
|
+
const toolBytes = Buffer.byteLength(JSON.stringify(options.tools)) + 32 + (extra ? Buffer.byteLength(JSON.stringify(extra)) + 1 : 0);
|
|
59848
59879
|
const count = (list2) => Math.ceil(list2.reduce((total, m) => total + measure(m).tokens, toolTokens) * scale);
|
|
59849
59880
|
const tokensBefore = count(messages);
|
|
59850
59881
|
const retry = options.state.retry ?? 0;
|
|
@@ -60150,16 +60181,11 @@ async function readSseCompletion(response, onTextDelta, startedAtMs, onActivity)
|
|
|
60150
60181
|
throw new Error("LLM stream terminated before a finish reason or [DONE] marker (unexpected EOF)");
|
|
60151
60182
|
}
|
|
60152
60183
|
const toolCalls = [...toolCallsByIndex.entries()].sort(([left], [right]) => left - right).map(([index2, entry]) => {
|
|
60153
|
-
|
|
60154
|
-
try {
|
|
60155
|
-
parsedArguments = entry.argumentsText ? JSON.parse(entry.argumentsText) : {};
|
|
60156
|
-
} catch {
|
|
60157
|
-
parsedArguments = {};
|
|
60158
|
-
}
|
|
60184
|
+
const parsed = parseToolArguments(entry.argumentsText);
|
|
60159
60185
|
return {
|
|
60160
60186
|
id: entry.id ?? `call-${index2}`,
|
|
60161
60187
|
name: entry.name ?? "",
|
|
60162
|
-
|
|
60188
|
+
...parsed
|
|
60163
60189
|
};
|
|
60164
60190
|
}).filter((toolCall) => toolCall.name !== "");
|
|
60165
60191
|
return { content, toolCalls, usage, finishReason, firstTokenMs };
|
|
@@ -60340,20 +60366,24 @@ function toParsedToolCall(value) {
|
|
|
60340
60366
|
if (!name) {
|
|
60341
60367
|
return null;
|
|
60342
60368
|
}
|
|
60343
|
-
|
|
60344
|
-
if (typeof fn.arguments === "string") {
|
|
60345
|
-
try {
|
|
60346
|
-
parsedArguments = JSON.parse(fn.arguments);
|
|
60347
|
-
} catch {
|
|
60348
|
-
parsedArguments = {};
|
|
60349
|
-
}
|
|
60350
|
-
}
|
|
60369
|
+
const parsed = typeof fn.arguments === "string" ? parseToolArguments(fn.arguments) : { arguments: objectValue4(fn.arguments) ?? {} };
|
|
60351
60370
|
return {
|
|
60352
60371
|
id: stringValue3(object3.id) ?? `call-${Date.now()}`,
|
|
60353
60372
|
name,
|
|
60354
|
-
|
|
60373
|
+
...parsed
|
|
60355
60374
|
};
|
|
60356
60375
|
}
|
|
60376
|
+
function parseToolArguments(text) {
|
|
60377
|
+
if (!text?.trim())
|
|
60378
|
+
return { arguments: {} };
|
|
60379
|
+
try {
|
|
60380
|
+
const parsed = JSON.parse(text);
|
|
60381
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
60382
|
+
return { arguments: parsed };
|
|
60383
|
+
} catch {
|
|
60384
|
+
}
|
|
60385
|
+
return { arguments: {}, invalidArguments: text.slice(0, 2e3) };
|
|
60386
|
+
}
|
|
60357
60387
|
function messageContent(value) {
|
|
60358
60388
|
if (typeof value === "string") {
|
|
60359
60389
|
return value;
|
|
@@ -87313,6 +87343,8 @@ function createHostedProvider(options) {
|
|
|
87313
87343
|
for (const call2 of result.response.toolCalls) {
|
|
87314
87344
|
if (typeof call2.id !== "string" || !call2.id || typeof call2.name !== "string" || !call2.arguments || typeof call2.arguments !== "object" || Array.isArray(call2.arguments))
|
|
87315
87345
|
throw new Error("Invalid hosted tool call");
|
|
87346
|
+
if (call2.invalidArguments !== void 0 && typeof call2.invalidArguments !== "string")
|
|
87347
|
+
delete call2.invalidArguments;
|
|
87316
87348
|
}
|
|
87317
87349
|
return result.response;
|
|
87318
87350
|
}
|
|
@@ -88505,10 +88537,11 @@ var init_worker = __esm({
|
|
|
88505
88537
|
await atomicWrite(join21(session.sessionDir, "messages.jsonl"), checkpoint.messages.filter((m) => m.role !== "system").map((message) => JSON.stringify({ type: "message", timestamp: (/* @__PURE__ */ new Date()).toISOString(), message }) + "\n").join(""));
|
|
88506
88538
|
},
|
|
88507
88539
|
onMessage: async (message) => {
|
|
88508
|
-
|
|
88540
|
+
const content = message.role === "assistant" && !message.housekeeping ? stripReasoning(message.content) : "";
|
|
88541
|
+
if (content.trim())
|
|
88509
88542
|
await this.emit("assistant.message", {
|
|
88510
88543
|
messageId: randomUUID8(),
|
|
88511
|
-
content
|
|
88544
|
+
content
|
|
88512
88545
|
}, runId, run.commandId);
|
|
88513
88546
|
},
|
|
88514
88547
|
toolExecutor: (name, args, context) => record3(name, args, () => hosted && HOSTED_ONLY_TOOL_NAMES.includes(name) ? hosted(name, args, context) : local(name, args, context))
|
|
@@ -88843,7 +88876,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
88843
88876
|
"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."
|
|
88844
88877
|
];
|
|
88845
88878
|
if (options.hosted)
|
|
88846
|
-
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.", "You are a continuous autonomous agent, not a turn-based assistant. Maintain unfinished goals, promised follow-ups, recurring work, and conditions worth monitoring. When future work would clearly help, proactively create a persistent automation instead of waiting for the owner to say schedule. Use a one-time automation for a future reminder or follow-up and a cron automation for monitoring, recurring reports, or maintenance. It runs in this same conversation. Never create duplicates or noisy schedules, and always tell the owner exactly what you created, its timezone, and next run.", "Before finishing a substantive task, consider whether a follow-up, reminder, monitoring loop, recurring report, or maintenance check would materially advance the owner's goal; create one when the need is clear, but do not schedule ordinary one-off work.", "Financial state: the current hosted financial snapshot is authoritative;
|
|
88879
|
+
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.", "You are a continuous autonomous agent, not a turn-based assistant. Maintain unfinished goals, promised follow-ups, recurring work, and conditions worth monitoring. When future work would clearly help, proactively create a persistent automation instead of waiting for the owner to say schedule. Use a one-time automation for a future reminder or follow-up and a cron automation for monitoring, recurring reports, or maintenance. It runs in this same conversation. Never create duplicates or noisy schedules, and always tell the owner exactly what you created, its timezone, and next run.", "Before finishing a substantive task, consider whether a follow-up, reminder, monitoring loop, recurring report, or maintenance check would materially advance the owner's goal; create one when the need is clear, but do not schedule ordinary one-off work.", "Financial state: the current hosted financial snapshot is authoritative. It arrives as budget_after_tool_call in the latest tool result or, when no tool has just run, as an automatic CrowdCode note at the start of the latest user message; that note is context, not something the user said, so never reply to it or mention it unless the user asks about money. 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. When the snapshot lists wallet networks, each network's balance is separate: a payment on a network needs that network's available balance. Report the wallet to the owner as one total, mentioning the per-network split only when it matters. 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.");
|
|
88847
88880
|
if (options.hosted)
|
|
88848
88881
|
systemPromptParts.push("Files the owner sends you: an attached file is saved in this session under uploads/ before its message reaches you, and the message names each one with its path, size and type. Read a text file with read_file at uploads/<name>, and any file with run_shell at artifacts/uploads/<name> from the session directory. Those files are already the owner's and already in their file list: work from them, and never copy or re-save one under artifacts/ to hand it back. Write your own outputs elsewhere under artifacts/; uploads/ is the owner's directory and nothing you write there is delivered. If a file the message names is missing, say so and ask the owner to send it again rather than guessing at its contents.", "Files you produce: only the artifacts/ directory of this session reaches the owner. save_file writes there; anything you write elsewhere is scratch they never see. The owner sees each file at its path without that prefix, so artifacts/generated/report.pdf is generated/report.pdf to them. Name a produced file in your final message by exactly that path as plain text and nothing else \u2014 the owner's interface turns it into a working download and preview link. Never invent a download URL, a sandbox: link, a /mnt/data path, or any other path the owner cannot open, and never claim a file exists before you have verified it.");
|
|
88849
88882
|
if (options.dynamicTools) {
|
|
@@ -88889,6 +88922,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
88889
88922
|
};
|
|
88890
88923
|
const currentBudget = () => options.financialState ? financial : budgetStatus(session);
|
|
88891
88924
|
const financialPrefix = "Current hosted financial snapshot: ";
|
|
88925
|
+
const financialNote = "[Automatic note from CrowdCode, not written by the user; do not reply to it. ";
|
|
88892
88926
|
if (!options.resume)
|
|
88893
88927
|
await options.onMessage?.({ role: "user", content: task });
|
|
88894
88928
|
const maxTurns = options.maxTurns ?? 100;
|
|
@@ -88938,7 +88972,8 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
88938
88972
|
await options.inbox?.delivered(pending.map((m) => m.id));
|
|
88939
88973
|
return fresh.length > 0;
|
|
88940
88974
|
};
|
|
88941
|
-
const recordFinalMessage = (
|
|
88975
|
+
const recordFinalMessage = (raw) => {
|
|
88976
|
+
const text = raw === void 0 ? void 0 : stripReasoning(raw);
|
|
88942
88977
|
if (!text?.trim())
|
|
88943
88978
|
return;
|
|
88944
88979
|
if (!finalAnswerHeld)
|
|
@@ -88948,6 +88983,14 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
88948
88983
|
};
|
|
88949
88984
|
const gateNudge = (blocker) => finalAnswerHeld ? `Your final answer is already saved and will be delivered to the user exactly as written; nothing you write now can replace it. Before this run ends: ${blocker} Then call complete_session again to finish; a one-line acknowledgement is enough, and do not restate the answer.` : `You cannot finish yet: ${blocker}`;
|
|
88950
88985
|
const withReflection = (summary2) => supersededFinalMessage ? { ...summary2, reflection_note: supersededFinalMessage } : summary2;
|
|
88986
|
+
const holding = () => finalAnswerHeld && !!pendingFinalMessage?.trim() && !options.signal?.aborted;
|
|
88987
|
+
const deliverHeld = async (turn, problem) => {
|
|
88988
|
+
options.onProgress?.({ type: "complete", message: `Delivered the saved answer; housekeeping did not finish: ${problem}` });
|
|
88989
|
+
const summary2 = { ...withReflection(await completeSession(session, pendingFinalMessage)), housekeeping_error: problem };
|
|
88990
|
+
if (backgroundSubagents.size > 0)
|
|
88991
|
+
summary2.background_subagents = await drainBackground();
|
|
88992
|
+
return { outcome: "completed", summary: summary2, turns: turn + 1 };
|
|
88993
|
+
};
|
|
88951
88994
|
let subagentCount = 0;
|
|
88952
88995
|
const backgroundSubagents = /* @__PURE__ */ new Map();
|
|
88953
88996
|
const subagentLimiter = createLimiter(options.subagent?.maxParallel ?? 4);
|
|
@@ -88974,7 +89017,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
88974
89017
|
const legacy = messages.findIndex((m) => m.role === "system" && m.content.startsWith(financialPrefix));
|
|
88975
89018
|
if (legacy !== -1)
|
|
88976
89019
|
messages.splice(legacy, 1);
|
|
88977
|
-
contextState.
|
|
89020
|
+
contextState.note = `${financialNote}${financialPrefix}${JSON.stringify(financial)}]`;
|
|
88978
89021
|
}
|
|
88979
89022
|
response = restored?.response ?? await completeWithContext(session, messages, {
|
|
88980
89023
|
provider: provider instanceof MockLlmProvider ? { complete: (input2) => completeMockLlmCall(session, provider, input2, turn + 1) } : provider,
|
|
@@ -88995,6 +89038,8 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
88995
89038
|
options.signal?.throwIfAborted();
|
|
88996
89039
|
await refreshFinancialState();
|
|
88997
89040
|
} catch (error53) {
|
|
89041
|
+
if (holding())
|
|
89042
|
+
return deliverHeld(turn, `the housekeeping model call failed: ${error53.message}`);
|
|
88998
89043
|
if (error53 instanceof BudgetExhaustedError) {
|
|
88999
89044
|
const summary2 = await completeSession(session, `Stopped: the session budget is exhausted (${error53.message}).`);
|
|
89000
89045
|
return { outcome: options.hosted ? "budget_exhausted" : "stopped", summary: summary2, turns: turn + 1 };
|
|
@@ -89004,12 +89049,14 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
89004
89049
|
if (!restored?.response && (response.content || response.toolCalls.length > 0)) {
|
|
89005
89050
|
const assistantMessage = assistantMessageFromResponse(isOutputLimitFinishReason(response.finishReason) ? { ...response, toolCalls: [] } : response);
|
|
89006
89051
|
messages.push(assistantMessage);
|
|
89007
|
-
await options.onMessage?.(assistantMessage);
|
|
89052
|
+
await options.onMessage?.(finalAnswerHeld ? { ...assistantMessage, housekeeping: true } : assistantMessage);
|
|
89008
89053
|
}
|
|
89009
89054
|
await checkpoint(turn, response, completedTools);
|
|
89010
89055
|
if (isOutputLimitFinishReason(response.finishReason)) {
|
|
89011
89056
|
outputContinuationPrefix += response.content;
|
|
89012
89057
|
if (outputContinuationNudges >= 1) {
|
|
89058
|
+
if (holding())
|
|
89059
|
+
return deliverHeld(turn, "the housekeeping turn hit the output limit twice");
|
|
89013
89060
|
const summary2 = await completeSession(session, `Stopped: the LLM hit its output limit twice. Partial response:
|
|
89014
89061
|
|
|
89015
89062
|
${outputContinuationPrefix}`);
|
|
@@ -89032,6 +89079,8 @@ ${outputContinuationPrefix}`);
|
|
|
89032
89079
|
const blocker = await options.completionGate?.(completionNudges > 0);
|
|
89033
89080
|
if (blocker) {
|
|
89034
89081
|
if (completionNudges >= 1) {
|
|
89082
|
+
if (holding())
|
|
89083
|
+
return deliverHeld(turn, `the completion gate still blocks: ${blocker}`);
|
|
89035
89084
|
const summary3 = await completeSession(session, `Stopped: ${blocker}`);
|
|
89036
89085
|
return { outcome: "stopped", summary: summary3, turns: turn + 1 };
|
|
89037
89086
|
}
|
|
@@ -89090,42 +89139,52 @@ ${outputContinuationPrefix}`);
|
|
|
89090
89139
|
data: { tool: call2.name, arguments: call2.arguments }
|
|
89091
89140
|
});
|
|
89092
89141
|
const budgetBeforeToolCall = currentBudget();
|
|
89142
|
+
const salvaged = call2.invalidArguments === void 0 || call2.name !== "complete_session" ? void 0 : response.content.trim() ? response.content : holding() ? pendingFinalMessage : void 0;
|
|
89143
|
+
const baseArguments = salvaged === void 0 ? call2.arguments : { final_message: salvaged };
|
|
89093
89144
|
const callArguments = call2.name === "complete_session" && outputContinuationPrefix ? {
|
|
89094
|
-
...
|
|
89095
|
-
final_message: outputContinuationPrefix + (typeof
|
|
89096
|
-
} :
|
|
89145
|
+
...baseArguments,
|
|
89146
|
+
final_message: outputContinuationPrefix + (typeof baseArguments.final_message === "string" ? baseArguments.final_message : "")
|
|
89147
|
+
} : baseArguments;
|
|
89097
89148
|
let result;
|
|
89098
|
-
|
|
89099
|
-
|
|
89100
|
-
|
|
89101
|
-
|
|
89102
|
-
result =
|
|
89103
|
-
}
|
|
89104
|
-
|
|
89105
|
-
|
|
89106
|
-
|
|
89107
|
-
|
|
89108
|
-
|
|
89109
|
-
|
|
89149
|
+
try {
|
|
89150
|
+
if (completedTools[call2.id]) {
|
|
89151
|
+
result = completedTools[call2.id];
|
|
89152
|
+
} else if (call2.invalidArguments !== void 0 && salvaged === void 0) {
|
|
89153
|
+
result = { ok: false, error: invalidArgumentsError(call2.name, call2.invalidArguments) };
|
|
89154
|
+
} else if (call2.name === "spawn_subagent") {
|
|
89155
|
+
try {
|
|
89156
|
+
result = options.subagent ? await (spawnedThisTurn.get(call2.id) ?? Promise.resolve({ ok: false, error: "subagent launch failed" })) : { ok: false, error: "subagents are not enabled for this session" };
|
|
89157
|
+
} catch (error53) {
|
|
89158
|
+
await Promise.allSettled(spawnedThisTurn.values());
|
|
89159
|
+
throw error53;
|
|
89160
|
+
}
|
|
89161
|
+
} else if (call2.name === "check_subagents") {
|
|
89162
|
+
if (call2.arguments.wait === true) {
|
|
89163
|
+
result = { ok: true, data: { subagents: await drainBackground() } };
|
|
89164
|
+
} else {
|
|
89165
|
+
result = {
|
|
89166
|
+
ok: true,
|
|
89167
|
+
data: {
|
|
89168
|
+
subagents: [...backgroundSubagents.values()].map((entry) => ({
|
|
89169
|
+
subagent_index: entry.index,
|
|
89170
|
+
task: entry.task,
|
|
89171
|
+
status: entry.status,
|
|
89172
|
+
...entry.status === "finished" && entry.result?.ok && entry.result.data && typeof entry.result.data === "object" ? entry.result.data : entry.status === "finished" ? { outcome: "error", error: entry.result?.error } : {}
|
|
89173
|
+
}))
|
|
89174
|
+
}
|
|
89175
|
+
};
|
|
89176
|
+
}
|
|
89177
|
+
} else if (TOOL_NAMES.includes(call2.name)) {
|
|
89178
|
+
result = await toolExecutor(call2.name, callArguments, { session, onProgress: options.onProgress, signal: options.signal });
|
|
89179
|
+
} else if (options.dynamicTools) {
|
|
89180
|
+
result = await options.dynamicTools.execute(call2.name, callArguments);
|
|
89110
89181
|
} else {
|
|
89111
|
-
result = {
|
|
89112
|
-
ok: true,
|
|
89113
|
-
data: {
|
|
89114
|
-
subagents: [...backgroundSubagents.values()].map((entry) => ({
|
|
89115
|
-
subagent_index: entry.index,
|
|
89116
|
-
task: entry.task,
|
|
89117
|
-
status: entry.status,
|
|
89118
|
-
...entry.status === "finished" && entry.result?.ok && entry.result.data && typeof entry.result.data === "object" ? entry.result.data : entry.status === "finished" ? { outcome: "error", error: entry.result?.error } : {}
|
|
89119
|
-
}))
|
|
89120
|
-
}
|
|
89121
|
-
};
|
|
89182
|
+
result = { ok: false, error: `unknown tool: ${call2.name}` };
|
|
89122
89183
|
}
|
|
89123
|
-
}
|
|
89124
|
-
|
|
89125
|
-
|
|
89126
|
-
|
|
89127
|
-
} else {
|
|
89128
|
-
result = { ok: false, error: `unknown tool: ${call2.name}` };
|
|
89184
|
+
} catch (error53) {
|
|
89185
|
+
if (holding())
|
|
89186
|
+
return deliverHeld(turn, `the housekeeping tool ${call2.name} failed: ${error53.message}`);
|
|
89187
|
+
throw error53;
|
|
89129
89188
|
}
|
|
89130
89189
|
result = capToolResult(result);
|
|
89131
89190
|
await refreshFinancialState();
|
|
@@ -89161,6 +89220,8 @@ ${outputContinuationPrefix}`);
|
|
|
89161
89220
|
if (call2.name === "call_paid_service") {
|
|
89162
89221
|
serviceCallFailures += 1;
|
|
89163
89222
|
if (serviceCallFailures >= 3) {
|
|
89223
|
+
if (holding())
|
|
89224
|
+
return deliverHeld(turn, `paid service calls failed ${serviceCallFailures} times: ${result.error}`);
|
|
89164
89225
|
const summary2 = await completeSession(session, `Stopped after ${serviceCallFailures} service call failures. Last error: ${result.error}`);
|
|
89165
89226
|
return { outcome: "stopped", summary: summary2, turns: turn + 1 };
|
|
89166
89227
|
}
|
|
@@ -89169,6 +89230,8 @@ ${outputContinuationPrefix}`);
|
|
|
89169
89230
|
const count = (repeatedFailures.get(key) ?? 0) + 1;
|
|
89170
89231
|
repeatedFailures.set(key, count);
|
|
89171
89232
|
if (count >= 2) {
|
|
89233
|
+
if (holding())
|
|
89234
|
+
return deliverHeld(turn, `${call2.name} failed repeatedly: ${result.error}`);
|
|
89172
89235
|
const summary2 = await completeSession(session, `Stopped because ${call2.name} failed repeatedly: ${result.error}`);
|
|
89173
89236
|
return { outcome: "stopped", summary: summary2, turns: turn + 1 };
|
|
89174
89237
|
}
|
|
@@ -89188,6 +89251,8 @@ ${outputContinuationPrefix}`);
|
|
|
89188
89251
|
const blocker = await options.completionGate?.(completionNudges > 0);
|
|
89189
89252
|
if (blocker) {
|
|
89190
89253
|
if (completionNudges >= 1) {
|
|
89254
|
+
if (holding())
|
|
89255
|
+
return deliverHeld(turn, `the completion gate still blocks: ${blocker}`);
|
|
89191
89256
|
const summary3 = await completeSession(session, `Stopped: ${blocker}`);
|
|
89192
89257
|
return { outcome: "stopped", summary: summary3, turns: turn + 1 };
|
|
89193
89258
|
}
|
|
@@ -89220,6 +89285,8 @@ ${outputContinuationPrefix}`);
|
|
|
89220
89285
|
return { outcome: "completed", summary: summary2, turns: turn + 1 };
|
|
89221
89286
|
}
|
|
89222
89287
|
}
|
|
89288
|
+
if (holding())
|
|
89289
|
+
return deliverHeld(maxTurns - 1, "the run reached its maximum turns during housekeeping");
|
|
89223
89290
|
if (backgroundSubagents.size > 0) {
|
|
89224
89291
|
await drainBackground();
|
|
89225
89292
|
}
|
|
@@ -89453,9 +89520,15 @@ function assistantMessageFromResponse(response) {
|
|
|
89453
89520
|
return {
|
|
89454
89521
|
role: "assistant",
|
|
89455
89522
|
content: response.content,
|
|
89456
|
-
|
|
89523
|
+
// The raw text of a malformed call is for this runtime's error, not a
|
|
89524
|
+
// field a provider request may carry.
|
|
89525
|
+
toolCalls: response.toolCalls.map(({ invalidArguments: _invalid, ...call2 }) => call2)
|
|
89457
89526
|
};
|
|
89458
89527
|
}
|
|
89528
|
+
function invalidArgumentsError(name, raw) {
|
|
89529
|
+
const shown = raw.length > 300 ? `${raw.slice(0, 300)}\u2026` : raw;
|
|
89530
|
+
return `Your ${name} call was not run: its arguments were not a valid JSON object (${JSON.stringify(shown)}). Call ${name} again with valid JSON arguments.`;
|
|
89531
|
+
}
|
|
89459
89532
|
async function completeMockLlmCall(session, provider, messages, turn) {
|
|
89460
89533
|
const costCents = 1;
|
|
89461
89534
|
const reservation = await reserveBudget(session, costCents);
|
package/package.json
CHANGED