@agent-commons/cli 0.4.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.
Files changed (3) hide show
  1. package/README.md +22 -0
  2. package/dist/bin.js +997 -301
  3. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/bin.ts
27
- var import_commander22 = require("commander");
27
+ var import_commander24 = require("commander");
28
28
  var import_child_process3 = require("child_process");
29
29
 
30
30
  // src/commands/login.ts
@@ -178,7 +178,7 @@ var sym = {
178
178
  bullet: import_chalk.default.dim("\u2022"),
179
179
  dot: import_chalk.default.dim("\xB7")
180
180
  };
181
- function banner(version = "0.4.0") {
181
+ function banner(version = "0.6.1") {
182
182
  console.log("");
183
183
  console.log(import_chalk.default.cyan(" \u25C7"));
184
184
  console.log(import_chalk.default.cyan(" \u256D\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
@@ -664,11 +664,11 @@ function agentsCommand() {
664
664
  process.exit(1);
665
665
  }
666
666
  });
667
- cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (agentId2, opts) => {
667
+ cmd.command("get <agentId>").description("Show details for an agent").option("--json", "Output as JSON").action(async (agentId3, opts) => {
668
668
  const spinner = spin("Fetching agent\u2026");
669
669
  try {
670
670
  const client = makeClient();
671
- const res = await client.agents.get(agentId2);
671
+ const res = await client.agents.get(agentId3);
672
672
  const agent = res?.data ?? res;
673
673
  spinner.stop();
674
674
  if (opts.json) return jsonOut(agent);
@@ -747,10 +747,10 @@ ${sym.ok} Agent created`);
747
747
  }
748
748
  });
749
749
  const runtime = cmd.command("runtime").description("Manage an agent runtime");
750
- runtime.command("status <agentId>").description("Show managed runtime status and capabilities").option("--json", "Output as JSON").action(async (agentId2, opts) => {
750
+ runtime.command("status <agentId>").description("Show managed runtime status and capabilities").option("--json", "Output as JSON").action(async (agentId3, opts) => {
751
751
  const spinner = spin("Fetching runtime status\u2026");
752
752
  try {
753
- const result = await makeClient().agents.getRuntime(agentId2);
753
+ const result = await makeClient().agents.getRuntime(agentId3);
754
754
  spinner.stop();
755
755
  if (opts.json) return jsonOut(result.data);
756
756
  detail([
@@ -768,13 +768,13 @@ ${sym.ok} Agent created`);
768
768
  for (const action of ["deploy", "restart", "sleep"]) {
769
769
  runtime.command(`${action} <agentId>`).description(
770
770
  `${action[0].toUpperCase()}${action.slice(1)} the managed agent runtime`
771
- ).action(async (agentId2) => {
771
+ ).action(async (agentId3) => {
772
772
  const spinner = spin(
773
773
  `${action[0].toUpperCase()}${action.slice(1)}ing runtime\u2026`
774
774
  );
775
775
  try {
776
776
  const client = makeClient();
777
- const result = action === "deploy" ? await client.agents.deployRuntime(agentId2) : action === "restart" ? await client.agents.restartRuntime(agentId2) : await client.agents.sleepRuntime(agentId2);
777
+ const result = action === "deploy" ? await client.agents.deployRuntime(agentId3) : action === "restart" ? await client.agents.restartRuntime(agentId3) : await client.agents.sleepRuntime(agentId3);
778
778
  spinner.stop();
779
779
  console.log(`
780
780
  ${sym.ok} Runtime ${result.data.status}`);
@@ -887,12 +887,12 @@ function sessionsCommand() {
887
887
  const spinner = spin("Fetching sessions\u2026");
888
888
  try {
889
889
  const client = makeClient();
890
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
891
- const res = agentId2 ? await client.sessions.list(agentId2, cfg.initiator) : await client.sessions.listByUser(cfg.initiator);
890
+ const agentId3 = opts.agent;
891
+ const res = agentId3 ? await client.sessions.list(agentId3, cfg.initiator) : await client.sessions.listByUser(cfg.initiator);
892
892
  const sessions = res?.data ?? res ?? [];
893
893
  spinner.stop();
894
894
  if (opts.json) return jsonOut(sessions);
895
- section(`Sessions (${sessions.length})${agentId2 ? ` \u2014 agent ${agentId2.slice(0, 8)}\u2026` : " \u2014 all agents"}`);
895
+ section(`Sessions (${sessions.length})${agentId3 ? ` \u2014 agent ${agentId3.slice(0, 8)}\u2026` : " \u2014 all agents"}`);
896
896
  table(
897
897
  sessions.map((s) => ({
898
898
  ID: s.sessionId.slice(0, 8) + "\u2026",
@@ -933,8 +933,8 @@ function sessionsCommand() {
933
933
  });
934
934
  cmd.command("create").description("Create a new session").option("--agent <agentId>", "Agent ID").option("--title <title>", "Session title").option("--model <id>", "Model ID (e.g. gpt-5.4-mini, claude-sonnet-4-6)").option("--provider <provider>", "Model provider").option("--json", "Output as JSON").action(async (opts) => {
935
935
  const cfg = loadConfig();
936
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
937
- if (!agentId2) {
936
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
937
+ if (!agentId3) {
938
938
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
939
939
  process.exit(1);
940
940
  }
@@ -946,7 +946,7 @@ function sessionsCommand() {
946
946
  try {
947
947
  const client = makeClient();
948
948
  const res = await client.sessions.create({
949
- agentId: agentId2,
949
+ agentId: agentId3,
950
950
  initiator: cfg.initiator,
951
951
  title: opts.title,
952
952
  ...opts.model && { model: { modelId: opts.model, provider: opts.provider } }
@@ -1102,8 +1102,8 @@ ${sym.ok} Tool created`);
1102
1102
  });
1103
1103
  cmd.command("exec <toolName>").description("Execute a tool directly by name").option("--agent <agentId>", "Agent context for tool execution").option("--args <json>", "Tool arguments as JSON object", "{}").option("--json", "Output result as JSON").action(async (toolName, opts) => {
1104
1104
  const cfg = loadConfig();
1105
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1106
- if (!agentId2) {
1105
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1106
+ if (!agentId3) {
1107
1107
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1108
1108
  process.exit(1);
1109
1109
  }
@@ -1119,7 +1119,7 @@ ${sym.ok} Tool created`);
1119
1119
  try {
1120
1120
  const client = makeClient();
1121
1121
  const result = await client.run.once({
1122
- agentId: agentId2,
1122
+ agentId: agentId3,
1123
1123
  messages: [{ role: "user", content: prompt }],
1124
1124
  ...cfg.initiator && { initiatorId: cfg.initiator }
1125
1125
  });
@@ -1241,7 +1241,10 @@ ${sym.ok} Authorize the connection in your browser:`);
1241
1241
  ["Account", connection.providerUserEmail ?? connection.providerUserName ?? ""],
1242
1242
  ["Status", connection.status],
1243
1243
  ["Scopes", connection.scopes.join(", ")],
1244
- ["Expires", connection.expiresAt ?? c.dim("(not reported)")]
1244
+ [
1245
+ "Expires",
1246
+ connection.accessTokenExpiresAt ?? connection.expiresAt ?? c.dim("(does not expire)")
1247
+ ]
1245
1248
  ]);
1246
1249
  } catch (err) {
1247
1250
  spinner.stop();
@@ -1445,8 +1448,8 @@ ${sym.ok} Workflow created`);
1445
1448
  }
1446
1449
  const templateName = templateNameRaw;
1447
1450
  const needsAgent = templateName === "agent-research-summary" || templateName === "multi-agent-field-report";
1448
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1449
- if (needsAgent && !agentId2) {
1451
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1452
+ if (needsAgent && !agentId3) {
1450
1453
  console.error(c.error("This template requires --agent <agentId> or a configured defaultAgentId."));
1451
1454
  process.exit(1);
1452
1455
  }
@@ -1470,7 +1473,7 @@ ${sym.ok} Workflow created`);
1470
1473
  const ctx = {
1471
1474
  ownerId: cfg.initiator,
1472
1475
  prefix,
1473
- agentId: agentId2,
1476
+ agentId: agentId3,
1474
1477
  reviewerAgentId: opts.reviewerAgent,
1475
1478
  childWorkflowId
1476
1479
  };
@@ -1490,7 +1493,7 @@ ${sym.ok} Workflow created`);
1490
1493
  }
1491
1494
  }
1492
1495
  execution = await makeClient().workflows.execute(result.workflow.workflowId, {
1493
- agentId: agentId2,
1496
+ agentId: agentId3,
1494
1497
  inputData,
1495
1498
  userId: cfg.initiator
1496
1499
  });
@@ -1561,7 +1564,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1561
1564
  });
1562
1565
  cmd.command("run <workflowId>").description("Execute a workflow").option("--agent <agentId>", "Agent context").option("--session <sessionId>", "Session context").option("--input <json>", "Input data as JSON string", "{}").option("--watch", "Stream execution progress via SSE").option("--json", "Output result as JSON").action(async (workflowId, opts) => {
1563
1566
  const cfg = loadConfig();
1564
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1567
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1565
1568
  let inputData = {};
1566
1569
  try {
1567
1570
  inputData = JSON.parse(opts.input);
@@ -1573,7 +1576,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1573
1576
  try {
1574
1577
  const client = makeClient();
1575
1578
  const execution = await client.workflows.execute(workflowId, {
1576
- agentId: agentId2,
1579
+ agentId: agentId3,
1577
1580
  sessionId: opts.session,
1578
1581
  inputData
1579
1582
  });
@@ -1718,12 +1721,12 @@ function taskCommand() {
1718
1721
  const cmd = new import_commander7.Command("task").description("Manage and execute tasks").alias("t");
1719
1722
  cmd.command("list").description("List tasks").option("--agent <agentId>", "Filter by agent ID").option("--session <sessionId>", "Filter by session ID").option("--json", "Output as JSON").action(async (opts) => {
1720
1723
  const cfg = loadConfig();
1721
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1724
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1722
1725
  const spinner = spin("Fetching tasks\u2026");
1723
1726
  try {
1724
1727
  const client = makeClient();
1725
1728
  const filter = {};
1726
- if (agentId2) filter.agentId = agentId2;
1729
+ if (agentId3) filter.agentId = agentId3;
1727
1730
  if (opts.session) filter.sessionId = opts.session;
1728
1731
  if (cfg.initiator) {
1729
1732
  filter.ownerId = cfg.initiator;
@@ -1779,8 +1782,8 @@ function taskCommand() {
1779
1782
  });
1780
1783
  cmd.command("create").description("Create a new task").requiredOption("--title <title>", "Task title").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Session ID").option("--workflow <workflowId>", "Workflow ID to attach").option("--input <json>", "Input data as JSON", "{}").option("--timeout <ms>", "Execution timeout in milliseconds").option("--execute", "Execute immediately after creation").option("--watch", "Stream execution progress (implies --execute)").option("--json", "Output as JSON").action(async (opts) => {
1781
1784
  const cfg = loadConfig();
1782
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1783
- if (!agentId2) {
1785
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1786
+ if (!agentId3) {
1784
1787
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1785
1788
  process.exit(1);
1786
1789
  }
@@ -1796,7 +1799,7 @@ function taskCommand() {
1796
1799
  const client = makeClient();
1797
1800
  const res = await client.tasks.create({
1798
1801
  title: opts.title,
1799
- agentId: agentId2,
1802
+ agentId: agentId3,
1800
1803
  sessionId: opts.session,
1801
1804
  workflowId: opts.workflow,
1802
1805
  inputData,
@@ -1902,14 +1905,237 @@ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1902
1905
  // src/commands/run.ts
1903
1906
  var import_commander8 = require("commander");
1904
1907
  var readline2 = __toESM(require("readline"));
1908
+ var import_fs6 = require("fs");
1909
+ var import_path4 = require("path");
1905
1910
 
1906
1911
  // src/local-tools.ts
1907
1912
  var import_fs5 = require("fs");
1908
1913
  var import_path3 = require("path");
1909
1914
  var import_child_process2 = require("child_process");
1915
+ var import_crypto = require("crypto");
1910
1916
  var readline = __toESM(require("readline"));
1917
+
1918
+ // src/edit-preview.ts
1919
+ function editPreview(before, after, limit = 12e3) {
1920
+ if (before === after) return "(no content change)";
1921
+ const oldLines = before ? before.split("\n") : [];
1922
+ const newLines = after ? after.split("\n") : [];
1923
+ let start = 0;
1924
+ while (start < oldLines.length && start < newLines.length && oldLines[start] === newLines[start]) start++;
1925
+ let oldEnd = oldLines.length, newEnd = newLines.length;
1926
+ while (oldEnd > start && newEnd > start && oldLines[oldEnd - 1] === newLines[newEnd - 1]) {
1927
+ oldEnd--;
1928
+ newEnd--;
1929
+ }
1930
+ const lines = [
1931
+ `@@ -${start + 1},${oldEnd - start} +${start + 1},${newEnd - start} @@`,
1932
+ ...oldLines.slice(Math.max(0, start - 3), start).map((line) => ` ${line}`),
1933
+ ...oldLines.slice(start, oldEnd).map((line) => `-${line}`),
1934
+ ...newLines.slice(start, newEnd).map((line) => `+${line}`),
1935
+ ...newLines.slice(newEnd, newEnd + 3).map((line) => ` ${line}`)
1936
+ ];
1937
+ const preview = lines.join("\n").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "?");
1938
+ return preview.length > limit ? preview.slice(0, limit) + "\n\u2026 (preview truncated)" : preview;
1939
+ }
1940
+
1941
+ // src/disk-usage.ts
1942
+ var import_node_fs = require("fs");
1943
+ var import_node_path = require("path");
1944
+ async function scanDiskUsage(root, signal, allowPath) {
1945
+ if (!(await import_node_fs.promises.lstat(root)).isDirectory()) throw new Error("disk_usage requires a directory");
1946
+ const deadline = Date.now() + 15e3;
1947
+ const limit = 5e4;
1948
+ let scanned = 0;
1949
+ let skipped = 0;
1950
+ let incomplete = false;
1951
+ const measure = async (start) => {
1952
+ let bytes = 0;
1953
+ const pending = [start];
1954
+ while (pending.length) {
1955
+ if (signal?.aborted) throw new Error("Disk usage scan cancelled");
1956
+ if (scanned >= limit || Date.now() >= deadline) {
1957
+ incomplete = true;
1958
+ break;
1959
+ }
1960
+ const path = pending.pop();
1961
+ try {
1962
+ allowPath(path);
1963
+ const item = await import_node_fs.promises.lstat(path);
1964
+ scanned += 1;
1965
+ if (item.isSymbolicLink()) {
1966
+ skipped += 1;
1967
+ continue;
1968
+ }
1969
+ if (item.isDirectory()) {
1970
+ for await (const child of await import_node_fs.promises.opendir(path)) {
1971
+ if (pending.length + scanned >= limit || Date.now() >= deadline) {
1972
+ incomplete = true;
1973
+ break;
1974
+ }
1975
+ pending.push((0, import_node_path.join)(path, child.name));
1976
+ }
1977
+ } else if (item.isFile()) bytes += item.size;
1978
+ } catch {
1979
+ skipped += 1;
1980
+ }
1981
+ }
1982
+ return bytes;
1983
+ };
1984
+ const entries = [];
1985
+ for await (const child of await import_node_fs.promises.opendir(root)) {
1986
+ if (incomplete) break;
1987
+ if (child.isSymbolicLink()) {
1988
+ skipped += 1;
1989
+ continue;
1990
+ }
1991
+ const path = (0, import_node_path.join)(root, child.name);
1992
+ try {
1993
+ allowPath(path);
1994
+ } catch {
1995
+ skipped += 1;
1996
+ continue;
1997
+ }
1998
+ entries.push({ path: child.name, kind: child.isDirectory() ? "directory" : "file", sizeBytes: await measure(path) });
1999
+ }
2000
+ entries.sort((left, right) => right.sizeBytes - left.sizeBytes);
2001
+ return {
2002
+ scope: root,
2003
+ unit: "logical file bytes",
2004
+ entries: entries.slice(0, 30),
2005
+ scanned,
2006
+ skipped,
2007
+ incomplete,
2008
+ note: incomplete ? "The bounded scan stopped early. Sizes may be underestimates; narrow the path and scan again." : "Sizes cover this selected folder only, not the whole computer."
2009
+ };
2010
+ }
2011
+
2012
+ // src/office-text.ts
2013
+ var import_zlib = require("zlib");
2014
+ var MAX_ENTRY_BYTES = 4e7;
2015
+ function readEntries(buffer) {
2016
+ const floor = Math.max(0, buffer.length - 65557);
2017
+ let end = -1;
2018
+ for (let index = buffer.length - 22; index >= floor; index -= 1) {
2019
+ if (buffer.readUInt32LE(index) === 101010256) {
2020
+ end = index;
2021
+ break;
2022
+ }
2023
+ }
2024
+ if (end < 0) throw new Error("Not a zip archive");
2025
+ const count = buffer.readUInt16LE(end + 10);
2026
+ let cursor = buffer.readUInt32LE(end + 16);
2027
+ const entries = [];
2028
+ for (let index = 0; index < count && cursor + 46 <= buffer.length; index += 1) {
2029
+ if (buffer.readUInt32LE(cursor) !== 33639248) break;
2030
+ const method = buffer.readUInt16LE(cursor + 10);
2031
+ const compressedSize = buffer.readUInt32LE(cursor + 20);
2032
+ const nameLength = buffer.readUInt16LE(cursor + 28);
2033
+ const extraLength = buffer.readUInt16LE(cursor + 30);
2034
+ const commentLength = buffer.readUInt16LE(cursor + 32);
2035
+ const offset = buffer.readUInt32LE(cursor + 42);
2036
+ const name = buffer.toString("utf8", cursor + 46, cursor + 46 + nameLength);
2037
+ entries.push({ name, method, compressedSize, offset });
2038
+ cursor += 46 + nameLength + extraLength + commentLength;
2039
+ }
2040
+ return entries;
2041
+ }
2042
+ function readEntry(buffer, entry) {
2043
+ if (buffer.readUInt32LE(entry.offset) !== 67324752) throw new Error("Corrupt zip entry");
2044
+ const nameLength = buffer.readUInt16LE(entry.offset + 26);
2045
+ const extraLength = buffer.readUInt16LE(entry.offset + 28);
2046
+ const start = entry.offset + 30 + nameLength + extraLength;
2047
+ const data = buffer.subarray(start, start + entry.compressedSize);
2048
+ if (entry.method === 0) return data.toString("utf8");
2049
+ if (entry.method === 8) {
2050
+ return (0, import_zlib.inflateRawSync)(data, { maxOutputLength: MAX_ENTRY_BYTES }).toString("utf8");
2051
+ }
2052
+ throw new Error(`Unsupported zip compression method ${entry.method}`);
2053
+ }
2054
+ function decodeXml(value) {
2055
+ return value.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCodePoint(parseInt(code, 16))).replace(/&amp;/g, "&");
2056
+ }
2057
+ function docxText(xml) {
2058
+ const paragraphs = xml.match(/<w:p[\s>][\s\S]*?<\/w:p>/g) ?? [];
2059
+ const lines = paragraphs.map((paragraph) => {
2060
+ const style = /<w:pStyle w:val="([^"]+)"/.exec(paragraph)?.[1] ?? "";
2061
+ const heading = /^(?:Heading|heading)\s?(\d)$/.exec(style)?.[1] ?? (style === "Title" ? "1" : "");
2062
+ const listItem = /<w:numPr>/.test(paragraph);
2063
+ const text = paragraph.replace(/<w:tab\/>/g, " ").replace(/<w:(?:br|cr)\/>/g, "\n").match(/<w:t(?:\s[^>]*)?>[\s\S]*?<\/w:t>|\t|\n/g)?.map((part) => part === " " || part === "\n" ? part : decodeXml(part.replace(/<[^>]+>/g, ""))).join("") ?? "";
2064
+ if (!text.trim()) return "";
2065
+ if (heading) return `${"#".repeat(Math.min(6, Number(heading)))} ${text.trim()}`;
2066
+ return listItem ? `- ${text.trim()}` : text;
2067
+ });
2068
+ return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
2069
+ }
2070
+ function drawingText(xml) {
2071
+ const paragraphs = xml.match(/<a:p[\s>][\s\S]*?<\/a:p>/g) ?? [];
2072
+ return paragraphs.map(
2073
+ (paragraph) => (paragraph.match(/<a:t>[\s\S]*?<\/a:t>/g) ?? []).map((part) => decodeXml(part.replace(/<[^>]+>/g, ""))).join("")
2074
+ ).filter((line) => line.trim()).join("\n");
2075
+ }
2076
+ function slideNumber(name) {
2077
+ return Number(/(\d+)\.xml$/.exec(name)?.[1] ?? 0);
2078
+ }
2079
+ function extractOfficeOpenXmlText(buffer, extension) {
2080
+ let entries;
2081
+ try {
2082
+ entries = readEntries(buffer);
2083
+ } catch {
2084
+ return null;
2085
+ }
2086
+ const find = (name) => entries.find((entry) => entry.name === name);
2087
+ try {
2088
+ if (extension === ".docx") {
2089
+ const document = find("word/document.xml");
2090
+ return document ? docxText(readEntry(buffer, document)) : null;
2091
+ }
2092
+ if (extension === ".pptx") {
2093
+ const slides = entries.filter((entry) => /^ppt\/slides\/slide\d+\.xml$/.test(entry.name)).sort((left, right) => slideNumber(left.name) - slideNumber(right.name));
2094
+ if (!slides.length) return null;
2095
+ return slides.map((slide) => `## Slide ${slideNumber(slide.name)}
2096
+ ${drawingText(readEntry(buffer, slide))}`).join("\n\n").trim();
2097
+ }
2098
+ if (extension === ".xlsx") {
2099
+ const shared = find("xl/sharedStrings.xml");
2100
+ const strings = shared ? (readEntry(buffer, shared).match(/<si>[\s\S]*?<\/si>/g) ?? []).map(
2101
+ (item) => (item.match(/<t(?:\s[^>]*)?>[\s\S]*?<\/t>/g) ?? []).map((part) => decodeXml(part.replace(/<[^>]+>/g, ""))).join("")
2102
+ ) : [];
2103
+ const sheets = entries.filter((entry) => /^xl\/worksheets\/sheet\d+\.xml$/.test(entry.name)).sort((left, right) => slideNumber(left.name) - slideNumber(right.name));
2104
+ if (!sheets.length) return null;
2105
+ return sheets.map((sheet) => {
2106
+ const rows = (readEntry(buffer, sheet).match(/<row[\s\S]*?<\/row>/g) ?? []).slice(0, 2e3).map(
2107
+ (row) => (row.match(/<c[\s\S]*?<\/c>|<c[^>]*\/>/g) ?? []).map((cell) => {
2108
+ const value = /<v>([\s\S]*?)<\/v>/.exec(cell)?.[1] ?? /<t(?:\s[^>]*)?>([\s\S]*?)<\/t>/.exec(cell)?.[1] ?? "";
2109
+ return /\bt="s"/.test(cell) ? strings[Number(value)] ?? "" : decodeXml(value);
2110
+ }).join(" ")
2111
+ );
2112
+ return `## Sheet ${slideNumber(sheet.name)}
2113
+ ${rows.join("\n")}`;
2114
+ }).join("\n\n").trim();
2115
+ }
2116
+ } catch {
2117
+ return null;
2118
+ }
2119
+ return null;
2120
+ }
2121
+
2122
+ // src/local-tools.ts
1911
2123
  var pdfParse = require("pdf-parse/lib/pdf-parse.js");
1912
2124
  var managedProcesses = /* @__PURE__ */ new Map();
2125
+ function stopProcessTree(proc) {
2126
+ const pid = proc.child.pid;
2127
+ if (!pid) return;
2128
+ if (process.platform === "win32") {
2129
+ const killer = (0, import_child_process2.spawn)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
2130
+ killer.on("error", () => proc.child.kill("SIGTERM"));
2131
+ } else {
2132
+ try {
2133
+ process.kill(-pid, "SIGTERM");
2134
+ } catch {
2135
+ proc.child.kill("SIGTERM");
2136
+ }
2137
+ }
2138
+ }
1913
2139
  function capBuffer(existing, chunk, maxBytes) {
1914
2140
  const joined = existing + chunk;
1915
2141
  if (joined.length <= maxBytes) return joined;
@@ -1946,12 +2172,8 @@ function buildDirSnapshot(dir, maxDepth = 2) {
1946
2172
  }
1947
2173
  function readFileForContext(rootDir, filePath) {
1948
2174
  try {
1949
- const abs = (0, import_path3.resolve)(rootDir, filePath);
1950
- const rel = (0, import_path3.relative)(rootDir, abs);
1951
- if (rel.startsWith("..") || rel.startsWith("/")) return `[error: path escapes session root]`;
1952
- for (const pat of [/\/\.ssh\//, /\/\.aws\//, /\/\.env$/, /\/\.env\./, /id_rsa/, /id_ed25519/]) {
1953
- if (pat.test(abs)) return `[error: sensitive path blocked]`;
1954
- }
2175
+ const abs = safePath(rootDir, filePath);
2176
+ assertNotSensitive(abs);
1955
2177
  if (!(0, import_fs5.existsSync)(abs)) return `[error: file not found: ${filePath}]`;
1956
2178
  const stat = (0, import_fs5.statSync)(abs);
1957
2179
  if (stat.isDirectory()) return `[error: "${filePath}" is a directory \u2014 use list_directory]`;
@@ -1996,9 +2218,11 @@ ${fileSection}
1996
2218
  | Tool | What it does |
1997
2219
  |------|-------------|
1998
2220
  | \`cli_list_directory\` | List files and folders at a path |
1999
- | \`cli_read_file\` | Read a file (PDF and Word docs are extracted to text) |
2221
+ | \`cli_read_file\` | Read a bounded part of a file (PDF and Word docs are extracted to text) |
2222
+ | \`cli_search_file\` | Find passages and character offsets inside one large document |
2000
2223
  | \`cli_write_file\` | Write or overwrite a file (user confirmation required) |
2001
2224
  | \`cli_search_files\` | Find files matching a pattern |
2225
+ | \`cli_disk_usage\` | Measure and rank file and folder sizes inside the selected root (read only; bounded scan) |
2002
2226
  | \`cli_run_command\` | Run a short command and return its output (user confirmation required) |
2003
2227
  | \`cli_start_process\` | Start a long-running command in the background; returns a processId immediately |
2004
2228
  | \`cli_wait_for_process\` | Block up to N seconds for a background process, then return current output |
@@ -2057,102 +2281,50 @@ function extractToolCall(text) {
2057
2281
  }
2058
2282
  return null;
2059
2283
  }
2060
- function injectAgcTrailer(command, args, agentId2, agentName) {
2284
+ function injectAgcTrailer(command, args, agentId3, agentName) {
2061
2285
  if (command !== "git") return args;
2062
2286
  if (!args.some((a) => a === "commit")) return args;
2063
2287
  if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
2064
- const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
2288
+ const identity = agentName ? `${agentName} (agc)` : agentId3 ? `agc/${agentId3}` : "agc agent";
2065
2289
  return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
2066
2290
  }
2067
- var AGC_HOOK_MARKER = "# agc-session:";
2068
- var HOOK_BACKUP_SUFFIX = ".agc-backup";
2069
- function findGitDir(rootDir) {
2070
- const gitPath = (0, import_path3.join)(rootDir, ".git");
2071
- if (!(0, import_fs5.existsSync)(gitPath)) return null;
2072
- const s = (0, import_fs5.statSync)(gitPath);
2073
- if (s.isDirectory()) return gitPath;
2074
- if (s.isFile()) {
2075
- const content = (0, import_fs5.readFileSync)(gitPath, "utf8");
2076
- const match = content.match(/^gitdir:\s*(.+)$/m);
2077
- if (match) return match[1].trim();
2078
- }
2079
- return null;
2080
- }
2081
- function installGitHook(rootDir, sessionId, agentId2, agentName) {
2082
- const gitDir = findGitDir(rootDir);
2083
- if (!gitDir) return;
2084
- const hooksDir = (0, import_path3.join)(gitDir, "hooks");
2085
- (0, import_fs5.mkdirSync)(hooksDir, { recursive: true });
2086
- const hookPath = (0, import_path3.join)(hooksDir, "prepare-commit-msg");
2087
- if ((0, import_fs5.existsSync)(hookPath)) {
2088
- const existing = (0, import_fs5.readFileSync)(hookPath, "utf8");
2089
- if (!existing.includes(AGC_HOOK_MARKER)) {
2090
- (0, import_fs5.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
2091
- }
2092
- }
2093
- const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
2094
- const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
2095
- const chainLine = (0, import_fs5.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
2096
- # chain pre-existing hook
2097
- "$(dirname "$0")/prepare-commit-msg${HOOK_BACKUP_SUFFIX}" "$@" 2>/dev/null || true
2098
- ` : "";
2099
- const hook = `#!/bin/sh
2100
- ${AGC_HOOK_MARKER}${sessionId}
2101
- COMMIT_MSG_FILE="$1"
2102
- COMMIT_SOURCE="$2"
2103
- ${chainLine}
2104
- case "$COMMIT_SOURCE" in merge|squash) exit 0 ;; esac
2105
- TRAILER="${trailer}"
2106
- grep -qF "$TRAILER" "$COMMIT_MSG_FILE" 2>/dev/null && exit 0
2107
- printf '\\n%s\\n' "$TRAILER" >> "$COMMIT_MSG_FILE"
2108
- `;
2109
- (0, import_fs5.writeFileSync)(hookPath, hook, { mode: 493 });
2110
- }
2111
- function removeGitHook(rootDir) {
2112
- const gitDir = findGitDir(rootDir);
2113
- if (!gitDir) return;
2114
- const hookPath = (0, import_path3.join)(gitDir, "hooks", "prepare-commit-msg");
2115
- if (!(0, import_fs5.existsSync)(hookPath)) return;
2116
- const content = (0, import_fs5.readFileSync)(hookPath, "utf8");
2117
- if (!content.includes(AGC_HOOK_MARKER)) return;
2118
- const backupPath = hookPath + HOOK_BACKUP_SUFFIX;
2119
- if ((0, import_fs5.existsSync)(backupPath)) {
2120
- (0, import_fs5.writeFileSync)(hookPath, (0, import_fs5.readFileSync)(backupPath, "utf8"), { mode: 493 });
2121
- (0, import_fs5.unlinkSync)(backupPath);
2122
- } else {
2123
- (0, import_fs5.unlinkSync)(hookPath);
2124
- }
2125
- }
2126
2291
  function safePath(root, userPath) {
2127
- const abs = (0, import_path3.resolve)(root, userPath);
2128
- const rel = (0, import_path3.relative)(root, abs);
2129
- if (rel.startsWith("..") || rel.startsWith("/")) {
2130
- throw new Error(`Path "${userPath}" escapes the session root. Access denied.`);
2292
+ const canonicalRoot = (0, import_fs5.realpathSync)(root);
2293
+ const abs = (0, import_path3.resolve)(canonicalRoot, userPath);
2294
+ const inside = (path) => {
2295
+ const rel = (0, import_path3.relative)(canonicalRoot, path);
2296
+ return rel !== ".." && !rel.startsWith(`..${import_path3.sep}`) && !(0, import_path3.isAbsolute)(rel);
2297
+ };
2298
+ if (!inside(abs) && !(0, import_path3.isAbsolute)(userPath)) throw new Error(`Path "${userPath}" escapes the session root. Access denied.`);
2299
+ let ancestor = abs;
2300
+ while (!(0, import_fs5.existsSync)(ancestor)) {
2301
+ try {
2302
+ if ((0, import_fs5.lstatSync)(ancestor).isSymbolicLink()) throw new Error("Dangling symlink is not allowed.");
2303
+ } catch (error) {
2304
+ if (error.code !== "ENOENT") throw error;
2305
+ }
2306
+ const parent = (0, import_path3.dirname)(ancestor);
2307
+ if (parent === ancestor) throw new Error("Cannot resolve path.");
2308
+ ancestor = parent;
2131
2309
  }
2132
- return abs;
2310
+ const canonicalAncestor = (0, import_fs5.realpathSync)(ancestor);
2311
+ if (!inside(canonicalAncestor)) throw new Error(`Path "${userPath}" resolves outside the session root. Access denied.`);
2312
+ assertNotSensitive(abs);
2313
+ assertNotSensitive(canonicalAncestor);
2314
+ return (0, import_path3.resolve)(canonicalAncestor, (0, import_path3.relative)(ancestor, abs));
2133
2315
  }
2134
- var BLOCKED_PATTERNS = [
2135
- /\/\.ssh\//,
2136
- /\/\.gnupg\//,
2137
- /\/\.agc\//,
2138
- /\/\.aws\//,
2139
- /\/\.env$/,
2140
- /\/\.env\./,
2141
- /id_rsa/,
2142
- /id_ed25519/
2143
- ];
2144
2316
  function assertNotSensitive(abs) {
2145
- for (const pat of BLOCKED_PATTERNS) {
2146
- if (pat.test(abs)) {
2147
- throw new Error(`Access to "${abs}" is blocked for security reasons.`);
2148
- }
2317
+ const segments = abs.split(/[\\/]/);
2318
+ if (segments.some((part) => /^(?:\.ssh|\.gnupg|\.agc|\.aws|\.env(?:\..*)?|id_rsa|id_ed25519)$/i.test(part))) {
2319
+ throw new Error(`Access to "${abs}" is blocked for security reasons.`);
2149
2320
  }
2150
2321
  }
2151
2322
  async function confirm(message, config, permissionKey) {
2152
- if (config.autoApprove) return true;
2153
2323
  const cached = config.permissions.get(permissionKey);
2154
2324
  if (cached === "allow") return true;
2155
2325
  if (cached === "deny") return false;
2326
+ if (config.autoApprove) return true;
2327
+ if (config.confirm) return config.confirm(message, permissionKey);
2156
2328
  return new Promise((resolve2) => {
2157
2329
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2158
2330
  process.stdout.write(
@@ -2161,22 +2333,23 @@ async function confirm(message, config, permissionKey) {
2161
2333
  \x1B[2m[y] Yes [n] No [A] Always allow this type [N] Never allow this type\x1B[0m
2162
2334
  \x1B[36m?\x1B[0m `
2163
2335
  );
2336
+ rl.once("close", () => resolve2(false));
2164
2337
  rl.once("line", (answer) => {
2165
- rl.close();
2166
- const a = answer.trim().toLowerCase();
2167
- if (a === "a") {
2338
+ const a = answer.trim();
2339
+ if (a === "A") {
2168
2340
  config.permissions.set(permissionKey, "allow");
2169
2341
  resolve2(true);
2170
- } else if (a === "n" || a === "nn") {
2342
+ } else if (a === "N") {
2171
2343
  config.permissions.set(permissionKey, "deny");
2172
2344
  resolve2(false);
2173
2345
  } else {
2174
- resolve2(a === "y" || a === "yes" || a === "");
2346
+ resolve2(a.toLowerCase() === "y" || a.toLowerCase() === "yes");
2175
2347
  }
2348
+ rl.close();
2176
2349
  });
2177
2350
  });
2178
2351
  }
2179
- var OFFICE_EXTS = /* @__PURE__ */ new Set([".docx", ".doc", ".rtf", ".odt", ".pages"]);
2352
+ var OFFICE_EXTS = /* @__PURE__ */ new Set([".docx", ".doc", ".rtf", ".odt", ".pages", ".pptx", ".xlsx"]);
2180
2353
  var PDF_EXTS = /* @__PURE__ */ new Set([".pdf"]);
2181
2354
  var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
2182
2355
  ".png",
@@ -2208,9 +2381,7 @@ var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
2208
2381
  ".ai",
2209
2382
  ".sketch",
2210
2383
  ".figma",
2211
- ".xlsx",
2212
2384
  ".xls",
2213
- ".pptx",
2214
2385
  ".ppt"
2215
2386
  ]);
2216
2387
  function extractViaCommand(cmd, cmdArgs) {
@@ -2221,17 +2392,16 @@ function extractViaCommand(cmd, cmdArgs) {
2221
2392
  });
2222
2393
  });
2223
2394
  }
2224
- async function extractPdfText(abs) {
2395
+ async function extractPdfText(abs, maxChars = 15e4) {
2225
2396
  try {
2226
2397
  const buffer = (0, import_fs5.readFileSync)(abs);
2227
2398
  const data = await pdfParse(buffer);
2228
2399
  const text2 = data.text?.trim();
2229
2400
  if (text2) {
2230
- const MAX_CHARS = 15e4;
2231
- if (text2.length > MAX_CHARS) {
2232
- return text2.slice(0, MAX_CHARS) + `
2401
+ if (text2.length > maxChars) {
2402
+ return text2.slice(0, maxChars) + `
2233
2403
 
2234
- [\u2026truncated \u2014 showing first ${MAX_CHARS.toLocaleString()} characters of ${text2.length.toLocaleString()} total]`;
2404
+ [\u2026truncated \u2014 showing first ${maxChars.toLocaleString()} characters of ${text2.length.toLocaleString()} total]`;
2235
2405
  }
2236
2406
  return text2;
2237
2407
  }
@@ -2242,47 +2412,129 @@ async function extractPdfText(abs) {
2242
2412
  return `[Cannot extract PDF text: the file may be scanned/image-only or password-protected]`;
2243
2413
  }
2244
2414
  async function extractOfficeText(abs, ext) {
2415
+ const openXml = extractOfficeOpenXmlText((0, import_fs5.readFileSync)(abs), ext);
2416
+ if (openXml) return openXml;
2245
2417
  const text = await extractViaCommand("textutil", ["-stdout", "-cat", "txt", abs]);
2246
2418
  if (text) return text;
2247
- return `[Cannot extract ${ext} text: textutil failed or is unavailable on this system]`;
2419
+ return `[Cannot extract ${ext} text: this document format is not supported on this system]`;
2420
+ }
2421
+ var documentTextCache = /* @__PURE__ */ new Map();
2422
+ async function cachedDocumentText(abs, stat, ext) {
2423
+ const cached = documentTextCache.get(abs);
2424
+ if (cached?.size === stat.size && cached.mtimeMs === stat.mtimeMs) return cached.text;
2425
+ const text = PDF_EXTS.has(ext) ? await extractPdfText(abs, Number.MAX_SAFE_INTEGER) : await extractOfficeText(abs, ext);
2426
+ documentTextCache.delete(abs);
2427
+ if (text.length <= 2e6) documentTextCache.set(abs, { size: stat.size, mtimeMs: stat.mtimeMs, text });
2428
+ while (documentTextCache.size > 4) documentTextCache.delete(documentTextCache.keys().next().value);
2429
+ return text;
2248
2430
  }
2249
2431
  async function toolReadFile(args, cfg) {
2250
2432
  const { path: userPath } = args;
2251
2433
  if (!userPath) throw new Error('read_file requires a "path" argument');
2252
2434
  const abs = safePath(cfg.rootDir, userPath);
2253
2435
  assertNotSensitive(abs);
2254
- if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${userPath}`);
2436
+ if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${userPath} (resolved to ${abs}). List the parent directory and use a path relative to the selected workspace root: ${cfg.rootDir}.`);
2255
2437
  const stat = (0, import_fs5.statSync)(abs);
2256
2438
  if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
2257
2439
  const ext = (0, import_path3.extname)(abs).toLowerCase();
2258
2440
  if (PDF_EXTS.has(ext)) {
2259
2441
  if (stat.size > 5e7) throw new Error(`PDF too large to read (${Math.round(stat.size / 1e6)} MB). Max 50 MB.`);
2260
- return extractPdfText(abs);
2442
+ return pageFileText(await cachedDocumentText(abs, stat, ext), args.offset, userPath, true);
2261
2443
  }
2262
2444
  if (OFFICE_EXTS.has(ext)) {
2263
2445
  if (stat.size > 2e7) throw new Error(`Document too large to read (${Math.round(stat.size / 1e6)} MB). Max 20 MB.`);
2264
- return extractOfficeText(abs, ext);
2446
+ return pageFileText(await cachedDocumentText(abs, stat, ext), args.offset, userPath, true);
2265
2447
  }
2266
2448
  if (UNREADABLE_BINARY_EXTS.has(ext)) {
2267
2449
  throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
2268
2450
  }
2269
2451
  if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
2270
- return (0, import_fs5.readFileSync)(abs, "utf8");
2452
+ return pageFileText((0, import_fs5.readFileSync)(abs, "utf8"), args.offset, userPath, false);
2453
+ }
2454
+ async function toolSearchFile(args, cfg) {
2455
+ const path = String(args.path ?? "").trim();
2456
+ const query = String(args.query ?? "").trim().slice(0, 120);
2457
+ if (!path || !query) throw new Error("search_file requires a path and search query");
2458
+ const abs = safePath(cfg.rootDir, path);
2459
+ assertNotSensitive(abs);
2460
+ if (!(0, import_fs5.existsSync)(abs)) throw new Error(`File not found: ${path}`);
2461
+ const stat = (0, import_fs5.statSync)(abs);
2462
+ if (!stat.isFile()) throw new Error(`"${path}" is not a file`);
2463
+ const ext = (0, import_path3.extname)(abs).toLowerCase();
2464
+ if (PDF_EXTS.has(ext) && stat.size > 5e7) throw new Error("PDF exceeds the 50 MB Local read limit");
2465
+ if (OFFICE_EXTS.has(ext) && stat.size > 2e7) throw new Error("Document exceeds the 20 MB Local read limit");
2466
+ if (UNREADABLE_BINARY_EXTS.has(ext)) throw new Error(`Cannot search binary file "${path}"`);
2467
+ if (!PDF_EXTS.has(ext) && !OFFICE_EXTS.has(ext) && stat.size > 5e5) throw new Error("Text file exceeds the 500 KB Local read limit");
2468
+ const text = PDF_EXTS.has(ext) || OFFICE_EXTS.has(ext) ? await cachedDocumentText(abs, stat, ext) : (0, import_fs5.readFileSync)(abs, "utf8");
2469
+ return JSON.stringify(searchTextPassages(text, path, query, "Use cli_read_file with a matching offset for more context."));
2470
+ }
2471
+ function searchTextPassages(text, path, rawQuery, hint) {
2472
+ const query = rawQuery.trim().slice(0, 120);
2473
+ if (!query) throw new Error("A search query is required");
2474
+ const lower = text.toLowerCase();
2475
+ const phrase = query.toLowerCase();
2476
+ const terms = [...new Set(phrase.match(/[\p{L}\p{N}]{3,}/gu) ?? [])].slice(0, 8);
2477
+ const positions = [];
2478
+ const collect2 = (needle) => {
2479
+ let offset = 0;
2480
+ while (positions.length < 5e3) {
2481
+ const found = lower.indexOf(needle, offset);
2482
+ if (found < 0) break;
2483
+ positions.push(found);
2484
+ offset = found + Math.max(needle.length, 1);
2485
+ }
2486
+ };
2487
+ collect2(phrase);
2488
+ if (!positions.length) for (const term of terms) collect2(term);
2489
+ const ranked = positions.map((offset) => {
2490
+ const start = Math.max(0, offset - 180);
2491
+ const end = Math.min(text.length, offset + 320);
2492
+ const excerpt = text.slice(start, end).replace(/\s+/g, " ").trim();
2493
+ const score = terms.filter((term) => excerpt.toLowerCase().includes(term)).length;
2494
+ return { offset, score, excerpt };
2495
+ }).sort((a, b) => b.score - a.score || b.offset - a.offset);
2496
+ const selected = [];
2497
+ for (const match of ranked) {
2498
+ if (selected.some((entry) => Math.abs(entry.offset - match.offset) < 250)) continue;
2499
+ selected.push(match);
2500
+ if (selected.length >= 8) break;
2501
+ }
2502
+ return { path, query, totalChars: text.length, matches: selected.map(({ offset, excerpt }) => ({ offset, excerpt })), hint };
2503
+ }
2504
+ function pageFileText(text, rawOffset, path, includeTail) {
2505
+ const offset = rawOffset === void 0 ? 0 : Number(rawOffset);
2506
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset > text.length) throw new Error("read_file offset must be a character position within the file");
2507
+ if (offset === 0 && text.length <= 7e3) return text;
2508
+ const end = Math.min(text.length, offset + 6e3);
2509
+ const next = end < text.length ? ` Use cli_read_file with path ${JSON.stringify(path)} and offset ${end} to continue.` : "";
2510
+ const tail = includeTail && offset === 0 && end < text.length - 1500 ? `
2511
+
2512
+ [Final 1,500 characters of document]
2513
+ ${text.slice(-1500)}` : "";
2514
+ return `[Showing characters ${offset}-${end} of ${text.length}.${next}]
2515
+ ${text.slice(offset, end)}${tail}`;
2271
2516
  }
2272
2517
  async function toolWriteFile(args, cfg) {
2273
2518
  const { path: userPath, content } = args;
2274
2519
  if (!userPath) throw new Error('write_file requires a "path" argument');
2275
- if (content === void 0) throw new Error('write_file requires a "content" argument');
2520
+ if (typeof content !== "string") throw new Error('write_file requires a string "content" argument');
2276
2521
  const abs = safePath(cfg.rootDir, userPath);
2277
2522
  assertNotSensitive(abs);
2523
+ const before = (0, import_fs5.existsSync)(abs) ? (0, import_fs5.readFileSync)(abs, "utf8") : "";
2524
+ const preview = editPreview(before, content);
2278
2525
  const ok = await confirm(
2279
- `Agent wants to write file: \x1B[1m${abs}\x1B[0m (${String(content).length} chars)`,
2526
+ `Agent wants to write file: \x1B[1m${abs}\x1B[0m (${String(content).length} chars)
2527
+ ${preview}`,
2280
2528
  cfg,
2281
2529
  "write_file"
2282
2530
  );
2283
2531
  if (!ok) return "User denied write operation.";
2284
- (0, import_fs5.mkdirSync)((0, import_path3.dirname)(abs), { recursive: true });
2285
- (0, import_fs5.writeFileSync)(abs, content, "utf8");
2532
+ const checked = safePath(cfg.rootDir, userPath);
2533
+ if (((0, import_fs5.existsSync)(checked) ? (0, import_fs5.readFileSync)(checked, "utf8") : "") !== before) {
2534
+ return "Error: file changed during approval. Read it again before retrying.";
2535
+ }
2536
+ (0, import_fs5.mkdirSync)((0, import_path3.dirname)(checked), { recursive: true });
2537
+ (0, import_fs5.writeFileSync)(checked, content, "utf8");
2286
2538
  return `Written ${String(content).length} bytes to ${userPath}`;
2287
2539
  }
2288
2540
  async function toolListDirectory(args, cfg) {
@@ -2297,6 +2549,10 @@ async function toolListDirectory(args, cfg) {
2297
2549
  });
2298
2550
  return lines.join("\n") || "(empty directory)";
2299
2551
  }
2552
+ async function toolDiskUsage(args, cfg) {
2553
+ const root = safePath(cfg.rootDir, String(args.path ?? "."));
2554
+ return JSON.stringify(await scanDiskUsage(root, cfg.signal, assertNotSensitive));
2555
+ }
2300
2556
  async function toolSearchFiles(args, cfg) {
2301
2557
  const { pattern, directory } = args;
2302
2558
  if (!pattern) throw new Error('search_files requires a "pattern" argument');
@@ -2356,9 +2612,10 @@ async function toolRunCommand(args, cfg) {
2356
2612
  });
2357
2613
  }
2358
2614
  return new Promise((resolve2) => {
2359
- (0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
2615
+ (0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024, signal: cfg.signal }, (err, stdout, stderr) => {
2360
2616
  const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
2361
- if (err && !out) return resolve2(`Error: ${err.message}`);
2617
+ if (err) return resolve2(`Error: command failed (${err.code ?? "unknown"}): ${err.message}
2618
+ ${out}`);
2362
2619
  resolve2(out || "(no output)");
2363
2620
  });
2364
2621
  });
@@ -2376,11 +2633,14 @@ async function toolStartProcess(args, cfg) {
2376
2633
  "start_process"
2377
2634
  );
2378
2635
  if (!ok) return JSON.stringify({ error: "User denied process start." });
2379
- const id = `proc_${Date.now().toString(36)}`;
2636
+ const finished = [...managedProcesses.values()].filter((proc2) => proc2.status !== "running");
2637
+ for (const proc2 of finished.slice(0, Math.max(0, finished.length - 99))) managedProcesses.delete(proc2.id);
2638
+ const id = `proc_${(0, import_crypto.randomUUID)()}`;
2380
2639
  const child = (0, import_child_process2.spawn)(command, cmdArgs.map(String), {
2381
2640
  cwd: workDir,
2382
2641
  stdio: ["ignore", "pipe", "pipe"],
2383
- detached: false
2642
+ detached: process.platform !== "win32",
2643
+ windowsHide: true
2384
2644
  });
2385
2645
  const proc = {
2386
2646
  id,
@@ -2393,6 +2653,10 @@ async function toolStartProcess(args, cfg) {
2393
2653
  endedAt: null,
2394
2654
  child
2395
2655
  };
2656
+ if (cfg.signal) {
2657
+ if (cfg.signal.aborted) stopProcessTree(proc);
2658
+ else cfg.signal.addEventListener("abort", () => stopProcessTree(proc), { once: true });
2659
+ }
2396
2660
  child.stdout?.on("data", (chunk) => {
2397
2661
  proc.stdout = capBuffer(proc.stdout, chunk.toString(), 2e5);
2398
2662
  });
@@ -2400,7 +2664,7 @@ async function toolStartProcess(args, cfg) {
2400
2664
  proc.stderr = capBuffer(proc.stderr, chunk.toString(), 5e4);
2401
2665
  });
2402
2666
  child.on("close", (code) => {
2403
- proc.status = code === 0 ? "done" : "error";
2667
+ if (proc.status !== "killed") proc.status = code === 0 ? "done" : "error";
2404
2668
  proc.exitCode = code;
2405
2669
  proc.endedAt = /* @__PURE__ */ new Date();
2406
2670
  });
@@ -2440,7 +2704,8 @@ async function toolWaitForProcess(args, _cfg) {
2440
2704
  const proc = managedProcesses.get(processId);
2441
2705
  if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2442
2706
  if (proc.status !== "running") return processSnapshot(proc);
2443
- const maxWait = Math.min((typeof wait_seconds === "number" ? wait_seconds : 60) * 1e3, 12e4);
2707
+ const requested = typeof wait_seconds === "number" && Number.isFinite(wait_seconds) ? wait_seconds : 60;
2708
+ const maxWait = Math.max(0, Math.min(requested, 60)) * 1e3;
2444
2709
  const deadline = Date.now() + maxWait;
2445
2710
  await new Promise((resolve2) => {
2446
2711
  const tick = setInterval(() => {
@@ -2458,7 +2723,7 @@ async function toolKillProcess(args, cfg) {
2458
2723
  const proc = managedProcesses.get(processId);
2459
2724
  if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2460
2725
  if (proc.status !== "running") return JSON.stringify({ error: `Process "${processId}" is not running (status: ${proc.status})` });
2461
- proc.child.kill("SIGTERM");
2726
+ stopProcessTree(proc);
2462
2727
  proc.status = "killed";
2463
2728
  proc.endedAt = /* @__PURE__ */ new Date();
2464
2729
  cfg.appendLog({ type: "process_killed", processId, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
@@ -2474,6 +2739,15 @@ async function toolListProcesses(_args, _cfg) {
2474
2739
  }));
2475
2740
  return JSON.stringify(list);
2476
2741
  }
2742
+ function stopLocalProcesses() {
2743
+ for (const proc of managedProcesses.values()) {
2744
+ if (proc.status === "running") {
2745
+ stopProcessTree(proc);
2746
+ proc.status = "killed";
2747
+ proc.endedAt = /* @__PURE__ */ new Date();
2748
+ }
2749
+ }
2750
+ }
2477
2751
  async function runLocalTool(call, cfg) {
2478
2752
  const { tool, args } = call;
2479
2753
  cfg.appendLog({
@@ -2488,6 +2762,9 @@ async function runLocalTool(call, cfg) {
2488
2762
  case "read_file":
2489
2763
  result = await toolReadFile(args, cfg);
2490
2764
  break;
2765
+ case "search_file":
2766
+ result = await toolSearchFile(args, cfg);
2767
+ break;
2491
2768
  case "write_file":
2492
2769
  result = await toolWriteFile(args, cfg);
2493
2770
  break;
@@ -2497,6 +2774,9 @@ async function runLocalTool(call, cfg) {
2497
2774
  case "search_files":
2498
2775
  result = await toolSearchFiles(args, cfg);
2499
2776
  break;
2777
+ case "disk_usage":
2778
+ result = await toolDiskUsage(args, cfg);
2779
+ break;
2500
2780
  case "run_command":
2501
2781
  result = await toolRunCommand(args, cfg);
2502
2782
  break;
@@ -2516,7 +2796,7 @@ async function runLocalTool(call, cfg) {
2516
2796
  result = await toolListProcesses(args, cfg);
2517
2797
  break;
2518
2798
  default:
2519
- result = `Unknown tool: "${tool}". Available: read_file, write_file, list_directory, search_files, run_command, start_process, wait_for_process, process_status, kill_process, list_processes`;
2799
+ result = `Unknown tool: "${tool}". Available: read_file, search_file, write_file, list_directory, search_files, disk_usage, run_command, start_process, wait_for_process, process_status, kill_process, list_processes`;
2520
2800
  }
2521
2801
  } catch (err) {
2522
2802
  result = `Error: ${err?.message ?? String(err)}`;
@@ -2533,19 +2813,72 @@ async function runLocalTool(call, cfg) {
2533
2813
 
2534
2814
  // src/commands/run.ts
2535
2815
  function runCommand() {
2536
- return new import_commander8.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Resume an existing session by ID").option("--new-session", "Create a new session and print its ID for future use").option("--computer", "Give the agent access to its persistent cloud computer").option("--local", "Enable local file system access (with permission prompts)").option("-y, --yes", "Enable local file system access and auto-approve all operations").option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").action(async (prompt, opts) => {
2816
+ const append = (value, previous) => [...previous, value];
2817
+ return new import_commander8.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--file <path>", "Upload and attach a file (repeatable, up to 25 MB each)", append, []).option("--attach <fileId>", "Attach an existing Library file ID (repeatable)", append, []).option("--session <sessionId>", "Resume an existing session by ID").option(
2818
+ "--new-session",
2819
+ "Create a new session and print its ID for future use"
2820
+ ).option(
2821
+ "--computer",
2822
+ "Give the agent access to its persistent cloud computer"
2823
+ ).option(
2824
+ "--local",
2825
+ "Enable local file system access (with permission prompts)"
2826
+ ).option(
2827
+ "-y, --yes",
2828
+ "Enable local file system access and auto-approve all operations"
2829
+ ).option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").option(
2830
+ "--provenance <mode>",
2831
+ "Provenance capture: off, metadata, or full",
2832
+ "metadata"
2833
+ ).option(
2834
+ "--onchain-provenance",
2835
+ "Request an optional on-chain anchor for this run"
2836
+ ).action(async (prompt, opts) => {
2537
2837
  const cfg = loadConfig();
2538
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
2539
- if (!agentId2) {
2540
- console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
2838
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
2839
+ if (!agentId3) {
2840
+ console.error(
2841
+ c.error(
2842
+ "Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"
2843
+ )
2844
+ );
2541
2845
  process.exit(1);
2542
2846
  }
2543
2847
  if (opts.session && opts.newSession) {
2544
- console.error(c.error("Cannot use --session and --new-session together."));
2848
+ console.error(
2849
+ c.error("Cannot use --session and --new-session together.")
2850
+ );
2851
+ process.exit(1);
2852
+ }
2853
+ if (!["off", "metadata", "full"].includes(opts.provenance)) {
2854
+ console.error(c.error("--provenance must be off, metadata, or full."));
2855
+ process.exit(1);
2856
+ }
2857
+ if (opts.onchainProvenance && opts.provenance === "off") {
2858
+ console.error(
2859
+ c.error("--onchain-provenance requires metadata or full capture.")
2860
+ );
2545
2861
  process.exit(1);
2546
2862
  }
2547
2863
  const client = makeClient();
2548
2864
  let sessionId = opts.session;
2865
+ const filePaths = opts.file;
2866
+ const attachmentIds = opts.attach;
2867
+ if (filePaths.length + attachmentIds.length > 10) {
2868
+ console.error(c.error("Attach no more than 10 files to a run."));
2869
+ process.exit(1);
2870
+ }
2871
+ try {
2872
+ for (const path of filePaths) {
2873
+ const stat = (0, import_fs6.statSync)(path);
2874
+ if (!stat.isFile() || stat.size < 1 || stat.size > 25 * 1024 * 1024) {
2875
+ throw new Error(`Choose a non-empty file up to 25 MB: ${path}`);
2876
+ }
2877
+ }
2878
+ } catch (error) {
2879
+ printError(error);
2880
+ process.exit(1);
2881
+ }
2549
2882
  if (opts.session) {
2550
2883
  const spinner = spin("Loading session\u2026");
2551
2884
  try {
@@ -2561,7 +2894,7 @@ function runCommand() {
2561
2894
  const spinner = spin("Creating session\u2026");
2562
2895
  try {
2563
2896
  const res = await client.sessions.create({
2564
- agentId: agentId2,
2897
+ agentId: agentId3,
2565
2898
  initiator: cfg.initiator ?? "",
2566
2899
  title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2567
2900
  source: "cli"
@@ -2575,6 +2908,24 @@ function runCommand() {
2575
2908
  process.exit(1);
2576
2909
  }
2577
2910
  }
2911
+ const attachments = attachmentIds.map((fileId) => ({ fileId }));
2912
+ for (const path of filePaths) {
2913
+ const spinner = spin(`Uploading ${(0, import_path4.basename)(path)}\u2026`);
2914
+ try {
2915
+ const result = await client.files.upload(
2916
+ [{ data: new Blob([new Uint8Array((0, import_fs6.readFileSync)(path))]), name: (0, import_path4.basename)(path) }],
2917
+ { agentId: agentId3, sessionId }
2918
+ );
2919
+ const fileId = result.data[0]?.fileId;
2920
+ if (!fileId) throw new Error(`The upload did not return a file ID for ${path}.`);
2921
+ attachments.push({ fileId });
2922
+ spinner.stop();
2923
+ } catch (error) {
2924
+ spinner.stop();
2925
+ printError(error);
2926
+ process.exit(1);
2927
+ }
2928
+ }
2578
2929
  const localEnabled = opts.yes || opts.local;
2579
2930
  const autoApprove = !!opts.yes;
2580
2931
  let localToolsCfg = null;
@@ -2587,11 +2938,16 @@ function runCommand() {
2587
2938
  appendLog: () => {
2588
2939
  },
2589
2940
  permissions: /* @__PURE__ */ new Map(),
2590
- agentId: agentId2,
2941
+ agentId: agentId3,
2591
2942
  autoApprove
2592
2943
  };
2593
2944
  const snapshot = buildDirSnapshot(rootDir, 2);
2594
- cliContext = buildLocalToolsManifest(rootDir, snapshot, [], autoApprove);
2945
+ cliContext = buildLocalToolsManifest(
2946
+ rootDir,
2947
+ snapshot,
2948
+ [],
2949
+ autoApprove
2950
+ );
2595
2951
  }
2596
2952
  if (!opts.json) {
2597
2953
  const rows = [];
@@ -2600,10 +2956,16 @@ function runCommand() {
2600
2956
  rows.push(["Session", label]);
2601
2957
  }
2602
2958
  if (localEnabled) {
2603
- rows.push(["Local tools", autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")]);
2959
+ rows.push([
2960
+ "Local tools",
2961
+ autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")
2962
+ ]);
2604
2963
  }
2605
2964
  if (opts.computer) {
2606
- rows.push(["Cloud computer", c.success("enabled") + c.dim(" (persistent, remote)")]);
2965
+ rows.push([
2966
+ "Cloud computer",
2967
+ c.success("enabled") + c.dim(" (persistent, remote)")
2968
+ ]);
2607
2969
  }
2608
2970
  if (rows.length) {
2609
2971
  detail(rows);
@@ -2611,12 +2973,17 @@ function runCommand() {
2611
2973
  }
2612
2974
  }
2613
2975
  const params = {
2614
- agentId: agentId2,
2976
+ agentId: agentId3,
2615
2977
  sessionId,
2616
2978
  messages: [{ role: "user", content: prompt }],
2979
+ ...attachments.length && { attachments },
2617
2980
  ...cfg.initiator && { initiatorId: cfg.initiator },
2618
2981
  ...opts.computer && { computerRequest: { enabled: true } },
2619
- ...cliContext && { cliContext }
2982
+ ...cliContext && { cliContext },
2983
+ provenance: {
2984
+ mode: opts.provenance,
2985
+ onchain: Boolean(opts.onchainProvenance)
2986
+ }
2620
2987
  };
2621
2988
  if (opts.noStream && !localEnabled) {
2622
2989
  const spinner = spin("Running\u2026");
@@ -2626,8 +2993,13 @@ function runCommand() {
2626
2993
  if (opts.json) return jsonOut(result);
2627
2994
  const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
2628
2995
  console.log(text);
2629
- if (sessionId) console.log(c.dim(`
2630
- Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
2996
+ if (sessionId)
2997
+ console.log(
2998
+ c.dim(
2999
+ `
3000
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
3001
+ )
3002
+ );
2631
3003
  } catch (err) {
2632
3004
  spinner.stop();
2633
3005
  printError(err);
@@ -2659,7 +3031,10 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2659
3031
  let result;
2660
3032
  let toolOk = true;
2661
3033
  try {
2662
- result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
3034
+ result = await runLocalTool(
3035
+ { tool: displayName, args: args ?? {} },
3036
+ localToolsCfg
3037
+ );
2663
3038
  } catch (err) {
2664
3039
  result = `Error: ${err?.message ?? String(err)}`;
2665
3040
  toolOk = false;
@@ -2667,8 +3042,10 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2667
3042
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2668
3043
  readline2.cursorTo(process.stdout, 0);
2669
3044
  readline2.clearLine(process.stdout, 0);
2670
- process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
2671
- `);
3045
+ process.stdout.write(
3046
+ ` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
3047
+ `
3048
+ );
2672
3049
  try {
2673
3050
  await client.agents.submitCliToolResult(requestId, result);
2674
3051
  } catch {
@@ -2685,20 +3062,29 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2685
3062
  const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2686
3063
  readline2.cursorTo(process.stdout, 0);
2687
3064
  readline2.clearLine(process.stdout, 0);
2688
- process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2689
- `);
3065
+ process.stdout.write(
3066
+ ` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
3067
+ `
3068
+ );
2690
3069
  } else if (event.type === "final") {
2691
3070
  if (hasOutput) process.stdout.write("\n");
2692
3071
  const e = event;
2693
3072
  const finalText = e.content ?? e.payload?.content ?? e.payload?.text ?? e.payload?.message;
2694
3073
  if (finalText && !hasOutput) console.log(finalText);
2695
- if (sessionId) console.log(c.dim(`
2696
- Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
3074
+ if (sessionId)
3075
+ console.log(
3076
+ c.dim(
3077
+ `
3078
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
3079
+ )
3080
+ );
2697
3081
  break;
2698
3082
  } else if (event.type === "error") {
2699
3083
  if (hasOutput) process.stdout.write("\n");
2700
- console.error(`
2701
- ${sym.fail} ${c.error(event.message ?? "Error")}`);
3084
+ console.error(
3085
+ `
3086
+ ${sym.fail} ${c.error(event.message ?? "Error")}`
3087
+ );
2702
3088
  process.exit(1);
2703
3089
  }
2704
3090
  }
@@ -2713,18 +3099,19 @@ ${sym.fail} ${c.error(event.message ?? "Error")}`);
2713
3099
  // src/commands/chat.ts
2714
3100
  var import_commander9 = require("commander");
2715
3101
  var readline3 = __toESM(require("readline"));
2716
- var import_fs6 = require("fs");
2717
- var import_path4 = require("path");
3102
+ var import_fs7 = require("fs");
3103
+ var import_path5 = require("path");
2718
3104
  var import_os3 = require("os");
2719
- var SESSIONS_DIR = (0, import_path4.join)((0, import_os3.homedir)(), ".agc", "sessions");
3105
+ var SESSIONS_DIR = (0, import_path5.join)((0, import_os3.homedir)(), ".agc", "sessions");
2720
3106
  function ensureSessionsDir() {
2721
- if (!(0, import_fs6.existsSync)(SESSIONS_DIR)) (0, import_fs6.mkdirSync)(SESSIONS_DIR, { recursive: true });
3107
+ if (!(0, import_fs7.existsSync)(SESSIONS_DIR)) (0, import_fs7.mkdirSync)(SESSIONS_DIR, { recursive: true });
2722
3108
  }
2723
3109
  function appendSessionLog(sessionId, record) {
2724
3110
  try {
3111
+ if (!/^[a-zA-Z0-9_-]{1,128}$/.test(sessionId)) return;
2725
3112
  ensureSessionsDir();
2726
- const file = (0, import_path4.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
2727
- (0, import_fs6.appendFileSync)(file, JSON.stringify(record) + "\n", { mode: 384 });
3113
+ const file = (0, import_path5.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
3114
+ (0, import_fs7.appendFileSync)(file, JSON.stringify(record) + "\n", { mode: 384 });
2728
3115
  } catch {
2729
3116
  }
2730
3117
  }
@@ -2747,27 +3134,35 @@ var LOCAL_TOOLS_DISCLAIMER = `
2747
3134
  ${c.dim("and execute shell commands on your machine.")}
2748
3135
 
2749
3136
  ${c.dim("Rules:")}
2750
- ${sym.bullet} ${c.dim("All paths are restricted to:")} ${c.primary(process.cwd())}
3137
+ ${sym.bullet} ${c.dim("File tools are restricted to:")} ${c.primary(process.cwd())}
2751
3138
  ${sym.bullet} ${c.dim("Sensitive paths (.ssh, .env, .aws, credentials) are always blocked")}
2752
3139
  ${sym.bullet} ${c.dim("Write and run_command operations require your confirmation")}
2753
- ${sym.bullet} ${c.dim("You can deny any individual request")}
3140
+ ${sym.bullet} ${c.dim("Approved commands run with your operating-system permissions; they are not sandboxed")}
2754
3141
 
2755
3142
  ${c.dim("Session activity is logged to")} ${c.primary("~/.agc/sessions/")}
2756
3143
  `;
2757
3144
  function chatCommand() {
2758
- return new import_commander9.Command("chat").description("Start an interactive chat REPL with an agent").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--computer", "Give the agent access to its persistent cloud computer").option("--no-stream", "Disable token streaming (wait for full response)").option("--no-local", "Disable local file system access for the agent").action(async (opts) => {
3145
+ return new import_commander9.Command("chat").alias("code").description("Start an interactive coding session with an agent").argument("[prompt]", "Initial task to send when the session opens").option("--prompt-file <path>", "Read the initial task from a UTF-8 file (maximum 100 KB)").option("--read-only", "Allow local reads but deny local edits and commands").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--computer", "Give the agent access to its persistent cloud computer").option("--no-stream", "Disable token streaming (wait for full response)").option("--no-local", "Disable local file system access for the agent").action(async (prompt, opts) => {
3146
+ let initialPrompt = prompt;
3147
+ if (opts.promptFile) {
3148
+ if (prompt) throw new Error("Use either a prompt argument or --prompt-file, not both.");
3149
+ if ((0, import_fs7.statSync)(opts.promptFile).size > 1e5) throw new Error("Prompt file exceeds 100 KB.");
3150
+ initialPrompt = (0, import_fs7.readFileSync)(opts.promptFile, "utf8").trim();
3151
+ }
3152
+ if (!process.stdin.isTTY) throw new Error("Interactive coding requires a terminal. Use `agc run` for scripts.");
2759
3153
  const localEnabled = opts.local !== false;
3154
+ if (localEnabled && opts.stream === false) throw new Error("Local tools require streaming. Use --no-local with --no-stream.");
2760
3155
  const cfg = loadConfig();
2761
- let agentId2 = opts.agent ?? cfg.defaultAgentId;
2762
- if (!agentId2 && cfg.initiator) {
3156
+ let agentId3 = opts.agent ?? cfg.defaultAgentId;
3157
+ if (!agentId3 && cfg.initiator) {
2763
3158
  try {
2764
3159
  const listed = await makeClient().agents.list(cfg.initiator);
2765
3160
  const agents = listed?.data ?? listed ?? [];
2766
- agentId2 = agents.find((agent) => agent.isDefault)?.agentId ?? agents[0]?.agentId;
3161
+ agentId3 = agents.find((agent) => agent.isDefault)?.agentId ?? agents[0]?.agentId;
2767
3162
  } catch {
2768
3163
  }
2769
3164
  }
2770
- if (!agentId2) {
3165
+ if (!agentId3) {
2771
3166
  console.error(c.error("No default agent is available. Specify --agent <agentId> or run `agc agents list`."));
2772
3167
  process.exit(1);
2773
3168
  }
@@ -2783,7 +3178,7 @@ function chatCommand() {
2783
3178
  const spinner = spin("Creating session\u2026");
2784
3179
  try {
2785
3180
  const res = await client.sessions.create({
2786
- agentId: agentId2,
3181
+ agentId: agentId3,
2787
3182
  initiator,
2788
3183
  title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2789
3184
  source: "cli"
@@ -2794,7 +3189,7 @@ function chatCommand() {
2794
3189
  appendSessionLog(sessionId, {
2795
3190
  type: "session_start",
2796
3191
  sessionId,
2797
- agentId: agentId2,
3192
+ agentId: agentId3,
2798
3193
  initiator,
2799
3194
  source: "cli",
2800
3195
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -2809,12 +3204,8 @@ function chatCommand() {
2809
3204
  try {
2810
3205
  const res = await client.sessions.get(sessionId);
2811
3206
  const session = res?.data ?? res;
2812
- if (session.agentId && session.agentId !== agentId2) {
2813
- spinner.stop();
2814
- console.log(c.warn(` Note: session ${sessionId} was created with agent ${session.agentId}, not ${agentId2}`));
2815
- } else {
2816
- spinner.stop();
2817
- }
3207
+ if (session.agentId) agentId3 = session.agentId;
3208
+ spinner.stop();
2818
3209
  } catch {
2819
3210
  spinner.stop();
2820
3211
  console.error(c.error(`Session "${sessionId}" not found.`));
@@ -2824,10 +3215,10 @@ function chatCommand() {
2824
3215
  let agentName;
2825
3216
  let walletLine = "";
2826
3217
  await Promise.allSettled([
2827
- client.agents.get(agentId2).then((res) => {
3218
+ client.agents.get(agentId3).then((res) => {
2828
3219
  agentName = (res?.data ?? res)?.name;
2829
3220
  }),
2830
- client.wallets.primary(agentId2).then(async (primary) => {
3221
+ client.wallets.primary(agentId3).then(async (primary) => {
2831
3222
  const w = primary?.data ?? primary;
2832
3223
  if (w?.id) {
2833
3224
  const bal = await client.wallets.balance(w.id).catch(() => null);
@@ -2839,9 +3230,9 @@ function chatCommand() {
2839
3230
  })
2840
3231
  ]);
2841
3232
  console.log(`
2842
- ${c.bold("Agent Commons Chat")}`);
3233
+ ${c.bold("Agent Commons \xB7 Code")}`);
2843
3234
  const headerRows = [
2844
- ["Agent", agentName ? `${agentName} ${c.dim(agentId2)}` : agentId2],
3235
+ ["Agent", agentName ? `${agentName} ${c.dim(agentId3)}` : agentId3],
2845
3236
  ["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
2846
3237
  ];
2847
3238
  if (walletLine) headerRows.push(["Wallet", walletLine]);
@@ -2850,17 +3241,16 @@ ${c.bold("Agent Commons Chat")}`);
2850
3241
  detail(headerRows);
2851
3242
  let localToolsCfg = null;
2852
3243
  if (localEnabled) {
2853
- console.log(LOCAL_TOOLS_DISCLAIMER);
3244
+ console.log(opts.readOnly ? c.dim(" Local read-only mode: edits and commands are denied.\n") : LOCAL_TOOLS_DISCLAIMER);
2854
3245
  const rootDir = process.cwd();
2855
3246
  localToolsCfg = {
2856
3247
  rootDir,
2857
3248
  sessionId,
2858
- agentId: agentId2,
3249
+ agentId: agentId3,
2859
3250
  agentName,
2860
3251
  appendLog: (record) => appendSessionLog(sessionId, record),
2861
- permissions: /* @__PURE__ */ new Map()
3252
+ permissions: new Map(opts.readOnly ? ["write_file", "run_command", "start_process"].map((key) => [key, "deny"]) : [])
2862
3253
  };
2863
- installGitHook(rootDir, sessionId, agentId2, agentName);
2864
3254
  appendSessionLog(sessionId, {
2865
3255
  type: "local_tools_enabled",
2866
3256
  rootDir,
@@ -2874,8 +3264,10 @@ ${c.bold("Agent Commons Chat")}`);
2874
3264
  terminal: true,
2875
3265
  prompt: c.primary("you") + c.dim(" \u203A ")
2876
3266
  });
2877
- rl.prompt();
3267
+ let busy = false;
3268
+ if (!initialPrompt) rl.prompt();
2878
3269
  rl.on("line", async (line) => {
3270
+ if (busy) return;
2879
3271
  const input = line.trim();
2880
3272
  if (!input) {
2881
3273
  rl.prompt();
@@ -2928,6 +3320,7 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
2928
3320
  rl.prompt();
2929
3321
  return;
2930
3322
  }
3323
+ busy = true;
2931
3324
  rl.pause();
2932
3325
  appendSessionLog(sessionId, {
2933
3326
  type: "message",
@@ -2941,6 +3334,11 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
2941
3334
  const rootDir = localToolsCfg.rootDir;
2942
3335
  const atRefs = [...input.matchAll(/@([\S]+)/g)].map((m) => m[1]);
2943
3336
  const fileContextBlocks = [];
3337
+ if ((0, import_fs7.existsSync)((0, import_path5.join)(rootDir, "AGENTS.md"))) {
3338
+ fileContextBlocks.push(`Project instructions (AGENTS.md):
3339
+ ${readFileForContext(rootDir, "AGENTS.md")}`);
3340
+ }
3341
+ if (opts.readOnly) fileContextBlocks.push("Local read-only mode: do not write files or execute commands. Local reads are available.");
2944
3342
  for (const ref of atRefs) {
2945
3343
  const content = readFileForContext(rootDir, ref);
2946
3344
  fileContextBlocks.push(`**${ref}**
@@ -2952,13 +3350,13 @@ ${content}
2952
3350
  cliContext = buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks);
2953
3351
  }
2954
3352
  const params = {
2955
- agentId: agentId2,
3353
+ agentId: agentId3,
2956
3354
  sessionId,
2957
3355
  messages: [{ role: "user", content: userMessage }],
2958
3356
  ...opts.computer && { computerRequest: { enabled: true } },
2959
3357
  ...cliContext && { cliContext }
2960
3358
  };
2961
- if (opts.noStream) {
3359
+ if (opts.stream === false) {
2962
3360
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2963
3361
  const spinner = spin("thinking\u2026");
2964
3362
  try {
@@ -2979,12 +3377,12 @@ ${content}
2979
3377
  ${sym.fail} ${c.error(err.message ?? String(err))}`);
2980
3378
  }
2981
3379
  } else {
3380
+ const thinkingSpinner = spin("thinking\u2026");
2982
3381
  try {
2983
3382
  let hasOutput = false;
2984
3383
  let agentContent = "";
2985
3384
  let toolStartMs = 0;
2986
3385
  let lastToolName = "";
2987
- const thinkingSpinner = spin("thinking\u2026");
2988
3386
  for await (const event of client.agents.stream(params)) {
2989
3387
  if (event.type === "token") {
2990
3388
  if (thinkingSpinner.isSpinning) {
@@ -3113,36 +3511,53 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
3113
3511
  if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
3114
3512
  process.stdout.write("\n");
3115
3513
  if (localToolsCfg && agentContent) {
3116
- await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId2, sessionId, appendSessionLog, !!opts.computer);
3514
+ await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId3, sessionId, appendSessionLog, !!opts.computer);
3117
3515
  }
3118
3516
  } catch (err) {
3119
3517
  process.stdout.write("\n");
3120
3518
  console.error(`${sym.fail} ${c.error(err.message ?? String(err))}`);
3519
+ } finally {
3520
+ thinkingSpinner.stop();
3121
3521
  }
3122
3522
  }
3123
3523
  console.log();
3124
3524
  readline3.cursorTo(process.stdout, 0);
3125
3525
  readline3.clearLine(process.stdout, 0);
3526
+ busy = false;
3126
3527
  rl.resume();
3127
3528
  rl.prompt();
3128
3529
  });
3129
3530
  const cleanup = () => {
3130
- if (localToolsCfg) removeGitHook(localToolsCfg.rootDir);
3531
+ stopLocalProcesses();
3131
3532
  };
3132
3533
  rl.on("close", () => {
3133
3534
  cleanup();
3134
3535
  process.exit(0);
3135
3536
  });
3537
+ rl.on("SIGINT", () => {
3538
+ cleanup();
3539
+ console.log(c.dim(`
3540
+ Session preserved. Resume with: agc code --resume ${sessionId}`));
3541
+ process.exit(130);
3542
+ });
3136
3543
  process.on("SIGINT", () => {
3137
3544
  cleanup();
3138
3545
  console.log(c.dim(`
3139
3546
  Session preserved. Resume with: agc chat --resume ${sessionId}`));
3140
3547
  process.exit(130);
3141
3548
  });
3549
+ process.on("SIGTERM", () => {
3550
+ cleanup();
3551
+ process.exit(143);
3552
+ });
3553
+ if (initialPrompt) {
3554
+ console.log(c.primary("you") + c.dim(" \u203A ") + initialPrompt);
3555
+ rl.emit("line", initialPrompt);
3556
+ }
3142
3557
  });
3143
3558
  }
3144
3559
  var MAX_TOOL_DEPTH = 10;
3145
- async function handleLocalToolLoop(agentText, cfg, client, agentId2, sessionId, appendLog, computerEnabled = false, depth = 0) {
3560
+ async function handleLocalToolLoop(agentText, cfg, client, agentId3, sessionId, appendLog, computerEnabled = false, depth = 0) {
3146
3561
  if (depth >= MAX_TOOL_DEPTH) {
3147
3562
  console.log(c.dim(`
3148
3563
  [local] Max tool depth reached (${MAX_TOOL_DEPTH}). Stopping tool loop.
@@ -3189,7 +3604,7 @@ ${result}
3189
3604
  let loopToolName = "";
3190
3605
  let loopToolStartMs = 0;
3191
3606
  for await (const evt of client.agents.stream({
3192
- agentId: agentId2,
3607
+ agentId: agentId3,
3193
3608
  sessionId,
3194
3609
  messages: [{ role: "user", content: resultMsg }],
3195
3610
  ...computerEnabled && { computerRequest: { enabled: true } }
@@ -3235,7 +3650,7 @@ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
3235
3650
  console.error(`${sym.fail} ${c.error(err?.message ?? String(err))}`);
3236
3651
  return;
3237
3652
  }
3238
- await handleLocalToolLoop(followContent, cfg, client, agentId2, sessionId, appendLog, computerEnabled, depth + 1);
3653
+ await handleLocalToolLoop(followContent, cfg, client, agentId3, sessionId, appendLog, computerEnabled, depth + 1);
3239
3654
  }
3240
3655
  function truncate(s, max) {
3241
3656
  const str = String(s ?? "");
@@ -3245,6 +3660,8 @@ function toolArgSummary(toolName, args) {
3245
3660
  switch (toolName) {
3246
3661
  case "read_file":
3247
3662
  return truncate(args.path ?? "", 60);
3663
+ case "search_file":
3664
+ return truncate(`${args.path ?? ""} \xB7 ${args.query ?? ""}`, 60);
3248
3665
  case "write_file":
3249
3666
  return truncate(args.path ?? "", 60);
3250
3667
  case "delete_file":
@@ -3280,6 +3697,14 @@ function toolResultPreview(toolName, result) {
3280
3697
  const lines = result.split("\n").length;
3281
3698
  return `${lines} lines`;
3282
3699
  }
3700
+ case "search_file": {
3701
+ try {
3702
+ const matches = JSON.parse(result)?.matches;
3703
+ return Array.isArray(matches) ? `${matches.length} passage${matches.length === 1 ? "" : "s"}` : "";
3704
+ } catch {
3705
+ return "";
3706
+ }
3707
+ }
3283
3708
  case "write_file":
3284
3709
  return "written";
3285
3710
  case "delete_file":
@@ -3882,19 +4307,19 @@ function walletCommand() {
3882
4307
  const cmd = new import_commander12.Command("wallet").description("Manage agent wallets");
3883
4308
  cmd.command("list").description("List all wallets for an agent").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--json", "Output as JSON").action(async (opts) => {
3884
4309
  const cfg = loadConfig();
3885
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3886
- if (!agentId2) {
4310
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4311
+ if (!agentId3) {
3887
4312
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3888
4313
  process.exit(1);
3889
4314
  }
3890
4315
  const spinner = spin("Fetching wallets\u2026");
3891
4316
  try {
3892
4317
  const client = makeClient();
3893
- const wallets = await client.wallets.list(agentId2);
4318
+ const wallets = await client.wallets.list(agentId3);
3894
4319
  spinner.stop();
3895
4320
  if (opts.json) return jsonOut(wallets);
3896
4321
  const list = wallets?.data ?? wallets ?? [];
3897
- section(`Wallets for agent ${agentId2.slice(0, 8)}\u2026 (${list.length})`);
4322
+ section(`Wallets for agent ${agentId3.slice(0, 8)}\u2026 (${list.length})`);
3898
4323
  table(
3899
4324
  list.map((w) => ({
3900
4325
  ID: w.id.slice(0, 8) + "\u2026",
@@ -3914,19 +4339,19 @@ function walletCommand() {
3914
4339
  });
3915
4340
  cmd.command("show").description("Show the agent's primary wallet address").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--json", "Output as JSON").action(async (opts) => {
3916
4341
  const cfg = loadConfig();
3917
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3918
- if (!agentId2) {
4342
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4343
+ if (!agentId3) {
3919
4344
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3920
4345
  process.exit(1);
3921
4346
  }
3922
4347
  const spinner = spin("Fetching primary wallet\u2026");
3923
4348
  try {
3924
4349
  const client = makeClient();
3925
- const wallet = await client.wallets.primary(agentId2);
4350
+ const wallet = await client.wallets.primary(agentId3);
3926
4351
  spinner.stop();
3927
4352
  if (!wallet) {
3928
- console.log(c.warn(` No wallet found for agent ${agentId2}`));
3929
- console.log(c.dim(` Run: agc wallet create --agent ${agentId2}`));
4353
+ console.log(c.warn(` No wallet found for agent ${agentId3}`));
4354
+ console.log(c.dim(` Run: agc wallet create --agent ${agentId3}`));
3930
4355
  return;
3931
4356
  }
3932
4357
  const w = wallet?.data ?? wallet;
@@ -3947,8 +4372,8 @@ function walletCommand() {
3947
4372
  });
3948
4373
  cmd.command("balance").description("Show the agent's wallet USDC and ETH balance").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--wallet <walletId>", "Specific wallet ID (defaults to primary)").option("--json", "Output as JSON").action(async (opts) => {
3949
4374
  const cfg = loadConfig();
3950
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3951
- if (!agentId2) {
4375
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4376
+ if (!agentId3) {
3952
4377
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3953
4378
  process.exit(1);
3954
4379
  }
@@ -3957,11 +4382,11 @@ function walletCommand() {
3957
4382
  const client = makeClient();
3958
4383
  let walletId = opts.wallet;
3959
4384
  if (!walletId) {
3960
- const primary = await client.wallets.primary(agentId2);
4385
+ const primary = await client.wallets.primary(agentId3);
3961
4386
  const w = primary?.data ?? primary;
3962
4387
  if (!w) {
3963
4388
  spinner.stop();
3964
- console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${agentId2}`));
4389
+ console.log(c.warn(` No wallet found. Run: agc wallet create --agent ${agentId3}`));
3965
4390
  return;
3966
4391
  }
3967
4392
  walletId = w.id;
@@ -3988,8 +4413,8 @@ function walletCommand() {
3988
4413
  });
3989
4414
  cmd.command("create").description("Create a new wallet for an agent").option("--agent <agentId>", "Agent ID (or use defaultAgentId from config)").option("--type <type>", "Wallet type: eoa | external (default: eoa)", "eoa").option("--label <label>", "Wallet label (default: Primary)", "Primary").option("--address <address>", "For --type external: owner-provided address").option("--json", "Output as JSON").action(async (opts) => {
3990
4415
  const cfg = loadConfig();
3991
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3992
- if (!agentId2) {
4416
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4417
+ if (!agentId3) {
3993
4418
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3994
4419
  process.exit(1);
3995
4420
  }
@@ -4001,7 +4426,7 @@ function walletCommand() {
4001
4426
  try {
4002
4427
  const client = makeClient();
4003
4428
  const wallet = await client.wallets.create({
4004
- agentId: agentId2,
4429
+ agentId: agentId3,
4005
4430
  walletType: opts.type,
4006
4431
  label: opts.label,
4007
4432
  externalAddress: opts.address
@@ -4159,22 +4584,22 @@ function memoryCommand() {
4159
4584
  const cmd = new import_commander14.Command("memory").description("View and manage agent memories");
4160
4585
  cmd.command("list").description("List memories for an agent").option("--agent <agentId>", "Agent ID (defaults to configured agent)").option("--type <type>", "Filter by type: episodic | semantic | procedural").option("--limit <n>", "Max results", "50").option("--json", "Output as JSON").action(async (opts) => {
4161
4586
  const cfg = loadConfig();
4162
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4163
- if (!agentId2) {
4587
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4588
+ if (!agentId3) {
4164
4589
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4165
4590
  process.exit(1);
4166
4591
  }
4167
4592
  const spinner = spin("Fetching memories\u2026");
4168
4593
  try {
4169
4594
  const client = makeClient();
4170
- const res = await client.memory.list(agentId2, {
4595
+ const res = await client.memory.list(agentId3, {
4171
4596
  type: opts.type,
4172
4597
  limit: parseInt(opts.limit, 10)
4173
4598
  });
4174
4599
  const memories = res?.data ?? res ?? [];
4175
4600
  spinner.stop();
4176
4601
  if (opts.json) return jsonOut(memories);
4177
- section(`Memories for ${agentId2.slice(0, 12)}\u2026 (${memories.length})`);
4602
+ section(`Memories for ${agentId3.slice(0, 12)}\u2026 (${memories.length})`);
4178
4603
  if (memories.length === 0) {
4179
4604
  console.log(c.dim(" No memories yet"));
4180
4605
  return;
@@ -4196,15 +4621,15 @@ function memoryCommand() {
4196
4621
  });
4197
4622
  cmd.command("stats").description("Show memory statistics for an agent").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
4198
4623
  const cfg = loadConfig();
4199
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4200
- if (!agentId2) {
4624
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4625
+ if (!agentId3) {
4201
4626
  console.error(c.error("Specify --agent <agentId>"));
4202
4627
  process.exit(1);
4203
4628
  }
4204
4629
  const spinner = spin("Fetching stats\u2026");
4205
4630
  try {
4206
4631
  const client = makeClient();
4207
- const res = await client.memory.stats(agentId2);
4632
+ const res = await client.memory.stats(agentId3);
4208
4633
  const stats = res?.data ?? res;
4209
4634
  spinner.stop();
4210
4635
  if (opts.json) return jsonOut(stats);
@@ -4264,15 +4689,15 @@ ${sym.ok} Memory ${c.id(memoryId)} deleted`);
4264
4689
  });
4265
4690
  cmd.command("search <query>").description("Semantic search over agent memories").option("--agent <agentId>", "Agent ID").option("--limit <n>", "Max results", "10").option("--json", "Output as JSON").action(async (query, opts) => {
4266
4691
  const cfg = loadConfig();
4267
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4268
- if (!agentId2) {
4692
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4693
+ if (!agentId3) {
4269
4694
  console.error(c.error("Specify --agent <agentId>"));
4270
4695
  process.exit(1);
4271
4696
  }
4272
4697
  const spinner = spin("Searching memories\u2026");
4273
4698
  try {
4274
4699
  const client = makeClient();
4275
- const res = await client.memory.retrieve(agentId2, query, parseInt(opts.limit, 10));
4700
+ const res = await client.memory.retrieve(agentId3, query, parseInt(opts.limit, 10));
4276
4701
  const memories = res?.data ?? res ?? [];
4277
4702
  spinner.stop();
4278
4703
  if (opts.json) return jsonOut(memories);
@@ -4361,18 +4786,18 @@ function usageCommand() {
4361
4786
  process.exit(1);
4362
4787
  }
4363
4788
  });
4364
- cmd.command("agent <agentId>").description("Show detailed usage for a specific agent").option("--from <date>", "Start date (ISO)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (agentId2, opts) => {
4789
+ cmd.command("agent <agentId>").description("Show detailed usage for a specific agent").option("--from <date>", "Start date (ISO)").option("--to <date>", "End date (ISO)").option("--json", "Output as JSON").action(async (agentId3, opts) => {
4365
4790
  const spinner = spin("Fetching usage\u2026");
4366
4791
  try {
4367
4792
  const client = makeClient();
4368
- const res = await client.usage.getAgentUsage(agentId2, {
4793
+ const res = await client.usage.getAgentUsage(agentId3, {
4369
4794
  from: opts.from,
4370
4795
  to: opts.to
4371
4796
  });
4372
4797
  const data = res?.data ?? res;
4373
4798
  spinner.stop();
4374
4799
  if (opts.json) return jsonOut(data);
4375
- section(`Usage \u2014 ${agentId2.slice(0, 12)}\u2026`);
4800
+ section(`Usage \u2014 ${agentId3.slice(0, 12)}\u2026`);
4376
4801
  detail([
4377
4802
  ["Calls", (data.callCount ?? 0).toLocaleString()],
4378
4803
  ["Input tokens", (data.totalInputTokens ?? 0).toLocaleString()],
@@ -4509,15 +4934,15 @@ function logsCommand() {
4509
4934
  const cmd = new import_commander17.Command("logs").description("View agent activity logs");
4510
4935
  cmd.command("list").alias("ls").description("List recent log entries for an agent").option("--agent <agentId>", "Agent ID (defaults to configured agent)").option("--session <sessionId>", "Filter by session ID").option("--status <status>", "Filter: success | error | warning").option("--limit <n>", "Max entries to show", "50").option("--json", "Output as JSON").action(async (opts) => {
4511
4936
  const cfg = loadConfig();
4512
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4513
- if (!agentId2) {
4937
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4938
+ if (!agentId3) {
4514
4939
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4515
4940
  process.exit(1);
4516
4941
  }
4517
4942
  const spinner = spin("Fetching logs\u2026");
4518
4943
  try {
4519
4944
  const client = makeClient();
4520
- const res = await client.logs.list(agentId2, {
4945
+ const res = await client.logs.list(agentId3, {
4521
4946
  limit: Number(opts.limit),
4522
4947
  sessionId: opts.session
4523
4948
  });
@@ -4525,7 +4950,7 @@ function logsCommand() {
4525
4950
  if (opts.status) logs = logs.filter((l) => l.status === opts.status);
4526
4951
  spinner.stop();
4527
4952
  if (opts.json) return jsonOut(logs);
4528
- section(`Logs \u2014 ${agentId2.slice(0, 12)}\u2026 (${logs.length})`);
4953
+ section(`Logs \u2014 ${agentId3.slice(0, 12)}\u2026 (${logs.length})`);
4529
4954
  if (logs.length === 0) {
4530
4955
  console.log(c.dim(" No logs yet"));
4531
4956
  return;
@@ -4550,21 +4975,21 @@ function logsCommand() {
4550
4975
  });
4551
4976
  cmd.command("errors").description("Show only error log entries for an agent").option("--agent <agentId>", "Agent ID").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action(async (opts) => {
4552
4977
  const cfg = loadConfig();
4553
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4554
- if (!agentId2) {
4978
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4979
+ if (!agentId3) {
4555
4980
  console.error(c.error("Specify --agent <agentId>"));
4556
4981
  process.exit(1);
4557
4982
  }
4558
4983
  const spinner = spin("Fetching error logs\u2026");
4559
4984
  try {
4560
4985
  const client = makeClient();
4561
- const res = await client.logs.list(agentId2, {
4986
+ const res = await client.logs.list(agentId3, {
4562
4987
  limit: Number(opts.limit)
4563
4988
  });
4564
4989
  const errors = (res?.data ?? []).filter((l) => l.status === "error");
4565
4990
  spinner.stop();
4566
4991
  if (opts.json) return jsonOut(errors);
4567
- section(`Errors \u2014 ${agentId2.slice(0, 12)}\u2026 (${errors.length})`);
4992
+ section(`Errors \u2014 ${agentId3.slice(0, 12)}\u2026 (${errors.length})`);
4568
4993
  if (errors.length === 0) {
4569
4994
  console.log(`${sym.ok} No errors found`);
4570
4995
  return;
@@ -4593,13 +5018,13 @@ var RESOURCE_PROFILES = [
4593
5018
  ];
4594
5019
  var RESOURCE_MODES = ["fixed", "elastic"];
4595
5020
  function resolveAgentId(opts) {
4596
- const agentId2 = opts.agent ?? loadConfig().defaultAgentId;
4597
- if (!agentId2) {
5021
+ const agentId3 = opts.agent ?? loadConfig().defaultAgentId;
5022
+ if (!agentId3) {
4598
5023
  throw new Error(
4599
5024
  "Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`."
4600
5025
  );
4601
5026
  }
4602
- return agentId2;
5027
+ return agentId3;
4603
5028
  }
4604
5029
  function unwrap(response) {
4605
5030
  return response?.data ?? response;
@@ -4650,17 +5075,17 @@ function parseNumber(value, name, options) {
4650
5075
  }
4651
5076
  return parsed;
4652
5077
  }
4653
- async function changeEnabled(agentId2, enabled, json) {
5078
+ async function changeEnabled(agentId3, enabled, json) {
4654
5079
  const spinner = spin(`${enabled ? "Enabling" : "Disabling"} persistent cloud computer\u2026`);
4655
5080
  try {
4656
- const response = await makeClient().agents.updateComputerConfig(agentId2, { enabled });
5081
+ const response = await makeClient().agents.updateComputerConfig(agentId3, { enabled });
4657
5082
  const config = unwrap(response);
4658
5083
  spinner.stop();
4659
5084
  if (json) return jsonOut(config);
4660
5085
  console.log(`
4661
- ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agent ${c.id(agentId2)}`);
5086
+ ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agent ${c.id(agentId3)}`);
4662
5087
  if (enabled) {
4663
- console.log(c.dim(` Wake it now with: agc computer wake --agent ${agentId2}`));
5088
+ console.log(c.dim(` Wake it now with: agc computer wake --agent ${agentId3}`));
4664
5089
  }
4665
5090
  } catch (error) {
4666
5091
  spinner.stop();
@@ -4668,12 +5093,12 @@ ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agen
4668
5093
  process.exitCode = 1;
4669
5094
  }
4670
5095
  }
4671
- async function lifecycleAction(action, agentId2, reason, json) {
5096
+ async function lifecycleAction(action, agentId3, reason, json) {
4672
5097
  const verb = action === "wake" ? "Waking" : action === "sleep" ? "Sleeping" : "Restarting";
4673
5098
  const spinner = spin(`${verb} persistent cloud computer\u2026`);
4674
5099
  try {
4675
5100
  const client = makeClient();
4676
- const response = action === "wake" ? await client.agents.wakeComputer(agentId2, reason ? { reason } : void 0) : action === "sleep" ? await client.agents.sleepComputer(agentId2, reason ? { reason } : void 0) : await client.agents.restartComputer(agentId2, reason ? { reason } : void 0);
5101
+ const response = action === "wake" ? await client.agents.wakeComputer(agentId3, reason ? { reason } : void 0) : action === "sleep" ? await client.agents.sleepComputer(agentId3, reason ? { reason } : void 0) : await client.agents.restartComputer(agentId3, reason ? { reason } : void 0);
4677
5102
  const computer = unwrap(response);
4678
5103
  spinner.stop();
4679
5104
  if (json) return jsonOut(computer);
@@ -4692,9 +5117,9 @@ function addAgentOption(command) {
4692
5117
  function computerCommand() {
4693
5118
  const command = new import_commander18.Command("computer").description("Manage an agent's one persistent cloud computer");
4694
5119
  addAgentOption(command.command("status").description("Show persistent cloud computer status")).option("--json", "Output as JSON").action(async (opts) => {
4695
- let agentId2;
5120
+ let agentId3;
4696
5121
  try {
4697
- agentId2 = resolveAgentId(opts);
5122
+ agentId3 = resolveAgentId(opts);
4698
5123
  } catch (error) {
4699
5124
  printError(error);
4700
5125
  process.exitCode = 1;
@@ -4702,7 +5127,7 @@ function computerCommand() {
4702
5127
  }
4703
5128
  const spinner = spin("Fetching persistent cloud computer\u2026");
4704
5129
  try {
4705
- const computer = unwrap(await makeClient().agents.getComputer(agentId2));
5130
+ const computer = unwrap(await makeClient().agents.getComputer(agentId3));
4706
5131
  spinner.stop();
4707
5132
  if (opts.json) return jsonOut(computer);
4708
5133
  displayComputer(computer);
@@ -4744,10 +5169,10 @@ function computerCommand() {
4744
5169
  });
4745
5170
  }
4746
5171
  addAgentOption(command.command("resize").description("Resize the persistent cloud computer")).option("--profile <profile>", `Resource profile: ${RESOURCE_PROFILES.join(" | ")}`).option("--mode <mode>", `Resource mode: ${RESOURCE_MODES.join(" | ")}`).option("--vcpu <count>", "Requested virtual CPU count").option("--cpu <count>", "Alias for --vcpu").option("--memory <gib>", "Requested memory in GiB").option("--storage <gib>", "Requested persistent storage in GiB").option("--gpu-type <type>", "GPU type, such as nvidia-h100").option("--gpu-count <count>", "GPU count (0 removes GPU allocation)").option("--json", "Output as JSON").action(async (opts) => {
4747
- let agentId2;
5172
+ let agentId3;
4748
5173
  let resize;
4749
5174
  try {
4750
- agentId2 = resolveAgentId(opts);
5175
+ agentId3 = resolveAgentId(opts);
4751
5176
  if (opts.profile && !RESOURCE_PROFILES.includes(opts.profile)) {
4752
5177
  throw new Error(`--profile must be one of: ${RESOURCE_PROFILES.join(", ")}.`);
4753
5178
  }
@@ -4784,7 +5209,7 @@ function computerCommand() {
4784
5209
  }
4785
5210
  const spinner = spin("Resizing persistent cloud computer\u2026");
4786
5211
  try {
4787
- const computer = unwrap(await makeClient().agents.resizeComputer(agentId2, resize));
5212
+ const computer = unwrap(await makeClient().agents.resizeComputer(agentId3, resize));
4788
5213
  spinner.stop();
4789
5214
  if (opts.json) return jsonOut(computer);
4790
5215
  console.log(`
@@ -4799,10 +5224,10 @@ ${sym.ok} Persistent cloud computer resize requested`);
4799
5224
  addAgentOption(
4800
5225
  command.command("exec").description("Run a command in the persistent cloud computer").argument("<command...>", "Command and arguments to run")
4801
5226
  ).option("--cwd <path>", "Working directory").option("--timeout <seconds>", "Command timeout in seconds", "120").option("--json", "Output as JSON").action(async (commandParts, opts) => {
4802
- let agentId2;
5227
+ let agentId3;
4803
5228
  let timeoutSeconds;
4804
5229
  try {
4805
- agentId2 = resolveAgentId(opts);
5230
+ agentId3 = resolveAgentId(opts);
4806
5231
  timeoutSeconds = parseNumber(opts.timeout, "Timeout");
4807
5232
  } catch (error) {
4808
5233
  printError(error);
@@ -4811,7 +5236,7 @@ ${sym.ok} Persistent cloud computer resize requested`);
4811
5236
  }
4812
5237
  const spinner = spin("Running command in persistent cloud computer\u2026");
4813
5238
  try {
4814
- const result = unwrap(await makeClient().agents.execComputer(agentId2, {
5239
+ const result = unwrap(await makeClient().agents.execComputer(agentId3, {
4815
5240
  command: commandParts.join(" "),
4816
5241
  ...opts.cwd && { cwd: opts.cwd },
4817
5242
  ...timeoutSeconds !== void 0 && { timeoutSeconds }
@@ -4831,10 +5256,10 @@ ${sym.ok} Persistent cloud computer resize requested`);
4831
5256
  }
4832
5257
  });
4833
5258
  addAgentOption(command.command("events").description("List recent persistent cloud computer events")).option("--limit <count>", "Maximum events", "50").option("--json", "Output as JSON").action(async (opts) => {
4834
- let agentId2;
5259
+ let agentId3;
4835
5260
  let limit;
4836
5261
  try {
4837
- agentId2 = resolveAgentId(opts);
5262
+ agentId3 = resolveAgentId(opts);
4838
5263
  limit = parseNumber(opts.limit, "Limit", { integer: true });
4839
5264
  } catch (error) {
4840
5265
  printError(error);
@@ -4843,7 +5268,7 @@ ${sym.ok} Persistent cloud computer resize requested`);
4843
5268
  }
4844
5269
  const spinner = spin("Fetching persistent cloud computer events\u2026");
4845
5270
  try {
4846
- const events = unwrap(await makeClient().agents.listComputerEvents(agentId2, limit));
5271
+ const events = unwrap(await makeClient().agents.listComputerEvents(agentId3, limit));
4847
5272
  spinner.stop();
4848
5273
  if (opts.json) return jsonOut(events);
4849
5274
  section(`Cloud computer events (${events.length})`);
@@ -4867,8 +5292,8 @@ ${sym.ok} Persistent cloud computer resize requested`);
4867
5292
 
4868
5293
  // src/commands/library.ts
4869
5294
  var import_commander19 = require("commander");
4870
- var import_fs7 = require("fs");
4871
- var import_path5 = require("path");
5295
+ var import_fs8 = require("fs");
5296
+ var import_path6 = require("path");
4872
5297
  function libraryCommand() {
4873
5298
  const command = new import_commander19.Command("library").alias("files").description("Upload, find, and manage files in your Commons library");
4874
5299
  command.command("list", { isDefault: true }).alias("ls").description("List library items").option("--query <text>", "Search names and descriptions").option("--source <source>", "Filter by source").option("--session <sessionId>", "Filter by session").option("--favorites", "Show favorites only").option("--limit <n>", "Maximum items", "50").option("--json", "Output as JSON").action(async (opts) => {
@@ -4930,8 +5355,8 @@ function libraryCommand() {
4930
5355
  throw new Error("--storage must be either s3 or ipfs.");
4931
5356
  }
4932
5357
  const files = paths.map((path) => ({
4933
- data: new Blob([new Uint8Array((0, import_fs7.readFileSync)(path))]),
4934
- name: (0, import_path5.basename)(path)
5358
+ data: new Blob([new Uint8Array((0, import_fs8.readFileSync)(path))]),
5359
+ name: (0, import_path6.basename)(path)
4935
5360
  }));
4936
5361
  const result = await makeClient().files.upload(files, {
4937
5362
  agentId: opts.agent,
@@ -4990,7 +5415,7 @@ ${sym.ok} Uploaded ${result.data.length} file${result.data.length === 1 ? "" : "
4990
5415
 
4991
5416
  // src/commands/projects.ts
4992
5417
  var import_commander20 = require("commander");
4993
- var import_fs8 = require("fs");
5418
+ var import_fs9 = require("fs");
4994
5419
  function agentId(value) {
4995
5420
  const resolved = value ?? loadConfig().defaultAgentId;
4996
5421
  if (!resolved) {
@@ -5002,7 +5427,7 @@ function agentId(value) {
5002
5427
  }
5003
5428
  function projectFiles(path) {
5004
5429
  if (!path) return void 0;
5005
- const parsed = JSON.parse((0, import_fs8.readFileSync)(path, "utf8"));
5430
+ const parsed = JSON.parse((0, import_fs9.readFileSync)(path, "utf8"));
5006
5431
  const files = Array.isArray(parsed) ? parsed : parsed.files;
5007
5432
  if (!Array.isArray(files)) {
5008
5433
  throw new Error("The files document must be an array or an object with a files array.");
@@ -5163,8 +5588,160 @@ ${sym.ok} Project published.`);
5163
5588
  return command;
5164
5589
  }
5165
5590
 
5166
- // src/commands/api-keys.ts
5591
+ // src/commands/arcade.ts
5167
5592
  var import_commander21 = require("commander");
5593
+ var import_fs10 = require("fs");
5594
+ var import_path7 = require("path");
5595
+ function agentId2(value) {
5596
+ const resolved = value ?? loadConfig().defaultAgentId;
5597
+ if (!resolved) {
5598
+ throw new Error(
5599
+ "Specify --agent <agentId> or set a default with `agc config set defaultAgentId <id>`."
5600
+ );
5601
+ }
5602
+ return resolved;
5603
+ }
5604
+ var SOURCE_EXTENSIONS = /\.(html|css|js|mjs|cjs|ts|tsx|jsx|json|svg|md|txt)$/i;
5605
+ function readGameDirectory(root) {
5606
+ const files = [];
5607
+ const walk = (directory) => {
5608
+ for (const name of (0, import_fs10.readdirSync)(directory)) {
5609
+ if (name.startsWith(".") || name === "node_modules" || name === "dist") continue;
5610
+ const full = (0, import_path7.join)(directory, name);
5611
+ if ((0, import_fs10.statSync)(full).isDirectory()) walk(full);
5612
+ else if (SOURCE_EXTENSIONS.test(name)) {
5613
+ files.push({
5614
+ path: (0, import_path7.relative)(root, full).split(import_path7.sep).join("/"),
5615
+ content: (0, import_fs10.readFileSync)(full, "utf8")
5616
+ });
5617
+ }
5618
+ }
5619
+ };
5620
+ walk(root);
5621
+ if (!files.length) throw new Error(`No game source files found in ${root}.`);
5622
+ return files;
5623
+ }
5624
+ async function run(label, work) {
5625
+ const spinner = spin(label);
5626
+ try {
5627
+ const result = await work();
5628
+ spinner.stop();
5629
+ return result;
5630
+ } catch (error) {
5631
+ spinner.stop();
5632
+ printError(error);
5633
+ process.exit(1);
5634
+ }
5635
+ }
5636
+ function arcadeCommand() {
5637
+ const command = new import_commander21.Command("arcade").description(
5638
+ "Build, test, and publish Common Arcade games in your agent owner's account"
5639
+ );
5640
+ command.command("status").description("Check whether this platform is connected to Common Arcade").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
5641
+ const result = await run(
5642
+ "Checking Arcade connection\u2026",
5643
+ () => makeClient().arcade.status(agentId2(opts.agent))
5644
+ );
5645
+ if (opts.json) return jsonOut(result.data);
5646
+ console.log(
5647
+ result.data.connected ? `${sym.ok} Connected to Common Arcade.` : `${sym.fail} Common Arcade is not connected on this deployment.`
5648
+ );
5649
+ });
5650
+ command.command("list", { isDefault: true }).alias("ls").description("List Arcade game projects").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
5651
+ const result = await run(
5652
+ "Fetching Arcade projects\u2026",
5653
+ () => makeClient().arcade.list(agentId2(opts.agent))
5654
+ );
5655
+ if (opts.json) return jsonOut(result.data);
5656
+ section(`Arcade projects (${result.data.length})`);
5657
+ table(
5658
+ result.data.map((project) => ({
5659
+ ID: project.projectId,
5660
+ Title: project.title ?? "",
5661
+ Published: project.isPublished ? "yes" : "no",
5662
+ Studio: project.studioUrl
5663
+ })),
5664
+ ["ID", "Title", "Published", "Studio"]
5665
+ );
5666
+ });
5667
+ command.command("get <projectId>").description("Show an Arcade game project").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (projectId, opts) => {
5668
+ const result = await run(
5669
+ "Fetching Arcade project\u2026",
5670
+ () => makeClient().arcade.get(agentId2(opts.agent), projectId)
5671
+ );
5672
+ if (opts.json) return jsonOut(result.data);
5673
+ const project = result.data;
5674
+ section(project.title ?? project.projectId);
5675
+ detail([
5676
+ ["Project ID", c.id(project.projectId)],
5677
+ ["Revision", String(project.revision)],
5678
+ ["Files", project.document.files.map((file) => file.path).join(", ")],
5679
+ ["Published", project.isPublished ? "yes" : "no"],
5680
+ ["Thumbnail", project.hasThumbnail ? "yes" : "no"],
5681
+ ["Studio", project.studioUrl]
5682
+ ]);
5683
+ });
5684
+ command.command("create").description("Create an Arcade game project").requiredOption("--title <title>", "Game title").option("--description <text>", "How to play").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
5685
+ const result = await run(
5686
+ "Creating Arcade project\u2026",
5687
+ () => makeClient().arcade.create(agentId2(opts.agent), {
5688
+ title: opts.title,
5689
+ description: opts.description
5690
+ })
5691
+ );
5692
+ if (opts.json) return jsonOut(result.data);
5693
+ console.log(`
5694
+ ${sym.ok} Arcade project created.`);
5695
+ detail([
5696
+ ["Project ID", c.id(result.data.projectId)],
5697
+ ["Studio", result.data.studioUrl]
5698
+ ]);
5699
+ });
5700
+ command.command("push <projectId> <directory>").description("Upload a local game directory to an Arcade project").option("--agent <agentId>", "Agent ID").option("--entry <file>", "HTML entry file (default: index.html)").option("--runtime <file>", "Authoritative rules file, required to publish").option("--thumbnail <url>", "HTTPS thumbnail URL, required to publish").option("--replace", "Remove project files that are not in the directory").option("--json", "Output as JSON").action(async (projectId, directory, opts) => {
5701
+ const files = readGameDirectory(directory);
5702
+ const result = await run(
5703
+ `Uploading ${files.length} files\u2026`,
5704
+ () => makeClient().arcade.write(agentId2(opts.agent), projectId, {
5705
+ files,
5706
+ replaceFiles: Boolean(opts.replace),
5707
+ entryFile: opts.entry,
5708
+ thumbnail: opts.thumbnail,
5709
+ runtime: opts.runtime ? { entryFile: opts.runtime } : void 0
5710
+ })
5711
+ );
5712
+ if (opts.json) return jsonOut(result.data);
5713
+ console.log(`${sym.ok} Saved revision ${result.data.revision}.`);
5714
+ detail([["Studio", result.data.studioUrl]]);
5715
+ });
5716
+ command.command("test <projectId>").description("Run Arcade's validation and runtime harness").option("--agent <agentId>", "Agent ID").option("--steps <n>", "Steps to simulate", (value) => Number(value)).option("--seed <seed>", "Deterministic seed").action(async (projectId, opts) => {
5717
+ const result = await run(
5718
+ "Testing game\u2026",
5719
+ () => makeClient().arcade.test(agentId2(opts.agent), projectId, {
5720
+ steps: opts.steps,
5721
+ seed: opts.seed
5722
+ })
5723
+ );
5724
+ jsonOut(result.data);
5725
+ });
5726
+ command.command("publish <projectId>").description("Publish an Arcade game as an immutable release").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (projectId, opts) => {
5727
+ const result = await run(
5728
+ "Publishing game\u2026",
5729
+ () => makeClient().arcade.publish(agentId2(opts.agent), projectId)
5730
+ );
5731
+ if (opts.json) return jsonOut(result.data);
5732
+ console.log(`
5733
+ ${sym.ok} Game published.`);
5734
+ detail([
5735
+ ["Release", result.data.releaseId],
5736
+ ["Play", result.data.gameUrl],
5737
+ ["Studio", result.data.studioUrl]
5738
+ ]);
5739
+ });
5740
+ return command;
5741
+ }
5742
+
5743
+ // src/commands/api-keys.ts
5744
+ var import_commander22 = require("commander");
5168
5745
  async function resolveProject(projectId) {
5169
5746
  const projects = (await makeClient().developer.listProjects()).data;
5170
5747
  const project = projectId ? projects.find((candidate) => candidate.id === projectId) : projects[0];
@@ -5176,7 +5753,7 @@ async function resolveProject(projectId) {
5176
5753
  return project;
5177
5754
  }
5178
5755
  function apiKeysCommand() {
5179
- const command = new import_commander21.Command("keys").alias("api-keys").description("Create and manage project-scoped developer API keys");
5756
+ const command = new import_commander22.Command("keys").alias("api-keys").description("Create and manage project-scoped developer API keys");
5180
5757
  command.command("list", { isDefault: true }).alias("ls").description("List API keys for a developer project").option("--project <projectId>", "Developer project ID (defaults to newest)").option("--json", "Output as JSON").action(async (opts) => {
5181
5758
  const spinner = spin("Fetching developer keys\u2026");
5182
5759
  try {
@@ -5330,6 +5907,96 @@ ${sym.ok} Developer project created.`);
5330
5907
  return command;
5331
5908
  }
5332
5909
 
5910
+ // src/commands/provenance.ts
5911
+ var import_commander23 = require("commander");
5912
+ function printTrajectory(trajectory) {
5913
+ const sources = trajectory.events.flatMap(
5914
+ (event) => event.metadata?.lineage?.sources ?? []
5915
+ );
5916
+ const delegates = trajectory.events.flatMap(
5917
+ (event) => event.metadata?.lineage?.delegation ? [event.metadata.lineage.delegation] : []
5918
+ );
5919
+ const matches = trajectory.events.flatMap(
5920
+ (event) => event.metadata?.lineage?.library?.results ?? []
5921
+ );
5922
+ section("Provenance report");
5923
+ detail([
5924
+ ["Runs", String(trajectory.summary?.runs ?? trajectory.runs?.length ?? 0)],
5925
+ [
5926
+ "Events",
5927
+ String(trajectory.summary?.events ?? trajectory.events?.length ?? 0)
5928
+ ],
5929
+ [
5930
+ "Model / tool calls",
5931
+ `${trajectory.summary?.modelCalls ?? 0} / ${trajectory.summary?.toolCalls ?? 0}`
5932
+ ],
5933
+ ["Sources", String(sources.length)],
5934
+ ["Agent delegations", String(delegates.length)],
5935
+ ["Library matches", String(matches.length)],
5936
+ ["Dropped events", String(trajectory.summary?.droppedEvents ?? 0)]
5937
+ ]);
5938
+ if (sources.length) {
5939
+ section("Web sources");
5940
+ for (const source of sources)
5941
+ console.log(
5942
+ ` ${c.primary(source.domain)} ${source.title ?? ""}
5943
+ ${c.dim(source.url)}`
5944
+ );
5945
+ }
5946
+ if (delegates.length) {
5947
+ section("Agent contributions");
5948
+ for (const item of delegates)
5949
+ console.log(
5950
+ ` ${item.fromAgentId ?? "workflow"} \u2192 ${c.primary(item.toAgentId)} ${c.dim(item.role ?? "")}`
5951
+ );
5952
+ }
5953
+ if (matches.length) {
5954
+ section("Library matches");
5955
+ for (const item of matches)
5956
+ console.log(
5957
+ ` ${c.primary(`${item.percentageMatch}%`)} ${item.name ?? item.itemId} ${c.dim(`#${item.rank}`)}`
5958
+ );
5959
+ }
5960
+ }
5961
+ function provenanceCommand() {
5962
+ const cmd = new import_commander23.Command("provenance").description(
5963
+ "Inspect and export provenance and attribution trails"
5964
+ );
5965
+ cmd.command("session <sessionId>").description("Show a plain-language sources and contributors report").option("--json", "Output the complete machine-readable trajectory").action(async (sessionId, opts) => {
5966
+ const spinner = spin("Building provenance report\u2026");
5967
+ try {
5968
+ const { data } = await makeClient().provenance.session(sessionId);
5969
+ spinner.stop();
5970
+ if (opts.json) return jsonOut(data);
5971
+ printTrajectory(data);
5972
+ } catch (error) {
5973
+ spinner.stop();
5974
+ printError(error);
5975
+ process.exitCode = 1;
5976
+ }
5977
+ });
5978
+ cmd.command("workflow <executionId>").description("Show provenance for a workflow execution").option("--json", "Output the complete machine-readable trajectory").action(async (executionId, opts) => {
5979
+ const spinner = spin("Building workflow provenance report\u2026");
5980
+ try {
5981
+ const { data } = await makeClient().provenance.scope(
5982
+ "workflow",
5983
+ executionId
5984
+ );
5985
+ spinner.stop();
5986
+ if (opts.json) return jsonOut(data);
5987
+ printTrajectory(data);
5988
+ } catch (error) {
5989
+ spinner.stop();
5990
+ printError(error);
5991
+ process.exitCode = 1;
5992
+ }
5993
+ });
5994
+ cmd.command("bundle <traceId>").description("Export the standards-based EAA provenance bundle as JSON").action(
5995
+ async (traceId) => jsonOut((await makeClient().provenance.bundle(traceId)).data)
5996
+ );
5997
+ return cmd;
5998
+ }
5999
+
5333
6000
  // src/bin.ts
5334
6001
  async function interactiveMenu() {
5335
6002
  banner();
@@ -5337,9 +6004,15 @@ async function interactiveMenu() {
5337
6004
  const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
5338
6005
  if (!isSetup) {
5339
6006
  console.log(c.bold(" Welcome to Agent Commons CLI!"));
5340
- console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
5341
- console.log(` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
5342
- `);
6007
+ console.log(
6008
+ c.dim(
6009
+ " Looks like this is your first time here \u2014 let's get you set up.\n"
6010
+ )
6011
+ );
6012
+ console.log(
6013
+ ` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
6014
+ `
6015
+ );
5343
6016
  runSubcommand(["login"]);
5344
6017
  return;
5345
6018
  }
@@ -5350,7 +6023,11 @@ async function interactiveMenu() {
5350
6023
  const action = await select("What would you like to do?", [
5351
6024
  { label: "Chat with an agent", value: "chat", hint: "agc chat" },
5352
6025
  { label: "Run an agent (one-shot)", value: "run", hint: "agc run" },
5353
- { label: "Manage an agent cloud computer", value: "computer", hint: "agc computer status" },
6026
+ {
6027
+ label: "Manage an agent cloud computer",
6028
+ value: "computer",
6029
+ hint: "agc computer status"
6030
+ },
5354
6031
  { label: "View sessions", value: "sessions", hint: "agc sessions list" },
5355
6032
  { label: "Manage agents", value: "agents", hint: "agc agents list" },
5356
6033
  { label: "Tasks", value: "tasks", hint: "agc task list" },
@@ -5370,19 +6047,19 @@ async function interactiveMenu() {
5370
6047
  process.exit(0);
5371
6048
  }
5372
6049
  const needsAgent = action === "chat" || action === "run" || action === "computer";
5373
- const agentId2 = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
5374
- if (needsAgent && !agentId2) return;
6050
+ const agentId3 = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
6051
+ if (needsAgent && !agentId3) return;
5375
6052
  if (action === "run") {
5376
6053
  const prompt = await askPrompt("Enter your prompt:");
5377
6054
  if (!prompt) return;
5378
- runSubcommand(["run", "--agent", agentId2, prompt]);
6055
+ runSubcommand(["run", "--agent", agentId3, prompt]);
5379
6056
  return;
5380
6057
  }
5381
6058
  const commandMap = {
5382
- chat: ["chat", "--agent", agentId2],
6059
+ chat: ["chat", "--agent", agentId3],
5383
6060
  run: [],
5384
6061
  // handled above
5385
- computer: ["computer", "status", "--agent", agentId2],
6062
+ computer: ["computer", "status", "--agent", agentId3],
5386
6063
  sessions: ["sessions", "list"],
5387
6064
  agents: ["agents", "list"],
5388
6065
  tasks: ["task", "list"],
@@ -5403,7 +6080,10 @@ async function interactiveMenu() {
5403
6080
  async function askPrompt(question) {
5404
6081
  const { createInterface: createInterface3 } = await import("readline");
5405
6082
  return new Promise((resolve2) => {
5406
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
6083
+ const rl = createInterface3({
6084
+ input: process.stdin,
6085
+ output: process.stdout
6086
+ });
5407
6087
  process.stdout.write(`
5408
6088
  ${c.bold(question)}
5409
6089
  ${c.primary("\u203A")} `);
@@ -5431,26 +6111,35 @@ async function pickAgentInteractively(action) {
5431
6111
  spinner.stop();
5432
6112
  } catch {
5433
6113
  spinner.stop();
5434
- console.log(`
6114
+ console.log(
6115
+ `
5435
6116
  ${c.warn("\u26A0")} Could not fetch agents. Check your sign-in and connection.
5436
- `);
6117
+ `
6118
+ );
5437
6119
  return null;
5438
6120
  }
5439
6121
  if (agents.length === 0) {
5440
6122
  console.log(`
5441
6123
  ${c.warn("\u26A0")} You don't have any agents yet.
5442
6124
  `);
5443
- const choice = await select("What would you like to do?", [
5444
- { label: "Create a new agent now", value: "create", hint: "agc agents create" },
5445
- { label: "Go back", value: "cancel" }
5446
- ]);
6125
+ const choice = await select(
6126
+ "What would you like to do?",
6127
+ [
6128
+ {
6129
+ label: "Create a new agent now",
6130
+ value: "create",
6131
+ hint: "agc agents create"
6132
+ },
6133
+ { label: "Go back", value: "cancel" }
6134
+ ]
6135
+ );
5447
6136
  if (choice === "create") {
5448
6137
  runSubcommand(["agents", "create"]);
5449
6138
  }
5450
6139
  return null;
5451
6140
  }
5452
6141
  console.log();
5453
- const agentId2 = await select(
6142
+ const agentId3 = await select(
5454
6143
  action === "computer" ? "Choose the agent whose cloud computer you want to manage:" : `Choose an agent to ${action} with:`,
5455
6144
  agents.map((a) => ({
5456
6145
  label: a.name,
@@ -5463,15 +6152,17 @@ async function pickAgentInteractively(action) {
5463
6152
  { label: "No \u2014 just this once", value: false }
5464
6153
  ]);
5465
6154
  if (saveDefault) {
5466
- saveConfig({ defaultAgentId: agentId2 });
5467
- const chosen = agents.find((a) => a.agentId === agentId2);
5468
- console.log(` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId2)}
5469
- `);
6155
+ saveConfig({ defaultAgentId: agentId3 });
6156
+ const chosen = agents.find((a) => a.agentId === agentId3);
6157
+ console.log(
6158
+ ` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId3)}
6159
+ `
6160
+ );
5470
6161
  }
5471
- return agentId2;
6162
+ return agentId3;
5472
6163
  }
5473
- var program = new import_commander22.Command();
5474
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.4.0", "-v, --version").showHelpAfterError("(run `agc --help` for usage)").configureHelp({
6164
+ var program = new import_commander24.Command();
6165
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.6.1", "-v, --version").showHelpAfterError("(run `agc --help` for usage)").configureHelp({
5475
6166
  sortOptions: true,
5476
6167
  sortSubcommands: true
5477
6168
  }).addHelpText(
@@ -5489,7 +6180,8 @@ Docs: https://docs.agentcommons.io/docs/cli
5489
6180
  await interactiveMenu();
5490
6181
  });
5491
6182
  program.hook("preAction", async (_thisCommand, actionCommand) => {
5492
- if (actionCommand.name() === "login" || actionCommand.name() === "logout") return;
6183
+ if (actionCommand.name() === "login" || actionCommand.name() === "logout")
6184
+ return;
5493
6185
  await ensureAccessToken();
5494
6186
  });
5495
6187
  program.addCommand(loginCommand());
@@ -5502,7 +6194,9 @@ program.addCommand(toolsCommand());
5502
6194
  program.addCommand(connectionsCommand());
5503
6195
  program.addCommand(libraryCommand());
5504
6196
  program.addCommand(projectsCommand());
6197
+ program.addCommand(arcadeCommand());
5505
6198
  program.addCommand(apiKeysCommand());
6199
+ program.addCommand(provenanceCommand());
5506
6200
  program.addCommand(workflowCommand());
5507
6201
  program.addCommand(taskCommand());
5508
6202
  program.addCommand(runCommand());
@@ -5527,8 +6221,10 @@ program.on("command:*", () => {
5527
6221
  process.exit(1);
5528
6222
  });
5529
6223
  program.parseAsync(process.argv).catch((error) => {
5530
- console.error(`
6224
+ console.error(
6225
+ `
5531
6226
  ${sym.fail} ${c.error(error instanceof Error ? error.message : String(error))}
5532
- `);
6227
+ `
6228
+ );
5533
6229
  process.exitCode = 1;
5534
6230
  });