opencrowd 0.16.4 → 0.16.5

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.
Files changed (2) hide show
  1. package/bundle/opencrowd.js +98 -24
  2. package/package.json +1 -1
@@ -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,20 +283,74 @@ 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 walk4(start, files, state.artifactsDir);
288
+ await walkFiles(start, state.artifactsDir, files);
290
289
  return files.sort();
291
- async function walk4(dir, out, root) {
292
- for (const entry of await readdir3(dir, { withFileTypes: true })) {
293
- const full = join3(dir, entry.name);
294
- if (entry.isDirectory()) {
295
- await walk4(full, out, root);
296
- } else if (entry.isFile()) {
297
- out.push(relative2(root, full));
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 {
313
+ }
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;
299
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));
300
354
  }
301
355
  }
302
356
  function safeArtifactPath(state, requestedPath) {
@@ -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
 
@@ -17017,7 +17073,7 @@ function toolDescription(name) {
17017
17073
  case "read_file":
17018
17074
  return "Read a session artifact.";
17019
17075
  case "list_files":
17020
- return "List session artifacts.";
17076
+ 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
17077
  case "run_shell":
17022
17078
  return "Run a gated local shell command only when shell access is enabled for the session.";
17023
17079
  case "spawn_subagent":
@@ -17041,7 +17097,10 @@ function toolParameters(name) {
17041
17097
  case "get_budget_status":
17042
17098
  return objectSchema({});
17043
17099
  case "list_files":
17044
- return objectSchema({ prefix: stringSchema("Optional artifact path prefix.") });
17100
+ return objectSchema({
17101
+ prefix: stringSchema("Optional artifact path prefix (this chat only)."),
17102
+ 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." }
17103
+ });
17045
17104
  case "save_file":
17046
17105
  return objectSchema({
17047
17106
  path: stringSchema("Relative artifact path."),
@@ -17239,8 +17298,21 @@ async function executeTool(name, args, context) {
17239
17298
  return ok(await saveArtifact(context.session, requiredString(args.path, "path"), requiredString(args.content, "content"), objectValue(args.metadata)));
17240
17299
  case "read_file":
17241
17300
  return ok({ content: await readArtifact(context.session, requiredString(args.path, "path")) });
17242
- case "list_files":
17243
- return ok(await listArtifacts(context.session, optionalString(args.prefix, "prefix") ?? ""));
17301
+ case "list_files": {
17302
+ const files = await listArtifacts(context.session, optionalString(args.prefix, "prefix") ?? "");
17303
+ const scope = optionalString(args.scope, "scope") ?? "chat";
17304
+ if (scope !== "chat" && scope !== "all")
17305
+ throw new Error("scope must be chat or all");
17306
+ if (scope === "chat")
17307
+ return ok(files);
17308
+ const other = await listOtherChatArtifacts(context.session);
17309
+ return ok({
17310
+ files,
17311
+ other_chats: other.chats,
17312
+ ...other.truncated ? { truncated: true } : {},
17313
+ 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."
17314
+ });
17315
+ }
17244
17316
  case "run_shell": {
17245
17317
  context.onProgress?.({ type: "running_shell", message: "Running gated shell command" });
17246
17318
  const result = await runShell(context.session, requiredString(args.command, "command"), optionalString(args.cwd, "cwd") ?? context.session.sessionDir, shellTimeoutMs(args.timeout_ms), { signal: context.signal });
@@ -89181,8 +89253,10 @@ function createMockToolExecutor() {
89181
89253
  });
89182
89254
  case "read_file":
89183
89255
  return ok3({ content: `Mock file content for ${stringValue6(args.path) ?? "unknown path"}.` });
89184
- case "list_files":
89185
- return ok3(["mock-output.txt"].filter((path3) => path3.startsWith(stringValue6(args.prefix) ?? "")));
89256
+ case "list_files": {
89257
+ const files = ["mock-output.txt"].filter((path3) => path3.startsWith(stringValue6(args.prefix) ?? ""));
89258
+ return ok3(args.scope === "all" ? { files, other_chats: [], note: "No other chat of this agent has files." } : files);
89259
+ }
89186
89260
  case "run_shell":
89187
89261
  context.onProgress?.({ type: "running_shell", message: "Mock running gated shell command" });
89188
89262
  return ok3({
@@ -89358,7 +89432,7 @@ async function runAgentTaskDetailed(session, task, options = {}) {
89358
89432
  if (options.hosted)
89359
89433
  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
89434
  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.");
89435
+ 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
89436
  if (options.dynamicTools) {
89363
89437
  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
89438
  "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.",
@@ -90591,7 +90665,7 @@ var init_dist6 = __esm({
90591
90665
  });
90592
90666
 
90593
90667
  // src/shared.ts
90594
- import { readdir } from "node:fs/promises";
90668
+ import { readdir as readdir2 } from "node:fs/promises";
90595
90669
  import { join as join24, resolve as resolve5 } from "node:path";
90596
90670
  import { stdout as output } from "node:process";
90597
90671
  function renderKeyValues(value) {
@@ -90716,7 +90790,7 @@ function isConsumedOption(args, index2, options) {
90716
90790
  async function latestSessionId(workspaceRoot) {
90717
90791
  const sessionsDir = join24(resolve5(workspaceRoot), "sessions");
90718
90792
  try {
90719
- const entries = await readdir(sessionsDir, { withFileTypes: true });
90793
+ const entries = await readdir2(sessionsDir, { withFileTypes: true });
90720
90794
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().at(-1);
90721
90795
  } catch (error53) {
90722
90796
  if (error53.code === "ENOENT") {
@@ -91899,7 +91973,7 @@ var init_tasks = __esm({
91899
91973
  });
91900
91974
 
91901
91975
  // ../../packages/evals/dist/scoring.js
91902
- import { readFile as readFile21, stat } from "node:fs/promises";
91976
+ import { readFile as readFile21, stat as stat2 } from "node:fs/promises";
91903
91977
  import { resolve as resolveDns } from "node:dns/promises";
91904
91978
  import { join as join30 } from "node:path";
91905
91979
  async function scoreTask(task, finalMessage, context = {}) {
@@ -91949,7 +92023,7 @@ async function scoreTask(task, finalMessage, context = {}) {
91949
92023
  const path3 = join30(context.artifactsDir, name);
91950
92024
  let size5;
91951
92025
  try {
91952
- size5 = (await stat(path3)).size;
92026
+ size5 = (await stat2(path3)).size;
91953
92027
  } catch {
91954
92028
  continue;
91955
92029
  }
@@ -92547,7 +92621,7 @@ var init_evidence = __esm({
92547
92621
 
92548
92622
  // ../../packages/evals/dist/knowledge.js
92549
92623
  import { createHash as createHash8 } from "node:crypto";
92550
- import { mkdir as mkdir18, readdir as readdir2, readFile as readFile23, writeFile as writeFile12 } from "node:fs/promises";
92624
+ import { mkdir as mkdir18, readdir as readdir3, readFile as readFile23, writeFile as writeFile12 } from "node:fs/promises";
92551
92625
  import { dirname as dirname15, join as join32, relative as relative4 } from "node:path";
92552
92626
  function approxTokens(text) {
92553
92627
  return Math.ceil(text.length / 4);
@@ -92650,7 +92724,7 @@ async function writeTree(dir, tree, meta4) {
92650
92724
  async function readTree(dir) {
92651
92725
  const tree = {};
92652
92726
  const walk4 = async (current) => {
92653
- for (const entry of await readdir2(current, { withFileTypes: true })) {
92727
+ for (const entry of await readdir3(current, { withFileTypes: true })) {
92654
92728
  const path3 = join32(current, entry.name);
92655
92729
  if (entry.isDirectory()) {
92656
92730
  await walk4(path3);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrowd",
3
- "version": "0.16.4",
3
+ "version": "0.16.5",
4
4
  "description": "A CLI agent with its own wallet — discovers x402 services, pays in USDC, and completes tasks under a local budget",
5
5
  "type": "module",
6
6
  "license": "MIT",