@chit-run/cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/chit.js +191 -26
  2. package/package.json +1 -1
package/dist/chit.js CHANGED
@@ -1473,6 +1473,24 @@ var init_parse = __esm(() => {
1473
1473
  TEMPLATE_REF_RE = /\{\{\s*([\w.]+)\s*\}\}/g;
1474
1474
  });
1475
1475
  // ../../packages/core/src/show.ts
1476
+ function participantPermissionDisplay(p) {
1477
+ if (p.permissions.filesystem === "read_only") {
1478
+ return {
1479
+ filesystem: "read_only",
1480
+ readOnlyEnforcement: p.enforcesReadOnly ? "enforced" : "NOT ENFORCED",
1481
+ readOnlyEnforcementClass: p.enforcesReadOnly ? "ok" : "warn"
1482
+ };
1483
+ }
1484
+ return {
1485
+ filesystem: "write",
1486
+ readOnlyEnforcement: p.enforcesReadOnly ? "not requested (adapter supports)" : "not requested (adapter cannot enforce)",
1487
+ readOnlyEnforcementClass: "info"
1488
+ };
1489
+ }
1490
+ function participantPermissionText(p) {
1491
+ const display = participantPermissionDisplay(p);
1492
+ return `filesystem=${display.filesystem} read_only_enforcement=${display.readOnlyEnforcement}`;
1493
+ }
1476
1494
  function configPairs(c) {
1477
1495
  const pairs = [
1478
1496
  ["model", c.model ?? "default"],
@@ -1547,8 +1565,7 @@ function renderAscii(m) {
1547
1565
  out.push("");
1548
1566
  out.push("participants:");
1549
1567
  for (const [pid, p] of Object.entries(m.participants)) {
1550
- const enforces = p.enforcesReadOnly ? "enforces=yes" : "enforces=NO";
1551
- out.push(` ${pid} agent=${p.agentId} session=${p.session} permissions=${p.permissions.filesystem} adapter=${p.adapter} ${enforces}`);
1568
+ out.push(` ${pid} agent=${p.agentId} session=${p.session} ${participantPermissionText(p)} adapter=${p.adapter}`);
1552
1569
  if (p.adapter === "unknown") {
1553
1570
  out.push(" config unresolved (unknown agent)");
1554
1571
  } else {
@@ -1637,7 +1654,7 @@ function renderHtml(m) {
1637
1654
  levelColumns.push(renderLevelColumn(m, level));
1638
1655
  }
1639
1656
  const participantsSection = Object.entries(m.participants).map(([pid, p]) => {
1640
- const enforceBadge = p.enforcesReadOnly ? '<span class="badge ok">enforces read-only</span>' : '<span class="badge warn">does not enforce</span>';
1657
+ const permission = participantPermissionDisplay(p);
1641
1658
  const configBadges = p.adapter === "unknown" ? '<span class="badge warn">config: unresolved (unknown agent)</span>' : configPairs(p.config).map(([k, v]) => `<span class="badge info">${escapeHtml(k)}: ${escapeHtml(v)}</span>`).join(`
1642
1659
  `);
1643
1660
  return `<div class="participant">
@@ -1646,8 +1663,8 @@ function renderHtml(m) {
1646
1663
  <span class="badge info">agent: ${escapeHtml(p.agentId)}</span>
1647
1664
  <span class="badge info">adapter: ${escapeHtml(p.adapter)}</span>
1648
1665
  <span class="badge info">session: ${escapeHtml(p.session)}</span>
1649
- <span class="badge info">filesystem: ${escapeHtml(p.permissions.filesystem)}</span>
1650
- ${enforceBadge}
1666
+ <span class="badge info">filesystem: ${escapeHtml(permission.filesystem)}</span>
1667
+ <span class="badge ${permission.readOnlyEnforcementClass}">read_only enforcement: ${escapeHtml(permission.readOnlyEnforcement)}</span>
1651
1668
  </div>
1652
1669
  <div class="participant-config">
1653
1670
  ${configBadges}
@@ -10820,6 +10837,11 @@ class ClaudeCliAdapter {
10820
10837
  if (noProgress)
10821
10838
  throw new Error(`claude --print made no progress for ${noProgressMs}ms`);
10822
10839
  if (exitCode !== 0) {
10840
+ const rateLimit = detectClaudeRateLimit(`${stdoutText}
10841
+ ${stderrText}`);
10842
+ if (rateLimit !== undefined) {
10843
+ throw new Error(`claude --print rate limited${rateLimit ? `: ${rateLimit}` : ` (exit ${exitCode})`}`);
10844
+ }
10823
10845
  const cleaned = sanitize(stderrText || stdoutText, sensitive);
10824
10846
  const tail = cleaned.trim().split(`
10825
10847
  `).slice(-5).join(`
@@ -10953,6 +10975,42 @@ function parseClaudeResult(stdout) {
10953
10975
  }
10954
10976
  return result;
10955
10977
  }
10978
+ function detectClaudeRateLimit(stdout) {
10979
+ const pickStr = (v) => typeof v === "string" && v.trim() !== "" ? v.trim().replace(/\s+/g, " ").slice(0, 120) : undefined;
10980
+ const pickNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
10981
+ let found = false;
10982
+ let detail = "";
10983
+ for (const line of stdout.split(`
10984
+ `)) {
10985
+ const trimmed = line.trim();
10986
+ if (!trimmed)
10987
+ continue;
10988
+ let evt;
10989
+ try {
10990
+ evt = JSON.parse(trimmed);
10991
+ } catch {
10992
+ continue;
10993
+ }
10994
+ if (evt.type !== "rate_limit_event")
10995
+ continue;
10996
+ found = true;
10997
+ const rl = evt.rate_limit_info && typeof evt.rate_limit_info === "object" ? evt.rate_limit_info : evt.rate_limit && typeof evt.rate_limit === "object" ? evt.rate_limit : evt;
10998
+ const parts = [];
10999
+ const status = pickStr(rl.status);
11000
+ if (status)
11001
+ parts.push(`status=${status}`);
11002
+ const resetsAt = pickStr(rl.resetsAt);
11003
+ if (resetsAt)
11004
+ parts.push(`resets ${resetsAt}`);
11005
+ const retryAfter = pickNum(rl.retryAfter) ?? pickNum(rl.resetInSeconds);
11006
+ if (retryAfter !== undefined)
11007
+ parts.push(`retry after ${retryAfter}s`);
11008
+ if (rl.isUsingOverage === false)
11009
+ parts.push("overage disabled");
11010
+ detail = parts.join(", ");
11011
+ }
11012
+ return found ? detail : undefined;
11013
+ }
10956
11014
 
10957
11015
  // src/adapters/codex-exec.ts
10958
11016
  var DEFAULT_CALL_TIMEOUT_MS2 = 15 * 60000;
@@ -10972,7 +11030,7 @@ class CodexExecAdapter {
10972
11030
  const sensitive = findSensitiveValues(this.config.env);
10973
11031
  try {
10974
11032
  const priorThreadId = getCodexThreadId(req.session);
10975
- const cmd = this.buildCommand(priorThreadId);
11033
+ const cmd = this.buildCommand(priorThreadId, req.filesystem);
10976
11034
  const proc = Bun.spawn({
10977
11035
  cmd,
10978
11036
  cwd: req.cwd,
@@ -11049,7 +11107,7 @@ class CodexExecAdapter {
11049
11107
  throw new Error(message);
11050
11108
  }
11051
11109
  }
11052
- buildCommand(priorThreadId) {
11110
+ buildCommand(priorThreadId, filesystem) {
11053
11111
  if (priorThreadId) {
11054
11112
  return ["codex", "exec", "resume", "--json", "--skip-git-repo-check", priorThreadId, "-"];
11055
11113
  }
@@ -11059,7 +11117,8 @@ class CodexExecAdapter {
11059
11117
  if (this.config.reasoningEffort) {
11060
11118
  cmd.push("-c", `model_reasoning_effort="${this.config.reasoningEffort}"`);
11061
11119
  }
11062
- cmd.push("--sandbox", "read-only", "--skip-git-repo-check", "-");
11120
+ const sandbox = filesystem === "write" ? "workspace-write" : "read-only";
11121
+ cmd.push("--sandbox", sandbox, "--skip-git-repo-check", "-");
11063
11122
  return cmd;
11064
11123
  }
11065
11124
  }
@@ -12146,6 +12205,9 @@ function computeFingerprint(input) {
12146
12205
  reasoningEffort: agent.reasoningEffort ?? null,
12147
12206
  passModelOnResume: agent.adapter === "claude-cli" ? agent.passModelOnResume : null,
12148
12207
  strictMcp: agent.adapter === "claude-cli" ? agent.strictMcp !== false : null,
12208
+ ...agent.adapter === "codex-exec" && {
12209
+ codexSandbox: participant.permissions.filesystem === "write" ? "workspace-write" : "read-only"
12210
+ },
12149
12211
  baseUrl,
12150
12212
  role: participant.role,
12151
12213
  session: participant.session,
@@ -12554,7 +12616,8 @@ async function runConvergeIteration(ctx) {
12554
12616
  if (!result.ok) {
12555
12617
  return {
12556
12618
  ok: false,
12557
- failure: `manifest run failed at step "${result.failedStep}": ${result.error}`
12619
+ failure: `manifest run failed at step "${result.failedStep}": ${result.error}`,
12620
+ ...result.auditRunId && { auditRunId: result.auditRunId }
12558
12621
  };
12559
12622
  }
12560
12623
  const reviewText = result.outputs.review ?? "";
@@ -12923,7 +12986,12 @@ async function runJobWorker(jobId, deps) {
12923
12986
  const workerToken = crypto.randomUUID();
12924
12987
  const setPhase = (phase) => {
12925
12988
  try {
12926
- store.update(jobId, (c) => ({ ...c, phase, lastHeartbeatAt: iso2(now()) }));
12989
+ store.update(jobId, (c) => ({
12990
+ ...c,
12991
+ phase,
12992
+ ...c.phase !== phase && { phaseStartedAt: iso2(now()) },
12993
+ lastHeartbeatAt: iso2(now())
12994
+ }));
12927
12995
  } catch {}
12928
12996
  };
12929
12997
  const controller = new AbortController;
@@ -12946,7 +13014,8 @@ async function runJobWorker(jobId, deps) {
12946
13014
  pgid: process.pid,
12947
13015
  workerToken,
12948
13016
  lastHeartbeatAt: iso2(now()),
12949
- phase: "starting"
13017
+ phase: "starting",
13018
+ phaseStartedAt: iso2(now())
12950
13019
  }));
12951
13020
  const resolved = resolveExecute(job);
12952
13021
  if (!resolved.ok) {
@@ -12981,6 +13050,7 @@ async function runJobWorker(jobId, deps) {
12981
13050
  ...c,
12982
13051
  iteration: i,
12983
13052
  phase: "implementing",
13053
+ ...c.phase !== "implementing" && { phaseStartedAt: iso2(now()) },
12984
13054
  lastHeartbeatAt: iso2(now())
12985
13055
  }));
12986
13056
  let iter;
@@ -13010,6 +13080,10 @@ async function runJobWorker(jobId, deps) {
13010
13080
  return;
13011
13081
  }
13012
13082
  if (!iter.ok) {
13083
+ if (iter.auditRunId) {
13084
+ const ref = iter.auditRunId;
13085
+ store.update(jobId, (c) => ({ ...c, auditRefs: [...c.auditRefs, ref] }));
13086
+ }
13013
13087
  if (controller.signal.aborted) {
13014
13088
  stopLoopSafely(job, "cancelled", "cancelled mid-iteration (signal)");
13015
13089
  finish(store, jobId, now, "cancelled", { stopStatus: "cancelled" });
@@ -13022,6 +13096,7 @@ async function runJobWorker(jobId, deps) {
13022
13096
  store.update(jobId, (c) => ({
13023
13097
  ...c,
13024
13098
  phase: "recording",
13099
+ ...c.phase !== "recording" && { phaseStartedAt: iso2(now()) },
13025
13100
  iterationsCompleted: i,
13026
13101
  lastVerdict: iter.verdict,
13027
13102
  auditRefs: iter.auditRunId ? [...c.auditRefs, iter.auditRunId] : c.auditRefs,
@@ -13062,6 +13137,7 @@ function finish(store, jobId, now, state, extra) {
13062
13137
  state,
13063
13138
  endedAt: iso2(now()),
13064
13139
  phase: undefined,
13140
+ phaseStartedAt: undefined,
13065
13141
  lastHeartbeatAt: iso2(now()),
13066
13142
  ...extra.stopStatus !== undefined && { stopStatus: extra.stopStatus },
13067
13143
  ...extra.failure !== undefined && { failure: extra.failure }
@@ -35951,6 +36027,7 @@ class StdioServerTransport {
35951
36027
  }
35952
36028
 
35953
36029
  // src/audit/reader.ts
36030
+ init_src();
35954
36031
  var USAGE_KEYS2 = [
35955
36032
  "inputTokens",
35956
36033
  "outputTokens",
@@ -36048,6 +36125,30 @@ function listAudit(store, limit) {
36048
36125
  summaries.sort((a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? ""));
36049
36126
  return limit !== undefined ? summaries.slice(0, limit) : summaries;
36050
36127
  }
36128
+ function receiptTimelineEvent(e) {
36129
+ if (e.type !== "run.started")
36130
+ return e;
36131
+ const { participants: _participants, ...rest } = e;
36132
+ return rest;
36133
+ }
36134
+ function participantReceipts(snapshots) {
36135
+ if (snapshots === undefined)
36136
+ return;
36137
+ return Object.fromEntries(Object.entries(snapshots).map(([pid, p]) => {
36138
+ const permission = participantPermissionDisplay(p);
36139
+ return [
36140
+ pid,
36141
+ {
36142
+ agentId: p.agentId,
36143
+ adapter: p.adapter,
36144
+ session: p.session,
36145
+ filesystem: permission.filesystem,
36146
+ readOnlyEnforcement: permission.readOnlyEnforcement,
36147
+ config: p.config
36148
+ }
36149
+ ];
36150
+ }));
36151
+ }
36051
36152
  function readBody(store, runId, ref) {
36052
36153
  try {
36053
36154
  return store.readBlob(runId, ref);
@@ -36064,21 +36165,22 @@ function hiddenAdapterEventCount(events2) {
36064
36165
  function auditTimeline(store, runId, events2, opts) {
36065
36166
  const rows = opts.verbose ? events2 : events2.filter(isReceiptEvent);
36066
36167
  return rows.map((e) => {
36168
+ const row = receiptTimelineEvent(e);
36067
36169
  if (!opts.includeBodies)
36068
- return e;
36170
+ return row;
36069
36171
  if (e.type === "adapter.call.started") {
36070
- return { ...e, input: readBody(store, runId, e.inputBlob) };
36172
+ return { ...row, input: readBody(store, runId, e.inputBlob) };
36071
36173
  }
36072
36174
  if (e.type === "adapter.event" && e.rawBlob !== undefined) {
36073
- return { ...e, raw: readBody(store, runId, e.rawBlob) };
36175
+ return { ...row, raw: readBody(store, runId, e.rawBlob) };
36074
36176
  }
36075
36177
  if (e.type === "adapter.call.completed") {
36076
- return { ...e, output: readBody(store, runId, e.outputBlob) };
36178
+ return { ...row, output: readBody(store, runId, e.outputBlob) };
36077
36179
  }
36078
36180
  if (e.type === "step.completed" && e.outputBlob !== undefined) {
36079
- return { ...e, output: readBody(store, runId, e.outputBlob) };
36181
+ return { ...row, output: readBody(store, runId, e.outputBlob) };
36080
36182
  }
36081
- return e;
36183
+ return row;
36082
36184
  });
36083
36185
  }
36084
36186
  function showAudit(store, runId, opts) {
@@ -36092,7 +36194,7 @@ function showAudit(store, runId, opts) {
36092
36194
  out.incompleteReason = describeIncomplete(summary, events2);
36093
36195
  const started = events2.find((e) => e.type === "run.started");
36094
36196
  if (started?.type === "run.started" && started.participants !== undefined) {
36095
- out.participants = started.participants;
36197
+ out.participants = participantReceipts(started.participants);
36096
36198
  }
36097
36199
  if (!opts.verbose) {
36098
36200
  const hidden = hiddenAdapterEventCount(events2);
@@ -36126,6 +36228,40 @@ function isStale(job, nowMs, staleAfterMs = STALE_AFTER_MS) {
36126
36228
  const heartbeatOld = !Number.isFinite(beat) || nowMs - beat > staleAfterMs;
36127
36229
  return heartbeatOld || !pidAlive(job.pid);
36128
36230
  }
36231
+ function jobTiming(job, nowMs) {
36232
+ const inFlight = job.state === "queued" || job.state === "running";
36233
+ const timing = {};
36234
+ const startMs = Date.parse(job.startedAt ?? job.createdAt);
36235
+ const endMs = job.endedAt ? Date.parse(job.endedAt) : nowMs;
36236
+ if (Number.isFinite(startMs) && Number.isFinite(endMs) && endMs >= startMs) {
36237
+ timing.elapsedMs = endMs - startMs;
36238
+ }
36239
+ if (inFlight && job.lastHeartbeatAt) {
36240
+ const beat = Date.parse(job.lastHeartbeatAt);
36241
+ if (Number.isFinite(beat) && nowMs >= beat)
36242
+ timing.lastHeartbeatAgeMs = nowMs - beat;
36243
+ }
36244
+ if (job.phase !== undefined && job.phaseStartedAt) {
36245
+ const phaseStart = Date.parse(job.phaseStartedAt);
36246
+ if (Number.isFinite(phaseStart) && nowMs >= phaseStart) {
36247
+ timing.phaseElapsedMs = nowMs - phaseStart;
36248
+ }
36249
+ }
36250
+ return timing;
36251
+ }
36252
+ function formatDuration(ms) {
36253
+ const totalSec = Math.floor(ms / 1000);
36254
+ if (totalSec < 60)
36255
+ return `${totalSec}s`;
36256
+ const totalMin = Math.floor(totalSec / 60);
36257
+ if (totalMin < 60) {
36258
+ const sec = totalSec % 60;
36259
+ return sec ? `${totalMin}m${sec}s` : `${totalMin}m`;
36260
+ }
36261
+ const hours = Math.floor(totalMin / 60);
36262
+ const min = totalMin % 60;
36263
+ return min ? `${hours}h${min}m` : `${hours}h`;
36264
+ }
36129
36265
 
36130
36266
  // src/surfaces/mcp/converge-engine.ts
36131
36267
  class ConvergeEngineError extends Error {
@@ -36578,10 +36714,22 @@ function summarizeRunForStatus(run) {
36578
36714
  audited: run.recorder !== undefined && run.recorder.lastError === undefined
36579
36715
  };
36580
36716
  }
36717
+ function runningNextAction(job, timing) {
36718
+ const parts = [];
36719
+ if (timing.elapsedMs !== undefined)
36720
+ parts.push(`running for ${formatDuration(timing.elapsedMs)}`);
36721
+ if (job.phase) {
36722
+ parts.push(timing.phaseElapsedMs !== undefined ? `${job.phase} for ${formatDuration(timing.phaseElapsedMs)}` : job.phase);
36723
+ }
36724
+ const lead = parts.length > 0 ? parts.join(", ") : "in progress";
36725
+ return `${lead}; chit_job_status / chit_job_cancel "${job.jobId}"`;
36726
+ }
36581
36727
  function summarizeJobForStatus(job, nowMs) {
36582
36728
  const stale = isStale(job, nowMs);
36583
36729
  const display = stale ? "stale" : job.state;
36584
- const nextAction = display === "running" ? `in progress${job.phase ? ` (${job.phase})` : ""}; chit_job_status / chit_job_cancel "${job.jobId}"` : display === "queued" ? "queued; the worker is starting" : display === "stale" ? `worker appears dead; chit_job_status "${job.jobId}" to inspect, then start a fresh job` : `${display}${job.stopStatus ? ` (${job.stopStatus})` : ""}; chit_job_status "${job.jobId}" or chit_audit_show <ref>`;
36730
+ const timing = jobTiming(job, nowMs);
36731
+ const latestRef = job.auditRefs.at(-1);
36732
+ const nextAction = display === "running" ? runningNextAction(job, timing) : display === "queued" ? "queued; the worker is starting" : display === "stale" ? `worker appears dead; chit_job_status "${job.jobId}" to inspect, then start a fresh job` : `${display}${job.stopStatus ? ` (${job.stopStatus})` : ""}; chit_job_status "${job.jobId}"${latestRef ? ` or chit_audit_show ${latestRef}` : ""}`;
36585
36733
  return {
36586
36734
  jobId: job.jobId,
36587
36735
  loopId: job.loopId,
@@ -36594,6 +36742,11 @@ function summarizeJobForStatus(job, nowMs) {
36594
36742
  ...job.stopStatus !== undefined && { stopStatus: job.stopStatus },
36595
36743
  auditRefs: job.auditRefs,
36596
36744
  createdAt: job.createdAt,
36745
+ ...timing.elapsedMs !== undefined && { elapsedMs: timing.elapsedMs },
36746
+ ...timing.lastHeartbeatAgeMs !== undefined && {
36747
+ lastHeartbeatAgeMs: timing.lastHeartbeatAgeMs
36748
+ },
36749
+ ...timing.phaseElapsedMs !== undefined && { phaseElapsedMs: timing.phaseElapsedMs },
36597
36750
  nextAction
36598
36751
  };
36599
36752
  }
@@ -37118,7 +37271,13 @@ function describeJob(job) {
37118
37271
  };
37119
37272
  }
37120
37273
  } catch {}
37121
- const nextAction = display === "running" ? "in progress; chit_job_cancel to stop, or wait and poll again" : display === "queued" ? "queued; the worker is starting" : display === "stale" ? "worker appears dead; inspect with chit_job_status (and chit_audit_show <auditRef> for transcripts), then start a fresh job" : `${display}${job.stopStatus ? ` (${job.stopStatus})` : ""}; open a transcript with chit_audit_show <auditRef>`;
37274
+ const timing = jobTiming(job, now);
37275
+ const latestRef = job.auditRefs.at(-1);
37276
+ const runningDetail = [
37277
+ timing.elapsedMs !== undefined ? `running for ${formatDuration(timing.elapsedMs)}` : undefined,
37278
+ job.phase ? timing.phaseElapsedMs !== undefined ? `${job.phase} for ${formatDuration(timing.phaseElapsedMs)}` : job.phase : undefined
37279
+ ].filter(Boolean);
37280
+ const nextAction = display === "running" ? `${runningDetail.length > 0 ? `${runningDetail.join(", ")}; ` : ""}chit_job_cancel to stop, or wait and poll again` : display === "queued" ? "queued; the worker is starting" : display === "stale" ? `worker appears dead; inspect with chit_job_status${latestRef ? ` (chit_audit_show ${latestRef} for the transcript)` : ""}, then start a fresh job` : `${display}${job.stopStatus ? ` (${job.stopStatus})` : ""}; ${latestRef ? `open a transcript with chit_audit_show ${latestRef}` : "no audit transcript was recorded"}`;
37122
37281
  return {
37123
37282
  jobId: job.jobId,
37124
37283
  loopId: job.loopId,
@@ -37140,12 +37299,18 @@ function describeJob(job) {
37140
37299
  ...job.startedAt !== undefined && { startedAt: job.startedAt },
37141
37300
  ...job.endedAt !== undefined && { endedAt: job.endedAt },
37142
37301
  ...job.lastHeartbeatAt !== undefined && { lastHeartbeatAt: job.lastHeartbeatAt },
37302
+ ...job.phaseStartedAt !== undefined && { phaseStartedAt: job.phaseStartedAt },
37303
+ ...timing.elapsedMs !== undefined && { elapsedMs: timing.elapsedMs },
37304
+ ...timing.lastHeartbeatAgeMs !== undefined && {
37305
+ lastHeartbeatAgeMs: timing.lastHeartbeatAgeMs
37306
+ },
37307
+ ...timing.phaseElapsedMs !== undefined && { phaseElapsedMs: timing.phaseElapsedMs },
37143
37308
  ...latest !== undefined && { latest },
37144
37309
  nextAction
37145
37310
  };
37146
37311
  }
37147
37312
  server.registerTool("chit_job_status", {
37148
- description: "Show one background job: state (queued/running/completed/cancelled/failed, or derived `stale` when the worker is gone), current phase, loop id, iterations, last verdict, audit refs, and the latest iteration's changed files / workspace warnings / usage. Read-only.",
37313
+ description: "Show one background job: state (queued/running/completed/cancelled/failed, or derived `stale` when the worker is gone), current phase, timing fields (elapsedMs, lastHeartbeatAgeMs, phaseElapsedMs), loop id, iterations, last verdict, audit refs, and the latest iteration's changed files / workspace warnings / usage. Read-only.",
37149
37314
  inputSchema: { job_id: exports_external.string() }
37150
37315
  }, async ({ job_id }) => {
37151
37316
  const job = jobStore.get(job_id);
@@ -37337,8 +37502,7 @@ participants (recorded config):
37337
37502
  `);
37338
37503
  } else {
37339
37504
  for (const [pid, p] of Object.entries(snapshots)) {
37340
- const enforces = p.enforcesReadOnly ? "enforces=yes" : "enforces=NO";
37341
- io.out(` ${pid} agent=${p.agentId} session=${p.session} permissions=${p.permissions.filesystem} adapter=${p.adapter} ${enforces}
37505
+ io.out(` ${pid} agent=${p.agentId} session=${p.session} ${participantPermissionText(p)} adapter=${p.adapter}
37342
37506
  `);
37343
37507
  const pairs = p.adapter === "unknown" ? "unresolved (unknown agent)" : configPairs(p.config).map(([k, v]) => `${k}=${v}`).join(" ");
37344
37508
  io.out(` config ${pairs}
@@ -38092,9 +38256,10 @@ Permission enforcement: each participant's declared permissions are checked
38092
38256
  against the chosen adapter's capabilities at install time. If the adapter
38093
38257
  cannot enforce a permission, the run is rejected unless
38094
38258
  --allow-unenforced-permissions is set. Both built-in adapters enforce
38095
- filesystem read_only today: codex-exec via --sandbox read-only (a hard OS
38096
- sandbox), claude-cli via --permission-mode plan (Claude plan-mode permissions,
38097
- not an OS/filesystem sandbox).
38259
+ filesystem read_only today: codex-exec via an OS sandbox (--sandbox read-only
38260
+ for a reviewer, --sandbox workspace-write for a write-capable implementer),
38261
+ claude-cli via --permission-mode plan (a Claude plan-mode permission, not an
38262
+ OS/filesystem sandbox).
38098
38263
 
38099
38264
  Limitations in this build:
38100
38265
  - file[] inputs are not yet supported via the CLI.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chit-run/cli",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Versioned, cross-vendor agent routines with an audit trail. Stop being the glue between your agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",