@azure-id/orc 1.1.0 → 1.2.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.
- package/CHANGELOG.md +2518 -2437
- package/README.md +681 -694
- package/bin/cli.js +472 -1
- package/bin/verify-contracts.js +38 -1
- package/package.json +1 -1
- package/templates/hooks/orc-statusline.js +188 -1
- package/templates/skills/_shared/phases/execution.md +2 -0
- package/templates/skills/_shared/return-validation.md +222 -145
- package/templates/skills/orc/SKILL.md +241 -237
- package/templates/skills/orc-doc/SKILL.md +484 -480
- package/templates/skills/orc-fast/SKILL.md +216 -214
- package/templates/skills/orc-mini/SKILL.md +246 -243
- package/templates/skills/orc-quick/SKILL.md +347 -345
- package/templates/skills/orc-wiki/SKILL.md +174 -170
package/bin/cli.js
CHANGED
|
@@ -3091,6 +3091,18 @@ const LANE_CALLS = {
|
|
|
3091
3091
|
never: "never merge `.claude/orc.config.yaml` yourself, and never re-derive a precedence — the answer already carries it",
|
|
3092
3092
|
lanes: ["context-combiner", "orc", "orc-aftermath", "orc-analyze", "orc-analyze-mini", "orc-boundary", "orc-brainstorm", "orc-budget", "orc-challenge", "orc-claude", "orc-diy", "orc-doc", "orc-explain", "orc-export", "orc-fast", "orc-grill", "orc-handoff", "orc-learn", "orc-mini", "orc-pact", "orc-pattern", "orc-poly", "orc-pr-driver", "orc-pr-setup", "orc-quick", "orc-retro", "orc-route", "orc-verify", "orc-wiki"],
|
|
3093
3093
|
},
|
|
3094
|
+
"run-inflight": {
|
|
3095
|
+
cmd: "orc run inflight [--json]",
|
|
3096
|
+
what: "is a dispatch from this run still alive — the ONE reader of the trace's pending sidecar",
|
|
3097
|
+
exits: { 0: "clear — provably nothing in flight", 1: "in-flight — at least one dispatch has not returned", 2: "unknown — cannot prove either way" },
|
|
3098
|
+
states: ["clear", "in-flight", "unknown"],
|
|
3099
|
+
cost: "free",
|
|
3100
|
+
when: "before ANY re-dispatch, requeue or repair round, and before the first dispatch of a resumed run",
|
|
3101
|
+
on_absent: "exit 2 REFUSES by default — the one place an absent reading blocks, because a wrongly-refused dispatch costs a question and a wrongly-issued one costs a second Opus agent for an hour",
|
|
3102
|
+
canonical: "_shared/return-validation.md",
|
|
3103
|
+
never: "never read `clear` as proof that an AD-HOC dispatch finished — the hook writes no SPAWN for one, so no record exists",
|
|
3104
|
+
lanes: ["orc", "orc-doc", "orc-fast", "orc-mini", "orc-quick", "orc-wiki"],
|
|
3105
|
+
},
|
|
3094
3106
|
"lane-phases": {
|
|
3095
3107
|
cmd: "orc lane phases <lane> [--json]",
|
|
3096
3108
|
what: "which SHARED phases this lane runs, in order — the file, the layers to read, and when",
|
|
@@ -9006,6 +9018,149 @@ function resume() {
|
|
|
9006
9018
|
}
|
|
9007
9019
|
|
|
9008
9020
|
// `orc run list` / `orc run show <slug|n>`
|
|
9021
|
+
// ── `orc run inflight` — is a previous dispatch still alive? (v1.2.0) ───────
|
|
9022
|
+
//
|
|
9023
|
+
// WHY THIS EXISTS. Claude Code's Task tool returning an error does NOT kill the
|
|
9024
|
+
// subagent behind it. The agent keeps running and keeps writing files. Every
|
|
9025
|
+
// lane's retry rule ("a broken return = a failure, re-dispatch") silently
|
|
9026
|
+
// assumed the opposite, so an interrupted turn produced a SECOND agent on the
|
|
9027
|
+
// same task while the first was still working. A graded run put THREE
|
|
9028
|
+
// `orc-executor-opus-5-low` agents on one task for 50m19s + 115m22s + 100m53s
|
|
9029
|
+
// — 266 minutes of Opus 5 for one authorised dispatch, all editing the same
|
|
9030
|
+
// files. The hook already recorded every one of them; nothing ever READ it.
|
|
9031
|
+
//
|
|
9032
|
+
// The pending sidecar (`<trace>.pending.json`, written by orc-trace.js on every
|
|
9033
|
+
// SPAWN) is the evidence. This command makes it authoritative:
|
|
9034
|
+
// `a lane that re-dispatches over a live attempt` has broken the contract in
|
|
9035
|
+
// `_shared/return-validation.md` §0.
|
|
9036
|
+
//
|
|
9037
|
+
// UNKNOWN IS NOT ZERO. A missing pointer, an unreadable sidecar or a record too
|
|
9038
|
+
// old to trust all exit 2 — never 0. "I cannot prove anything is running" and
|
|
9039
|
+
// "I proved nothing is running" are different facts, and re-dispatching on the
|
|
9040
|
+
// first one is exactly the bug. Only a readable sidecar that is EMPTY *and* a
|
|
9041
|
+
// trace whose SPAWN/RETURN counts agree earns exit 0.
|
|
9042
|
+
//
|
|
9043
|
+
// exit 0 clear — provably nothing in flight
|
|
9044
|
+
// exit 1 in-flight — >=1 dispatch has not returned
|
|
9045
|
+
// exit 2 unknown — cannot prove either way
|
|
9046
|
+
const INFLIGHT_STALE_MS = 6 * 60 * 60 * 1000;
|
|
9047
|
+
|
|
9048
|
+
function runInflightCmd(claudeDir) {
|
|
9049
|
+
const dir = resolveLogDir(claudeDir);
|
|
9050
|
+
const out = {
|
|
9051
|
+
ok: true,
|
|
9052
|
+
state: "unknown",
|
|
9053
|
+
reason: null,
|
|
9054
|
+
count: 0,
|
|
9055
|
+
entries: [],
|
|
9056
|
+
trace: null,
|
|
9057
|
+
lane: null,
|
|
9058
|
+
slug: null,
|
|
9059
|
+
log_dir: dir,
|
|
9060
|
+
sidecar: null,
|
|
9061
|
+
sidecar_readable: false,
|
|
9062
|
+
spawns: null,
|
|
9063
|
+
returns: null,
|
|
9064
|
+
balance_agrees: null,
|
|
9065
|
+
stale_entries: 0,
|
|
9066
|
+
};
|
|
9067
|
+
|
|
9068
|
+
const finish = (state, reason, code) => {
|
|
9069
|
+
out.state = state;
|
|
9070
|
+
out.reason = reason;
|
|
9071
|
+
if (wantsJson()) emitJson(out);
|
|
9072
|
+
else renderInflight(out);
|
|
9073
|
+
process.exit(code);
|
|
9074
|
+
};
|
|
9075
|
+
|
|
9076
|
+
// 1. The run pointer. No pointer = no open run we can reason about.
|
|
9077
|
+
let cur = null;
|
|
9078
|
+
try {
|
|
9079
|
+
cur = fs.readFileSync(path.join(dir, ".current"), "utf8").trim();
|
|
9080
|
+
} catch (_) {}
|
|
9081
|
+
if (!cur) return finish("unknown", "no trace pointer — no run is open, or the pointer was never written", 2);
|
|
9082
|
+
out.trace = cur;
|
|
9083
|
+
const m = TRACE_NAME.exec(cur);
|
|
9084
|
+
if (m) { out.lane = m[1]; out.slug = m[2]; }
|
|
9085
|
+
|
|
9086
|
+
// 2. The trace's own SPAWN/RETURN balance — an independent second opinion.
|
|
9087
|
+
// Counted from the hook's own skeleton lines only.
|
|
9088
|
+
const tracePath = path.join(dir, cur);
|
|
9089
|
+
try {
|
|
9090
|
+
const text = fs.readFileSync(tracePath, "utf8");
|
|
9091
|
+
out.spawns = (text.match(/\] hook\s+SPAWN /g) || []).length;
|
|
9092
|
+
const all = (text.match(/\] hook\s+RETURN /g) || []).length;
|
|
9093
|
+
const loose = (text.match(/\] hook\s+RETURN ~agent :: unattributed/g) || []).length;
|
|
9094
|
+
out.returns = all - loose;
|
|
9095
|
+
} catch (_) {
|
|
9096
|
+
return finish("unknown", `trace pointer names "${cur}" but it cannot be read`, 2);
|
|
9097
|
+
}
|
|
9098
|
+
|
|
9099
|
+
// 3. The pending sidecar — the record of what was dispatched and never closed.
|
|
9100
|
+
const side = path.join(dir, cur + ".pending.json");
|
|
9101
|
+
out.sidecar = side;
|
|
9102
|
+
let pend = null;
|
|
9103
|
+
try {
|
|
9104
|
+
const raw = JSON.parse(fs.readFileSync(side, "utf8"));
|
|
9105
|
+
if (Array.isArray(raw)) { pend = raw; out.sidecar_readable = true; }
|
|
9106
|
+
} catch (_) {}
|
|
9107
|
+
|
|
9108
|
+
const now = Date.now();
|
|
9109
|
+
if (pend) {
|
|
9110
|
+
out.entries = pend.map((r) => {
|
|
9111
|
+
const ts = typeof r.ts === "number" ? r.ts : null;
|
|
9112
|
+
const age = ts == null ? null : Math.round((now - ts) / 1000);
|
|
9113
|
+
return {
|
|
9114
|
+
agent: r.agent == null ? null : String(r.agent),
|
|
9115
|
+
desc: r.desc == null ? null : String(r.desc),
|
|
9116
|
+
started_ms: ts,
|
|
9117
|
+
age_s: age,
|
|
9118
|
+
stale: ts != null && now - ts > INFLIGHT_STALE_MS,
|
|
9119
|
+
};
|
|
9120
|
+
});
|
|
9121
|
+
out.count = out.entries.length;
|
|
9122
|
+
out.stale_entries = out.entries.filter((e) => e.stale).length;
|
|
9123
|
+
}
|
|
9124
|
+
|
|
9125
|
+
const balance = out.spawns != null && out.returns != null ? out.spawns - out.returns : null;
|
|
9126
|
+
out.balance_agrees = balance == null || pend == null ? null : balance === out.count;
|
|
9127
|
+
|
|
9128
|
+
// 4. Verdict. Conservative in every direction.
|
|
9129
|
+
if (!out.sidecar_readable) {
|
|
9130
|
+
if (balance != null && balance > 0)
|
|
9131
|
+
return finish("in-flight", `sidecar unreadable, but the trace shows ${balance} SPAWN(s) with no RETURN`, 1);
|
|
9132
|
+
return finish("unknown", "the pending sidecar is missing or unreadable — cannot prove a dispatch finished", 2);
|
|
9133
|
+
}
|
|
9134
|
+
if (out.count > 0) {
|
|
9135
|
+
if (out.stale_entries === out.count)
|
|
9136
|
+
return finish(
|
|
9137
|
+
"unknown",
|
|
9138
|
+
`${out.count} record(s), all older than 6h — the run probably died without closing them`,
|
|
9139
|
+
2
|
|
9140
|
+
);
|
|
9141
|
+
return finish("in-flight", `${out.count} dispatch(es) have not returned`, 1);
|
|
9142
|
+
}
|
|
9143
|
+
// Sidecar empty. Only trust it when the trace agrees.
|
|
9144
|
+
if (balance != null && balance > 0)
|
|
9145
|
+
return finish("unknown", `sidecar is empty but the trace shows ${balance} unmatched SPAWN(s) — they disagree`, 2);
|
|
9146
|
+
return finish("clear", "no dispatch is in flight", 0);
|
|
9147
|
+
}
|
|
9148
|
+
|
|
9149
|
+
function renderInflight(o) {
|
|
9150
|
+
const head =
|
|
9151
|
+
o.state === "clear" ? "clear" : o.state === "in-flight" ? `IN FLIGHT (${o.count})` : "unknown";
|
|
9152
|
+
console.log(`${ui.color.bold("dispatch:")} ${head}`);
|
|
9153
|
+
console.log(` ${o.reason}`);
|
|
9154
|
+
if (o.trace) console.log(` trace ${o.trace}`);
|
|
9155
|
+
for (const e of o.entries) {
|
|
9156
|
+
const age = e.age_s == null ? "age unknown" : `${Math.floor(e.age_s / 60)}m${e.age_s % 60}s ago`;
|
|
9157
|
+
console.log(` - ${e.agent || "(unnamed)"} ${age}${e.stale ? " [stale]" : ""}`);
|
|
9158
|
+
if (e.desc) console.log(` ${e.desc}`);
|
|
9159
|
+
}
|
|
9160
|
+
if (o.state === "in-flight")
|
|
9161
|
+
console.log("\n Do NOT re-dispatch these tasks. A Task error does not kill the agent behind it.");
|
|
9162
|
+
}
|
|
9163
|
+
|
|
9009
9164
|
function runCmd() {
|
|
9010
9165
|
const claudeDir = resolveClaudeDir();
|
|
9011
9166
|
const pos = positionals(); // ["run", <sub?>, <arg?>]
|
|
@@ -9016,6 +9171,9 @@ function runCmd() {
|
|
|
9016
9171
|
// on disk. Neither deletes anything.
|
|
9017
9172
|
if (sub === "close" || sub === "reopen") return runCloseCmd(claudeDir, runs, sub, pos[2]);
|
|
9018
9173
|
|
|
9174
|
+
// Read-only, and the ONE reader of the pending sidecar. 0 clear / 1 in-flight / 2 unknown.
|
|
9175
|
+
if (sub === "inflight") return runInflightCmd(claudeDir);
|
|
9176
|
+
|
|
9019
9177
|
if (sub === "show") {
|
|
9020
9178
|
const arg = pos[2];
|
|
9021
9179
|
const pick = /^\d+$/.test(String(arg)) ? runs[Number(arg) - 1] : runs.find((r) => r.slug === arg);
|
|
@@ -33210,6 +33368,318 @@ function usageResetMs(v) {
|
|
|
33210
33368
|
return Number.isFinite(p) ? p : NaN;
|
|
33211
33369
|
}
|
|
33212
33370
|
|
|
33371
|
+
// ── `orc usage report` — where the window went (v1.2.0) ─────────────────────
|
|
33372
|
+
//
|
|
33373
|
+
// `orc usage check` answers "is there room". This answers the question a user
|
|
33374
|
+
// actually asks mid-run: "how much has THIS session eaten, and what ate it".
|
|
33375
|
+
//
|
|
33376
|
+
// THE HONEST PART, and it decides the whole shape of this command. Claude Code
|
|
33377
|
+
// records NO token usage for a dispatched subagent — `isSidechain` is never set
|
|
33378
|
+
// and no sidechain message carries a usage block, in any transcript on disk. So
|
|
33379
|
+
// a per-executor TOKEN figure cannot be measured, and inventing one would be
|
|
33380
|
+
// the same class of bug as a fake validator. What IS measured, exactly, is WALL
|
|
33381
|
+
// TIME, from the trace hook's own SPAWN/RETURN lines. Foreign workers are the
|
|
33382
|
+
// one exception: `orc extra` records real four-kind vectors, so those rows
|
|
33383
|
+
// carry tokens and say so. Every other row reports `tokens: null` plus the
|
|
33384
|
+
// reason — never 0. Unknown is not zero.
|
|
33385
|
+
const USAGE_TOP_N = 5;
|
|
33386
|
+
|
|
33387
|
+
// "12m43s" | "1m7s" | "45s" → seconds. Anything else → null, never 0.
|
|
33388
|
+
function usageDurSeconds(s) {
|
|
33389
|
+
if (!s) return null;
|
|
33390
|
+
const m = /^(?:(\d+)m)?(\d+)s$/.exec(String(s).trim());
|
|
33391
|
+
if (!m) return null;
|
|
33392
|
+
return (m[1] ? Number(m[1]) * 60 : 0) + Number(m[2]);
|
|
33393
|
+
}
|
|
33394
|
+
|
|
33395
|
+
// Per-agent wall time for the run the trace pointer names. Returns null when
|
|
33396
|
+
// there is no open run — an absent trace is an absent measurement, not zero.
|
|
33397
|
+
function usageRunConsumers(claudeDir) {
|
|
33398
|
+
const dir = resolveLogDir(claudeDir);
|
|
33399
|
+
let cur = null;
|
|
33400
|
+
try {
|
|
33401
|
+
cur = fs.readFileSync(path.join(dir, ".current"), "utf8").trim();
|
|
33402
|
+
} catch (_) {}
|
|
33403
|
+
if (!cur) return null;
|
|
33404
|
+
let text = "";
|
|
33405
|
+
try {
|
|
33406
|
+
text = fs.readFileSync(path.join(dir, cur), "utf8");
|
|
33407
|
+
} catch (_) {
|
|
33408
|
+
return null;
|
|
33409
|
+
}
|
|
33410
|
+
const nameMatch = TRACE_NAME.exec(cur);
|
|
33411
|
+
const by = new Map();
|
|
33412
|
+
const bump = (agent, secs, running) => {
|
|
33413
|
+
const k = agent || "(unnamed)";
|
|
33414
|
+
const r = by.get(k) || { agent: k, dispatches: 0, wall_seconds: 0, running: 0, unmeasured: 0 };
|
|
33415
|
+
r.dispatches += 1;
|
|
33416
|
+
if (running) r.running += 1;
|
|
33417
|
+
if (secs == null) r.unmeasured += 1;
|
|
33418
|
+
else r.wall_seconds += secs;
|
|
33419
|
+
by.set(k, r);
|
|
33420
|
+
};
|
|
33421
|
+
for (const line of text.split("\n")) {
|
|
33422
|
+
// Only the hook's own skeleton lines. `~agent :: unattributed` is a
|
|
33423
|
+
// bookkeeping artefact of >=2 in flight and is never a dispatch.
|
|
33424
|
+
const r = /\] hook\s+RETURN ~?([^\s:]+) :: /.exec(line);
|
|
33425
|
+
if (!r) continue;
|
|
33426
|
+
if (r[1] === "agent") continue;
|
|
33427
|
+
const d = /\bdur=(\S+)/.exec(line);
|
|
33428
|
+
bump(r[1], usageDurSeconds(d && d[1]), false);
|
|
33429
|
+
}
|
|
33430
|
+
// Anything still open is real spend happening RIGHT NOW — the exact case the
|
|
33431
|
+
// v1.2.0 in-flight guard exists for, and the one a user most wants to see.
|
|
33432
|
+
let pending = [];
|
|
33433
|
+
try {
|
|
33434
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, cur + ".pending.json"), "utf8"));
|
|
33435
|
+
if (Array.isArray(raw)) pending = raw;
|
|
33436
|
+
} catch (_) {}
|
|
33437
|
+
const now = Date.now();
|
|
33438
|
+
for (const p of pending) {
|
|
33439
|
+
const secs = typeof p.ts === "number" ? Math.round((now - p.ts) / 1000) : null;
|
|
33440
|
+
bump(p.agent, secs, true);
|
|
33441
|
+
}
|
|
33442
|
+
return {
|
|
33443
|
+
trace: cur,
|
|
33444
|
+
lane: nameMatch ? nameMatch[1] : null,
|
|
33445
|
+
slug: nameMatch ? nameMatch[2] : null,
|
|
33446
|
+
agents: [...by.values()],
|
|
33447
|
+
in_flight: pending.length,
|
|
33448
|
+
};
|
|
33449
|
+
}
|
|
33450
|
+
|
|
33451
|
+
// Foreign dispatches DO carry measured tokens. Same file the spend report reads.
|
|
33452
|
+
function usageForeignSpend(claudeDir) {
|
|
33453
|
+
const f = path.join(claudeDir, "orc", "extra-spend.jsonl");
|
|
33454
|
+
let lines = [];
|
|
33455
|
+
try {
|
|
33456
|
+
lines = fs.readFileSync(f, "utf8").split("\n").filter(Boolean);
|
|
33457
|
+
} catch (_) {
|
|
33458
|
+
return { rows: [], unreadable: 0 };
|
|
33459
|
+
}
|
|
33460
|
+
const rows = [];
|
|
33461
|
+
let unreadable = 0;
|
|
33462
|
+
for (const l of lines) {
|
|
33463
|
+
try {
|
|
33464
|
+
rows.push(JSON.parse(l));
|
|
33465
|
+
} catch (_) {
|
|
33466
|
+
unreadable += 1;
|
|
33467
|
+
}
|
|
33468
|
+
}
|
|
33469
|
+
return { rows, unreadable };
|
|
33470
|
+
}
|
|
33471
|
+
|
|
33472
|
+
function usageReportCmd(claudeDir) {
|
|
33473
|
+
const asJson = wantsJson();
|
|
33474
|
+
const now = Date.now();
|
|
33475
|
+
const cfg = resolvedConfig(claudeDir);
|
|
33476
|
+
const stopPct = Math.min(50, Math.max(1, Number(cfg.usage_stop_pct) || 10));
|
|
33477
|
+
|
|
33478
|
+
// Read the bridge RAW here rather than through readUsageBridge: a stale
|
|
33479
|
+
// reading is still worth SHOWING, with its age, where a gate would rightly
|
|
33480
|
+
// discard it. The state word still comes from freshness — age is displayed,
|
|
33481
|
+
// never ignored.
|
|
33482
|
+
let raw = null;
|
|
33483
|
+
try {
|
|
33484
|
+
raw = JSON.parse(fs.readFileSync(path.join(claudeDir, "orc", "usage.json"), "utf8"));
|
|
33485
|
+
} catch (_) {}
|
|
33486
|
+
const ageMin =
|
|
33487
|
+
raw && typeof raw.written_at === "number" ? Math.round((now - raw.written_at) / 60000) : null;
|
|
33488
|
+
const stale = ageMin == null ? true : now - raw.written_at > WAIT_BRIDGE_MAX_AGE_MS;
|
|
33489
|
+
|
|
33490
|
+
const view = (o, label) => {
|
|
33491
|
+
if (!o || typeof o.used_percentage !== "number") return null;
|
|
33492
|
+
const used = Math.round(o.used_percentage);
|
|
33493
|
+
const at = usageResetMs(o.resets_at);
|
|
33494
|
+
return {
|
|
33495
|
+
window: label,
|
|
33496
|
+
used_percentage: used,
|
|
33497
|
+
remaining_percentage: Math.max(0, 100 - used),
|
|
33498
|
+
resets_at: Number.isFinite(at) ? new Date(at).toISOString() : null,
|
|
33499
|
+
resets_in_minutes: Number.isFinite(at) && at > now ? Math.round((at - now) / 60000) : null,
|
|
33500
|
+
low: 100 - used <= stopPct,
|
|
33501
|
+
};
|
|
33502
|
+
};
|
|
33503
|
+
const fh = raw ? view(raw.five_hour, "5h") : null;
|
|
33504
|
+
const sd = raw ? view(raw.seven_day, "wk") : null;
|
|
33505
|
+
|
|
33506
|
+
// Session consumption — the ledger the statusline keeps.
|
|
33507
|
+
let led = null;
|
|
33508
|
+
try {
|
|
33509
|
+
led = JSON.parse(fs.readFileSync(path.join(claudeDir, "orc", "usage-session.json"), "utf8"));
|
|
33510
|
+
} catch (_) {}
|
|
33511
|
+
const consumed = (w) =>
|
|
33512
|
+
!w
|
|
33513
|
+
? null
|
|
33514
|
+
: {
|
|
33515
|
+
baseline_percentage: w.baseline,
|
|
33516
|
+
now_percentage: w.last,
|
|
33517
|
+
consumed_percentage: Math.max(0, w.accumulated + Math.max(0, w.last - w.baseline)),
|
|
33518
|
+
window_resets: w.resets,
|
|
33519
|
+
};
|
|
33520
|
+
const session = !led
|
|
33521
|
+
? null
|
|
33522
|
+
: {
|
|
33523
|
+
session_id: led.session_id || null,
|
|
33524
|
+
started_at: led.started_at ? new Date(led.started_at).toISOString() : null,
|
|
33525
|
+
running_minutes: led.started_at ? Math.round((now - led.started_at) / 60000) : null,
|
|
33526
|
+
five_hour: consumed(led.five_hour),
|
|
33527
|
+
seven_day: consumed(led.seven_day),
|
|
33528
|
+
still_counting: true,
|
|
33529
|
+
// Never overclaim. The window is per ACCOUNT: a second Claude Code
|
|
33530
|
+
// window, a cloud session, or anyone else on the same key moves it too.
|
|
33531
|
+
caveat:
|
|
33532
|
+
"this is how far the window moved while this session ran — other sessions on the same account share it",
|
|
33533
|
+
};
|
|
33534
|
+
|
|
33535
|
+
const run = usageRunConsumers(claudeDir);
|
|
33536
|
+
const foreign = usageForeignSpend(claudeDir);
|
|
33537
|
+
|
|
33538
|
+
// Rank by measured wall time. A row with nothing measured keeps its slot.
|
|
33539
|
+
const top = [];
|
|
33540
|
+
if (run) {
|
|
33541
|
+
const sorted = run.agents.slice().sort((a, b) => b.wall_seconds - a.wall_seconds);
|
|
33542
|
+
for (const a of sorted.slice(0, USAGE_TOP_N))
|
|
33543
|
+
top.push({
|
|
33544
|
+
agent: a.agent,
|
|
33545
|
+
dispatches: a.dispatches,
|
|
33546
|
+
running: a.running,
|
|
33547
|
+
wall_seconds: a.wall_seconds,
|
|
33548
|
+
unmeasured_dispatches: a.unmeasured,
|
|
33549
|
+
tokens: null,
|
|
33550
|
+
tokens_source: "unavailable",
|
|
33551
|
+
});
|
|
33552
|
+
}
|
|
33553
|
+
for (const r of foreign.rows.slice(-USAGE_TOP_N)) {
|
|
33554
|
+
top.push({
|
|
33555
|
+
agent: "extra:" + (r.profile || "?") + "/" + (r.model || "?"),
|
|
33556
|
+
dispatches: 1,
|
|
33557
|
+
running: 0,
|
|
33558
|
+
wall_seconds: usageDurSeconds(r.dur),
|
|
33559
|
+
unmeasured_dispatches: 0,
|
|
33560
|
+
tokens: r.usage || null,
|
|
33561
|
+
tokens_source: r.usage ? "measured" : "not reported by the worker",
|
|
33562
|
+
});
|
|
33563
|
+
}
|
|
33564
|
+
|
|
33565
|
+
const windows = [fh, sd].filter(Boolean);
|
|
33566
|
+
const state = !windows.length || stale ? "unknown" : windows.some((w) => w.low) ? "low" : "ok";
|
|
33567
|
+
const code = state === "low" ? 1 : state === "unknown" ? 2 : 0;
|
|
33568
|
+
const out = {
|
|
33569
|
+
ok: true,
|
|
33570
|
+
state,
|
|
33571
|
+
five_hour: fh,
|
|
33572
|
+
seven_day: sd,
|
|
33573
|
+
context_used_percentage:
|
|
33574
|
+
raw && typeof raw.context_used_percentage === "number" ? raw.context_used_percentage : null,
|
|
33575
|
+
reading_age_minutes: ageMin,
|
|
33576
|
+
reading_stale: stale,
|
|
33577
|
+
stop_pct: stopPct,
|
|
33578
|
+
gate: String(cfg.usage_gate || "off"),
|
|
33579
|
+
session,
|
|
33580
|
+
run: run
|
|
33581
|
+
? {
|
|
33582
|
+
trace: run.trace,
|
|
33583
|
+
lane: run.lane,
|
|
33584
|
+
slug: run.slug,
|
|
33585
|
+
dispatches: run.agents.reduce((n, a) => n + a.dispatches, 0),
|
|
33586
|
+
in_flight: run.in_flight,
|
|
33587
|
+
wall_seconds: run.agents.reduce((n, a) => n + a.wall_seconds, 0),
|
|
33588
|
+
}
|
|
33589
|
+
: null,
|
|
33590
|
+
top,
|
|
33591
|
+
foreign_spend_rows: foreign.rows.length,
|
|
33592
|
+
unreadable_spend_lines: foreign.unreadable,
|
|
33593
|
+
// Say it on every emission. A reader who does not know this reads a
|
|
33594
|
+
// wall-time ranking as a token ranking.
|
|
33595
|
+
tokens_note:
|
|
33596
|
+
"Claude Code records no token usage for a dispatched subagent, so a Claude agent's tokens are null and never 0. Rows are ranked by MEASURED WALL TIME. Only `orc extra` foreign workers report real token vectors.",
|
|
33597
|
+
};
|
|
33598
|
+
if (asJson) emitJson(out, code);
|
|
33599
|
+
|
|
33600
|
+
console.log(ui.header("ORC · usage"));
|
|
33601
|
+
console.log("");
|
|
33602
|
+
const pctLine = (w) =>
|
|
33603
|
+
!w
|
|
33604
|
+
? ui.color.gray(" — (no reading)")
|
|
33605
|
+
: " " +
|
|
33606
|
+
String(w.used_percentage).padStart(3) +
|
|
33607
|
+
"% used · " +
|
|
33608
|
+
w.remaining_percentage +
|
|
33609
|
+
"% left" +
|
|
33610
|
+
(w.resets_in_minutes != null ? " · resets in " + w.resets_in_minutes + "m" : "") +
|
|
33611
|
+
(w.low ? " " + ui.color.yellow("LOW") : "");
|
|
33612
|
+
console.log(" 5 hours");
|
|
33613
|
+
console.log(pctLine(fh));
|
|
33614
|
+
console.log(" 7 days");
|
|
33615
|
+
console.log(pctLine(sd));
|
|
33616
|
+
if (out.context_used_percentage != null)
|
|
33617
|
+
console.log(" context " + out.context_used_percentage + "% of the window");
|
|
33618
|
+
if (stale)
|
|
33619
|
+
console.log(
|
|
33620
|
+
ui.color.gray(
|
|
33621
|
+
" reading " +
|
|
33622
|
+
(ageMin == null ? "none" : ageMin + "m old") +
|
|
33623
|
+
" — treated as unknown, and a run is never stopped on it"
|
|
33624
|
+
)
|
|
33625
|
+
);
|
|
33626
|
+
console.log("");
|
|
33627
|
+
if (session && session.five_hour) {
|
|
33628
|
+
const c = session.five_hour;
|
|
33629
|
+
console.log(
|
|
33630
|
+
" " +
|
|
33631
|
+
ui.color.bold(
|
|
33632
|
+
"This session has consumed " +
|
|
33633
|
+
c.consumed_percentage +
|
|
33634
|
+
"% of the 5-hour window and is still counting"
|
|
33635
|
+
)
|
|
33636
|
+
);
|
|
33637
|
+
console.log(
|
|
33638
|
+
ui.color.gray(
|
|
33639
|
+
" " +
|
|
33640
|
+
c.baseline_percentage +
|
|
33641
|
+
"% → " +
|
|
33642
|
+
c.now_percentage +
|
|
33643
|
+
"%" +
|
|
33644
|
+
(c.window_resets ? " across " + c.window_resets + " window reset(s)" : "") +
|
|
33645
|
+
(session.running_minutes != null ? " · " + session.running_minutes + "m in session" : "")
|
|
33646
|
+
)
|
|
33647
|
+
);
|
|
33648
|
+
console.log(ui.color.gray(" " + session.caveat));
|
|
33649
|
+
} else {
|
|
33650
|
+
console.log(ui.color.gray(" session consumption: no reading yet (the statusline writes it)"));
|
|
33651
|
+
}
|
|
33652
|
+
console.log("");
|
|
33653
|
+
if (top.length) {
|
|
33654
|
+
console.log(" " + ui.color.bold("Top " + USAGE_TOP_N + " by measured wall time"));
|
|
33655
|
+
for (const t of top.slice(0, USAGE_TOP_N)) {
|
|
33656
|
+
const secs = t.wall_seconds;
|
|
33657
|
+
const w =
|
|
33658
|
+
secs == null
|
|
33659
|
+
? "—"
|
|
33660
|
+
: Math.floor(secs / 60) + "m" + String(secs % 60).padStart(2, "0") + "s";
|
|
33661
|
+
const tok = t.tokens ? "tokens measured" : ui.color.gray("tokens —");
|
|
33662
|
+
console.log(
|
|
33663
|
+
" " +
|
|
33664
|
+
w.padStart(8) +
|
|
33665
|
+
" " +
|
|
33666
|
+
t.agent +
|
|
33667
|
+
" " +
|
|
33668
|
+
ui.color.gray("x" + t.dispatches) +
|
|
33669
|
+
(t.running ? " " + ui.color.yellow(t.running + " RUNNING") : "") +
|
|
33670
|
+
" " +
|
|
33671
|
+
tok
|
|
33672
|
+
);
|
|
33673
|
+
}
|
|
33674
|
+
console.log(
|
|
33675
|
+
ui.color.gray(" wall time, not tokens — Claude Code does not record a subagent's tokens")
|
|
33676
|
+
);
|
|
33677
|
+
} else {
|
|
33678
|
+
console.log(ui.color.gray(" no dispatches in the open run (or no run is open)"));
|
|
33679
|
+
}
|
|
33680
|
+
process.exit(code);
|
|
33681
|
+
}
|
|
33682
|
+
|
|
33213
33683
|
function usageCheckCmd(claudeDir) {
|
|
33214
33684
|
const asJson = wantsJson();
|
|
33215
33685
|
const now = Date.now();
|
|
@@ -33641,8 +34111,9 @@ function jsonCrash(err) {
|
|
|
33641
34111
|
// v1.1.0 W4 — the ONE reader of the statusline's usage bridge.
|
|
33642
34112
|
case "usage":
|
|
33643
34113
|
if (positionals()[1] === "check") usageCheckCmd(resolveClaudeDir());
|
|
34114
|
+
else if (positionals()[1] === "report") usageReportCmd(resolveClaudeDir());
|
|
33644
34115
|
else {
|
|
33645
|
-
console.error("usage: orc usage check [--json]");
|
|
34116
|
+
console.error("usage: orc usage check|report [--json]");
|
|
33646
34117
|
process.exit(1);
|
|
33647
34118
|
}
|
|
33648
34119
|
break;
|
package/bin/verify-contracts.js
CHANGED
|
@@ -384,6 +384,43 @@ const CONTRACTS = [
|
|
|
384
384
|
binFiles: ["bin/cli.js"],
|
|
385
385
|
files: ["skills/_shared/extra-dispatch.md"],
|
|
386
386
|
},
|
|
387
|
+
{
|
|
388
|
+
// v1.2.0. A Task error does not kill the subagent behind it. Every lane's
|
|
389
|
+
// retry rule assumed it did, and one graded /orc-quick entry put THREE
|
|
390
|
+
// opus-5-low executors on one task for 266 minutes combined. The guard is
|
|
391
|
+
// only real if every lane that re-dispatches carries the same sentence and
|
|
392
|
+
// the CLI keeps the command it names.
|
|
393
|
+
name: "a re-dispatch is refused over a live attempt (v1.2.0)",
|
|
394
|
+
token: "a lane that re-dispatches over a live attempt",
|
|
395
|
+
binFiles: ["bin/cli.js"],
|
|
396
|
+
files: [
|
|
397
|
+
"skills/_shared/return-validation.md",
|
|
398
|
+
"skills/_shared/phases/execution.md",
|
|
399
|
+
"skills/orc/SKILL.md",
|
|
400
|
+
"skills/orc-doc/SKILL.md",
|
|
401
|
+
"skills/orc-wiki/SKILL.md",
|
|
402
|
+
"skills/orc-quick/SKILL.md",
|
|
403
|
+
"skills/orc-mini/SKILL.md",
|
|
404
|
+
"skills/orc-fast/SKILL.md",
|
|
405
|
+
],
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
// The command the contract above names. A rename on either side silently
|
|
409
|
+
// turns the guard into prose nobody can run.
|
|
410
|
+
name: "the in-flight read is ONE command (v1.2.0)",
|
|
411
|
+
token: "orc run inflight",
|
|
412
|
+
binFiles: ["bin/cli.js"],
|
|
413
|
+
files: [
|
|
414
|
+
"skills/_shared/return-validation.md",
|
|
415
|
+
"skills/_shared/phases/execution.md",
|
|
416
|
+
"skills/orc/SKILL.md",
|
|
417
|
+
"skills/orc-doc/SKILL.md",
|
|
418
|
+
"skills/orc-wiki/SKILL.md",
|
|
419
|
+
"skills/orc-quick/SKILL.md",
|
|
420
|
+
"skills/orc-mini/SKILL.md",
|
|
421
|
+
"skills/orc-fast/SKILL.md",
|
|
422
|
+
],
|
|
423
|
+
},
|
|
387
424
|
{
|
|
388
425
|
// The field a resumed return owes. Absent on a resume slice is MALFORMED,
|
|
389
426
|
// and `restarted` on a non-empty preexisting[] is a finding — both are
|
|
@@ -3343,7 +3380,7 @@ const BUDGETS = [
|
|
|
3343
3380
|
// 4 of 31 lanes. The shrinking in this release happens by moving prose to
|
|
3344
3381
|
// `_shared/phases/` (W11–W13) or to a lane's own `references/phases/`, which
|
|
3345
3382
|
// is what orc-wiki did this wave: 352 → 172.
|
|
3346
|
-
{ file: "skills/orc-mini/SKILL.md", maxLines:
|
|
3383
|
+
{ file: "skills/orc-mini/SKILL.md", maxLines: 253 },
|
|
3347
3384
|
// v0.39.0: deliberate raises 195→201 / 179→182 — the analyst gains hard rules
|
|
3348
3385
|
// 2b (a source it did not author is FOREIGN input) and 4a (the read ladder);
|
|
3349
3386
|
// fast gains the ladder as a slice line. Both are hard rules by nature: they
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-id/orc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "ORC — an orchestrator skill constellation for Claude Code: intake, planning, scored parallel subagents, code-pattern matching, review, verify, ship, plus a project knowledge-base wiki.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"orc": "bin/cli.js"
|