@agent-commons/cli 0.5.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 +993 -300
  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.5.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;
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
  });
@@ -1448,8 +1448,8 @@ ${sym.ok} Workflow created`);
1448
1448
  }
1449
1449
  const templateName = templateNameRaw;
1450
1450
  const needsAgent = templateName === "agent-research-summary" || templateName === "multi-agent-field-report";
1451
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1452
- if (needsAgent && !agentId2) {
1451
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1452
+ if (needsAgent && !agentId3) {
1453
1453
  console.error(c.error("This template requires --agent <agentId> or a configured defaultAgentId."));
1454
1454
  process.exit(1);
1455
1455
  }
@@ -1473,7 +1473,7 @@ ${sym.ok} Workflow created`);
1473
1473
  const ctx = {
1474
1474
  ownerId: cfg.initiator,
1475
1475
  prefix,
1476
- agentId: agentId2,
1476
+ agentId: agentId3,
1477
1477
  reviewerAgentId: opts.reviewerAgent,
1478
1478
  childWorkflowId
1479
1479
  };
@@ -1493,7 +1493,7 @@ ${sym.ok} Workflow created`);
1493
1493
  }
1494
1494
  }
1495
1495
  execution = await makeClient().workflows.execute(result.workflow.workflowId, {
1496
- agentId: agentId2,
1496
+ agentId: agentId3,
1497
1497
  inputData,
1498
1498
  userId: cfg.initiator
1499
1499
  });
@@ -1564,7 +1564,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1564
1564
  });
1565
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) => {
1566
1566
  const cfg = loadConfig();
1567
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1567
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1568
1568
  let inputData = {};
1569
1569
  try {
1570
1570
  inputData = JSON.parse(opts.input);
@@ -1576,7 +1576,7 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
1576
1576
  try {
1577
1577
  const client = makeClient();
1578
1578
  const execution = await client.workflows.execute(workflowId, {
1579
- agentId: agentId2,
1579
+ agentId: agentId3,
1580
1580
  sessionId: opts.session,
1581
1581
  inputData
1582
1582
  });
@@ -1721,12 +1721,12 @@ function taskCommand() {
1721
1721
  const cmd = new import_commander7.Command("task").description("Manage and execute tasks").alias("t");
1722
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) => {
1723
1723
  const cfg = loadConfig();
1724
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1724
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1725
1725
  const spinner = spin("Fetching tasks\u2026");
1726
1726
  try {
1727
1727
  const client = makeClient();
1728
1728
  const filter = {};
1729
- if (agentId2) filter.agentId = agentId2;
1729
+ if (agentId3) filter.agentId = agentId3;
1730
1730
  if (opts.session) filter.sessionId = opts.session;
1731
1731
  if (cfg.initiator) {
1732
1732
  filter.ownerId = cfg.initiator;
@@ -1782,8 +1782,8 @@ function taskCommand() {
1782
1782
  });
1783
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) => {
1784
1784
  const cfg = loadConfig();
1785
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
1786
- if (!agentId2) {
1785
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
1786
+ if (!agentId3) {
1787
1787
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1788
1788
  process.exit(1);
1789
1789
  }
@@ -1799,7 +1799,7 @@ function taskCommand() {
1799
1799
  const client = makeClient();
1800
1800
  const res = await client.tasks.create({
1801
1801
  title: opts.title,
1802
- agentId: agentId2,
1802
+ agentId: agentId3,
1803
1803
  sessionId: opts.session,
1804
1804
  workflowId: opts.workflow,
1805
1805
  inputData,
@@ -1905,14 +1905,237 @@ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1905
1905
  // src/commands/run.ts
1906
1906
  var import_commander8 = require("commander");
1907
1907
  var readline2 = __toESM(require("readline"));
1908
+ var import_fs6 = require("fs");
1909
+ var import_path4 = require("path");
1908
1910
 
1909
1911
  // src/local-tools.ts
1910
1912
  var import_fs5 = require("fs");
1911
1913
  var import_path3 = require("path");
1912
1914
  var import_child_process2 = require("child_process");
1915
+ var import_crypto = require("crypto");
1913
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
1914
2123
  var pdfParse = require("pdf-parse/lib/pdf-parse.js");
1915
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
+ }
1916
2139
  function capBuffer(existing, chunk, maxBytes) {
1917
2140
  const joined = existing + chunk;
1918
2141
  if (joined.length <= maxBytes) return joined;
@@ -1949,12 +2172,8 @@ function buildDirSnapshot(dir, maxDepth = 2) {
1949
2172
  }
1950
2173
  function readFileForContext(rootDir, filePath) {
1951
2174
  try {
1952
- const abs = (0, import_path3.resolve)(rootDir, filePath);
1953
- const rel = (0, import_path3.relative)(rootDir, abs);
1954
- if (rel.startsWith("..") || rel.startsWith("/")) return `[error: path escapes session root]`;
1955
- for (const pat of [/\/\.ssh\//, /\/\.aws\//, /\/\.env$/, /\/\.env\./, /id_rsa/, /id_ed25519/]) {
1956
- if (pat.test(abs)) return `[error: sensitive path blocked]`;
1957
- }
2175
+ const abs = safePath(rootDir, filePath);
2176
+ assertNotSensitive(abs);
1958
2177
  if (!(0, import_fs5.existsSync)(abs)) return `[error: file not found: ${filePath}]`;
1959
2178
  const stat = (0, import_fs5.statSync)(abs);
1960
2179
  if (stat.isDirectory()) return `[error: "${filePath}" is a directory \u2014 use list_directory]`;
@@ -1999,9 +2218,11 @@ ${fileSection}
1999
2218
  | Tool | What it does |
2000
2219
  |------|-------------|
2001
2220
  | \`cli_list_directory\` | List files and folders at a path |
2002
- | \`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 |
2003
2223
  | \`cli_write_file\` | Write or overwrite a file (user confirmation required) |
2004
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) |
2005
2226
  | \`cli_run_command\` | Run a short command and return its output (user confirmation required) |
2006
2227
  | \`cli_start_process\` | Start a long-running command in the background; returns a processId immediately |
2007
2228
  | \`cli_wait_for_process\` | Block up to N seconds for a background process, then return current output |
@@ -2060,102 +2281,50 @@ function extractToolCall(text) {
2060
2281
  }
2061
2282
  return null;
2062
2283
  }
2063
- function injectAgcTrailer(command, args, agentId2, agentName) {
2284
+ function injectAgcTrailer(command, args, agentId3, agentName) {
2064
2285
  if (command !== "git") return args;
2065
2286
  if (!args.some((a) => a === "commit")) return args;
2066
2287
  if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
2067
- const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
2288
+ const identity = agentName ? `${agentName} (agc)` : agentId3 ? `agc/${agentId3}` : "agc agent";
2068
2289
  return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
2069
2290
  }
2070
- var AGC_HOOK_MARKER = "# agc-session:";
2071
- var HOOK_BACKUP_SUFFIX = ".agc-backup";
2072
- function findGitDir(rootDir) {
2073
- const gitPath = (0, import_path3.join)(rootDir, ".git");
2074
- if (!(0, import_fs5.existsSync)(gitPath)) return null;
2075
- const s = (0, import_fs5.statSync)(gitPath);
2076
- if (s.isDirectory()) return gitPath;
2077
- if (s.isFile()) {
2078
- const content = (0, import_fs5.readFileSync)(gitPath, "utf8");
2079
- const match = content.match(/^gitdir:\s*(.+)$/m);
2080
- if (match) return match[1].trim();
2081
- }
2082
- return null;
2083
- }
2084
- function installGitHook(rootDir, sessionId, agentId2, agentName) {
2085
- const gitDir = findGitDir(rootDir);
2086
- if (!gitDir) return;
2087
- const hooksDir = (0, import_path3.join)(gitDir, "hooks");
2088
- (0, import_fs5.mkdirSync)(hooksDir, { recursive: true });
2089
- const hookPath = (0, import_path3.join)(hooksDir, "prepare-commit-msg");
2090
- if ((0, import_fs5.existsSync)(hookPath)) {
2091
- const existing = (0, import_fs5.readFileSync)(hookPath, "utf8");
2092
- if (!existing.includes(AGC_HOOK_MARKER)) {
2093
- (0, import_fs5.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
2094
- }
2095
- }
2096
- const identity = agentName ? `${agentName} (agc)` : agentId2 ? `agc/${agentId2}` : "agc agent";
2097
- const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
2098
- const chainLine = (0, import_fs5.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
2099
- # chain pre-existing hook
2100
- "$(dirname "$0")/prepare-commit-msg${HOOK_BACKUP_SUFFIX}" "$@" 2>/dev/null || true
2101
- ` : "";
2102
- const hook = `#!/bin/sh
2103
- ${AGC_HOOK_MARKER}${sessionId}
2104
- COMMIT_MSG_FILE="$1"
2105
- COMMIT_SOURCE="$2"
2106
- ${chainLine}
2107
- case "$COMMIT_SOURCE" in merge|squash) exit 0 ;; esac
2108
- TRAILER="${trailer}"
2109
- grep -qF "$TRAILER" "$COMMIT_MSG_FILE" 2>/dev/null && exit 0
2110
- printf '\\n%s\\n' "$TRAILER" >> "$COMMIT_MSG_FILE"
2111
- `;
2112
- (0, import_fs5.writeFileSync)(hookPath, hook, { mode: 493 });
2113
- }
2114
- function removeGitHook(rootDir) {
2115
- const gitDir = findGitDir(rootDir);
2116
- if (!gitDir) return;
2117
- const hookPath = (0, import_path3.join)(gitDir, "hooks", "prepare-commit-msg");
2118
- if (!(0, import_fs5.existsSync)(hookPath)) return;
2119
- const content = (0, import_fs5.readFileSync)(hookPath, "utf8");
2120
- if (!content.includes(AGC_HOOK_MARKER)) return;
2121
- const backupPath = hookPath + HOOK_BACKUP_SUFFIX;
2122
- if ((0, import_fs5.existsSync)(backupPath)) {
2123
- (0, import_fs5.writeFileSync)(hookPath, (0, import_fs5.readFileSync)(backupPath, "utf8"), { mode: 493 });
2124
- (0, import_fs5.unlinkSync)(backupPath);
2125
- } else {
2126
- (0, import_fs5.unlinkSync)(hookPath);
2127
- }
2128
- }
2129
2291
  function safePath(root, userPath) {
2130
- const abs = (0, import_path3.resolve)(root, userPath);
2131
- const rel = (0, import_path3.relative)(root, abs);
2132
- if (rel.startsWith("..") || rel.startsWith("/")) {
2133
- 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;
2134
2309
  }
2135
- 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));
2136
2315
  }
