opencrowd 0.16.4 → 0.16.6
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 +157 -36
- package/package.json +1 -1
package/bundle/opencrowd.js
CHANGED
|
@@ -262,7 +262,7 @@ var init_paths = __esm({
|
|
|
262
262
|
});
|
|
263
263
|
|
|
264
264
|
// ../../packages/core/dist/artifacts.js
|
|
265
|
-
import { mkdir as mkdir3, readFile as readFile3 } from "node:fs/promises";
|
|
265
|
+
import { mkdir as mkdir3, readFile as readFile3, readdir, stat } from "node:fs/promises";
|
|
266
266
|
import { dirname as dirname4, join as join3, relative as relative2 } from "node:path";
|
|
267
267
|
async function saveArtifact(state, path3, content, metadata) {
|
|
268
268
|
const target = await safeArtifactPath(state, path3);
|
|
@@ -283,21 +283,75 @@ async function readArtifact(state, path3) {
|
|
|
283
283
|
return readFile3(await safeArtifactPath(state, path3), "utf8");
|
|
284
284
|
}
|
|
285
285
|
async function listArtifacts(state, prefix = "") {
|
|
286
|
-
const { readdir: readdir3 } = await import("node:fs/promises");
|
|
287
286
|
const start = await safeArtifactPath(state, prefix || ".");
|
|
288
287
|
const files = [];
|
|
289
|
-
await
|
|
288
|
+
await walkFiles(start, state.artifactsDir, files);
|
|
290
289
|
return files.sort();
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
290
|
+
}
|
|
291
|
+
async function listOtherChatArtifacts(state) {
|
|
292
|
+
const sessionsDir = dirname4(state.sessionDir);
|
|
293
|
+
let entries;
|
|
294
|
+
try {
|
|
295
|
+
entries = await readdir(sessionsDir, { withFileTypes: true });
|
|
296
|
+
} catch {
|
|
297
|
+
return { chats: [], truncated: false };
|
|
298
|
+
}
|
|
299
|
+
const others = [];
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
if (!entry.isDirectory() || entry.name === state.sessionId)
|
|
302
|
+
continue;
|
|
303
|
+
try {
|
|
304
|
+
assertPathId(entry.name);
|
|
305
|
+
} catch {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const dir = join3(sessionsDir, entry.name, "artifacts");
|
|
309
|
+
try {
|
|
310
|
+
const info = await stat(join3(sessionsDir, entry.name));
|
|
311
|
+
others.push({ id: entry.name, dir, mtime: info.mtimeMs });
|
|
312
|
+
} catch {
|
|
299
313
|
}
|
|
300
314
|
}
|
|
315
|
+
others.sort((a, b) => b.mtime - a.mtime);
|
|
316
|
+
const chats = [];
|
|
317
|
+
let remaining = OTHER_CHAT_FILE_LIMIT, truncated = false;
|
|
318
|
+
for (const other of others) {
|
|
319
|
+
if (remaining <= 0) {
|
|
320
|
+
truncated = true;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
let root;
|
|
324
|
+
try {
|
|
325
|
+
root = await containedPath(sessionsDir, join3(other.id, "artifacts"));
|
|
326
|
+
} catch {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const files = [];
|
|
330
|
+
try {
|
|
331
|
+
await walkFiles(root, root, files);
|
|
332
|
+
} catch {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (!files.length)
|
|
336
|
+
continue;
|
|
337
|
+
files.sort();
|
|
338
|
+
if (files.length > remaining) {
|
|
339
|
+
truncated = true;
|
|
340
|
+
files.length = remaining;
|
|
341
|
+
}
|
|
342
|
+
remaining -= files.length;
|
|
343
|
+
chats.push({ session_id: other.id, shell_dir: `../${other.id}/artifacts`, files });
|
|
344
|
+
}
|
|
345
|
+
return { chats, truncated };
|
|
346
|
+
}
|
|
347
|
+
async function walkFiles(dir, root, out) {
|
|
348
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
349
|
+
const full = join3(dir, entry.name);
|
|
350
|
+
if (entry.isDirectory())
|
|
351
|
+
await walkFiles(full, root, out);
|
|
352
|
+
else if (entry.isFile())
|
|
353
|
+
out.push(relative2(root, full));
|
|
354
|
+
}
|
|
301
355
|
}
|
|
302
356
|
function safeArtifactPath(state, requestedPath) {
|
|
303
357
|
const normalizedRequest = requestedPath.replace(/^artifacts\//, "");
|
|
@@ -305,11 +359,13 @@ function safeArtifactPath(state, requestedPath) {
|
|
|
305
359
|
throw new Error(`artifact path: ${error53.message}`);
|
|
306
360
|
});
|
|
307
361
|
}
|
|
362
|
+
var OTHER_CHAT_FILE_LIMIT;
|
|
308
363
|
var init_artifacts = __esm({
|
|
309
364
|
"../../packages/core/dist/artifacts.js"() {
|
|
310
365
|
"use strict";
|
|
311
366
|
init_ledger();
|
|
312
367
|
init_paths();
|
|
368
|
+
OTHER_CHAT_FILE_LIMIT = 300;
|
|
313
369
|
}
|
|
314
370
|
});
|
|
315
371
|
|
|
@@ -15429,6 +15485,12 @@ var init_primitives = __esm({
|
|
|
15429
15485
|
*/
|
|
15430
15486
|
inputPriceUsdPerMillion: external_exports.number().nonnegative().max(1e4).optional(),
|
|
15431
15487
|
cachedInputPriceUsdPerMillion: external_exports.number().nonnegative().max(1e4).optional(),
|
|
15488
|
+
/**
|
|
15489
|
+
* The host offers this model for conversation only: the run advertises no
|
|
15490
|
+
* tools, sends no tool-use instructions, and executes no tool call. For a
|
|
15491
|
+
* model that writes its tool calls as text and makes up their results.
|
|
15492
|
+
*/
|
|
15493
|
+
chatOnly: external_exports.boolean().optional(),
|
|
15432
15494
|
submodel: external_exports.string().min(1).optional()
|
|
15433
15495
|
});
|
|
15434
15496
|
BudgetSchema = external_exports.looseObject({
|
|
@@ -17017,7 +17079,7 @@ function toolDescription(name) {
|
|
|
17017
17079
|
case "read_file":
|
|
17018
17080
|
return "Read a session artifact.";
|
|
17019
17081
|
case "list_files":
|
|
17020
|
-
return
|
|
17082
|
+
return `List this chat's artifacts. Each chat with the owner has its own artifacts/ directory, but the owner's Files view shows every chat's files together: set scope="all" to also list files from your other chats before you say a file the owner mentions does not exist.`;
|
|
17021
17083
|
case "run_shell":
|
|
17022
17084
|
return "Run a gated local shell command only when shell access is enabled for the session.";
|
|
17023
17085
|
case "spawn_subagent":
|
|
@@ -17041,7 +17103,10 @@ function toolParameters(name) {
|
|
|
17041
17103
|
case "get_budget_status":
|
|
17042
17104
|
return objectSchema({});
|
|
17043
17105
|
case "list_files":
|
|
17044
|
-
return objectSchema({
|
|
17106
|
+
return objectSchema({
|
|
17107
|
+
prefix: stringSchema("Optional artifact path prefix (this chat only)."),
|
|
17108
|
+
scope: { type: "string", enum: ["chat", "all"], description: "chat (default): this chat's artifacts. all: also every other chat's files, each with the shell_dir to reach it from run_shell." }
|
|
17109
|
+
});
|
|
17045
17110
|
case "save_file":
|
|
17046
17111
|
return objectSchema({
|
|
17047
17112
|
path: stringSchema("Relative artifact path."),
|
|
@@ -17239,8 +17304,21 @@ async function executeTool(name, args, context) {
|
|
|
17239
17304
|
return ok(await saveArtifact(context.session, requiredString(args.path, "path"), requiredString(args.content, "content"), objectValue(args.metadata)));
|
|
17240
17305
|
case "read_file":
|
|
17241
17306
|
return ok({ content: await readArtifact(context.session, requiredString(args.path, "path")) });
|
|
17242
|
-
case "list_files":
|
|
17243
|
-
|
|
17307
|
+
case "list_files": {
|
|
17308
|
+
const files = await listArtifacts(context.session, optionalString(args.prefix, "prefix") ?? "");
|
|
17309
|
+
const scope = optionalString(args.scope, "scope") ?? "chat";
|
|
17310
|
+
if (scope !== "chat" && scope !== "all")
|
|
17311
|
+
throw new Error("scope must be chat or all");
|
|
17312
|
+
if (scope === "chat")
|
|
17313
|
+
return ok(files);
|
|
17314
|
+
const other = await listOtherChatArtifacts(context.session);
|
|
17315
|
+
return ok({
|
|
17316
|
+
files,
|
|
17317
|
+
other_chats: other.chats,
|
|
17318
|
+
...other.truncated ? { truncated: true } : {},
|
|
17319
|
+
note: other.chats.length ? "Files from other chats are already the owner's and already in their Files view. Read one with run_shell at <shell_dir>/<file> (run_shell starts in this chat's session directory). To attach or upload one from this chat, copy it into artifacts/ first; never re-buy or regenerate a file you already hold." : "No other chat of this agent has files."
|
|
17320
|
+
});
|
|
17321
|
+
}
|
|
17244
17322
|
case "run_shell": {
|
|
17245
17323
|
context.onProgress?.({ type: "running_shell", message: "Running gated shell command" });
|
|
17246
17324
|
const result = await runShell(context.session, requiredString(args.command, "command"), optionalString(args.cwd, "cwd") ?? context.session.sessionDir, shellTimeoutMs(args.timeout_ms), { signal: context.signal });
|
|
@@ -87491,19 +87569,47 @@ var init_runtime2 = __esm({
|
|
|
87491
87569
|
}
|
|
87492
87570
|
});
|
|
87493
87571
|
|
|
87572
|
+
// ../../packages/agent-runtime/dist/chat-only.js
|
|
87573
|
+
function chatOnlyMessages(messages) {
|
|
87574
|
+
return messages.flatMap((message) => {
|
|
87575
|
+
if (message.role === "tool")
|
|
87576
|
+
return [];
|
|
87577
|
+
if (message.role !== "assistant" || !message.toolCalls?.length)
|
|
87578
|
+
return [message];
|
|
87579
|
+
if (!message.content.trim())
|
|
87580
|
+
return [];
|
|
87581
|
+
const { toolCalls: _calls, ...rest } = message;
|
|
87582
|
+
return [rest];
|
|
87583
|
+
});
|
|
87584
|
+
}
|
|
87585
|
+
var CHAT_ONLY_SYSTEM_PROMPT;
|
|
87586
|
+
var init_chat_only = __esm({
|
|
87587
|
+
"../../packages/agent-runtime/dist/chat-only.js"() {
|
|
87588
|
+
"use strict";
|
|
87589
|
+
CHAT_ONLY_SYSTEM_PROMPT = [
|
|
87590
|
+
"You are a hosted CrowdCode agent, talking with your owner.",
|
|
87591
|
+
"This agent runs on a chat-only model: in this chat you have no tools. You cannot run commands or code, read, write or send files, browse or search the web, buy or call paid services, send email or messages, schedule reminders or recurring tasks, or move money.",
|
|
87592
|
+
"Talk with the owner: answer questions, explain, write, brainstorm, and take part in role-play or creative writing when they ask for it, from your own knowledge.",
|
|
87593
|
+
"When the owner asks for something that needs one of those abilities, say plainly that this agent is on a chat-only model and cannot do it, suggest switching this agent to a model that can use tools in the model picker, and offer what you can do in words instead.",
|
|
87594
|
+
"Never pretend to have done such a thing. Do not write out a tool or function call, and never invent command output, search results, file contents, prices, or payment, booking or delivery confirmations.",
|
|
87595
|
+
"You cannot check anything live, so say so when an answer depends on current facts."
|
|
87596
|
+
].join(" ");
|
|
87597
|
+
}
|
|
87598
|
+
});
|
|
87599
|
+
|
|
87494
87600
|
// ../../packages/agent-runtime/dist/hosted-provider.js
|
|
87495
87601
|
import { request } from "node:http";
|
|
87496
87602
|
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
87497
87603
|
function createHostedProvider(options) {
|
|
87498
87604
|
if (!isAbsolute3(options.socketPath))
|
|
87499
87605
|
throw new Error("Hosted bridge socket must be absolute");
|
|
87500
|
-
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 ?? []];
|
|
87606
|
+
const tools = options.chatOnly ? [] : [...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 ?? []];
|
|
87501
87607
|
return {
|
|
87502
87608
|
async complete(messages, context) {
|
|
87503
87609
|
const operationId = context?.operationId;
|
|
87504
87610
|
if (!operationId?.startsWith(`${options.runId}:llm:`))
|
|
87505
87611
|
throw new Error("Hosted model operation scope is missing");
|
|
87506
|
-
const body = JSON.stringify({ runId: options.runId, sessionId: options.sessionId, operationId, messages: messages.map(({ contextArchive: _archive, ...message }) => message), tools });
|
|
87612
|
+
const body = JSON.stringify({ runId: options.runId, sessionId: options.sessionId, operationId, messages: (options.chatOnly ? chatOnlyMessages(messages) : messages).map(({ contextArchive: _archive, ...message }) => message), tools });
|
|
87507
87613
|
if (Buffer.byteLength(body) > MODEL_SELLER_REQUEST_MAX_BYTES)
|
|
87508
87614
|
throw new ContextWindowExceeded(`Hosted request is ${Buffer.byteLength(body)} bytes, above the ${MODEL_SELLER_REQUEST_MAX_BYTES} a seller accepts`);
|
|
87509
87615
|
const result = await new Promise((resolve9, reject) => {
|
|
@@ -87682,6 +87788,7 @@ var init_hosted_provider = __esm({
|
|
|
87682
87788
|
init_dist2();
|
|
87683
87789
|
init_dist4();
|
|
87684
87790
|
init_worker();
|
|
87791
|
+
init_chat_only();
|
|
87685
87792
|
HostedRequestError = class extends Error {
|
|
87686
87793
|
code;
|
|
87687
87794
|
status;
|
|
@@ -88833,11 +88940,12 @@ var init_worker = __esm({
|
|
|
88833
88940
|
await beginQuery(session, runId);
|
|
88834
88941
|
await this.emit("run.state", { sessionId: session.sessionId, state: "running" }, runId, run.commandId);
|
|
88835
88942
|
const local = this.options.toolExecutor ?? executeTool;
|
|
88836
|
-
const
|
|
88837
|
-
const
|
|
88943
|
+
const chatOnly = run.start.payload.modelPolicy.chatOnly === true;
|
|
88944
|
+
const hosted = chatOnly ? void 0 : this.options.hostedTools?.(run.start, session);
|
|
88945
|
+
const economy = chatOnly ? void 0 : this.options.economy?.(run.start, session);
|
|
88838
88946
|
const dynamicTools = economy ? hostedDynamicTools(economy) : void 0;
|
|
88839
88947
|
let latestFinancialState = { status: "unavailable", reason: "Hosted financial state has not been read." };
|
|
88840
|
-
const financialState = this.options.financialState ? async () => {
|
|
88948
|
+
const financialState = this.options.financialState && !chatOnly ? async () => {
|
|
88841
88949
|
try {
|
|
88842
88950
|
latestFinancialState = await this.options.financialState(run.start, session);
|
|
88843
88951
|
} catch {
|
|
@@ -88978,6 +89086,7 @@ var init_worker = __esm({
|
|
|
88978
89086
|
signal,
|
|
88979
89087
|
resume: run.checkpoint,
|
|
88980
89088
|
maxTurns: run.start.payload.maxTurns,
|
|
89089
|
+
...chatOnly ? { chatOnly: true } : {},
|
|
88981
89090
|
contextWindowTokens: run.start.payload.modelPolicy.contextWindowTokens,
|
|
88982
89091
|
maxOutputTokens: run.start.payload.modelPolicy.maxOutputTokens,
|
|
88983
89092
|
prices: {
|
|
@@ -89181,8 +89290,10 @@ function createMockToolExecutor() {
|
|
|
89181
89290
|
});
|
|
89182
89291
|
case "read_file":
|
|
89183
89292
|
return ok3({ content: `Mock file content for ${stringValue6(args.path) ?? "unknown path"}.` });
|
|
89184
|
-
case "list_files":
|
|
89185
|
-
|
|
89293
|
+
case "list_files": {
|
|
89294
|
+
const files = ["mock-output.txt"].filter((path3) => path3.startsWith(stringValue6(args.prefix) ?? ""));
|
|
89295
|
+
return ok3(args.scope === "all" ? { files, other_chats: [], note: "No other chat of this agent has files." } : files);
|
|
89296
|
+
}
|
|
89186
89297
|
case "run_shell":
|
|
89187
89298
|
context.onProgress?.({ type: "running_shell", message: "Mock running gated shell command" });
|
|
89188
89299
|
return ok3({
|
|
@@ -89326,8 +89437,8 @@ function wireToolDefinitions(names, extraTools) {
|
|
|
89326
89437
|
];
|
|
89327
89438
|
}
|
|
89328
89439
|
async function runAgentTaskDetailed(session, task, options = {}) {
|
|
89329
|
-
const enabledTools = options.tools ?? (options.hosted ? TOOL_NAMES.filter((name) => !["spawn_subagent", "check_subagents", "get_budget_status"].includes(name)) : TOOL_NAMES.filter((name) => !HOSTED_ONLY_TOOL_NAMES.includes(name) && (options.subagent || name !== "spawn_subagent")));
|
|
89330
|
-
const dynamicDefinitions = options.dynamicTools?.definitions ?? [];
|
|
89440
|
+
const enabledTools = options.chatOnly ? [] : options.tools ?? (options.hosted ? TOOL_NAMES.filter((name) => !["spawn_subagent", "check_subagents", "get_budget_status"].includes(name)) : TOOL_NAMES.filter((name) => !HOSTED_ONLY_TOOL_NAMES.includes(name) && (options.subagent || name !== "spawn_subagent")));
|
|
89441
|
+
const dynamicDefinitions = options.chatOnly ? [] : options.dynamicTools?.definitions ?? [];
|
|
89331
89442
|
const model = options.llm?.catalog?.find((m) => m.id === options.llm?.model);
|
|
89332
89443
|
const limits = contextLimits(options.contextWindowTokens ?? model?.contextWindowTokens, options.maxOutputTokens ?? model?.maxOutputTokens);
|
|
89333
89444
|
const contextState = { ...options.resume?.context };
|
|
@@ -89348,7 +89459,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
89348
89459
|
throw new Error("no LLM provider configured: pass an explicit provider (tests/demo) or a typed llm runtime");
|
|
89349
89460
|
}
|
|
89350
89461
|
const toolExecutor = options.toolExecutor ?? executeTool;
|
|
89351
|
-
const knowledge = options.capabilityIndex === void 0 && options.knowledge !== false && options.dynamicTools ? await loadKnowledgeTree(session, options.knowledge ?? {}) : void 0;
|
|
89462
|
+
const knowledge = !options.chatOnly && options.capabilityIndex === void 0 && options.knowledge !== false && options.dynamicTools ? await loadKnowledgeTree(session, options.knowledge ?? {}) : void 0;
|
|
89352
89463
|
const capabilityIndex = options.capabilityIndex ?? (knowledge ? renderCapabilityIndex(knowledge.l0) : void 0);
|
|
89353
89464
|
const systemPromptParts = [
|
|
89354
89465
|
options.hosted ? "You are a hosted OpenCrowd agent running in the cloud with your own USDC wallets on Base, Solana and Tempo." : "You are the local OpenCrowd agent, a CLI agent with a USDC wallet running on the user's personal machine.",
|
|
@@ -89358,7 +89469,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
89358
89469
|
if (options.hosted)
|
|
89359
89470
|
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.");
|
|
89360
89471
|
if (options.hosted)
|
|
89361
|
-
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.");
|
|
89472
|
+
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.", `Files from your other chats: each chat with the owner is its own session with its own artifacts/, so list_files and read_file see only this one, while the owner's Files view shows every chat's files together. When the owner mentions a file, PDF or report you do not see here, call list_files with scope="all" before you say it does not exist; it lists each other chat's files with the shell_dir that run_shell reaches them at.`);
|
|
89362
89473
|
if (options.dynamicTools) {
|
|
89363
89474
|
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: a call that never reached the seller is never the seller's fault. funding_required means your USDC is on the wrong network, a cap or a declined approval means the owner's policy stopped you, and an unpayable rail means you could not pay at all \u2014 in every one of those the service was never asked, so it gets no review and certainly not a low rating. Fix the cause and call it again instead; the gateway refuses those reviews, and trying anyway wastes a turn. A platform judge, not you, writes the public score and text of a paid review from the platform's record of the call; your rating and notes are evidence it reads, so report accurately and never try to talk it into a rating.", `Choosing a service: find_paid_service with a short capability query (for example "web search" or "object storage", not the task's subject) searches every catalog (AgentCash, Coinbase's x402 catalog, the MPP catalog and CrowdCode's reviews) across Base, Solana and Tempo and returns services you can pay, ranked best first by CrowdCode: quality (score and verified reviews), then effective price on the service's best network, including about $0.03 to move USDC between your own networks when that network's balance is short, then your balance there. Prefer the highest-ranked service that fits the task, whatever its network; a better or cheaper service on Solana or Tempo beats a Base default. Each result also lists \`networks\`, the networks it can be paid on; between services that fit the task equally well, one you can pay from a balance you already hold beats one that needs a transfer first, because a move costs about $0.03. When the chosen service is marked transfer_needed, move USDC between your own networks first if you have a tool for that; otherwise tell the user which network needs funds. When the ranking is unavailable, compare candidates yourself with inspect_paid_service (reputation, price and network) against your per-network balances.`, ...options.hosted ? [
|
|
89364
89475
|
"You can use new services without prior CrowdCode reviews. Payable: x402 USDC on Base or Solana and MPP USDC.e on Tempo; the hosted gateway pays the cheapest offered network your balance on that network covers. Other networks, including Monad, are not payable here. Payments stay 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.",
|
|
@@ -89387,7 +89498,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
89387
89498
|
systemPromptParts.push(`Delegation: spawn_subagent runs a cheaper, faster model (${options.subagent.model}${options.subagent.costHint ? `, ${options.subagent.costHint}` : ""}) with local file and shell tools only.`, "Delegate ONLY when you can spawn two or more INDEPENDENT subagents in the same reply \u2014 parallel fan-out is the entire benefit; multiple spawn_subagent calls in one reply run concurrently (2-4 is the sweet spot).", "If the work is one sequential thread where each step depends on the previous result, do it yourself in this main loop: a chain of single subagents is strictly slower than working directly and adds spawn/read overhead on top.", "Give each subagent an objective, expected output format, and clear boundaries so parallel subagents cannot make conflicting decisions; they see none of this conversation, so pass explicit context (file paths, constraints).", "Each subagent's files land under artifacts/subagents/<n>/ and its completion lists what it wrote.", "Suitable subagent work: summarizing, extracting, mechanical edits, or reading specific already-known URLs (subagents can curl free URLs via run_shell); keep planning, paid-service decisions, and final answers in this main loop.", "For web research: first buy one paid web search here in the main loop, then fan the returned URLs out to parallel subagents in a single reply. Never send a subagent off to guess URLs with curl.");
|
|
89388
89499
|
}
|
|
89389
89500
|
const messages = options.resume?.messages ?? [
|
|
89390
|
-
{ role: "system", content: systemPromptParts.join(" ") },
|
|
89501
|
+
{ role: "system", content: options.chatOnly ? CHAT_ONLY_SYSTEM_PROMPT : systemPromptParts.join(" ") },
|
|
89391
89502
|
...options.history ?? [],
|
|
89392
89503
|
{ role: "user", content: task }
|
|
89393
89504
|
];
|
|
@@ -89540,6 +89651,8 @@ async function runAgentTaskDetailed(session, task, options = {}) {
|
|
|
89540
89651
|
}
|
|
89541
89652
|
throw error53;
|
|
89542
89653
|
}
|
|
89654
|
+
if (options.chatOnly && response.toolCalls.length)
|
|
89655
|
+
response = { ...response, toolCalls: [] };
|
|
89543
89656
|
if (!restored?.response && (response.content || response.toolCalls.length > 0)) {
|
|
89544
89657
|
const assistantMessage = assistantMessageFromResponse(isOutputLimitFinishReason(response.finishReason) ? { ...response, toolCalls: [] } : response);
|
|
89545
89658
|
messages.push(assistantMessage);
|
|
@@ -89559,7 +89672,7 @@ ${outputContinuationPrefix}`);
|
|
|
89559
89672
|
outputContinuationNudges += 1;
|
|
89560
89673
|
const nudge = {
|
|
89561
89674
|
role: "user",
|
|
89562
|
-
content: "Your previous response hit the provider output limit. Any tool calls in that response were discarded and did not execute. If you were calling a tool, issue a complete, shorter tool call. Otherwise continue the answer exactly where it stopped. Put only the remaining suffix in complete_session.final_message; OpenCrowd will prepend the saved prefix. Do not repeat completed material."
|
|
89675
|
+
content: options.chatOnly ? "Your previous response hit the provider output limit. Continue the answer exactly where it stopped, without repeating what you already wrote." : "Your previous response hit the provider output limit. Any tool calls in that response were discarded and did not execute. If you were calling a tool, issue a complete, shorter tool call. Otherwise continue the answer exactly where it stopped. Put only the remaining suffix in complete_session.final_message; OpenCrowd will prepend the saved prefix. Do not repeat completed material."
|
|
89563
89676
|
};
|
|
89564
89677
|
messages.push(nudge);
|
|
89565
89678
|
await options.onMessage?.(nudge);
|
|
@@ -89570,7 +89683,7 @@ ${outputContinuationPrefix}`);
|
|
|
89570
89683
|
continue;
|
|
89571
89684
|
const finalMessage = response.content.trim() ? outputContinuationPrefix + response.content : pendingFinalMessage ?? outputContinuationPrefix;
|
|
89572
89685
|
recordFinalMessage(finalMessage);
|
|
89573
|
-
const blocker = await options.completionGate?.(completionNudges > 0);
|
|
89686
|
+
const blocker = options.chatOnly ? void 0 : await options.completionGate?.(completionNudges > 0);
|
|
89574
89687
|
const unanswered = answerNudges < 1 && placeholder();
|
|
89575
89688
|
if (blocker) {
|
|
89576
89689
|
if (completionNudges >= 1) {
|
|
@@ -90341,6 +90454,8 @@ var init_dist6 = __esm({
|
|
|
90341
90454
|
init_hosted_economy();
|
|
90342
90455
|
init_inbox();
|
|
90343
90456
|
init_x402_proxy();
|
|
90457
|
+
init_chat_only();
|
|
90458
|
+
init_chat_only();
|
|
90344
90459
|
init_knowledge();
|
|
90345
90460
|
init_context2();
|
|
90346
90461
|
init_tool_deadlines();
|
|
@@ -90591,7 +90706,7 @@ var init_dist6 = __esm({
|
|
|
90591
90706
|
});
|
|
90592
90707
|
|
|
90593
90708
|
// src/shared.ts
|
|
90594
|
-
import { readdir } from "node:fs/promises";
|
|
90709
|
+
import { readdir as readdir2 } from "node:fs/promises";
|
|
90595
90710
|
import { join as join24, resolve as resolve5 } from "node:path";
|
|
90596
90711
|
import { stdout as output } from "node:process";
|
|
90597
90712
|
function renderKeyValues(value) {
|
|
@@ -90716,7 +90831,7 @@ function isConsumedOption(args, index2, options) {
|
|
|
90716
90831
|
async function latestSessionId(workspaceRoot) {
|
|
90717
90832
|
const sessionsDir = join24(resolve5(workspaceRoot), "sessions");
|
|
90718
90833
|
try {
|
|
90719
|
-
const entries = await
|
|
90834
|
+
const entries = await readdir2(sessionsDir, { withFileTypes: true });
|
|
90720
90835
|
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().at(-1);
|
|
90721
90836
|
} catch (error53) {
|
|
90722
90837
|
if (error53.code === "ENOENT") {
|
|
@@ -90744,7 +90859,13 @@ async function workerCommand(args) {
|
|
|
90744
90859
|
if (args.includes("--demo") === !!socketPath) throw new Error("worker requires exactly one of --demo or --bridge-socket <path>");
|
|
90745
90860
|
const worker = new MachineWorker({
|
|
90746
90861
|
agentHome: home,
|
|
90747
|
-
provider: (run, session, extraTools) => socketPath ? createHostedProvider({
|
|
90862
|
+
provider: (run, session, extraTools) => socketPath ? createHostedProvider({
|
|
90863
|
+
socketPath,
|
|
90864
|
+
runId: run.runId,
|
|
90865
|
+
sessionId: session.sessionId,
|
|
90866
|
+
extraTools,
|
|
90867
|
+
chatOnly: run.payload.modelPolicy.chatOnly === true
|
|
90868
|
+
}) : createWorkerDemoProvider(),
|
|
90748
90869
|
hostedTools: socketPath ? (run, session) => createHostedToolExecutor({ socketPath, runId: run.runId, sessionId: session.sessionId }) : void 0,
|
|
90749
90870
|
economy: socketPath ? (run, session) => createHostedEconomy({ socketPath, runId: run.runId, sessionId: session.sessionId, session }) : void 0,
|
|
90750
90871
|
financialState: socketPath ? (run, session) => readHostedFinancialState({ socketPath, runId: run.runId, sessionId: session.sessionId }) : void 0,
|
|
@@ -91899,7 +92020,7 @@ var init_tasks = __esm({
|
|
|
91899
92020
|
});
|
|
91900
92021
|
|
|
91901
92022
|
// ../../packages/evals/dist/scoring.js
|
|
91902
|
-
import { readFile as readFile21, stat } from "node:fs/promises";
|
|
92023
|
+
import { readFile as readFile21, stat as stat2 } from "node:fs/promises";
|
|
91903
92024
|
import { resolve as resolveDns } from "node:dns/promises";
|
|
91904
92025
|
import { join as join30 } from "node:path";
|
|
91905
92026
|
async function scoreTask(task, finalMessage, context = {}) {
|
|
@@ -91949,7 +92070,7 @@ async function scoreTask(task, finalMessage, context = {}) {
|
|
|
91949
92070
|
const path3 = join30(context.artifactsDir, name);
|
|
91950
92071
|
let size5;
|
|
91951
92072
|
try {
|
|
91952
|
-
size5 = (await
|
|
92073
|
+
size5 = (await stat2(path3)).size;
|
|
91953
92074
|
} catch {
|
|
91954
92075
|
continue;
|
|
91955
92076
|
}
|
|
@@ -92547,7 +92668,7 @@ var init_evidence = __esm({
|
|
|
92547
92668
|
|
|
92548
92669
|
// ../../packages/evals/dist/knowledge.js
|
|
92549
92670
|
import { createHash as createHash8 } from "node:crypto";
|
|
92550
|
-
import { mkdir as mkdir18, readdir as
|
|
92671
|
+
import { mkdir as mkdir18, readdir as readdir3, readFile as readFile23, writeFile as writeFile12 } from "node:fs/promises";
|
|
92551
92672
|
import { dirname as dirname15, join as join32, relative as relative4 } from "node:path";
|
|
92552
92673
|
function approxTokens(text) {
|
|
92553
92674
|
return Math.ceil(text.length / 4);
|
|
@@ -92650,7 +92771,7 @@ async function writeTree(dir, tree, meta4) {
|
|
|
92650
92771
|
async function readTree(dir) {
|
|
92651
92772
|
const tree = {};
|
|
92652
92773
|
const walk4 = async (current) => {
|
|
92653
|
-
for (const entry of await
|
|
92774
|
+
for (const entry of await readdir3(current, { withFileTypes: true })) {
|
|
92654
92775
|
const path3 = join32(current, entry.name);
|
|
92655
92776
|
if (entry.isDirectory()) {
|
|
92656
92777
|
await walk4(path3);
|
package/package.json
CHANGED