2137
- var BLOCKED_PATTERNS = [
2138
- /\/\.ssh\//,
2139
- /\/\.gnupg\//,
2140
- /\/\.agc\//,
2141
- /\/\.aws\//,
2142
- /\/\.env$/,
2143
- /\/\.env\./,
2144
- /id_rsa/,
2145
- /id_ed25519/
2146
- ];
2147
2316
  function assertNotSensitive(abs) {
2148
- for (const pat of BLOCKED_PATTERNS) {
2149
- if (pat.test(abs)) {
2150
- throw new Error(`Access to "${abs}" is blocked for security reasons.`);
2151
- }
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.`);
2152
2320
  }
2153
2321
  }
2154
2322
  async function confirm(message, config, permissionKey) {
2155
- if (config.autoApprove) return true;
2156
2323
  const cached = config.permissions.get(permissionKey);
2157
2324
  if (cached === "allow") return true;
2158
2325
  if (cached === "deny") return false;
2326
+ if (config.autoApprove) return true;
2327
+ if (config.confirm) return config.confirm(message, permissionKey);
2159
2328
  return new Promise((resolve2) => {
2160
2329
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2161
2330
  process.stdout.write(
@@ -2164,22 +2333,23 @@ async function confirm(message, config, permissionKey) {
2164
2333
  \x1B[2m[y] Yes [n] No [A] Always allow this type [N] Never allow this type\x1B[0m
2165
2334
  \x1B[36m?\x1B[0m `
2166
2335
  );
2336
+ rl.once("close", () => resolve2(false));
2167
2337
  rl.once("line", (answer) => {
2168
- rl.close();
2169
- const a = answer.trim().toLowerCase();
2170
- if (a === "a") {
2338
+ const a = answer.trim();
2339
+ if (a === "A") {
2171
2340
  config.permissions.set(permissionKey, "allow");
2172
2341
  resolve2(true);
2173
- } else if (a === "n" || a === "nn") {
2342
+ } else if (a === "N") {
2174
2343
  config.permissions.set(permissionKey, "deny");
2175
2344
  resolve2(false);
2176
2345
  } else {
2177
- resolve2(a === "y" || a === "yes" || a === "");
2346
+ resolve2(a.toLowerCase() === "y" || a.toLowerCase() === "yes");
2178
2347
  }
2348
+ rl.close();
2179
2349
  });
2180
2350
  });
2181
2351
  }
2182
- 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"]);
2183
2353
  var PDF_EXTS = /* @__PURE__ */ new Set([".pdf"]);
2184
2354
  var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
2185
2355
  ".png",
@@ -2211,9 +2381,7 @@ var UNREADABLE_BINARY_EXTS = /* @__PURE__ */ new Set([
2211
2381
  ".ai",
2212
2382
  ".sketch",
2213
2383
  ".figma",
2214
- ".xlsx",
2215
2384
  ".xls",
2216
- ".pptx",
2217
2385
  ".ppt"
2218
2386
  ]);
2219
2387
  function extractViaCommand(cmd, cmdArgs) {
@@ -2224,17 +2392,16 @@ function extractViaCommand(cmd, cmdArgs) {
2224
2392
  });
2225
2393
  });
2226
2394
  }
2227
- async function extractPdfText(abs) {
2395
+ async function extractPdfText(abs, maxChars = 15e4) {
2228
2396
  try {
2229
2397
  const buffer = (0, import_fs5.readFileSync)(abs);
2230
2398
  const data = await pdfParse(buffer);
2231
2399
  const text2 = data.text?.trim();
2232
2400
  if (text2) {
2233
- const MAX_CHARS = 15e4;
2234
- if (text2.length > MAX_CHARS) {
2235
- return text2.slice(0, MAX_CHARS) + `
2401
+ if (text2.length > maxChars) {
2402
+ return text2.slice(0, maxChars) + `
2236
2403
 
2237
- [\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]`;
2238
2405
  }
2239
2406
  return text2;
2240
2407
  }
@@ -2245,47 +2412,129 @@ async function extractPdfText(abs) {
2245
2412
  return `[Cannot extract PDF text: the file may be scanned/image-only or password-protected]`;
2246
2413
  }
2247
2414
  async function extractOfficeText(abs, ext) {
2415
+ const openXml = extractOfficeOpenXmlText((0, import_fs5.readFileSync)(abs), ext);
2416
+ if (openXml) return openXml;
2248
2417
  const text = await extractViaCommand("textutil", ["-stdout", "-cat", "txt", abs]);
2249
2418
  if (text) return text;
2250
- 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;
2251
2430
  }
2252
2431
  async function toolReadFile(args, cfg) {
2253
2432
  const { path: userPath } = args;
2254
2433
  if (!userPath) throw new Error('read_file requires a "path" argument');
2255
2434
  const abs = safePath(cfg.rootDir, userPath);
2256
2435
  assertNotSensitive(abs);
2257
- 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}.`);
2258
2437
  const stat = (0, import_fs5.statSync)(abs);
2259
2438
  if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
2260
2439
  const ext = (0, import_path3.extname)(abs).toLowerCase();
2261
2440
  if (PDF_EXTS.has(ext)) {
2262
2441
  if (stat.size > 5e7) throw new Error(`PDF too large to read (${Math.round(stat.size / 1e6)} MB). Max 50 MB.`);
2263
- return extractPdfText(abs);
2442
+ return pageFileText(await cachedDocumentText(abs, stat, ext), args.offset, userPath, true);
2264
2443
  }
2265
2444
  if (OFFICE_EXTS.has(ext)) {
2266
2445
  if (stat.size > 2e7) throw new Error(`Document too large to read (${Math.round(stat.size / 1e6)} MB). Max 20 MB.`);
2267
- return extractOfficeText(abs, ext);
2446
+ return pageFileText(await cachedDocumentText(abs, stat, ext), args.offset, userPath, true);
2268
2447
  }
2269
2448
  if (UNREADABLE_BINARY_EXTS.has(ext)) {
2270
2449
  throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
2271
2450
  }
2272
2451
  if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
2273
- 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}`;
2274
2516
  }
2275
2517
  async function toolWriteFile(args, cfg) {
2276
2518
  const { path: userPath, content } = args;
2277
2519
  if (!userPath) throw new Error('write_file requires a "path" argument');
2278
- 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');
2279
2521
  const abs = safePath(cfg.rootDir, userPath);
2280
2522
  assertNotSensitive(abs);
2523
+ const before = (0, import_fs5.existsSync)(abs) ? (0, import_fs5.readFileSync)(abs, "utf8") : "";
2524
+ const preview = editPreview(before, content);
2281
2525
  const ok = await confirm(
2282
- `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}`,
2283
2528
  cfg,
2284
2529
  "write_file"
2285
2530
  );
2286
2531
  if (!ok) return "User denied write operation.";
2287
- (0, import_fs5.mkdirSync)((0, import_path3.dirname)(abs), { recursive: true });
2288
- (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");
2289
2538
  return `Written ${String(content).length} bytes to ${userPath}`;
2290
2539
  }
2291
2540
  async function toolListDirectory(args, cfg) {
@@ -2300,6 +2549,10 @@ async function toolListDirectory(args, cfg) {
2300
2549
  });
2301
2550
  return lines.join("\n") || "(empty directory)";
2302
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
+ }
2303
2556
  async function toolSearchFiles(args, cfg) {
2304
2557
  const { pattern, directory } = args;
2305
2558
  if (!pattern) throw new Error('search_files requires a "pattern" argument');
@@ -2359,9 +2612,10 @@ async function toolRunCommand(args, cfg) {
2359
2612
  });
2360
2613
  }
2361
2614
  return new Promise((resolve2) => {
2362
- (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) => {
2363
2616
  const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
2364
- if (err && !out) return resolve2(`Error: ${err.message}`);
2617
+ if (err) return resolve2(`Error: command failed (${err.code ?? "unknown"}): ${err.message}
2618
+ ${out}`);
2365
2619
  resolve2(out || "(no output)");
2366
2620
  });
2367
2621
  });
@@ -2379,11 +2633,14 @@ async function toolStartProcess(args, cfg) {
2379
2633
  "start_process"
2380
2634
  );
2381
2635
  if (!ok) return JSON.stringify({ error: "User denied process start." });
2382
- 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)()}`;
2383
2639
  const child = (0, import_child_process2.spawn)(command, cmdArgs.map(String), {
2384
2640
  cwd: workDir,
2385
2641
  stdio: ["ignore", "pipe", "pipe"],
2386
- detached: false
2642
+ detached: process.platform !== "win32",
2643
+ windowsHide: true
2387
2644
  });
2388
2645
  const proc = {
2389
2646
  id,
@@ -2396,6 +2653,10 @@ async function toolStartProcess(args, cfg) {
2396
2653
  endedAt: null,
2397
2654
  child
2398
2655
  };
2656
+ if (cfg.signal) {
2657
+ if (cfg.signal.aborted) stopProcessTree(proc);
2658
+ else cfg.signal.addEventListener("abort", () => stopProcessTree(proc), { once: true });
2659
+ }
2399
2660
  child.stdout?.on("data", (chunk) => {
2400
2661
  proc.stdout = capBuffer(proc.stdout, chunk.toString(), 2e5);
2401
2662
  });
@@ -2403,7 +2664,7 @@ async function toolStartProcess(args, cfg) {
2403
2664
  proc.stderr = capBuffer(proc.stderr, chunk.toString(), 5e4);
2404
2665
  });
2405
2666
  child.on("close", (code) => {
2406
- proc.status = code === 0 ? "done" : "error";
2667
+ if (proc.status !== "killed") proc.status = code === 0 ? "done" : "error";
2407
2668
  proc.exitCode = code;
2408
2669
  proc.endedAt = /* @__PURE__ */ new Date();
2409
2670
  });
@@ -2443,7 +2704,8 @@ async function toolWaitForProcess(args, _cfg) {
2443
2704
  const proc = managedProcesses.get(processId);
2444
2705
  if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2445
2706
  if (proc.status !== "running") return processSnapshot(proc);
2446
- 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;
2447
2709
  const deadline = Date.now() + maxWait;
2448
2710
  await new Promise((resolve2) => {
2449
2711
  const tick = setInterval(() => {
@@ -2461,7 +2723,7 @@ async function toolKillProcess(args, cfg) {
2461
2723
  const proc = managedProcesses.get(processId);
2462
2724
  if (!proc) return JSON.stringify({ error: `No process found with id "${processId}"` });
2463
2725
  if (proc.status !== "running") return JSON.stringify({ error: `Process "${processId}" is not running (status: ${proc.status})` });
2464
- proc.child.kill("SIGTERM");
2726
+ stopProcessTree(proc);
2465
2727
  proc.status = "killed";
2466
2728
  proc.endedAt = /* @__PURE__ */ new Date();
2467
2729
  cfg.appendLog({ type: "process_killed", processId, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
@@ -2477,6 +2739,15 @@ async function toolListProcesses(_args, _cfg) {
2477
2739
  }));
2478
2740
  return JSON.stringify(list);
2479
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
+ }
2480
2751
  async function runLocalTool(call, cfg) {
2481
2752
  const { tool, args } = call;
2482
2753
  cfg.appendLog({
@@ -2491,6 +2762,9 @@ async function runLocalTool(call, cfg) {
2491
2762
  case "read_file":
2492
2763
  result = await toolReadFile(args, cfg);
2493
2764
  break;
2765
+ case "search_file":
2766
+ result = await toolSearchFile(args, cfg);
2767
+ break;
2494
2768
  case "write_file":
2495
2769
  result = await toolWriteFile(args, cfg);
2496
2770
  break;
@@ -2500,6 +2774,9 @@ async function runLocalTool(call, cfg) {
2500
2774
  case "search_files":
2501
2775
  result = await toolSearchFiles(args, cfg);
2502
2776
  break;
2777
+ case "disk_usage":
2778
+ result = await toolDiskUsage(args, cfg);
2779
+ break;
2503
2780
  case "run_command":
2504
2781
  result = await toolRunCommand(args, cfg);
2505
2782
  break;
@@ -2519,7 +2796,7 @@ async function runLocalTool(call, cfg) {
2519
2796
  result = await toolListProcesses(args, cfg);
2520
2797
  break;
2521
2798
  default:
2522
- 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`;
2523
2800
  }
2524
2801
  } catch (err) {
2525
2802
  result = `Error: ${err?.message ?? String(err)}`;
@@ -2536,19 +2813,72 @@ async function runLocalTool(call, cfg) {
2536
2813
 
2537
2814
  // src/commands/run.ts
2538
2815
  function runCommand() {
2539
- 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) => {
2540
2837
  const cfg = loadConfig();
2541
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
2542
- if (!agentId2) {
2543
- 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
+ );
2544
2845
  process.exit(1);
2545
2846
  }
2546
2847
  if (opts.session && opts.newSession) {
2547
- 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
+ );
2548
2861
  process.exit(1);
2549
2862
  }
2550
2863
  const client = makeClient();
2551
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
+ }
2552
2882
  if (opts.session) {
2553
2883
  const spinner = spin("Loading session\u2026");
2554
2884
  try {
@@ -2564,7 +2894,7 @@ function runCommand() {
2564
2894
  const spinner = spin("Creating session\u2026");
2565
2895
  try {
2566
2896
  const res = await client.sessions.create({
2567
- agentId: agentId2,
2897
+ agentId: agentId3,
2568
2898
  initiator: cfg.initiator ?? "",
2569
2899
  title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2570
2900
  source: "cli"
@@ -2578,6 +2908,24 @@ function runCommand() {
2578
2908
  process.exit(1);
2579
2909
  }
2580
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
+ }
2581
2929
  const localEnabled = opts.yes || opts.local;
2582
2930
  const autoApprove = !!opts.yes;
2583
2931
  let localToolsCfg = null;
@@ -2590,11 +2938,16 @@ function runCommand() {
2590
2938
  appendLog: () => {
2591
2939
  },
2592
2940
  permissions: /* @__PURE__ */ new Map(),
2593
- agentId: agentId2,
2941
+ agentId: agentId3,
2594
2942
  autoApprove
2595
2943
  };
2596
2944
  const snapshot = buildDirSnapshot(rootDir, 2);
2597
- cliContext = buildLocalToolsManifest(rootDir, snapshot, [], autoApprove);
2945
+ cliContext = buildLocalToolsManifest(
2946
+ rootDir,
2947
+ snapshot,
2948
+ [],
2949
+ autoApprove
2950
+ );
2598
2951
  }
2599
2952
  if (!opts.json) {
2600
2953
  const rows = [];
@@ -2603,10 +2956,16 @@ function runCommand() {
2603
2956
  rows.push(["Session", label]);
2604
2957
  }
2605
2958
  if (localEnabled) {
2606
- 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
+ ]);
2607
2963
  }
2608
2964
  if (opts.computer) {
2609
- 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
+ ]);
2610
2969
  }
2611
2970
  if (rows.length) {
2612
2971
  detail(rows);
@@ -2614,12 +2973,17 @@ function runCommand() {
2614
2973
  }
2615
2974
  }
2616
2975
  const params = {
2617
- agentId: agentId2,
2976
+ agentId: agentId3,
2618
2977
  sessionId,
2619
2978
  messages: [{ role: "user", content: prompt }],
2979
+ ...attachments.length && { attachments },
2620
2980
  ...cfg.initiator && { initiatorId: cfg.initiator },
2621
2981
  ...opts.computer && { computerRequest: { enabled: true } },
2622
- ...cliContext && { cliContext }
2982
+ ...cliContext && { cliContext },
2983
+ provenance: {
2984
+ mode: opts.provenance,
2985
+ onchain: Boolean(opts.onchainProvenance)
2986
+ }
2623
2987
  };
2624
2988
  if (opts.noStream && !localEnabled) {
2625
2989
  const spinner = spin("Running\u2026");
@@ -2629,8 +2993,13 @@ function runCommand() {
2629
2993
  if (opts.json) return jsonOut(result);
2630
2994
  const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
2631
2995
  console.log(text);
2632
- if (sessionId) console.log(c.dim(`
2633
- 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
+ );
2634
3003
  } catch (err) {
2635
3004
  spinner.stop();
2636
3005
  printError(err);
@@ -2662,7 +3031,10 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2662
3031
  let result;
2663
3032
  let toolOk = true;
2664
3033
  try {
2665
- result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
3034
+ result = await runLocalTool(
3035
+ { tool: displayName, args: args ?? {} },
3036
+ localToolsCfg
3037
+ );
2666
3038
  } catch (err) {
2667
3039
  result = `Error: ${err?.message ?? String(err)}`;
2668
3040
  toolOk = false;
@@ -2670,8 +3042,10 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2670
3042
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2671
3043
  readline2.cursorTo(process.stdout, 0);
2672
3044
  readline2.clearLine(process.stdout, 0);
2673
- process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
2674
- `);
3045
+ process.stdout.write(
3046
+ ` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
3047
+ `
3048
+ );
2675
3049
  try {
2676
3050
  await client.agents.submitCliToolResult(requestId, result);
2677
3051
  } catch {
@@ -2688,20 +3062,29 @@ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`
2688
3062
  const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2689
3063
  readline2.cursorTo(process.stdout, 0);
2690
3064
  readline2.clearLine(process.stdout, 0);
2691
- process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2692
- `);
3065
+ process.stdout.write(
3066
+ ` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
3067
+ `
3068
+ );
2693
3069
  } else if (event.type === "final") {
2694
3070
  if (hasOutput) process.stdout.write("\n");
2695
3071
  const e = event;
2696
3072
  const finalText = e.content ?? e.payload?.content ?? e.payload?.text ?? e.payload?.message;
2697
3073
  if (finalText && !hasOutput) console.log(finalText);
2698
- if (sessionId) console.log(c.dim(`
2699
- 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
+ );
2700
3081
  break;
2701
3082
  } else if (event.type === "error") {
2702
3083
  if (hasOutput) process.stdout.write("\n");
2703
- console.error(`
2704
- ${sym.fail} ${c.error(event.message ?? "Error")}`);
3084
+ console.error(
3085
+ `
3086
+ ${sym.fail} ${c.error(event.message ?? "Error")}`
3087
+ );
2705
3088
  process.exit(1);
2706
3089
  }
2707
3090
  }
@@ -2716,18 +3099,19 @@ ${sym.fail} ${c.error(event.message ?? "Error")}`);
2716
3099
  // src/commands/chat.ts
2717
3100
  var import_commander9 = require("commander");
2718
3101
  var readline3 = __toESM(require("readline"));
2719
- var import_fs6 = require("fs");
2720
- var import_path4 = require("path");
3102
+ var import_fs7 = require("fs");
3103
+ var import_path5 = require("path");
2721
3104
  var import_os3 = require("os");
2722
- 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");
2723
3106
  function ensureSessionsDir() {
2724
- 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 });
2725
3108
  }
2726
3109
  function appendSessionLog(sessionId, record) {
2727
3110
  try {
3111
+ if (!/^[a-zA-Z0-9_-]{1,128}$/.test(sessionId)) return;
2728
3112
  ensureSessionsDir();
2729
- const file = (0, import_path4.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
2730
- (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 });
2731
3115
  } catch {
2732
3116
  }
2733
3117
  }
@@ -2750,27 +3134,35 @@ var LOCAL_TOOLS_DISCLAIMER = `
2750
3134
  ${c.dim("and execute shell commands on your machine.")}
2751
3135
 
2752
3136
  ${c.dim("Rules:")}
2753
- ${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())}
2754
3138
  ${sym.bullet} ${c.dim("Sensitive paths (.ssh, .env, .aws, credentials) are always blocked")}
2755
3139
  ${sym.bullet} ${c.dim("Write and run_command operations require your confirmation")}
2756
- ${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")}
2757
3141
 
2758
3142
  ${c.dim("Session activity is logged to")} ${c.primary("~/.agc/sessions/")}
2759
3143
  `;
2760
3144
  function chatCommand() {
2761
- 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.");
2762
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.");
2763
3155
  const cfg = loadConfig();
2764
- let agentId2 = opts.agent ?? cfg.defaultAgentId;
2765
- if (!agentId2 && cfg.initiator) {
3156
+ let agentId3 = opts.agent ?? cfg.defaultAgentId;
3157
+ if (!agentId3 && cfg.initiator) {
2766
3158
  try {
2767
3159
  const listed = await makeClient().agents.list(cfg.initiator);
2768
3160
  const agents = listed?.data ?? listed ?? [];
2769
- agentId2 = agents.find((agent) => agent.isDefault)?.agentId ?? agents[0]?.agentId;
3161
+ agentId3 = agents.find((agent) => agent.isDefault)?.agentId ?? agents[0]?.agentId;
2770
3162
  } catch {
2771
3163
  }
2772
3164
  }
2773
- if (!agentId2) {
3165
+ if (!agentId3) {
2774
3166
  console.error(c.error("No default agent is available. Specify --agent <agentId> or run `agc agents list`."));
2775
3167
  process.exit(1);
2776
3168
  }
@@ -2786,7 +3178,7 @@ function chatCommand() {
2786
3178
  const spinner = spin("Creating session\u2026");
2787
3179
  try {
2788
3180
  const res = await client.sessions.create({
2789
- agentId: agentId2,
3181
+ agentId: agentId3,
2790
3182
  initiator,
2791
3183
  title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
2792
3184
  source: "cli"
@@ -2797,7 +3189,7 @@ function chatCommand() {
2797
3189
  appendSessionLog(sessionId, {
2798
3190
  type: "session_start",
2799
3191
  sessionId,
2800
- agentId: agentId2,
3192
+ agentId: agentId3,
2801
3193
  initiator,
2802
3194
  source: "cli",
2803
3195
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -2812,12 +3204,8 @@ function chatCommand() {
2812
3204
  try {
2813
3205
  const res = await client.sessions.get(sessionId);
2814
3206
  const session = res?.data ?? res;
2815
- if (session.agentId && session.agentId !== agentId2) {
2816
- spinner.stop();
2817
- console.log(c.warn(` Note: session ${sessionId} was created with agent ${session.agentId}, not ${agentId2}`));
2818
- } else {
2819
- spinner.stop();
2820
- }
3207
+ if (session.agentId) agentId3 = session.agentId;
3208
+ spinner.stop();
2821
3209
  } catch {
2822
3210
  spinner.stop();
2823
3211
  console.error(c.error(`Session "${sessionId}" not found.`));
@@ -2827,10 +3215,10 @@ function chatCommand() {
2827
3215
  let agentName;
2828
3216
  let walletLine = "";
2829
3217
  await Promise.allSettled([
2830
- client.agents.get(agentId2).then((res) => {
3218
+ client.agents.get(agentId3).then((res) => {
2831
3219
  agentName = (res?.data ?? res)?.name;
2832
3220
  }),
2833
- client.wallets.primary(agentId2).then(async (primary) => {
3221
+ client.wallets.primary(agentId3).then(async (primary) => {
2834
3222
  const w = primary?.data ?? primary;
2835
3223
  if (w?.id) {
2836
3224
  const bal = await client.wallets.balance(w.id).catch(() => null);
@@ -2842,9 +3230,9 @@ function chatCommand() {
2842
3230
  })
2843
3231
  ]);
2844
3232
  console.log(`
2845
- ${c.bold("Agent Commons Chat")}`);
3233
+ ${c.bold("Agent Commons \xB7 Code")}`);
2846
3234
  const headerRows = [
2847
- ["Agent", agentName ? `${agentName} ${c.dim(agentId2)}` : agentId2],
3235
+ ["Agent", agentName ? `${agentName} ${c.dim(agentId3)}` : agentId3],
2848
3236
  ["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
2849
3237
  ];
2850
3238
  if (walletLine) headerRows.push(["Wallet", walletLine]);
@@ -2853,17 +3241,16 @@ ${c.bold("Agent Commons Chat")}`);
2853
3241
  detail(headerRows);
2854
3242
  let localToolsCfg = null;
2855
3243
  if (localEnabled) {
2856
- 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);
2857
3245
  const rootDir = process.cwd();
2858
3246
  localToolsCfg = {
2859
3247
  rootDir,
2860
3248
  sessionId,
2861
- agentId: agentId2,
3249
+ agentId: agentId3,
2862
3250
  agentName,
2863
3251
  appendLog: (record) => appendSessionLog(sessionId, record),
2864
- permissions: /* @__PURE__ */ new Map()
3252
+ permissions: new Map(opts.readOnly ? ["write_file", "run_command", "start_process"].map((key) => [key, "deny"]) : [])
2865
3253
  };
2866
- installGitHook(rootDir, sessionId, agentId2, agentName);
2867
3254
  appendSessionLog(sessionId, {
2868
3255
  type: "local_tools_enabled",
2869
3256
  rootDir,
@@ -2877,8 +3264,10 @@ ${c.bold("Agent Commons Chat")}`);
2877
3264
  terminal: true,
2878
3265
  prompt: c.primary("you") + c.dim(" \u203A ")
2879
3266
  });
2880
- rl.prompt();
3267
+ let busy = false;
3268
+ if (!initialPrompt) rl.prompt();
2881
3269
  rl.on("line", async (line) => {
3270
+ if (busy) return;
2882
3271
  const input = line.trim();
2883
3272
  if (!input) {
2884
3273
  rl.prompt();
@@ -2931,6 +3320,7 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
2931
3320
  rl.prompt();
2932
3321
  return;
2933
3322
  }
3323
+ busy = true;
2934
3324
  rl.pause();
2935
3325
  appendSessionLog(sessionId, {
2936
3326
  type: "message",
@@ -2944,6 +3334,11 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
2944
3334
  const rootDir = localToolsCfg.rootDir;
2945
3335
  const atRefs = [...input.matchAll(/@([\S]+)/g)].map((m) => m[1]);
2946
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.");
2947
3342
  for (const ref of atRefs) {
2948
3343
  const content = readFileForContext(rootDir, ref);
2949
3344
  fileContextBlocks.push(`**${ref}**
@@ -2955,13 +3350,13 @@ ${content}
2955
3350
  cliContext = buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks);
2956
3351
  }
2957
3352
  const params = {
2958
- agentId: agentId2,
3353
+ agentId: agentId3,
2959
3354
  sessionId,
2960
3355
  messages: [{ role: "user", content: userMessage }],
2961
3356
  ...opts.computer && { computerRequest: { enabled: true } },
2962
3357
  ...cliContext && { cliContext }
2963
3358
  };
2964
- if (opts.noStream) {
3359
+ if (opts.stream === false) {
2965
3360
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2966
3361
  const spinner = spin("thinking\u2026");
2967
3362
  try {
@@ -2982,12 +3377,12 @@ ${content}
2982
3377
  ${sym.fail} ${c.error(err.message ?? String(err))}`);
2983
3378
  }
2984
3379
  } else {
3380
+ const thinkingSpinner = spin("thinking\u2026");
2985
3381
  try {
2986
3382
  let hasOutput = false;
2987
3383
  let agentContent = "";
2988
3384
  let toolStartMs = 0;
2989
3385
  let lastToolName = "";
2990
- const thinkingSpinner = spin("thinking\u2026");
2991
3386
  for await (const event of client.agents.stream(params)) {
2992
3387
  if (event.type === "token") {
2993
3388
  if (thinkingSpinner.isSpinning) {
@@ -3116,36 +3511,53 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
3116
3511
  if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
3117
3512
  process.stdout.write("\n");
3118
3513
  if (localToolsCfg && agentContent) {
3119
- await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId2, sessionId, appendSessionLog, !!opts.computer);
3514
+ await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId3, sessionId, appendSessionLog, !!opts.computer);
3120
3515
  }
3121
3516
  } catch (err) {
3122
3517
  process.stdout.write("\n");
3123
3518
  console.error(`${sym.fail} ${c.error(err.message ?? String(err))}`);
3519
+ } finally {
3520
+ thinkingSpinner.stop();
3124
3521
  }
3125
3522
  }
3126
3523
  console.log();
3127
3524
  readline3.cursorTo(process.stdout, 0);
3128
3525
  readline3.clearLine(process.stdout, 0);
3526
+ busy = false;
3129
3527
  rl.resume();
3130
3528
  rl.prompt();
3131
3529
  });
3132
3530
  const cleanup = () => {
3133
- if (localToolsCfg) removeGitHook(localToolsCfg.rootDir);
3531
+ stopLocalProcesses();
3134
3532
  };
3135
3533
  rl.on("close", () => {
3136
3534
  cleanup();
3137
3535
  process.exit(0);
3138
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
+ });
3139
3543
  process.on("SIGINT", () => {
3140
3544
  cleanup();
3141
3545
  console.log(c.dim(`
3142
3546
  Session preserved. Resume with: agc chat --resume ${sessionId}`));
3143
3547
  process.exit(130);
3144
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
+ }
3145
3557
  });
3146
3558
  }
3147
3559
  var MAX_TOOL_DEPTH = 10;
3148
- 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) {
3149
3561
  if (depth >= MAX_TOOL_DEPTH) {
3150
3562
  console.log(c.dim(`
3151
3563
  [local] Max tool depth reached (${MAX_TOOL_DEPTH}). Stopping tool loop.
@@ -3192,7 +3604,7 @@ ${result}
3192
3604
  let loopToolName = "";
3193
3605
  let loopToolStartMs = 0;
3194
3606
  for await (const evt of client.agents.stream({
3195
- agentId: agentId2,
3607
+ agentId: agentId3,
3196
3608
  sessionId,
3197
3609
  messages: [{ role: "user", content: resultMsg }],
3198
3610
  ...computerEnabled && { computerRequest: { enabled: true } }
@@ -3238,7 +3650,7 @@ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
3238
3650
  console.error(`${sym.fail} ${c.error(err?.message ?? String(err))}`);
3239
3651
  return;
3240
3652
  }
3241
- await handleLocalToolLoop(followContent, cfg, client, agentId2, sessionId, appendLog, computerEnabled, depth + 1);
3653
+ await handleLocalToolLoop(followContent, cfg, client, agentId3, sessionId, appendLog, computerEnabled, depth + 1);
3242
3654
  }
3243
3655
  function truncate(s, max) {
3244
3656
  const str = String(s ?? "");
@@ -3248,6 +3660,8 @@ function toolArgSummary(toolName, args) {
3248
3660
  switch (toolName) {
3249
3661
  case "read_file":
3250
3662
  return truncate(args.path ?? "", 60);
3663
+ case "search_file":
3664
+ return truncate(`${args.path ?? ""} \xB7 ${args.query ?? ""}`, 60);
3251
3665
  case "write_file":
3252
3666
  return truncate(args.path ?? "", 60);
3253
3667
  case "delete_file":
@@ -3283,6 +3697,14 @@ function toolResultPreview(toolName, result) {
3283
3697
  const lines = result.split("\n").length;
3284
3698
  return `${lines} lines`;
3285
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
+ }
3286
3708
  case "write_file":
3287
3709
  return "written";
3288
3710
  case "delete_file":
@@ -3885,19 +4307,19 @@ function walletCommand() {
3885
4307
  const cmd = new import_commander12.Command("wallet").description("Manage agent wallets");
3886
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) => {
3887
4309
  const cfg = loadConfig();
3888
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3889
- if (!agentId2) {
4310
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4311
+ if (!agentId3) {
3890
4312
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3891
4313
  process.exit(1);
3892
4314
  }
3893
4315
  const spinner = spin("Fetching wallets\u2026");
3894
4316
  try {
3895
4317
  const client = makeClient();
3896
- const wallets = await client.wallets.list(agentId2);
4318
+ const wallets = await client.wallets.list(agentId3);
3897
4319
  spinner.stop();
3898
4320
  if (opts.json) return jsonOut(wallets);
3899
4321
  const list = wallets?.data ?? wallets ?? [];
3900
- section(`Wallets for agent ${agentId2.slice(0, 8)}\u2026 (${list.length})`);
4322
+ section(`Wallets for agent ${agentId3.slice(0, 8)}\u2026 (${list.length})`);
3901
4323
  table(
3902
4324
  list.map((w) => ({
3903
4325
  ID: w.id.slice(0, 8) + "\u2026",
@@ -3917,19 +4339,19 @@ function walletCommand() {
3917
4339
  });
3918
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) => {
3919
4341
  const cfg = loadConfig();
3920
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3921
- if (!agentId2) {
4342
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4343
+ if (!agentId3) {
3922
4344
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3923
4345
  process.exit(1);
3924
4346
  }
3925
4347
  const spinner = spin("Fetching primary wallet\u2026");
3926
4348
  try {
3927
4349
  const client = makeClient();
3928
- const wallet = await client.wallets.primary(agentId2);
4350
+ const wallet = await client.wallets.primary(agentId3);
3929
4351
  spinner.stop();
3930
4352
  if (!wallet) {
3931
- console.log(c.warn(` No wallet found for agent ${agentId2}`));
3932
- 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}`));
3933
4355
  return;
3934
4356
  }
3935
4357
  const w = wallet?.data ?? wallet;
@@ -3950,8 +4372,8 @@ function walletCommand() {
3950
4372
  });
3951
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) => {
3952
4374
  const cfg = loadConfig();
3953
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3954
- if (!agentId2) {
4375
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4376
+ if (!agentId3) {
3955
4377
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3956
4378
  process.exit(1);
3957
4379
  }
@@ -3960,11 +4382,11 @@ function walletCommand() {
3960
4382
  const client = makeClient();
3961
4383
  let walletId = opts.wallet;
3962
4384
  if (!walletId) {
3963
- const primary = await client.wallets.primary(agentId2);
4385
+ const primary = await client.wallets.primary(agentId3);
3964
4386
  const w = primary?.data ?? primary;
3965
4387
  if (!w) {
3966
4388
  spinner.stop();
3967
- 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}`));
3968
4390
  return;
3969
4391
  }
3970
4392
  walletId = w.id;
@@ -3991,8 +4413,8 @@ function walletCommand() {
3991
4413
  });
3992
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) => {
3993
4415
  const cfg = loadConfig();
3994
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
3995
- if (!agentId2) {
4416
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4417
+ if (!agentId3) {
3996
4418
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
3997
4419
  process.exit(1);
3998
4420
  }
@@ -4004,7 +4426,7 @@ function walletCommand() {
4004
4426
  try {
4005
4427
  const client = makeClient();
4006
4428
  const wallet = await client.wallets.create({
4007
- agentId: agentId2,
4429
+ agentId: agentId3,
4008
4430
  walletType: opts.type,
4009
4431
  label: opts.label,
4010
4432
  externalAddress: opts.address
@@ -4162,22 +4584,22 @@ function memoryCommand() {
4162
4584
  const cmd = new import_commander14.Command("memory").description("View and manage agent memories");
4163
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) => {
4164
4586
  const cfg = loadConfig();
4165
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4166
- if (!agentId2) {
4587
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4588
+ if (!agentId3) {
4167
4589
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4168
4590
  process.exit(1);
4169
4591
  }
4170
4592
  const spinner = spin("Fetching memories\u2026");
4171
4593
  try {
4172
4594
  const client = makeClient();
4173
- const res = await client.memory.list(agentId2, {
4595
+ const res = await client.memory.list(agentId3, {
4174
4596
  type: opts.type,
4175
4597
  limit: parseInt(opts.limit, 10)
4176
4598
  });
4177
4599
  const memories = res?.data ?? res ?? [];
4178
4600
  spinner.stop();
4179
4601
  if (opts.json) return jsonOut(memories);
4180
- section(`Memories for ${agentId2.slice(0, 12)}\u2026 (${memories.length})`);
4602
+ section(`Memories for ${agentId3.slice(0, 12)}\u2026 (${memories.length})`);
4181
4603
  if (memories.length === 0) {
4182
4604
  console.log(c.dim(" No memories yet"));
4183
4605
  return;
@@ -4199,15 +4621,15 @@ function memoryCommand() {
4199
4621
  });
4200
4622
  cmd.command("stats").description("Show memory statistics for an agent").option("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
4201
4623
  const cfg = loadConfig();
4202
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4203
- if (!agentId2) {
4624
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4625
+ if (!agentId3) {
4204
4626
  console.error(c.error("Specify --agent <agentId>"));
4205
4627
  process.exit(1);
4206
4628
  }
4207
4629
  const spinner = spin("Fetching stats\u2026");
4208
4630
  try {
4209
4631
  const client = makeClient();
4210
- const res = await client.memory.stats(agentId2);
4632
+ const res = await client.memory.stats(agentId3);
4211
4633
  const stats = res?.data ?? res;
4212
4634
  spinner.stop();
4213
4635
  if (opts.json) return jsonOut(stats);
@@ -4267,15 +4689,15 @@ ${sym.ok} Memory ${c.id(memoryId)} deleted`);
4267
4689
  });
4268
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) => {
4269
4691
  const cfg = loadConfig();
4270
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4271
- if (!agentId2) {
4692
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4693
+ if (!agentId3) {
4272
4694
  console.error(c.error("Specify --agent <agentId>"));
4273
4695
  process.exit(1);
4274
4696
  }
4275
4697
  const spinner = spin("Searching memories\u2026");
4276
4698
  try {
4277
4699
  const client = makeClient();
4278
- 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));
4279
4701
  const memories = res?.data ?? res ?? [];
4280
4702
  spinner.stop();
4281
4703
  if (opts.json) return jsonOut(memories);
@@ -4364,18 +4786,18 @@ function usageCommand() {
4364
4786
  process.exit(1);
4365
4787
  }
4366
4788
  });
4367
- 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) => {
4368
4790
  const spinner = spin("Fetching usage\u2026");
4369
4791
  try {
4370
4792
  const client = makeClient();
4371
- const res = await client.usage.getAgentUsage(agentId2, {
4793
+ const res = await client.usage.getAgentUsage(agentId3, {
4372
4794
  from: opts.from,
4373
4795
  to: opts.to
4374
4796
  });
4375
4797
  const data = res?.data ?? res;
4376
4798
  spinner.stop();
4377
4799
  if (opts.json) return jsonOut(data);
4378
- section(`Usage \u2014 ${agentId2.slice(0, 12)}\u2026`);
4800
+ section(`Usage \u2014 ${agentId3.slice(0, 12)}\u2026`);
4379
4801
  detail([
4380
4802
  ["Calls", (data.callCount ?? 0).toLocaleString()],
4381
4803
  ["Input tokens", (data.totalInputTokens ?? 0).toLocaleString()],
@@ -4512,15 +4934,15 @@ function logsCommand() {
4512
4934
  const cmd = new import_commander17.Command("logs").description("View agent activity logs");
4513
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) => {
4514
4936
  const cfg = loadConfig();
4515
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4516
- if (!agentId2) {
4937
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4938
+ if (!agentId3) {
4517
4939
  console.error(c.error("Specify --agent <agentId> or set defaultAgentId"));
4518
4940
  process.exit(1);
4519
4941
  }
4520
4942
  const spinner = spin("Fetching logs\u2026");
4521
4943
  try {
4522
4944
  const client = makeClient();
4523
- const res = await client.logs.list(agentId2, {
4945
+ const res = await client.logs.list(agentId3, {
4524
4946
  limit: Number(opts.limit),
4525
4947
  sessionId: opts.session
4526
4948
  });
@@ -4528,7 +4950,7 @@ function logsCommand() {
4528
4950
  if (opts.status) logs = logs.filter((l) => l.status === opts.status);
4529
4951
  spinner.stop();
4530
4952
  if (opts.json) return jsonOut(logs);
4531
- section(`Logs \u2014 ${agentId2.slice(0, 12)}\u2026 (${logs.length})`);
4953
+ section(`Logs \u2014 ${agentId3.slice(0, 12)}\u2026 (${logs.length})`);
4532
4954
  if (logs.length === 0) {
4533
4955
  console.log(c.dim(" No logs yet"));
4534
4956
  return;
@@ -4553,21 +4975,21 @@ function logsCommand() {
4553
4975
  });
4554
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) => {
4555
4977
  const cfg = loadConfig();
4556
- const agentId2 = opts.agent ?? cfg.defaultAgentId;
4557
- if (!agentId2) {
4978
+ const agentId3 = opts.agent ?? cfg.defaultAgentId;
4979
+ if (!agentId3) {
4558
4980
  console.error(c.error("Specify --agent <agentId>"));
4559
4981
  process.exit(1);
4560
4982
  }
4561
4983
  const spinner = spin("Fetching error logs\u2026");
4562
4984
  try {
4563
4985
  const client = makeClient();
4564
- const res = await client.logs.list(agentId2, {
4986
+ const res = await client.logs.list(agentId3, {
4565
4987
  limit: Number(opts.limit)
4566
4988
  });
4567
4989
  const errors = (res?.data ?? []).filter((l) => l.status === "error");
4568
4990
  spinner.stop();
4569
4991
  if (opts.json) return jsonOut(errors);
4570
- section(`Errors \u2014 ${agentId2.slice(0, 12)}\u2026 (${errors.length})`);
4992
+ section(`Errors \u2014 ${agentId3.slice(0, 12)}\u2026 (${errors.length})`);
4571
4993
  if (errors.length === 0) {
4572
4994
  console.log(`${sym.ok} No errors found`);
4573
4995
  return;
@@ -4596,13 +5018,13 @@ var RESOURCE_PROFILES = [
4596
5018
  ];
4597
5019
  var RESOURCE_MODES = ["fixed", "elastic"];
4598
5020
  function resolveAgentId(opts) {
4599
- const agentId2 = opts.agent ?? loadConfig().defaultAgentId;
4600
- if (!agentId2) {
5021
+ const agentId3 = opts.agent ?? loadConfig().defaultAgentId;
5022
+ if (!agentId3) {
4601
5023
  throw new Error(
4602
5024
  "Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`."
4603
5025
  );
4604
5026
  }
4605
- return agentId2;
5027
+ return agentId3;
4606
5028
  }
4607
5029
  function unwrap(response) {
4608
5030
  return response?.data ?? response;
@@ -4653,17 +5075,17 @@ function parseNumber(value, name, options) {
4653
5075
  }
4654
5076
  return parsed;
4655
5077
  }
4656
- async function changeEnabled(agentId2, enabled, json) {
5078
+ async function changeEnabled(agentId3, enabled, json) {
4657
5079
  const spinner = spin(`${enabled ? "Enabling" : "Disabling"} persistent cloud computer\u2026`);
4658
5080
  try {
4659
- const response = await makeClient().agents.updateComputerConfig(agentId2, { enabled });
5081
+ const response = await makeClient().agents.updateComputerConfig(agentId3, { enabled });
4660
5082
  const config = unwrap(response);
4661
5083
  spinner.stop();
4662
5084
  if (json) return jsonOut(config);
4663
5085
  console.log(`
4664
- ${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)}`);
4665
5087
  if (enabled) {
4666
- 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}`));
4667
5089
  }
4668
5090
  } catch (error) {
4669
5091
  spinner.stop();
@@ -4671,12 +5093,12 @@ ${sym.ok} Persistent cloud computer ${enabled ? "enabled" : "disabled"} for agen
4671
5093
  process.exitCode = 1;
4672
5094
  }
4673
5095
  }
4674
- async function lifecycleAction(action, agentId2, reason, json) {
5096
+ async function lifecycleAction(action, agentId3, reason, json) {
4675
5097
  const verb = action === "wake" ? "Waking" : action === "sleep" ? "Sleeping" : "Restarting";
4676
5098
  const spinner = spin(`${verb} persistent cloud computer\u2026`);
4677
5099
  try {
4678
5100
  const client = makeClient();
4679
- 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);
4680
5102
  const computer = unwrap(response);
4681
5103
  spinner.stop();
4682
5104
  if (json) return jsonOut(computer);
@@ -4695,9 +5117,9 @@ function addAgentOption(command) {
4695
5117
  function computerCommand() {
4696
5118
  const command = new import_commander18.Command("computer").description("Manage an agent's one persistent cloud computer");
4697
5119
  addAgentOption(command.command("status").description("Show persistent cloud computer status")).option("--json", "Output as JSON").action(async (opts) => {
4698
- let agentId2;
5120
+ let agentId3;
4699
5121
  try {
4700
- agentId2 = resolveAgentId(opts);
5122
+ agentId3 = resolveAgentId(opts);
4701
5123
  } catch (error) {
4702
5124
  printError(error);
4703
5125
  process.exitCode = 1;
@@ -4705,7 +5127,7 @@ function computerCommand() {
4705
5127
  }
4706
5128
  const spinner = spin("Fetching persistent cloud computer\u2026");
4707
5129
  try {
4708
- const computer = unwrap(await makeClient().agents.getComputer(agentId2));
5130
+ const computer = unwrap(await makeClient().agents.getComputer(agentId3));
4709
5131
  spinner.stop();
4710
5132
  if (opts.json) return jsonOut(computer);
4711
5133
  displayComputer(computer);
@@ -4747,10 +5169,10 @@ function computerCommand() {
4747
5169
  });
4748
5170
  }
4749
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) => {
4750
- let agentId2;
5172
+ let agentId3;
4751
5173
  let resize;
4752
5174
  try {
4753
- agentId2 = resolveAgentId(opts);
5175
+ agentId3 = resolveAgentId(opts);
4754
5176
  if (opts.profile && !RESOURCE_PROFILES.includes(opts.profile)) {
4755
5177
  throw new Error(`--profile must be one of: ${RESOURCE_PROFILES.join(", ")}.`);
4756
5178
  }
@@ -4787,7 +5209,7 @@ function computerCommand() {
4787
5209
  }
4788
5210
  const spinner = spin("Resizing persistent cloud computer\u2026");
4789
5211
  try {
4790
- const computer = unwrap(await makeClient().agents.resizeComputer(agentId2, resize));
5212
+ const computer = unwrap(await makeClient().agents.resizeComputer(agentId3, resize));
4791
5213
  spinner.stop();
4792
5214
  if (opts.json) return jsonOut(computer);
4793
5215
  console.log(`
@@ -4802,10 +5224,10 @@ ${sym.ok} Persistent cloud computer resize requested`);
4802
5224
  addAgentOption(
4803
5225
  command.command("exec").description("Run a command in the persistent cloud computer").argument("<command...>", "Command and arguments to run")
4804
5226
  ).option("--cwd <path>", "Working directory").option("--timeout <seconds>", "Command timeout in seconds", "120").option("--json", "Output as JSON").action(async (commandParts, opts) => {
4805
- let agentId2;
5227
+ let agentId3;
4806
5228
  let timeoutSeconds;
4807
5229
  try {
4808
- agentId2 = resolveAgentId(opts);
5230
+ agentId3 = resolveAgentId(opts);
4809
5231
  timeoutSeconds = parseNumber(opts.timeout, "Timeout");
4810
5232
  } catch (error) {
4811
5233
  printError(error);
@@ -4814,7 +5236,7 @@ ${sym.ok} Persistent cloud computer resize requested`);
4814
5236
  }
4815
5237
  const spinner = spin("Running command in persistent cloud computer\u2026");
4816
5238
  try {
4817
- const result = unwrap(await makeClient().agents.execComputer(agentId2, {
5239
+ const result = unwrap(await makeClient().agents.execComputer(agentId3, {
4818
5240
  command: commandParts.join(" "),
4819
5241
  ...opts.cwd && { cwd: opts.cwd },
4820
5242
  ...timeoutSeconds !== void 0 && { timeoutSeconds }
@@ -4834,10 +5256,10 @@ ${sym.ok} Persistent cloud computer resize requested`);
4834
5256
  }
4835
5257
  });
4836
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) => {
4837
- let agentId2;
5259
+ let agentId3;
4838
5260
  let limit;
4839
5261
  try {
4840
- agentId2 = resolveAgentId(opts);
5262
+ agentId3 = resolveAgentId(opts);
4841
5263
  limit = parseNumber(opts.limit, "Limit", { integer: true });
4842
5264
  } catch (error) {
4843
5265
  printError(error);
@@ -4846,7 +5268,7 @@ ${sym.ok} Persistent cloud computer resize requested`);
4846
5268
  }
4847
5269
  const spinner = spin("Fetching persistent cloud computer events\u2026");
4848
5270
  try {
4849
- const events = unwrap(await makeClient().agents.listComputerEvents(agentId2, limit));
5271
+ const events = unwrap(await makeClient().agents.listComputerEvents(agentId3, limit));
4850
5272
  spinner.stop();
4851
5273
  if (opts.json) return jsonOut(events);
4852
5274
  section(`Cloud computer events (${events.length})`);
@@ -4870,8 +5292,8 @@ ${sym.ok} Persistent cloud computer resize requested`);
4870
5292
 
4871
5293
  // src/commands/library.ts
4872
5294
  var import_commander19 = require("commander");
4873
- var import_fs7 = require("fs");
4874
- var import_path5 = require("path");
5295
+ var import_fs8 = require("fs");
5296
+ var import_path6 = require("path");
4875
5297
  function libraryCommand() {
4876
5298
  const command = new import_commander19.Command("library").alias("files").description("Upload, find, and manage files in your Commons library");
4877
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) => {
@@ -4933,8 +5355,8 @@ function libraryCommand() {
4933
5355
  throw new Error("--storage must be either s3 or ipfs.");
4934
5356
  }
4935
5357
  const files = paths.map((path) => ({
4936
- data: new Blob([new Uint8Array((0, import_fs7.readFileSync)(path))]),
4937
- name: (0, import_path5.basename)(path)
5358
+ data: new Blob([new Uint8Array((0, import_fs8.readFileSync)(path))]),
5359
+ name: (0, import_path6.basename)(path)
4938
5360
  }));
4939
5361
  const result = await makeClient().files.upload(files, {
4940
5362
  agentId: opts.agent,
@@ -4993,7 +5415,7 @@ ${sym.ok} Uploaded ${result.data.length} file${result.data.length === 1 ? "" : "
4993
5415
 
4994
5416
  // src/commands/projects.ts
4995
5417
  var import_commander20 = require("commander");
4996
- var import_fs8 = require("fs");
5418
+ var import_fs9 = require("fs");
4997
5419
  function agentId(value) {
4998
5420
  const resolved = value ?? loadConfig().defaultAgentId;
4999
5421
  if (!resolved) {
@@ -5005,7 +5427,7 @@ function agentId(value) {
5005
5427
  }
5006
5428
  function projectFiles(path) {
5007
5429
  if (!path) return void 0;
5008
- const parsed = JSON.parse((0, import_fs8.readFileSync)(path, "utf8"));
5430
+ const parsed = JSON.parse((0, import_fs9.readFileSync)(path, "utf8"));
5009
5431
  const files = Array.isArray(parsed) ? parsed : parsed.files;
5010
5432
  if (!Array.isArray(files)) {
5011
5433
  throw new Error("The files document must be an array or an object with a files array.");
@@ -5166,8 +5588,160 @@ ${sym.ok} Project published.`);
5166
5588
  return command;
5167
5589
  }
5168
5590
 
5169
- // src/commands/api-keys.ts
5591
+ // src/commands/arcade.ts
5170
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");
5171
5745
  async function resolveProject(projectId) {
5172
5746
  const projects = (await makeClient().developer.listProjects()).data;
5173
5747
  const project = projectId ? projects.find((candidate) => candidate.id === projectId) : projects[0];
@@ -5179,7 +5753,7 @@ async function resolveProject(projectId) {
5179
5753
  return project;
5180
5754
  }
5181
5755
  function apiKeysCommand() {
5182
- 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");
5183
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) => {
5184
5758
  const spinner = spin("Fetching developer keys\u2026");
5185
5759
  try {
@@ -5333,6 +5907,96 @@ ${sym.ok} Developer project created.`);
5333
5907
  return command;
5334
5908
  }
5335
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
+
5336
6000
  // src/bin.ts
5337
6001
  async function interactiveMenu() {
5338
6002
  banner();
@@ -5340,9 +6004,15 @@ async function interactiveMenu() {
5340
6004
  const isSetup = !!((cfg.accessToken || cfg.apiKey || cfg.sessionToken) && (cfg.userId || cfg.initiator));
5341
6005
  if (!isSetup) {
5342
6006
  console.log(c.bold(" Welcome to Agent Commons CLI!"));
5343
- console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
5344
- console.log(` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
5345
- `);
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
+ );
5346
6016
  runSubcommand(["login"]);
5347
6017
  return;
5348
6018
  }
@@ -5353,7 +6023,11 @@ async function interactiveMenu() {
5353
6023
  const action = await select("What would you like to do?", [
5354
6024
  { label: "Chat with an agent", value: "chat", hint: "agc chat" },
5355
6025
  { label: "Run an agent (one-shot)", value: "run", hint: "agc run" },
5356
- { 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
+ },
5357
6031
  { label: "View sessions", value: "sessions", hint: "agc sessions list" },
5358
6032
  { label: "Manage agents", value: "agents", hint: "agc agents list" },
5359
6033
  { label: "Tasks", value: "tasks", hint: "agc task list" },
@@ -5373,19 +6047,19 @@ async function interactiveMenu() {
5373
6047
  process.exit(0);
5374
6048
  }
5375
6049
  const needsAgent = action === "chat" || action === "run" || action === "computer";
5376
- const agentId2 = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
5377
- if (needsAgent && !agentId2) return;
6050
+ const agentId3 = needsAgent ? cfg.defaultAgentId ?? await pickAgentInteractively(action) : void 0;
6051
+ if (needsAgent && !agentId3) return;
5378
6052
  if (action === "run") {
5379
6053
  const prompt = await askPrompt("Enter your prompt:");
5380
6054
  if (!prompt) return;
5381
- runSubcommand(["run", "--agent", agentId2, prompt]);
6055
+ runSubcommand(["run", "--agent", agentId3, prompt]);
5382
6056
  return;
5383
6057
  }
5384
6058
  const commandMap = {
5385
- chat: ["chat", "--agent", agentId2],
6059
+ chat: ["chat", "--agent", agentId3],
5386
6060
  run: [],
5387
6061
  // handled above
5388
- computer: ["computer", "status", "--agent", agentId2],
6062
+ computer: ["computer", "status", "--agent", agentId3],
5389
6063
  sessions: ["sessions", "list"],
5390
6064
  agents: ["agents", "list"],
5391
6065
  tasks: ["task", "list"],
@@ -5406,7 +6080,10 @@ async function interactiveMenu() {
5406
6080
  async function askPrompt(question) {
5407
6081
  const { createInterface: createInterface3 } = await import("readline");
5408
6082
  return new Promise((resolve2) => {
5409
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
6083
+ const rl = createInterface3({
6084
+ input: process.stdin,
6085
+ output: process.stdout
6086
+ });
5410
6087
  process.stdout.write(`
5411
6088
  ${c.bold(question)}
5412
6089
  ${c.primary("\u203A")} `);
@@ -5434,26 +6111,35 @@ async function pickAgentInteractively(action) {
5434
6111
  spinner.stop();
5435
6112
  } catch {
5436
6113
  spinner.stop();
5437
- console.log(`
6114
+ console.log(
6115
+ `
5438
6116
  ${c.warn("\u26A0")} Could not fetch agents. Check your sign-in and connection.
5439
- `);
6117
+ `
6118
+ );
5440
6119
  return null;
5441
6120
  }
5442
6121
  if (agents.length === 0) {
5443
6122
  console.log(`
5444
6123
  ${c.warn("\u26A0")} You don't have any agents yet.
5445
6124
  `);
5446
- const choice = await select("What would you like to do?", [
5447
- { label: "Create a new agent now", value: "create", hint: "agc agents create" },
5448
- { label: "Go back", value: "cancel" }
5449
- ]);
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
+ );
5450
6136
  if (choice === "create") {
5451
6137
  runSubcommand(["agents", "create"]);
5452
6138
  }
5453
6139
  return null;
5454
6140
  }
5455
6141
  console.log();
5456
- const agentId2 = await select(
6142
+ const agentId3 = await select(
5457
6143
  action === "computer" ? "Choose the agent whose cloud computer you want to manage:" : `Choose an agent to ${action} with:`,
5458
6144
  agents.map((a) => ({
5459
6145
  label: a.name,
@@ -5466,15 +6152,17 @@ async function pickAgentInteractively(action) {
5466
6152
  { label: "No \u2014 just this once", value: false }
5467
6153
  ]);
5468
6154
  if (saveDefault) {
5469
- saveConfig({ defaultAgentId: agentId2 });
5470
- const chosen = agents.find((a) => a.agentId === agentId2);
5471
- console.log(` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId2)}
5472
- `);
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
+ );
5473
6161
  }
5474
- return agentId2;
6162
+ return agentId3;
5475
6163
  }
5476
- var program = new import_commander22.Command();
5477
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.5.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({
5478
6166
  sortOptions: true,
5479
6167
  sortSubcommands: true
5480
6168
  }).addHelpText(
@@ -5492,7 +6180,8 @@ Docs: https://docs.agentcommons.io/docs/cli
5492
6180
  await interactiveMenu();
5493
6181
  });
5494
6182
  program.hook("preAction", async (_thisCommand, actionCommand) => {
5495
- if (actionCommand.name() === "login" || actionCommand.name() === "logout") return;
6183
+ if (actionCommand.name() === "login" || actionCommand.name() === "logout")
6184
+ return;
5496
6185
  await ensureAccessToken();
5497
6186
  });
5498
6187
  program.addCommand(loginCommand());
@@ -5505,7 +6194,9 @@ program.addCommand(toolsCommand());
5505
6194
  program.addCommand(connectionsCommand());
5506
6195
  program.addCommand(libraryCommand());
5507
6196
  program.addCommand(projectsCommand());
6197
+ program.addCommand(arcadeCommand());
5508
6198
  program.addCommand(apiKeysCommand());
6199
+ program.addCommand(provenanceCommand());
5509
6200
  program.addCommand(workflowCommand());
5510
6201
  program.addCommand(taskCommand());
5511
6202
  program.addCommand(runCommand());
@@ -5530,8 +6221,10 @@ program.on("command:*", () => {
5530
6221
  process.exit(1);
5531
6222
  });
5532
6223
  program.parseAsync(process.argv).catch((error) => {
5533
- console.error(`
6224
+ console.error(
6225
+ `
5534
6226
  ${sym.fail} ${c.error(error instanceof Error ? error.message : String(error))}
5535
- `);
6227
+ `
6228
+ );
5536
6229
  process.exitCode = 1;
5537
6230
  });