halfcycle 0.7.1 → 0.8.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.
@@ -5,14 +5,10 @@ var __export = (target, all) => {
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
- // dist/guard-origin.js
9
- var DEFAULT_GUARD_ORIGIN = "https://guard.halfcycle.ai";
10
- function resolveGuardOrigin(env) {
11
- const configured = env["GUARD_SERVICE_URL"]?.trim();
12
- if (configured)
13
- return { origin: configured.replace(/\/+$/, ""), source: "environment" };
14
- return { origin: DEFAULT_GUARD_ORIGIN, source: "default" };
15
- }
8
+ // dist/request-shapes.js
9
+ import { appendFileSync, existsSync } from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import { join } from "node:path";
16
12
 
17
13
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
18
14
  var external_exports = {};
@@ -3052,13 +3048,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
3052
3048
  }
3053
3049
  return propValues;
3054
3050
  });
3055
- const isObject2 = isObject;
3051
+ const isObject3 = isObject;
3056
3052
  const catchall = def.catchall;
3057
3053
  let value;
3058
3054
  inst._zod.parse = (payload, ctx) => {
3059
3055
  value ?? (value = _normalized.value);
3060
3056
  const input = payload.value;
3061
- if (!isObject2(input)) {
3057
+ if (!isObject3(input)) {
3062
3058
  payload.issues.push({
3063
3059
  expected: "object",
3064
3060
  code: "invalid_type",
@@ -3185,7 +3181,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
3185
3181
  return (payload, ctx) => fn(shape, payload, ctx);
3186
3182
  };
3187
3183
  let fastpass;
3188
- const isObject2 = isObject;
3184
+ const isObject3 = isObject;
3189
3185
  const jit = !globalConfig.jitless;
3190
3186
  const allowsEval2 = allowsEval;
3191
3187
  const fastEnabled = jit && allowsEval2.value;
@@ -3194,7 +3190,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
3194
3190
  inst._zod.parse = (payload, ctx) => {
3195
3191
  value ?? (value = _normalized.value);
3196
3192
  const input = payload.value;
3197
- if (!isObject2(input)) {
3193
+ if (!isObject3(input)) {
3198
3194
  payload.issues.push({
3199
3195
  expected: "object",
3200
3196
  code: "invalid_type",
@@ -15050,6 +15046,7 @@ var SIGN_IN_COMMAND = '"npx halfcycle"';
15050
15046
  var HOOK_REMEDY = `Run ${SIGN_IN_COMMAND} in the project folder to sign in.`;
15051
15047
 
15052
15048
  // ../core/dist/task-plan.js
15049
+ var TASK_PLAN_FILE = "docs/build-records/task-plan.json";
15053
15050
  var AGENT_NAME_MAX = 32;
15054
15051
  var TASK_PLAN_TASKS_MAX = 500;
15055
15052
  var TASK_PLAN_ID_MAX = 64;
@@ -15262,6 +15259,262 @@ var WORKER_REPORT_LINES = Object.freeze({
15262
15259
  clear: () => "The report is complete and names the commit it is about."
15263
15260
  });
15264
15261
 
15262
+ // ../core/dist/request-shape.js
15263
+ var REQUEST_SHAPES_DIR_NAME = "debug";
15264
+ var REQUEST_SHAPES_SWITCH_FILENAME = "request-shapes.on";
15265
+ var REQUEST_SHAPES_LOG_FILENAME = "request-shapes.jsonl";
15266
+ var REQUEST_SHAPE_MAX_DEPTH = 8;
15267
+ var REQUEST_SHAPE_MAX_ARRAY_SHAPES = 10;
15268
+ var REQUEST_SHAPE_MAX_KEY_LENGTH = 64;
15269
+ var REQUEST_SHAPE_MAX_LINE_BYTES = 16 * 1024;
15270
+ var KEY_NAME = /^[A-Za-z][A-Za-z0-9_]*$/;
15271
+ var PATH_WORD = /^[a-z-]+$/;
15272
+ function byteLength(text) {
15273
+ return new TextEncoder().encode(text).length;
15274
+ }
15275
+ function keyOf(key) {
15276
+ return key.length <= REQUEST_SHAPE_MAX_KEY_LENGTH && KEY_NAME.test(key) ? key : "<key>";
15277
+ }
15278
+ function shapeOfValue(value, depth) {
15279
+ if (value === null)
15280
+ return "null";
15281
+ if (typeof value === "string")
15282
+ return "string";
15283
+ if (typeof value === "number")
15284
+ return "number";
15285
+ if (typeof value === "boolean")
15286
+ return "boolean";
15287
+ if (depth > REQUEST_SHAPE_MAX_DEPTH)
15288
+ return "\u2026";
15289
+ if (Array.isArray(value)) {
15290
+ const seen = [];
15291
+ for (const element of value) {
15292
+ if (seen.length >= REQUEST_SHAPE_MAX_ARRAY_SHAPES)
15293
+ break;
15294
+ const shape = shapeOfValue(element, depth + 1);
15295
+ if (!seen.includes(shape))
15296
+ seen.push(shape);
15297
+ }
15298
+ return `[${seen.join("|")}]`;
15299
+ }
15300
+ if (typeof value === "object") {
15301
+ const entries = Object.entries(value);
15302
+ if (entries.length === 0)
15303
+ return "{}";
15304
+ return `{ ${entries.map(([k, v]) => `${keyOf(k)}: ${shapeOfValue(v, depth + 1)}`).join(", ")} }`;
15305
+ }
15306
+ return "null";
15307
+ }
15308
+ function requestShapeOf(body) {
15309
+ let parsed;
15310
+ try {
15311
+ parsed = JSON.parse(body);
15312
+ } catch {
15313
+ return `text ${byteLength(body)} bytes`;
15314
+ }
15315
+ return shapeOfValue(parsed, 1);
15316
+ }
15317
+ function requestPathShapeOf(pathname) {
15318
+ return pathname.split("/").map((segment) => segment === "" || PATH_WORD.test(segment) ? segment : ":x").join("/");
15319
+ }
15320
+ function distinct(names) {
15321
+ const out = [];
15322
+ for (const name of names)
15323
+ if (!out.includes(name))
15324
+ out.push(name);
15325
+ return out;
15326
+ }
15327
+ function isObject2(value) {
15328
+ return typeof value === "object" && value !== null;
15329
+ }
15330
+ function isRequestLike(value) {
15331
+ return isObject2(value) && !(value instanceof URL) && typeof value["url"] === "string" && typeof value["method"] === "string";
15332
+ }
15333
+ function headerNamesOf(headers) {
15334
+ if (!isObject2(headers))
15335
+ return [];
15336
+ const names = [];
15337
+ if (Array.isArray(headers)) {
15338
+ for (const pair of headers)
15339
+ if (Array.isArray(pair) && typeof pair[0] === "string")
15340
+ names.push(pair[0].toLowerCase());
15341
+ return names;
15342
+ }
15343
+ const forEach = headers["forEach"];
15344
+ if (typeof forEach === "function") {
15345
+ forEach.call(headers, (_value, key) => {
15346
+ if (typeof key === "string")
15347
+ names.push(key.toLowerCase());
15348
+ });
15349
+ return names;
15350
+ }
15351
+ return Object.keys(headers).map((k) => k.toLowerCase());
15352
+ }
15353
+ function bytesShape(bytes) {
15354
+ let text;
15355
+ try {
15356
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
15357
+ } catch {
15358
+ return `text ${bytes.byteLength} bytes`;
15359
+ }
15360
+ return requestShapeOf(text);
15361
+ }
15362
+ function bodyShapeOf(body) {
15363
+ if (body === void 0 || body === null)
15364
+ return null;
15365
+ if (typeof body === "string")
15366
+ return requestShapeOf(body);
15367
+ if (body instanceof URLSearchParams)
15368
+ return `text ${byteLength(body.toString())} bytes`;
15369
+ if (body instanceof ArrayBuffer)
15370
+ return bytesShape(new Uint8Array(body));
15371
+ if (ArrayBuffer.isView(body))
15372
+ return bytesShape(new Uint8Array(body.buffer, body.byteOffset, body.byteLength));
15373
+ if (isObject2(body) && typeof body["size"] === "number" && typeof body["arrayBuffer"] === "function") {
15374
+ return `text ${body["size"]} bytes`;
15375
+ }
15376
+ return "stream";
15377
+ }
15378
+ function requestLineOf(input, init) {
15379
+ const request = isRequestLike(input) ? input : void 0;
15380
+ const address = request !== void 0 ? request.url : input instanceof URL ? input.href : String(input);
15381
+ let url2;
15382
+ try {
15383
+ url2 = new URL(address);
15384
+ } catch {
15385
+ url2 = void 0;
15386
+ }
15387
+ const options = isObject2(init) ? init : {};
15388
+ const method = (typeof options["method"] === "string" ? options["method"] : request?.method ?? "GET").toUpperCase();
15389
+ const headers = distinct([
15390
+ ...request !== void 0 ? headerNamesOf(request.headers) : [],
15391
+ ...headerNamesOf(options["headers"])
15392
+ ]);
15393
+ const shape = options["body"] !== void 0 && options["body"] !== null ? bodyShapeOf(options["body"]) : request !== void 0 && request.body !== null && request.body !== void 0 ? "stream" : null;
15394
+ return {
15395
+ method,
15396
+ host: url2?.host ?? "",
15397
+ path: url2 !== void 0 ? requestPathShapeOf(url2.pathname) : ":x",
15398
+ query: url2 !== void 0 ? distinct([...url2.searchParams.keys()]) : [],
15399
+ headers,
15400
+ shape
15401
+ };
15402
+ }
15403
+ function requestShapeLineText(line2) {
15404
+ const ordered = (shape) => JSON.stringify({
15405
+ at: line2.at,
15406
+ process: line2.process,
15407
+ method: line2.method,
15408
+ host: line2.host,
15409
+ path: line2.path,
15410
+ query: line2.query,
15411
+ headers: line2.headers,
15412
+ shape,
15413
+ status: line2.status
15414
+ });
15415
+ const whole = ordered(line2.shape);
15416
+ if (byteLength(whole) <= REQUEST_SHAPE_MAX_LINE_BYTES || line2.shape === null)
15417
+ return whole;
15418
+ const room = REQUEST_SHAPE_MAX_LINE_BYTES - byteLength(ordered("")) - byteLength("\u2026");
15419
+ if (room <= 0)
15420
+ return ordered("\u2026");
15421
+ let end = Math.min(line2.shape.length, room);
15422
+ for (let over = byteLength(line2.shape.slice(0, end)) - room; over > 0; over = byteLength(line2.shape.slice(0, end)) - room) {
15423
+ end -= over;
15424
+ }
15425
+ const prefix = line2.shape.slice(0, Math.max(0, end));
15426
+ const cut = Math.max(prefix.lastIndexOf(", "), prefix.lastIndexOf("|"));
15427
+ return ordered(`${cut > 0 ? prefix.slice(0, cut) : ""}\u2026`);
15428
+ }
15429
+
15430
+ // dist/request-shapes.js
15431
+ var RUNNER_VERB_NAMES = [
15432
+ "ci",
15433
+ "refresh-credential",
15434
+ "observe",
15435
+ "time",
15436
+ "prove",
15437
+ "pace",
15438
+ "statusline",
15439
+ "check-connection",
15440
+ "mcp-headers",
15441
+ "resolve-engagement",
15442
+ "capture"
15443
+ ];
15444
+ var RUNNER_HOOK_NAMES = [
15445
+ "post-tool-use",
15446
+ "stop",
15447
+ "subagent-stop",
15448
+ "pre-tool-use",
15449
+ "user-prompt-submit",
15450
+ "user-prompt-findings"
15451
+ ];
15452
+ var folder = join(homedir(), HALFCYCLE_DIR_NAME, REQUEST_SHAPES_DIR_NAME);
15453
+ var logPath = join(folder, REQUEST_SHAPES_LOG_FILENAME);
15454
+ var switchedOn = existsSync(join(folder, REQUEST_SHAPES_SWITCH_FILENAME));
15455
+ function processName() {
15456
+ const [verb, hook] = process.argv.slice(2);
15457
+ if (verb === "hook" && hook !== void 0 && RUNNER_HOOK_NAMES.includes(hook))
15458
+ return `runner hook ${hook}`;
15459
+ if (verb !== void 0 && verb !== "hook" && RUNNER_VERB_NAMES.includes(verb))
15460
+ return `runner ${verb}`;
15461
+ return "runner other";
15462
+ }
15463
+ function write(call, status) {
15464
+ if (call === void 0)
15465
+ return;
15466
+ try {
15467
+ const text = requestShapeLineText({ at: (/* @__PURE__ */ new Date()).toISOString(), process: processName(), ...call, status });
15468
+ appendFileSync(logPath, `${text}
15469
+ `, { mode: 384 });
15470
+ } catch {
15471
+ }
15472
+ }
15473
+ function describe3(args2) {
15474
+ try {
15475
+ return requestLineOf(args2[0], args2[1]);
15476
+ } catch {
15477
+ return void 0;
15478
+ }
15479
+ }
15480
+ function recordChildRequest(address, headerNames, exitedOk) {
15481
+ if (!switchedOn)
15482
+ return;
15483
+ const call = describe3([address, { method: "GET", headers: headerNames.map((name) => [name, ""]) }]);
15484
+ write(call, exitedOk ? "child-ok" : "child-failed");
15485
+ }
15486
+ if (switchedOn) {
15487
+ const original = globalThis.fetch;
15488
+ if (typeof original === "function") {
15489
+ globalThis.fetch = function recordedFetch(...args2) {
15490
+ const call = describe3(args2);
15491
+ let answer;
15492
+ try {
15493
+ answer = original.apply(this, args2);
15494
+ } catch (err) {
15495
+ write(call, "unreachable");
15496
+ throw err;
15497
+ }
15498
+ return answer.then((res) => {
15499
+ write(call, res.status);
15500
+ return res;
15501
+ }, (err) => {
15502
+ write(call, "unreachable");
15503
+ throw err;
15504
+ });
15505
+ };
15506
+ }
15507
+ }
15508
+
15509
+ // dist/guard-origin.js
15510
+ var DEFAULT_GUARD_ORIGIN = "https://guard.halfcycle.ai";
15511
+ function resolveGuardOrigin(env) {
15512
+ const configured = env["GUARD_SERVICE_URL"]?.trim();
15513
+ if (configured)
15514
+ return { origin: configured.replace(/\/+$/, ""), source: "environment" };
15515
+ return { origin: DEFAULT_GUARD_ORIGIN, source: "default" };
15516
+ }
15517
+
15265
15518
  // dist/control-origin.js
15266
15519
  var DEFAULT_CONTROL_ORIGIN = "https://control.halfcycle.ai";
15267
15520
  function resolveControlOrigin(env) {
@@ -18842,6 +19095,10 @@ var limitHeldSchema = external_exports.object({
18842
19095
  raiseBy: external_exports.number().int().nonnegative(),
18843
19096
  working: external_exports.number().int().nonnegative()
18844
19097
  }).strict();
19098
+ var paceUsageSchema = external_exports.object({
19099
+ used: external_exports.number().int().nonnegative(),
19100
+ limit: external_exports.number().int().positive()
19101
+ }).strict();
18845
19102
  var paceRunResultSchema = external_exports.discriminatedUnion("outcome", [
18846
19103
  external_exports.object({
18847
19104
  outcome: external_exports.literal("paced"),
@@ -18853,7 +19110,8 @@ var paceRunResultSchema = external_exports.discriminatedUnion("outcome", [
18853
19110
  keepFreePercent: keepFreePercentSchema.optional(),
18854
19111
  suggestion: paceSuggestionSchema.optional(),
18855
19112
  limit: external_exports.literal("reached").optional(),
18856
- limitHeld: limitHeldSchema.optional()
19113
+ limitHeld: limitHeldSchema.optional(),
19114
+ usage: paceUsageSchema.optional()
18857
19115
  }).strict(),
18858
19116
  external_exports.object({ outcome: external_exports.literal("not-paced"), reason: external_exports.enum(PACE_NOT_PACED_REASONS) }).strict()
18859
19117
  ]);
@@ -19232,6 +19490,7 @@ var relayedChoicesFileSchema = external_exports.object({
19232
19490
  }).strict();
19233
19491
 
19234
19492
  // ../events/dist/tier-figures.js
19493
+ var ROUTING_FILENAME = "routing.json";
19235
19494
  var routingRecordSchema = external_exports.object({
19236
19495
  phase: external_exports.string().min(1).max(200),
19237
19496
  cheapStarted: external_exports.array(external_exports.string().min(1).max(200)).max(2e3)
@@ -19248,6 +19507,17 @@ var phaseModelsFileSchema = external_exports.object({
19248
19507
  tasks: external_exports.record(external_exports.string().min(1).max(200), external_exports.record(external_exports.string().min(1).max(PHASE_MODEL_NAME_MAX), external_exports.number().int().min(0)))
19249
19508
  }).strict();
19250
19509
 
19510
+ // ../events/dist/checks-report-words.js
19511
+ var CHECKS_NO_REPORT_YET_STATE = "Set up, no report yet";
19512
+ var CHECKS_GONE_QUIET = "Halfcycle's checks have not reported from this machine lately.";
19513
+ function checksReportOfInstructions(text) {
19514
+ if (text.startsWith(CHECKS_NO_REPORT_YET_STATE))
19515
+ return "no-report-yet";
19516
+ if (text.startsWith(CHECKS_GONE_QUIET))
19517
+ return "gone-quiet";
19518
+ return "none";
19519
+ }
19520
+
19251
19521
  // dist/hook-protocol.js
19252
19522
  import { writeSync } from "node:fs";
19253
19523
 
@@ -19784,7 +20054,7 @@ async function attempt(url2, token, request, fetchFn, retry) {
19784
20054
 
19785
20055
  // dist/git.js
19786
20056
  import { spawnSync } from "node:child_process";
19787
- import { existsSync, readFileSync } from "node:fs";
20057
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
19788
20058
  import { relative } from "node:path";
19789
20059
  var COLOR_AWARE_SUBCOMMANDS = /* @__PURE__ */ new Set(["diff", "show", "log"]);
19790
20060
  function spawnGit(args2, cwd, timeoutMs, extra) {
@@ -19801,7 +20071,7 @@ function spawnGit(args2, cwd, timeoutMs, extra) {
19801
20071
  function git(args2, cwd, timeoutMs, extra) {
19802
20072
  const result = spawnGit(args2, cwd, timeoutMs, extra);
19803
20073
  if (result.error) {
19804
- const missing = result.error.code === "ENOENT" && existsSync(cwd);
20074
+ const missing = result.error.code === "ENOENT" && existsSync2(cwd);
19805
20075
  return { ok: false, message: `git error: ${result.error.message}`, ...missing ? { gitMissing: true } : {} };
19806
20076
  }
19807
20077
  if (result.status !== 0) {
@@ -20315,14 +20585,14 @@ function stripComments(code) {
20315
20585
  function extractSqlWrites(diffContent) {
20316
20586
  const writes = [];
20317
20587
  const seen = /* @__PURE__ */ new Set();
20318
- const push = (write) => {
20319
- if (!write)
20588
+ const push = (write2) => {
20589
+ if (!write2)
20320
20590
  return;
20321
- const key = `${write.kind}:${write.table}`;
20591
+ const key = `${write2.kind}:${write2.table}`;
20322
20592
  if (seen.has(key))
20323
20593
  return;
20324
20594
  seen.add(key);
20325
- writes.push(write);
20595
+ writes.push(write2);
20326
20596
  };
20327
20597
  for (const line2 of diffContent.split("\n")) {
20328
20598
  if (isRemovedLine(line2) || isContextLine(line2) || isDiffHeader(line2))
@@ -20633,7 +20903,7 @@ function detectBlastRadiusMarkers(filePath, diffContent) {
20633
20903
 
20634
20904
  // dist/extract/numeric-columns.js
20635
20905
  import { readFileSync as readFileSync2, readdirSync } from "node:fs";
20636
- import { join } from "node:path";
20906
+ import { join as join2 } from "node:path";
20637
20907
  function extractNumericColumns(diffContent, context = {}) {
20638
20908
  const addedLines = [];
20639
20909
  for (const line2 of diffContent.split("\n")) {
@@ -20700,12 +20970,12 @@ function walkForRegistrations(dir, repoRoot, acc) {
20700
20970
  if (entry.isDirectory()) {
20701
20971
  if (EXCLUDED_DIRS.has(entry.name))
20702
20972
  continue;
20703
- walkForRegistrations(join(dir, entry.name), repoRoot, acc);
20973
+ walkForRegistrations(join2(dir, entry.name), repoRoot, acc);
20704
20974
  continue;
20705
20975
  }
20706
20976
  if (!entry.isFile())
20707
20977
  continue;
20708
- const absPath = join(dir, entry.name);
20978
+ const absPath = join2(dir, entry.name);
20709
20979
  if (!isCodeFile(absPath))
20710
20980
  continue;
20711
20981
  let content;
@@ -20847,13 +21117,13 @@ function resolveWireFields(lines2, decls) {
20847
21117
  }
20848
21118
 
20849
21119
  // dist/fixtures-manifest.js
20850
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
20851
- import { dirname, join as join2 } from "node:path";
21120
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
21121
+ import { dirname, join as join3 } from "node:path";
20852
21122
  function manifestPath(repoRoot) {
20853
- return join2(repoRoot, "test", "fixtures", "captured", "manifest.json");
21123
+ return join3(repoRoot, "test", "fixtures", "captured", "manifest.json");
20854
21124
  }
20855
21125
  function fixturePath(repoRoot, provider, slug) {
20856
- return join2(repoRoot, "test", "fixtures", "captured", provider, `${slug}.json`);
21126
+ return join3(repoRoot, "test", "fixtures", "captured", provider, `${slug}.json`);
20857
21127
  }
20858
21128
  function loadManifest(repoRoot) {
20859
21129
  if (!repoRoot)
@@ -20886,7 +21156,7 @@ function upsertManifest(repoRoot, entry) {
20886
21156
  }
20887
21157
  function removeFixtureIfExists(path) {
20888
21158
  try {
20889
- if (existsSync2(path))
21159
+ if (existsSync3(path))
20890
21160
  unlinkSync(path);
20891
21161
  } catch {
20892
21162
  }
@@ -21134,7 +21404,7 @@ function toAstShapes(markers) {
21134
21404
 
21135
21405
  // dist/extract/context.js
21136
21406
  import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
21137
- import { join as join3, relative as relative2, sep } from "node:path";
21407
+ import { join as join4, relative as relative2, sep } from "node:path";
21138
21408
  var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set([
21139
21409
  "node_modules",
21140
21410
  ".git",
@@ -21168,7 +21438,7 @@ function walk(dir, repoRoot, acc) {
21168
21438
  if (entry.isDirectory()) {
21169
21439
  if (EXCLUDED_DIRS2.has(entry.name))
21170
21440
  continue;
21171
- walk(join3(dir, entry.name), repoRoot, acc);
21441
+ walk(join4(dir, entry.name), repoRoot, acc);
21172
21442
  continue;
21173
21443
  }
21174
21444
  if (!entry.isFile())
@@ -21176,7 +21446,7 @@ function walk(dir, repoRoot, acc) {
21176
21446
  const kind = classifyFile(entry.name);
21177
21447
  if (!kind)
21178
21448
  continue;
21179
- const absPath = join3(dir, entry.name);
21449
+ const absPath = join4(dir, entry.name);
21180
21450
  let content;
21181
21451
  try {
21182
21452
  content = readFileSync4(absPath, "utf-8");
@@ -21199,9 +21469,9 @@ function loadChangeSetContext(repoRoot) {
21199
21469
  }
21200
21470
 
21201
21471
  // dist/telemetry/emit.js
21202
- import { appendFileSync, mkdirSync as mkdirSync2 } from "node:fs";
21203
- import { join as join4, dirname as dirname2 } from "node:path";
21204
- import { homedir } from "node:os";
21472
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
21473
+ import { join as join5, dirname as dirname2 } from "node:path";
21474
+ import { homedir as homedir2 } from "node:os";
21205
21475
  import { randomUUID } from "node:crypto";
21206
21476
  var TELEMETRY_TIMEOUT_MS = 5e3;
21207
21477
  async function emitTelemetry(run, config2, logger, fetchFn = globalThis.fetch) {
@@ -21211,23 +21481,23 @@ async function emitTelemetry(run, config2, logger, fetchFn = globalThis.fetch) {
21211
21481
  }
21212
21482
  }
21213
21483
  function localLogPath(run, config2) {
21214
- const logDir = config2.guardEvalLogDir ?? join4(homedir(), ".halfcycle", "guard-eval-log");
21484
+ const logDir = config2.guardEvalLogDir ?? join5(homedir2(), ".halfcycle", "guard-eval-log");
21215
21485
  const phase = run.phase;
21216
21486
  const fileName = phase === void 0 || phase === "" ? "unphased.jsonl" : `${renderPhaseSegment(phase)}.jsonl`;
21217
- return join4(logDir, fileName);
21487
+ return join5(logDir, fileName);
21218
21488
  }
21219
21489
  function appendToLocalLog(run, config2, logger) {
21220
- let logPath;
21490
+ let logPath2;
21221
21491
  try {
21222
- logPath = localLogPath(run, config2);
21492
+ logPath2 = localLogPath(run, config2);
21223
21493
  } catch (err) {
21224
21494
  const msg = err instanceof Error ? err.message : String(err);
21225
21495
  logger.warn(`[Halfcycle telemetry] Phase name refused, nothing written (dropped): ${msg}`);
21226
21496
  return;
21227
21497
  }
21228
21498
  try {
21229
- mkdirSync2(dirname2(logPath), { recursive: true });
21230
- appendFileSync(logPath, JSON.stringify(run) + "\n", "utf-8");
21499
+ mkdirSync2(dirname2(logPath2), { recursive: true });
21500
+ appendFileSync2(logPath2, JSON.stringify(run) + "\n", "utf-8");
21231
21501
  } catch (err) {
21232
21502
  const msg = err instanceof Error ? err.message : String(err);
21233
21503
  logger.warn(`[Halfcycle telemetry] Local guard-eval log write failed (dropped): ${msg}`);
@@ -21536,21 +21806,21 @@ function projectFolder(env = process.env) {
21536
21806
  }
21537
21807
 
21538
21808
  // dist/credential-store.js
21539
- import { chmodSync, existsSync as existsSync3, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
21540
- import { homedir as homedir2, platform } from "node:os";
21541
- import { dirname as dirname3, join as join5 } from "node:path";
21809
+ import { chmodSync, existsSync as existsSync4, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
21810
+ import { homedir as homedir3, platform } from "node:os";
21811
+ import { dirname as dirname3, join as join6 } from "node:path";
21542
21812
  var REFRESHED_TOKEN_KEYS = ["HALFCYCLE_TOKEN", "GUARD_SERVICE_TOKEN"];
21543
21813
  var TOKEN_READ_KEY = "GUARD_SERVICE_TOKEN";
21544
21814
  function composeEngagementEnvPath(engagementId, home) {
21545
- return join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ENGAGEMENT_ENV_FILENAME);
21815
+ return join6(home ?? homedir3(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ENGAGEMENT_ENV_FILENAME);
21546
21816
  }
21547
21817
  function hasNotThisAccountMarker(engagementId, home) {
21548
- return existsSync3(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, NOT_THIS_ACCOUNT_MARKER_FILENAME));
21818
+ return existsSync4(join6(home ?? homedir3(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, NOT_THIS_ACCOUNT_MARKER_FILENAME));
21549
21819
  }
21550
21820
  function readAccountCredential(controlOrigin, home) {
21551
21821
  let raw;
21552
21822
  try {
21553
- raw = readFileSync6(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ACCOUNT_STORE_FILENAME), "utf-8");
21823
+ raw = readFileSync6(join6(home ?? homedir3(), HALFCYCLE_DIR_NAME, ACCOUNT_STORE_FILENAME), "utf-8");
21554
21824
  } catch {
21555
21825
  return void 0;
21556
21826
  }
@@ -21602,7 +21872,7 @@ function writeRefreshedToken(opts) {
21602
21872
  existing = null;
21603
21873
  }
21604
21874
  const body = reconcileEnvText(existing, { HALFCYCLE_TOKEN: opts.token, GUARD_SERVICE_TOKEN: opts.token }, REFRESHED_TOKEN_KEYS);
21605
- const temp = join5(dirname3(declaredReal), `.${ENGAGEMENT_ENV_FILENAME}.${process.pid}.tmp`);
21875
+ const temp = join6(dirname3(declaredReal), `.${ENGAGEMENT_ENV_FILENAME}.${process.pid}.tmp`);
21606
21876
  try {
21607
21877
  writeFileSync2(temp, body, { mode: 384 });
21608
21878
  if (platform() !== "win32")
@@ -21703,15 +21973,15 @@ async function refreshEngagementCredential(opts) {
21703
21973
  const minted = await mintThroughJoin(controlOrigin, opts.engagementId, accountCredential, opts.fetchFn ?? globalThis.fetch);
21704
21974
  if (!minted.ok)
21705
21975
  return minted;
21706
- const write = writeRefreshedToken({
21976
+ const write2 = writeRefreshedToken({
21707
21977
  envFilePath,
21708
21978
  engagementId: opts.engagementId,
21709
21979
  token: minted.token,
21710
21980
  home: opts.home
21711
21981
  });
21712
- if (!write.written) {
21982
+ if (!write2.written) {
21713
21983
  const warn = opts.warn ?? ((message) => process.stderr.write(message));
21714
- warn(`[Halfcycle] This engagement's credential was renewed for this run only \u2014 ${write.reason}.
21984
+ warn(`[Halfcycle] This engagement's credential was renewed for this run only \u2014 ${write2.reason}.
21715
21985
  `);
21716
21986
  }
21717
21987
  return minted;
@@ -21740,9 +22010,9 @@ async function evaluateWithRefresh(opts) {
21740
22010
 
21741
22011
  // dist/hooks/marker.js
21742
22012
  import { createHash } from "node:crypto";
21743
- import { existsSync as existsSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, statSync } from "node:fs";
22013
+ import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, statSync } from "node:fs";
21744
22014
  import { tmpdir } from "node:os";
21745
- import { join as join6 } from "node:path";
22015
+ import { join as join7 } from "node:path";
21746
22016
  var MARKER_TTL_MS = 24 * 60 * 60 * 1e3;
21747
22017
  var MARKER_PREFIX = "halfcycle-session-";
21748
22018
  var MARKER_SUFFIX = ".ref";
@@ -21756,18 +22026,21 @@ var FINDINGS_ID = new RegExp(FINDINGS_ID_SOURCE);
21756
22026
  function findingsPath(sessionId) {
21757
22027
  if (typeof sessionId !== "string" || !FINDINGS_ID.test(sessionId))
21758
22028
  return null;
21759
- return join6(markerDir(), `${FINDINGS_PREFIX}${sessionId}${FINDINGS_SUFFIX}`);
22029
+ return join7(markerDir(), `${FINDINGS_PREFIX}${sessionId}${FINDINGS_SUFFIX}`);
21760
22030
  }
21761
22031
  var HELD_STEP_PREFIX = "halfcycle-held-";
21762
22032
  var ALLOW_PREFIX = "halfcycle-allow-";
22033
+ var STEP_NUMBERS_PREFIX = "halfcycle-step-numbers-";
21763
22034
  var ALLOW_ONCE_SUFFIX = ".json";
22035
+ var STEP_NUMBERS_LOCK_SUFFIX = ".lock";
21764
22036
  function allowOncePaths(sessionId, dir) {
21765
22037
  if (typeof sessionId !== "string" || !FINDINGS_ID.test(sessionId))
21766
22038
  return null;
21767
22039
  const base = dir ?? markerDir();
21768
22040
  return {
21769
- held: join6(base, `${HELD_STEP_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`),
21770
- allow: join6(base, `${ALLOW_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`)
22041
+ held: join7(base, `${HELD_STEP_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`),
22042
+ allow: join7(base, `${ALLOW_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`),
22043
+ numbers: join7(base, `${STEP_NUMBERS_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`)
21771
22044
  };
21772
22045
  }
21773
22046
  function markerDir() {
@@ -21783,16 +22056,16 @@ function markerFileName(repoRoot, sessionId) {
21783
22056
  return `${MARKER_PREFIX}${key}${MARKER_SUFFIX}`;
21784
22057
  }
21785
22058
  function markerPath(repoRoot, sessionId) {
21786
- return join6(markerDir(), markerFileName(repoRoot, sessionId));
22059
+ return join7(markerDir(), markerFileName(repoRoot, sessionId));
21787
22060
  }
21788
22061
  function dedupePath(repoRoot, sessionId) {
21789
22062
  const hash2 = repoHash(repoRoot);
21790
22063
  const key = sessionId && sessionId.length > 0 ? `${hash2}-${sessionId}` : hash2;
21791
- return join6(markerDir(), `${MARKER_PREFIX}${key}${DEDUPE_SUFFIX}`);
22064
+ return join7(markerDir(), `${MARKER_PREFIX}${key}${DEDUPE_SUFFIX}`);
21792
22065
  }
21793
22066
  function readMarker(repoRoot, sessionId) {
21794
22067
  const path = markerPath(repoRoot, sessionId);
21795
- if (!existsSync4(path))
22068
+ if (!existsSync5(path))
21796
22069
  return null;
21797
22070
  try {
21798
22071
  return readFileSync7(path, "utf-8").trim() || null;
@@ -21803,7 +22076,7 @@ function readMarker(repoRoot, sessionId) {
21803
22076
  function deleteMarker(repoRoot, sessionId) {
21804
22077
  for (const path of [markerPath(repoRoot, sessionId), dedupePath(repoRoot, sessionId)]) {
21805
22078
  try {
21806
- if (existsSync4(path))
22079
+ if (existsSync5(path))
21807
22080
  unlinkSync3(path);
21808
22081
  } catch {
21809
22082
  }
@@ -21820,11 +22093,11 @@ var SESSION_NOTICE_SUFFIX = {
21820
22093
  "pre-tool-use-unreadable": ".pretool-unreadable",
21821
22094
  "outside-own-copy-unknown": ".owncopy-unknown"
21822
22095
  };
21823
- function firstNoticeThisSession(notice, folder, sessionId, dir = markerDir()) {
22096
+ function firstNoticeThisSession(notice, folder2, sessionId, dir = markerDir()) {
21824
22097
  if (!sessionId)
21825
22098
  return true;
21826
22099
  const suffix = SESSION_NOTICE_SUFFIX[notice];
21827
- const path = join6(dir, `${MARKER_PREFIX}${repoHash(folder)}-${sessionId}${suffix}`);
22100
+ const path = join7(dir, `${MARKER_PREFIX}${repoHash(folder2)}-${sessionId}${suffix}`);
21828
22101
  try {
21829
22102
  writeFileSync3(path, "", { encoding: "utf-8", flag: "wx" });
21830
22103
  return true;
@@ -21832,18 +22105,18 @@ function firstNoticeThisSession(notice, folder, sessionId, dir = markerDir()) {
21832
22105
  return err.code !== "EEXIST";
21833
22106
  }
21834
22107
  }
21835
- function warnOnceThisSession(notice, folder, sessionId) {
22108
+ function warnOnceThisSession(notice, folder2, sessionId) {
21836
22109
  return (message) => {
21837
- if (firstNoticeThisSession(notice, folder, sessionId))
22110
+ if (firstNoticeThisSession(notice, folder2, sessionId))
21838
22111
  process.stderr.write(message);
21839
22112
  };
21840
22113
  }
21841
- function firstNoRepoNoticeThisSession(folder, sessionId) {
21842
- return firstNoticeThisSession("no-repository", folder, sessionId);
22114
+ function firstNoRepoNoticeThisSession(folder2, sessionId) {
22115
+ return firstNoticeThisSession("no-repository", folder2, sessionId);
21843
22116
  }
21844
22117
  function readDedupe(repoRoot, sessionId) {
21845
22118
  const path = dedupePath(repoRoot, sessionId);
21846
- if (!existsSync4(path))
22119
+ if (!existsSync5(path))
21847
22120
  return null;
21848
22121
  try {
21849
22122
  const parsed = JSON.parse(readFileSync7(path, "utf-8"));
@@ -21891,7 +22164,7 @@ function orphanSweep(repoRoot, refKnown, keepSessionId, now = Date.now(), livene
21891
22164
  continue;
21892
22165
  if (name === keepName)
21893
22166
  continue;
21894
- const full = join6(dir, name);
22167
+ const full = join7(dir, name);
21895
22168
  let unusable = false;
21896
22169
  try {
21897
22170
  const ref = readFileSync7(full, "utf-8").trim();
@@ -21928,7 +22201,9 @@ function sweepSessionFiles(keepSessionId, now) {
21928
22201
  [FINDINGS_PREFIX, FINDINGS_SUFFIX],
21929
22202
  [OWN_WORK_PREFIX, OWN_WORK_SUFFIX],
21930
22203
  [HELD_STEP_PREFIX, ALLOW_ONCE_SUFFIX],
21931
- [ALLOW_PREFIX, ALLOW_ONCE_SUFFIX]
22204
+ [ALLOW_PREFIX, ALLOW_ONCE_SUFFIX],
22205
+ [STEP_NUMBERS_PREFIX, ALLOW_ONCE_SUFFIX],
22206
+ [STEP_NUMBERS_PREFIX, `${ALLOW_ONCE_SUFFIX}${STEP_NUMBERS_LOCK_SUFFIX}`]
21932
22207
  ];
21933
22208
  for (const name of names) {
21934
22209
  for (const [prefix, suffix] of kinds) {
@@ -21936,7 +22211,7 @@ function sweepSessionFiles(keepSessionId, now) {
21936
22211
  continue;
21937
22212
  if (keepSessionId !== void 0 && name === `${prefix}${keepSessionId}${suffix}`)
21938
22213
  continue;
21939
- const full = join6(dir, name);
22214
+ const full = join7(dir, name);
21940
22215
  try {
21941
22216
  if (now - statSync(full).mtimeMs > MARKER_TTL_MS)
21942
22217
  unlinkSync3(full);
@@ -21945,13 +22220,13 @@ function sweepSessionFiles(keepSessionId, now) {
21945
22220
  }
21946
22221
  }
21947
22222
  }
21948
- function observeThrottlePath(folder) {
21949
- return join6(markerDir(), `${MARKER_PREFIX}${repoHash(folder)}.observe-throttle`);
22223
+ function observeThrottlePath(folder2) {
22224
+ return join7(markerDir(), `${MARKER_PREFIX}${repoHash(folder2)}.observe-throttle`);
21950
22225
  }
21951
- function observeNoticesPath(folder, sessionId) {
22226
+ function observeNoticesPath(folder2, sessionId) {
21952
22227
  if (!sessionId)
21953
22228
  return null;
21954
- return join6(markerDir(), `${MARKER_PREFIX}${repoHash(folder)}-${sessionId}.observe-said`);
22229
+ return join7(markerDir(), `${MARKER_PREFIX}${repoHash(folder2)}-${sessionId}.observe-said`);
21955
22230
  }
21956
22231
  var LIVENESS_SUFFIX = ".live";
21957
22232
  var LIVENESS_NO_REPOSITORY_KEY = "norepository";
@@ -22005,7 +22280,7 @@ function sweepLiveness(common, now) {
22005
22280
  const parsed = parseLivenessFileName(name);
22006
22281
  if (parsed === null || !keys.has(parsed.key))
22007
22282
  continue;
22008
- const full = join6(dir, name);
22283
+ const full = join7(dir, name);
22009
22284
  try {
22010
22285
  if (now - statSync(full).mtimeMs > MARKER_TTL_MS) {
22011
22286
  unlinkSync3(full);
@@ -22016,22 +22291,22 @@ function sweepLiveness(common, now) {
22016
22291
  }
22017
22292
  }
22018
22293
  function removeLivenessIndexEntry(dir, sessionId, name) {
22019
- const folder = join6(dir, livenessIndexDirName(sessionId));
22294
+ const folder2 = join7(dir, livenessIndexDirName(sessionId));
22020
22295
  try {
22021
- unlinkSync3(join6(folder, name));
22296
+ unlinkSync3(join7(folder2, name));
22022
22297
  } catch {
22023
22298
  return;
22024
22299
  }
22025
22300
  try {
22026
- rmdirSync(folder);
22301
+ rmdirSync(folder2);
22027
22302
  } catch {
22028
22303
  }
22029
22304
  }
22030
22305
 
22031
22306
  // dist/seat-configuration.js
22032
- import { existsSync as existsSync5, readFileSync as readFileSync8, realpathSync as realpathSync2 } from "node:fs";
22033
- import { homedir as homedir3 } from "node:os";
22034
- import { join as join7, relative as relative3, resolve as resolve2 } from "node:path";
22307
+ import { existsSync as existsSync6, readFileSync as readFileSync8, realpathSync as realpathSync2 } from "node:fs";
22308
+ import { homedir as homedir4 } from "node:os";
22309
+ import { join as join8, relative as relative3, resolve as resolve2 } from "node:path";
22035
22310
  function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration, profile = AGENT_PROFILES["claude-code"]) {
22036
22311
  try {
22037
22312
  const facts = read(repoRoot, profile);
@@ -22046,7 +22321,7 @@ function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration, profile
22046
22321
  function readSeatConfiguration(repoRoot, profile = AGENT_PROFILES["claude-code"]) {
22047
22322
  const given = profile.projectRoot === "git-toplevel" ? resolve2(repoRoot) : resolve2(process.env["CLAUDE_PROJECT_DIR"] || process.cwd());
22048
22323
  const projectDir = realpathSync2.native(given);
22049
- const home = homedir3();
22324
+ const home = homedir4();
22050
22325
  let realHome = home;
22051
22326
  try {
22052
22327
  realHome = realpathSync2.native(home);
@@ -22067,7 +22342,7 @@ function isPlainObject3(value) {
22067
22342
  function readSettingsFile(projectDir, rel, ours) {
22068
22343
  let raw;
22069
22344
  try {
22070
- raw = readFileSync8(join7(projectDir, rel), "utf-8");
22345
+ raw = readFileSync8(join8(projectDir, rel), "utf-8");
22071
22346
  } catch (err) {
22072
22347
  const code = err.code;
22073
22348
  const state = code === "ENOENT" || code === "ENOTDIR" ? "absent" : "unreadable";
@@ -22139,7 +22414,7 @@ function readPathStates(projectDir, repoRoot, wiringPaths) {
22139
22414
  const status = getWiringStatus(wiringPaths, projectDir);
22140
22415
  if (!status.ok)
22141
22416
  return unknown2();
22142
- const mapped = mapWiringStatus(status.value, prefix, wiringPaths, (rel) => existsSync5(join7(projectDir, rel)));
22417
+ const mapped = mapWiringStatus(status.value, prefix, wiringPaths, (rel) => existsSync6(join8(projectDir, rel)));
22143
22418
  return mapped ?? unknown2();
22144
22419
  }
22145
22420
  function mapWiringStatus(output, prefix, paths, onDisk) {
@@ -22182,28 +22457,28 @@ function mapWiringStatus(output, prefix, paths, onDisk) {
22182
22457
 
22183
22458
  // dist/time-record.js
22184
22459
  import { createHash as createHash3 } from "node:crypto";
22185
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync6, readFileSync as readFileSync14, renameSync as renameSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "node:fs";
22186
- import { homedir as homedir7 } from "node:os";
22187
- import { dirname as dirname4, join as join10 } from "node:path";
22460
+ import { appendFileSync as appendFileSync3, closeSync as closeSync3, fstatSync as fstatSync2, mkdirSync as mkdirSync6, openSync as openSync3, readdirSync as readdirSync6, readFileSync as readFileSync15, readSync as readSync3, renameSync as renameSync6, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "node:fs";
22461
+ import { homedir as homedir8 } from "node:os";
22462
+ import { dirname as dirname4, join as join11 } from "node:path";
22188
22463
 
22189
22464
  // dist/codex-usage.js
22190
22465
  import { closeSync, fstatSync, lstatSync, openSync, readdirSync as readdirSync4, readFileSync as readFileSync9, readSync, realpathSync as realpathSync3 } from "node:fs";
22191
- import { homedir as homedir4 } from "node:os";
22192
- import { basename, isAbsolute, join as join8, relative as relative4, resolve as resolve3, sep as sep2 } from "node:path";
22466
+ import { homedir as homedir5 } from "node:os";
22467
+ import { basename, isAbsolute, join as join9, relative as relative4, resolve as resolve3, sep as sep2 } from "node:path";
22193
22468
  var CODEX_WINDOW_FRESH_MS = 10 * 60 * 1e3;
22194
22469
  var CODEX_DAYS_BACK = 7;
22195
22470
  var CODEX_FILES_MAX = 20;
22196
22471
  var CODEX_TAIL_BYTES = 256 * 1024;
22197
22472
  var CODEX_SAME_RESET_SECONDS = 60;
22198
22473
  var ROLLOUT_NAME = /^rollout-.+\.jsonl$/;
22199
- function codexHomeOf(env = process.env, home = homedir4()) {
22474
+ function codexHomeOf(env = process.env, home = homedir5()) {
22200
22475
  const set2 = env["CODEX_HOME"];
22201
- const folder = typeof set2 === "string" && set2 !== "" ? set2 : join8(home, ".codex");
22202
- if (folder === "~")
22476
+ const folder2 = typeof set2 === "string" && set2 !== "" ? set2 : join9(home, ".codex");
22477
+ if (folder2 === "~")
22203
22478
  return resolve3(home);
22204
- if (folder.startsWith("~/") || folder.startsWith("~\\"))
22205
- return resolve3(join8(home, folder.slice(2)));
22206
- return resolve3(folder);
22479
+ if (folder2.startsWith("~/") || folder2.startsWith("~\\"))
22480
+ return resolve3(join9(home, folder2.slice(2)));
22481
+ return resolve3(folder2);
22207
22482
  }
22208
22483
  function objectOf(v) {
22209
22484
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : null;
@@ -22287,20 +22562,20 @@ function dayFolders(sessions, now) {
22287
22562
  const out = [];
22288
22563
  for (let back = 0; back < CODEX_DAYS_BACK; back++) {
22289
22564
  const d = new Date(now.getFullYear(), now.getMonth(), now.getDate() - back);
22290
- out.push(join8(sessions, String(d.getFullYear()), twoDigits(d.getMonth() + 1), twoDigits(d.getDate())));
22565
+ out.push(join9(sessions, String(d.getFullYear()), twoDigits(d.getMonth() + 1), twoDigits(d.getDate())));
22291
22566
  }
22292
22567
  return out;
22293
22568
  }
22294
22569
  function recentRollouts(sessions, now) {
22295
22570
  const found2 = [];
22296
- for (const folder of dayFolders(sessions, now)) {
22571
+ for (const folder2 of dayFolders(sessions, now)) {
22297
22572
  try {
22298
- if (!lstatSync(folder).isDirectory())
22573
+ if (!lstatSync(folder2).isDirectory())
22299
22574
  continue;
22300
- for (const name of readdirSync4(folder)) {
22575
+ for (const name of readdirSync4(folder2)) {
22301
22576
  if (!ROLLOUT_NAME.test(name))
22302
22577
  continue;
22303
- const path = join8(folder, name);
22578
+ const path = join9(folder2, name);
22304
22579
  try {
22305
22580
  const st = lstatSync(path);
22306
22581
  if (!st.isFile())
@@ -22319,7 +22594,7 @@ function recentRollouts(sessions, now) {
22319
22594
  function readCodexWindows(codexHome, now) {
22320
22595
  try {
22321
22596
  const candidates = [];
22322
- for (const path of recentRollouts(join8(codexHome, "sessions"), now)) {
22597
+ for (const path of recentRollouts(join9(codexHome, "sessions"), now)) {
22323
22598
  try {
22324
22599
  candidates.push(...newestWindowsIn(path, now));
22325
22600
  } catch {
@@ -22349,7 +22624,7 @@ function isCodexSessionFile(path, codexHome) {
22349
22624
  return false;
22350
22625
  if (!lstatSync(path).isFile())
22351
22626
  return false;
22352
- const rel = relative4(realpathSync3(join8(codexHome, "sessions")), realpathSync3(path));
22627
+ const rel = relative4(realpathSync3(join9(codexHome, "sessions")), realpathSync3(path));
22353
22628
  return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute(rel);
22354
22629
  } catch {
22355
22630
  return false;
@@ -22444,18 +22719,18 @@ function firstLine(text) {
22444
22719
  const line2 = text.split("\n", 1)[0]?.replace(/[ \t\r]+$/, "") ?? "";
22445
22720
  return line2 === "" ? null : line2;
22446
22721
  }
22447
- function whichCopy(folder, deps = {}) {
22722
+ function whichCopy(folder2, deps = {}) {
22448
22723
  try {
22449
22724
  const rules = deps.rules ?? machineRules();
22450
22725
  const p = pathsFor(rules);
22451
22726
  const files = deps.files ?? DISK_FILES;
22452
22727
  const realpath = deps.realpath ?? ((x) => realpathSync4.native(x));
22453
22728
  const oneForm = rules === "windows" ? (x) => x.toLowerCase() : (x) => x;
22454
- if (!p.isAbsolute(folder))
22729
+ if (!p.isAbsolute(folder2))
22455
22730
  return null;
22456
- if (files.kind(p.resolve(folder)) !== "folder")
22731
+ if (files.kind(p.resolve(folder2)) !== "folder")
22457
22732
  return null;
22458
- let dir = realpath(p.resolve(folder));
22733
+ let dir = realpath(p.resolve(folder2));
22459
22734
  for (; ; ) {
22460
22735
  const dotGit = p.join(dir, ".git");
22461
22736
  const found2 = files.kind(dotGit);
@@ -22530,7 +22805,7 @@ function copyHolding(file2, deps = {}) {
22530
22805
 
22531
22806
  // dist/observed.js
22532
22807
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
22533
- import { join as join9 } from "node:path";
22808
+ import { join as join10 } from "node:path";
22534
22809
  function emptyTally() {
22535
22810
  return { added: 0, changed: 0, removed: 0, testFilesAdded: 0 };
22536
22811
  }
@@ -22662,9 +22937,9 @@ async function attemptObservation(opts) {
22662
22937
  return { outcome: "not-sent", reason: "refused" };
22663
22938
  return answer.data.outcome === "kept" ? { outcome: "kept" } : { outcome: "recorded", answer: answer.data };
22664
22939
  }
22665
- function readThrottle(folder) {
22940
+ function readThrottle(folder2) {
22666
22941
  try {
22667
- const parsed = JSON.parse(readFileSync11(observeThrottlePath(folder), "utf-8"));
22942
+ const parsed = JSON.parse(readFileSync11(observeThrottlePath(folder2), "utf-8"));
22668
22943
  if (typeof parsed !== "object" || parsed === null)
22669
22944
  return null;
22670
22945
  const { attemptedAt, accepted } = parsed;
@@ -22677,9 +22952,9 @@ function readThrottle(folder) {
22677
22952
  return null;
22678
22953
  }
22679
22954
  }
22680
- function writeThrottle(folder, state) {
22955
+ function writeThrottle(folder2, state) {
22681
22956
  try {
22682
- writeFileSync4(observeThrottlePath(folder), JSON.stringify(state), "utf-8");
22957
+ writeFileSync4(observeThrottlePath(folder2), JSON.stringify(state), "utf-8");
22683
22958
  } catch {
22684
22959
  }
22685
22960
  }
@@ -22700,9 +22975,9 @@ function locate(cwd) {
22700
22975
  const read = repoRoot === null ? null : headCommit(repoRoot);
22701
22976
  return { repoRoot, folder: repoRoot ?? cwd, head: read !== null && read.ok ? read.value : null };
22702
22977
  }
22703
- function recordAttempt(folder, prior, head, at, attempt2) {
22978
+ function recordAttempt(folder2, prior, head, at, attempt2) {
22704
22979
  const accepted = (attempt2.outcome === "recorded" || attempt2.outcome === "kept") && head !== null ? { head, at } : prior?.accepted ?? null;
22705
- writeThrottle(folder, { attemptedAt: at, accepted });
22980
+ writeThrottle(folder2, { attemptedAt: at, accepted });
22706
22981
  }
22707
22982
  async function runObserve(deps = {}) {
22708
22983
  let result;
@@ -22731,7 +23006,7 @@ async function runObserve(deps = {}) {
22731
23006
  }
22732
23007
  function readLocalSystemDescription(repoRoot) {
22733
23008
  try {
22734
- const raw = JSON.parse(readFileSync11(join9(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
23009
+ const raw = JSON.parse(readFileSync11(join10(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
22735
23010
  const checked = checkSystemDescription(raw);
22736
23011
  return checked.ok ? checked.description : null;
22737
23012
  } catch {
@@ -22905,23 +23180,23 @@ function writeSaid(path, state) {
22905
23180
  function reasonsOf(entry) {
22906
23181
  return entry.said.filter((task) => task.whyOutsidePlan !== void 0).map((task) => `${task.title}\0${task.whyOutsidePlan}`);
22907
23182
  }
22908
- function noticesFor(attempt2, folder, sessionId) {
23183
+ function noticesFor(attempt2, folder2, sessionId) {
22909
23184
  if (attempt2.outcome === "not-sent") {
22910
23185
  if (attempt2.reason === "start-not-here") {
22911
- return firstNoticeThisSession("observe-no-start-here", folder, sessionId) ? [R1_START_NOT_HERE] : [];
23186
+ return firstNoticeThisSession("observe-no-start-here", folder2, sessionId) ? [R1_START_NOT_HERE] : [];
22912
23187
  }
22913
23188
  if (attempt2.reason === "start-not-on-branch") {
22914
- return firstNoticeThisSession("observe-not-on-branch", folder, sessionId) ? [R2_START_NOT_ON_BRANCH] : [];
23189
+ return firstNoticeThisSession("observe-not-on-branch", folder2, sessionId) ? [R2_START_NOT_ON_BRANCH] : [];
22915
23190
  }
22916
23191
  if (attempt2.reason === "cannot-count" && attempt2.phase !== void 0) {
22917
- return firstNoticeThisSession("observe-no-start-recorded", folder, sessionId) ? [r3NoStartRecorded(attempt2.phase)] : [];
23192
+ return firstNoticeThisSession("observe-no-start-recorded", folder2, sessionId) ? [r3NoStartRecorded(attempt2.phase)] : [];
22918
23193
  }
22919
23194
  return [];
22920
23195
  }
22921
23196
  if (attempt2.outcome === "kept")
22922
23197
  return [];
22923
23198
  const { notInPlan, outside } = attempt2.answer;
22924
- const path = observeNoticesPath(folder, sessionId);
23199
+ const path = observeNoticesPath(folder2, sessionId);
22925
23200
  const prior = readSaid(path);
22926
23201
  const notices = [];
22927
23202
  const parts = {};
@@ -22949,7 +23224,7 @@ function noticesFor(attempt2, folder, sessionId) {
22949
23224
  // dist/pacing.js
22950
23225
  import { createHash as createHash2 } from "node:crypto";
22951
23226
  import { closeSync as closeSync2, mkdirSync as mkdirSync3, openSync as openSync2, readdirSync as readdirSync5, readFileSync as readFileSync12, readSync as readSync2, realpathSync as realpathSync5, renameSync as renameSync3, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
22952
- import { homedir as homedir5 } from "node:os";
23227
+ import { homedir as homedir6 } from "node:os";
22953
23228
  import nodePath from "node:path";
22954
23229
  var DEFAULT_CONFIG_FOLDER_NAME = ".claude";
22955
23230
  var CODEX_DEFAULT_FOLDER_NAME = ".codex";
@@ -22967,10 +23242,10 @@ function hookInputForRule(input, rule) {
22967
23242
  delete copy["rollout_path"];
22968
23243
  return copy;
22969
23244
  }
22970
- function normaliseFolder(folder, input = {}) {
23245
+ function normaliseFolder(folder2, input = {}) {
22971
23246
  const p = input.path ?? nodePath;
22972
- const home = input.home ?? homedir5();
22973
- let f = folder;
23247
+ const home = input.home ?? homedir6();
23248
+ let f = folder2;
22974
23249
  if (f === "~")
22975
23250
  f = home;
22976
23251
  else if (f.startsWith("~/") || f.startsWith("~\\"))
@@ -22984,7 +23259,7 @@ function configFolderKey(input = {}) {
22984
23259
  const codexHome = env["CODEX_HOME"];
22985
23260
  if (typeof codexHome === "string" && codexHome !== "")
22986
23261
  return normaliseFolder(codexHome, input);
22987
- return normaliseFolder(p.join(input.home ?? homedir5(), CODEX_DEFAULT_FOLDER_NAME), input);
23262
+ return normaliseFolder(p.join(input.home ?? homedir6(), CODEX_DEFAULT_FOLDER_NAME), input);
22988
23263
  }
22989
23264
  const fromEnv = env["CLAUDE_CONFIG_DIR"];
22990
23265
  if (typeof fromEnv === "string" && fromEnv !== "")
@@ -22993,7 +23268,7 @@ function configFolderKey(input = {}) {
22993
23268
  if (typeof transcript === "string" && transcript !== "") {
22994
23269
  return normaliseFolder(p.dirname(p.dirname(p.dirname(normaliseFolder(transcript, input)))), input);
22995
23270
  }
22996
- return normaliseFolder(p.join(input.home ?? homedir5(), DEFAULT_CONFIG_FOLDER_NAME), input);
23271
+ return normaliseFolder(p.join(input.home ?? homedir6(), DEFAULT_CONFIG_FOLDER_NAME), input);
22997
23272
  }
22998
23273
  function recordsFolderOf(key, path = nodePath) {
22999
23274
  return path.join(key, RECORDS_FOLDER_NAME);
@@ -23013,7 +23288,7 @@ var PACING_DIR_NAME = "pacing";
23013
23288
  var LEARNED_FILENAME = "learned.json";
23014
23289
  var REPORTED_FILENAME = "reported.json";
23015
23290
  function pacingDir(home) {
23016
- return nodePath.join(home ?? homedir5(), HALFCYCLE_DIR_NAME, PACING_DIR_NAME);
23291
+ return nodePath.join(home ?? homedir6(), HALFCYCLE_DIR_NAME, PACING_DIR_NAME);
23017
23292
  }
23018
23293
  function readJsonObject(path) {
23019
23294
  try {
@@ -23435,7 +23710,7 @@ function resolvedRecords(key, path) {
23435
23710
  }
23436
23711
  }
23437
23712
  function timeLogTranscripts(home) {
23438
- const engagements = nodePath.join(home ?? homedir5(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
23713
+ const engagements = nodePath.join(home ?? homedir6(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
23439
23714
  const out = [];
23440
23715
  for (const e of dirents(engagements)) {
23441
23716
  if (!e.isDirectory())
@@ -23454,7 +23729,7 @@ function timeLogTranscripts(home) {
23454
23729
  }
23455
23730
  function teacherOf(key, deps = {}) {
23456
23731
  const path = deps.path ?? nodePath;
23457
- const home = deps.home ?? homedir5();
23732
+ const home = deps.home ?? homedir6();
23458
23733
  const ours = resolvedRecords(key, path);
23459
23734
  if (ours === null)
23460
23735
  return { shared: false, teaches: () => false };
@@ -23624,13 +23899,13 @@ function knownWindowOf(learned, reported, now) {
23624
23899
 
23625
23900
  // dist/phase-limit.js
23626
23901
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync as renameSync4, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
23627
- import { homedir as homedir6 } from "node:os";
23902
+ import { homedir as homedir7 } from "node:os";
23628
23903
  import nodePath2 from "node:path";
23629
23904
  var PHASE_USAGE_FILENAME = "phase-usage.json";
23630
23905
  function phaseLimitDir(engagementId, home) {
23631
23906
  if (!isEngagementId(engagementId))
23632
23907
  return null;
23633
- return nodePath2.join(home ?? homedir6(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
23908
+ return nodePath2.join(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
23634
23909
  }
23635
23910
  function readJsonFile(path) {
23636
23911
  try {
@@ -23678,16 +23953,14 @@ function readPhaseUsage(dir, phase, openedAt = null) {
23678
23953
  return 0;
23679
23954
  return parsed.data.tokens;
23680
23955
  }
23956
+ function readPhaseUsageFile(dir) {
23957
+ const parsed = phaseUsageFileSchema.safeParse(readJsonFile(nodePath2.join(dir, PHASE_USAGE_FILENAME)));
23958
+ return parsed.success ? parsed.data : null;
23959
+ }
23681
23960
  function writePhaseUsage(dir, file2) {
23682
23961
  if (!phaseUsageFileSchema.safeParse(file2).success)
23683
- return;
23684
- writeWhole(nodePath2.join(dir, PHASE_USAGE_FILENAME), file2);
23685
- }
23686
- function phaseTokensOf(buckets) {
23687
- let total = 0;
23688
- for (const b of buckets)
23689
- total += b.inputTokens + b.outputTokens + b.cacheWriteTokens + b.cacheReadTokens;
23690
- return Math.max(0, Math.round(total));
23962
+ return false;
23963
+ return writeWhole(nodePath2.join(dir, PHASE_USAGE_FILENAME), file2);
23691
23964
  }
23692
23965
  function roundTokens(n) {
23693
23966
  return Math.max(LIMIT_ROUND_TOKENS, Math.round(n / LIMIT_ROUND_TOKENS) * LIMIT_ROUND_TOKENS);
@@ -23865,6 +24138,164 @@ function setLimit(file2, limit, multiple, used, by, at) {
23865
24138
  };
23866
24139
  }
23867
24140
 
24141
+ // dist/scan-lock.js
24142
+ import { spawn } from "node:child_process";
24143
+ import { linkSync, mkdirSync as mkdirSync5, readFileSync as readFileSync14, renameSync as renameSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "node:fs";
24144
+ import nodePath3 from "node:path";
24145
+ var SCAN_LOCK_FILENAME = "scan.lock";
24146
+ var SCAN_LOCK_STALE_MS = 15 * 60 * 1e3;
24147
+ var SCAN_LOCK_POLL_MS = 100;
24148
+ function scanLockPath(home) {
24149
+ return nodePath3.join(pacingDir(home), SCAN_LOCK_FILENAME);
24150
+ }
24151
+ function readLockBody(path) {
24152
+ let text;
24153
+ try {
24154
+ text = readFileSync14(path, "utf-8");
24155
+ } catch (err) {
24156
+ return err.code === "ENOENT" ? "missing" : null;
24157
+ }
24158
+ try {
24159
+ const v = JSON.parse(text);
24160
+ return typeof v["pid"] === "number" && typeof v["at"] === "number" ? { pid: v["pid"], at: v["at"] } : null;
24161
+ } catch {
24162
+ return null;
24163
+ }
24164
+ }
24165
+ function processAlive(pid) {
24166
+ if (!Number.isInteger(pid) || pid <= 0)
24167
+ return false;
24168
+ try {
24169
+ process.kill(pid, 0);
24170
+ return true;
24171
+ } catch (err) {
24172
+ return err.code === "EPERM";
24173
+ }
24174
+ }
24175
+ function isStale(body, staleMs, deps) {
24176
+ if (body === null)
24177
+ return true;
24178
+ const now = (deps.clock ?? Date.now)();
24179
+ if (Math.abs(now - body.at) > staleMs)
24180
+ return true;
24181
+ return !(deps.alive ?? processAlive)(body.pid);
24182
+ }
24183
+ function writeLock(path, pid, deps, flag) {
24184
+ writeFileSync7(path, JSON.stringify({ pid, at: (deps.clock ?? Date.now)() }), { flag, mode: 384 });
24185
+ }
24186
+ function tryTakeScanLock(deps = {}) {
24187
+ return tryTakeLockFile(scanLockPath(deps.home), SCAN_LOCK_STALE_MS, deps);
24188
+ }
24189
+ function tryTakeLockFile(path, staleMs, deps = {}) {
24190
+ const pid = deps.pid ?? process.pid;
24191
+ try {
24192
+ mkdirSync5(nodePath3.dirname(path), { recursive: true, mode: 448 });
24193
+ } catch {
24194
+ return false;
24195
+ }
24196
+ for (let attempt2 = 0; attempt2 < 2; attempt2++) {
24197
+ try {
24198
+ writeLock(path, pid, deps, "wx");
24199
+ return true;
24200
+ } catch (err) {
24201
+ if (err.code !== "EEXIST")
24202
+ return false;
24203
+ }
24204
+ const seen = readLockBody(path);
24205
+ if (seen === "missing")
24206
+ continue;
24207
+ if (!isStale(seen, staleMs, deps))
24208
+ return false;
24209
+ const aside = `${path}.${String(pid)}.${String((deps.clock ?? Date.now)())}.stale`;
24210
+ try {
24211
+ renameSync5(path, aside);
24212
+ } catch {
24213
+ continue;
24214
+ }
24215
+ const taken = readLockBody(aside);
24216
+ const same = taken !== "missing" && (seen === null ? taken === null : taken !== null && taken.pid === seen.pid && taken.at === seen.at);
24217
+ if (!same && taken !== "missing" && !isStale(taken, staleMs, deps)) {
24218
+ try {
24219
+ linkSync(aside, path);
24220
+ } catch {
24221
+ }
24222
+ try {
24223
+ unlinkSync4(aside);
24224
+ } catch {
24225
+ }
24226
+ return false;
24227
+ }
24228
+ try {
24229
+ unlinkSync4(aside);
24230
+ } catch {
24231
+ }
24232
+ }
24233
+ return false;
24234
+ }
24235
+ function releaseLockFile(path, pid = process.pid) {
24236
+ const body = readLockBody(path);
24237
+ if (body === "missing" || body === null || body.pid !== pid)
24238
+ return;
24239
+ try {
24240
+ unlinkSync4(path);
24241
+ } catch {
24242
+ }
24243
+ }
24244
+ function releaseScanLock(deps = {}) {
24245
+ releaseLockFile(scanLockPath(deps.home), deps.pid ?? process.pid);
24246
+ }
24247
+ function sleepSync(ms) {
24248
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
24249
+ }
24250
+ function scanUnderLock(key, deps = {}, waitMs = 0) {
24251
+ const clock = deps.clock ?? Date.now;
24252
+ const giveUpAt = clock() + waitMs;
24253
+ while (!tryTakeScanLock(deps)) {
24254
+ if (clock() >= giveUpAt)
24255
+ return null;
24256
+ sleepSync(SCAN_LOCK_POLL_MS);
24257
+ }
24258
+ try {
24259
+ return scanRecords(key, deps);
24260
+ } finally {
24261
+ releaseScanLock(deps);
24262
+ }
24263
+ }
24264
+ var spawnDetachedReal = (command, args2) => {
24265
+ const child = spawn(command, [...args2], { detached: true, stdio: "ignore", windowsHide: true });
24266
+ child.on("error", () => void 0);
24267
+ child.unref();
24268
+ return child.pid;
24269
+ };
24270
+ function startScanChild(key, binaryPath, deps = {}) {
24271
+ if (!tryTakeScanLock(deps))
24272
+ return false;
24273
+ let childPid;
24274
+ try {
24275
+ childPid = (deps.spawnDetached ?? spawnDetachedReal)(deps.nodePath ?? process.execPath, [
24276
+ binaryPath,
24277
+ "pace",
24278
+ "--scan",
24279
+ "--config-folder",
24280
+ key,
24281
+ "--lock-held"
24282
+ ]);
24283
+ } catch {
24284
+ childPid = void 0;
24285
+ }
24286
+ if (childPid === void 0) {
24287
+ releaseScanLock(deps);
24288
+ return false;
24289
+ }
24290
+ try {
24291
+ writeLock(scanLockPath(deps.home), childPid, deps, "w");
24292
+ } catch {
24293
+ releaseScanLock(deps);
24294
+ return false;
24295
+ }
24296
+ return true;
24297
+ }
24298
+
23868
24299
  // dist/time-record.js
23869
24300
  var TIME_EVENT_KINDS = ["action", "asked", "edit", "turn-end", "lock-wait-start", "lock-wait-end"];
23870
24301
  function isLockWaitEvent(event) {
@@ -23878,25 +24309,25 @@ function inputString(input, key) {
23878
24309
  const value = input[key];
23879
24310
  return typeof value === "string" && value !== "" ? value : null;
23880
24311
  }
23881
- function readBranch(folder, deps = {}) {
23882
- const copy = whichCopy(folder, deps);
24312
+ function readBranch(folder2, deps = {}) {
24313
+ const copy = whichCopy(folder2, deps);
23883
24314
  return copy === null ? null : copyBranch(copy, deps);
23884
24315
  }
23885
24316
  function timeLogPath(engagementId, home) {
23886
24317
  if (!isEngagementId(engagementId))
23887
24318
  return null;
23888
- return join10(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TIME_LOG_DIR_NAME, TIME_LOG_FILENAME);
24319
+ return join11(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TIME_LOG_DIR_NAME, TIME_LOG_FILENAME);
23889
24320
  }
23890
24321
  function timeEventLine(event, input, deps = {}) {
23891
24322
  const at = (deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
23892
- const folder = inputString(input, "cwd") ?? (deps.cwd ?? (() => process.cwd()))();
24323
+ const folder2 = inputString(input, "cwd") ?? (deps.cwd ?? (() => process.cwd()))();
23893
24324
  return {
23894
24325
  at,
23895
24326
  event,
23896
24327
  session: inputString(input, "session_id"),
23897
24328
  agent: inputString(input, "agent_id"),
23898
- folder,
23899
- branch: readBranch(folder),
24329
+ folder: folder2,
24330
+ branch: readBranch(folder2),
23900
24331
  transcript: inputString(input, "transcript_path"),
23901
24332
  toolUse: inputString(input, "tool_use_id"),
23902
24333
  rollout: inputString(input, "rollout_path")
@@ -23914,12 +24345,12 @@ function recordTimeEvent(event, input, deps = {}) {
23914
24345
  const text = `${JSON.stringify(line2)}
23915
24346
  `;
23916
24347
  try {
23917
- appendFileSync2(path, text, { mode: 384 });
24348
+ appendFileSync3(path, text, { mode: 384 });
23918
24349
  } catch (err) {
23919
24350
  if (err.code !== "ENOENT")
23920
24351
  return;
23921
- mkdirSync5(dirname4(path), { recursive: true, mode: 448 });
23922
- appendFileSync2(path, text, { mode: 384 });
24352
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
24353
+ appendFileSync3(path, text, { mode: 384 });
23923
24354
  }
23924
24355
  } catch {
23925
24356
  }
@@ -23978,7 +24409,7 @@ function readSessionTimeLines(sessionId, deps = {}) {
23978
24409
  if (path === null)
23979
24410
  return [];
23980
24411
  const needle = JSON.stringify(sessionId);
23981
- const kept = readFileSync14(path, "utf-8").split("\n").filter((raw) => raw.includes(needle));
24412
+ const kept = readFileSync15(path, "utf-8").split("\n").filter((raw) => raw.includes(needle));
23982
24413
  return parseTimeLog(kept.join("\n")).filter((line2) => line2.session === sessionId);
23983
24414
  } catch {
23984
24415
  return [];
@@ -24005,8 +24436,8 @@ function separatorOf(path) {
24005
24436
  function subagentsFolderOf(transcript) {
24006
24437
  if (!transcript.endsWith(".jsonl") || transcript.length === ".jsonl".length)
24007
24438
  return null;
24008
- const sep7 = separatorOf(transcript);
24009
- return `${transcript.slice(0, -".jsonl".length)}${sep7}subagents`;
24439
+ const sep8 = separatorOf(transcript);
24440
+ return `${transcript.slice(0, -".jsonl".length)}${sep8}subagents`;
24010
24441
  }
24011
24442
  function subagentRecordName(agentId) {
24012
24443
  return /^[A-Za-z0-9_-]+$/.test(agentId) ? `agent-${agentId}.jsonl` : null;
@@ -24014,12 +24445,12 @@ function subagentRecordName(agentId) {
24014
24445
  function subagentRecordPath(transcript, agentId) {
24015
24446
  if (transcript === null)
24016
24447
  return null;
24017
- const folder = subagentsFolderOf(transcript);
24448
+ const folder2 = subagentsFolderOf(transcript);
24018
24449
  const name = subagentRecordName(agentId);
24019
- return folder === null || name === null ? null : `${folder}${separatorOf(transcript)}${name}`;
24450
+ return folder2 === null || name === null ? null : `${folder2}${separatorOf(transcript)}${name}`;
24020
24451
  }
24021
- function folderParts(folder) {
24022
- return folder.split(/[\\/]+/).filter((part, i) => part !== "" || i === 0);
24452
+ function folderParts(folder2) {
24453
+ return folder2.split(/[\\/]+/).filter((part, i) => part !== "" || i === 0);
24023
24454
  }
24024
24455
  function folderHolds(outer, inner) {
24025
24456
  const o = folderParts(outer);
@@ -24072,15 +24503,52 @@ function buildLanes(events, taskIds) {
24072
24503
  });
24073
24504
  }
24074
24505
  var UNREADABLE2 = { readable: false, lines: [] };
24075
- function viewRecord(path, deps) {
24076
- if (path === null)
24077
- return UNREADABLE2;
24078
- let text;
24079
- try {
24080
- text = (deps.readFile ?? ((p) => readFileSync14(p, "utf-8")))(path);
24081
- } catch {
24082
- return UNREADABLE2;
24506
+ function plainObject(v) {
24507
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : null;
24508
+ }
24509
+ var USAGE_KEYS = ["input_tokens", "output_tokens", "cache_creation_input_tokens", "cache_read_input_tokens"];
24510
+ function reducedLine(o) {
24511
+ const out = { type: o["type"] };
24512
+ if (typeof o["timestamp"] === "string")
24513
+ out["timestamp"] = o["timestamp"];
24514
+ if (typeof o["gitBranch"] === "string")
24515
+ out["gitBranch"] = o["gitBranch"];
24516
+ if ("toolUseResult" in o)
24517
+ out["toolUseResult"] = true;
24518
+ if (o["isApiErrorMessage"] === true)
24519
+ out["isApiErrorMessage"] = true;
24520
+ if (typeof o["error"] === "string")
24521
+ out["error"] = o["error"];
24522
+ const message = plainObject(o["message"]);
24523
+ if (message !== null) {
24524
+ const m = {};
24525
+ if (typeof message["id"] === "string")
24526
+ m["id"] = message["id"];
24527
+ if (typeof message["model"] === "string")
24528
+ m["model"] = message["model"];
24529
+ const usage = plainObject(message["usage"]);
24530
+ if (usage !== null) {
24531
+ const u = {};
24532
+ for (const key of USAGE_KEYS)
24533
+ if (key in usage)
24534
+ u[key] = usage[key];
24535
+ m["usage"] = u;
24536
+ }
24537
+ const content = message["content"];
24538
+ if (Array.isArray(content) && content.some((block) => plainObject(block)?.["type"] === "tool_result"))
24539
+ m["content"] = [{ type: "tool_result" }];
24540
+ out["message"] = m;
24083
24541
  }
24542
+ return out;
24543
+ }
24544
+ function recordLineOfValue(value) {
24545
+ const o = plainObject(value);
24546
+ if (o === null || typeof o["type"] !== "string")
24547
+ return null;
24548
+ const t = typeof o["timestamp"] === "string" ? Date.parse(o["timestamp"]) : NaN;
24549
+ return { value: o, t: Number.isNaN(t) ? null : t };
24550
+ }
24551
+ function recordLinesOf(text) {
24084
24552
  const lines2 = [];
24085
24553
  for (const raw of text.split("\n")) {
24086
24554
  if (raw.trim() === "")
@@ -24091,29 +24559,129 @@ function viewRecord(path, deps) {
24091
24559
  } catch {
24092
24560
  continue;
24093
24561
  }
24094
- if (typeof value !== "object" || value === null || Array.isArray(value))
24562
+ const o = plainObject(value);
24563
+ if (o === null || typeof o["type"] !== "string")
24095
24564
  continue;
24096
- const o = value;
24097
- if (typeof o["type"] !== "string")
24098
- continue;
24099
- const t = typeof o["timestamp"] === "string" ? Date.parse(o["timestamp"]) : NaN;
24100
- lines2.push({ value: o, t: Number.isNaN(t) ? null : t });
24565
+ lines2.push(recordLineOfValue(reducedLine(o)));
24566
+ }
24567
+ return lines2;
24568
+ }
24569
+ function viewRecord(path, deps) {
24570
+ let text;
24571
+ try {
24572
+ text = (deps.readFile ?? ((p) => readFileSync15(p, "utf-8")))(path);
24573
+ } catch {
24574
+ return UNREADABLE2;
24101
24575
  }
24576
+ const lines2 = recordLinesOf(text);
24102
24577
  return lines2.length === 0 ? UNREADABLE2 : { readable: true, lines: lines2 };
24103
24578
  }
24579
+ function readFileFrom(path, offset) {
24580
+ const fd = openSync3(path, "r");
24581
+ try {
24582
+ const size = fstatSync2(fd).size;
24583
+ if (size < offset)
24584
+ return null;
24585
+ const buf = Buffer.alloc(size - offset);
24586
+ let got = 0;
24587
+ while (got < buf.length) {
24588
+ const n = readSync3(fd, buf, got, buf.length - got, offset + got);
24589
+ if (n === 0)
24590
+ break;
24591
+ got += n;
24592
+ }
24593
+ return buf.subarray(0, got);
24594
+ } finally {
24595
+ closeSync3(fd);
24596
+ }
24597
+ }
24598
+ function readFromOf(deps) {
24599
+ if (deps.readFrom !== void 0)
24600
+ return deps.readFrom;
24601
+ const readFile = deps.readFile;
24602
+ if (readFile === void 0)
24603
+ return readFileFrom;
24604
+ return (path, offset) => {
24605
+ const bytes = Buffer.from(readFile(path), "utf-8");
24606
+ return bytes.length < offset ? null : bytes.subarray(offset);
24607
+ };
24608
+ }
24609
+ function viewRecordFrom(path, deps, offsets) {
24610
+ const readFrom = readFromOf(deps);
24611
+ const stored = offsets.files.get(path);
24612
+ let start = stored?.offset ?? 0;
24613
+ let prior = stored?.lines ?? [];
24614
+ let bytes;
24615
+ try {
24616
+ bytes = readFrom(path, start);
24617
+ if (bytes === null && start > 0) {
24618
+ start = 0;
24619
+ prior = [];
24620
+ bytes = readFrom(path, 0);
24621
+ }
24622
+ } catch {
24623
+ return UNREADABLE2;
24624
+ }
24625
+ if (bytes === null)
24626
+ return UNREADABLE2;
24627
+ const buf = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
24628
+ const end = buf.lastIndexOf(10) + 1;
24629
+ const fresh = end === 0 ? [] : recordLinesOf(buf.subarray(0, end).toString("utf-8"));
24630
+ const values = [...prior, ...fresh.map((l) => l.value)];
24631
+ offsets.files.set(path, { offset: start + end, lines: values });
24632
+ const lines2 = values.map((v) => recordLineOfValue(v));
24633
+ return lines2.length === 0 ? UNREADABLE2 : { readable: true, lines: lines2 };
24634
+ }
24635
+ var viewsOfOffsets = /* @__PURE__ */ new WeakMap();
24104
24636
  function recordReader(deps) {
24105
- const cache2 = /* @__PURE__ */ new Map();
24637
+ let cache2 = /* @__PURE__ */ new Map();
24638
+ if (deps.offsets !== void 0) {
24639
+ cache2 = viewsOfOffsets.get(deps.offsets) ?? /* @__PURE__ */ new Map();
24640
+ viewsOfOffsets.set(deps.offsets, cache2);
24641
+ }
24106
24642
  return (path) => {
24107
24643
  if (path === null)
24108
24644
  return UNREADABLE2;
24109
24645
  const hit = cache2.get(path);
24110
24646
  if (hit !== void 0)
24111
24647
  return hit;
24112
- const view = viewRecord(path, deps);
24648
+ const view = deps.offsets === void 0 ? viewRecord(path, deps) : viewRecordFrom(path, deps, deps.offsets);
24113
24649
  cache2.set(path, view);
24114
24650
  return view;
24115
24651
  };
24116
24652
  }
24653
+ var TIME_READ_OFFSETS_FILENAME = "read-offsets.json";
24654
+ function loadReadOffsets(dir, phase, openedAt) {
24655
+ const fresh = { phase, openedAt, files: /* @__PURE__ */ new Map() };
24656
+ try {
24657
+ const o = plainObject(JSON.parse(readFileSync15(join11(dir, TIME_READ_OFFSETS_FILENAME), "utf-8")));
24658
+ if (o === null || o["phase"] !== phase || o["openedAt"] !== openedAt)
24659
+ return fresh;
24660
+ const files = plainObject(o["files"]);
24661
+ if (files === null)
24662
+ return fresh;
24663
+ const out = /* @__PURE__ */ new Map();
24664
+ for (const [path, entry] of Object.entries(files)) {
24665
+ const e = plainObject(entry);
24666
+ const offset = e?.["offset"];
24667
+ const lines2 = e?.["lines"];
24668
+ if (typeof offset !== "number" || !Number.isSafeInteger(offset) || offset < 0 || !Array.isArray(lines2))
24669
+ return fresh;
24670
+ if (!lines2.every((line2) => recordLineOfValue(line2) !== null))
24671
+ return fresh;
24672
+ out.set(path, { offset, lines: lines2.map((line2) => reducedLine(line2)) });
24673
+ }
24674
+ return { phase, openedAt, files: out };
24675
+ } catch {
24676
+ return fresh;
24677
+ }
24678
+ }
24679
+ function saveReadOffsets(dir, offsets) {
24680
+ try {
24681
+ writeJsonFile(join11(dir, TIME_READ_OFFSETS_FILENAME), { phase: offsets.phase, openedAt: offsets.openedAt, files: Object.fromEntries(offsets.files) });
24682
+ } catch {
24683
+ }
24684
+ }
24117
24685
  function isPrompt(line2) {
24118
24686
  const o = line2.value;
24119
24687
  if (o["type"] !== "user" || line2.t === null || "toolUseResult" in o)
@@ -24304,23 +24872,76 @@ function tokensOf(usage) {
24304
24872
  }
24305
24873
  return out;
24306
24874
  }
24307
- function builderTaskOf(own, t) {
24875
+ var TIME_SETTLED_FILENAME = "settled.json";
24876
+ function taskNamedIn(text, taskIds) {
24877
+ let named = null;
24878
+ for (const id of new Set(taskIds)) {
24879
+ if (id === "")
24880
+ continue;
24881
+ const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24882
+ if (!new RegExp(`(?<![A-Za-z0-9_-])${escaped}(?![A-Za-z0-9_-])`).test(text))
24883
+ continue;
24884
+ if (named !== null)
24885
+ return null;
24886
+ named = id;
24887
+ }
24888
+ return named;
24889
+ }
24890
+ function spawnDescriptionOf(recordPath, deps) {
24891
+ if (!recordPath.endsWith(".jsonl"))
24892
+ return null;
24893
+ try {
24894
+ const text = (deps.readFile ?? ((p) => readFileSync15(p, "utf-8")))(`${recordPath.slice(0, -".jsonl".length)}.meta.json`);
24895
+ const value = JSON.parse(text);
24896
+ if (typeof value !== "object" || value === null || Array.isArray(value))
24897
+ return null;
24898
+ const description = value["description"];
24899
+ return typeof description === "string" ? description : null;
24900
+ } catch {
24901
+ return null;
24902
+ }
24903
+ }
24904
+ function sameFolder(a, b) {
24905
+ const pa = folderParts(a);
24906
+ const pb = folderParts(b);
24907
+ return pa.length === pb.length && pa.every((part, k) => part === pb[k]);
24908
+ }
24909
+ function nextTaskOf(own, t) {
24308
24910
  let next;
24309
- let last;
24310
24911
  for (const lane of own) {
24311
24912
  if (lane.task === null)
24312
24913
  continue;
24313
- const times = lane.events.map((e) => msOf(e.at));
24314
- const after = times.find((at) => at > t);
24914
+ const after = lane.events.map((e) => msOf(e.at)).find((at) => at > t);
24315
24915
  if (after !== void 0 && (next === void 0 || after < next.t))
24316
24916
  next = { task: lane.task, t: after };
24317
- const before = lastAtOrBefore(times, t);
24917
+ }
24918
+ return next?.task ?? null;
24919
+ }
24920
+ function lastTaskOf(own, t) {
24921
+ let last;
24922
+ for (const lane of own) {
24923
+ if (lane.task === null)
24924
+ continue;
24925
+ const before = lastAtOrBefore(lane.events.map((e) => msOf(e.at)), t);
24318
24926
  if (before !== void 0 && (last === void 0 || before >= last.t))
24319
24927
  last = { task: lane.task, t: before };
24320
24928
  }
24321
- return next?.task ?? last?.task ?? null;
24929
+ return last?.task ?? null;
24322
24930
  }
24323
- function readUsage(lanes, window, deps = {}, models) {
24931
+ function nextTaskInFolderOf(lanes, folder2, t) {
24932
+ let next;
24933
+ for (const lane of lanes) {
24934
+ if (lane.task === null)
24935
+ continue;
24936
+ for (const e of lane.events) {
24937
+ const at = msOf(e.at);
24938
+ if (at > t && sameFolder(e.folder, folder2) && (next === void 0 || at < next.t))
24939
+ next = { task: lane.task, t: at };
24940
+ }
24941
+ }
24942
+ return next?.task ?? null;
24943
+ }
24944
+ function readUsage(lanes, window, deps = {}, models, settling = {}) {
24324
24945
  const from = Date.parse(window.from);
24325
24946
  const to = window.to === void 0 || window.to === null ? Infinity : Date.parse(window.to);
24326
24947
  const inWindow = (t) => t >= from && t <= to;
@@ -24336,7 +24957,29 @@ function readUsage(lanes, window, deps = {}, models) {
24336
24957
  return buckets.get(key) ?? buckets.set(key, { source, task, readable, messages: 0, inputTokens: 0, outputTokens: 0, cacheWriteTokens: 0, cacheReadTokens: 0 }).get(key);
24337
24958
  };
24338
24959
  const seen = /* @__PURE__ */ new Set();
24339
- const count = (path, view, taskAt, builderTaskAt = () => null) => {
24960
+ const held = /* @__PURE__ */ new Set();
24961
+ const settled2 = settling.settled ?? /* @__PURE__ */ new Map();
24962
+ const final = settling.final === true;
24963
+ const figureIds = /* @__PURE__ */ new Set();
24964
+ const addToFigure = (view) => {
24965
+ const figure = settling.figure;
24966
+ if (figure === void 0)
24967
+ return;
24968
+ for (const line2 of view.lines) {
24969
+ if (line2.value["type"] !== "assistant" || line2.t === null || !inWindow(line2.t))
24970
+ continue;
24971
+ const message = plainObject(line2.value["message"]);
24972
+ const id = message?.["id"];
24973
+ if (typeof id !== "string" || id === "" || figureIds.has(id))
24974
+ continue;
24975
+ const tokens = tokensOf(message["usage"]);
24976
+ if (tokens === null)
24977
+ continue;
24978
+ figureIds.add(id);
24979
+ figure.claudeCode += tokens[0] + tokens[1] + tokens[2] + tokens[3];
24980
+ }
24981
+ };
24982
+ const count = (path, view, taskAt) => {
24340
24983
  const source = recordSourceOf(path);
24341
24984
  for (const line2 of view.lines) {
24342
24985
  const o = line2.value;
@@ -24346,19 +24989,22 @@ function readUsage(lanes, window, deps = {}, models) {
24346
24989
  if (typeof message !== "object" || message === null)
24347
24990
  continue;
24348
24991
  const id = message["id"];
24349
- if (typeof id !== "string" || id === "" || seen.has(id))
24992
+ if (typeof id !== "string" || id === "" || seen.has(id) || held.has(id))
24350
24993
  continue;
24351
24994
  const tokens = tokensOf(message["usage"]);
24352
24995
  if (tokens === null)
24353
24996
  continue;
24997
+ const task = taskAt(line2, id);
24998
+ if (task === void 0) {
24999
+ held.add(id);
25000
+ continue;
25001
+ }
24354
25002
  seen.add(id);
24355
- const task = taskAt(line2);
24356
25003
  const bucket = bucketFor(source, task, true);
24357
25004
  bucket.messages += 1;
24358
25005
  const model = message["model"];
24359
- const modelTask = models === void 0 ? null : task ?? builderTaskAt(line2);
24360
- if (models !== void 0 && modelTask !== null && isTalliedModel(model)) {
24361
- const ofTask = models.get(modelTask) ?? models.set(modelTask, /* @__PURE__ */ new Map()).get(modelTask);
25006
+ if (models !== void 0 && task !== null && isTalliedModel(model)) {
25007
+ const ofTask = models.get(task) ?? models.set(task, /* @__PURE__ */ new Map()).get(task);
24362
25008
  ofTask.set(model, (ofTask.get(model) ?? 0) + 1);
24363
25009
  }
24364
25010
  bucket.inputTokens += tokens[0];
@@ -24369,42 +25015,77 @@ function readUsage(lanes, window, deps = {}, models) {
24369
25015
  };
24370
25016
  for (const main2 of mains) {
24371
25017
  const view = read(main2);
25018
+ addToFigure(view);
24372
25019
  if (view.readable) {
25020
+ let coordinatingFrom = Infinity;
25021
+ for (const lane of lanes) {
25022
+ if (lane.agent === MAIN_AGENT || lane.transcript !== main2)
25023
+ continue;
25024
+ for (const e of lane.events)
25025
+ coordinatingFrom = Math.min(coordinatingFrom, msOf(e.at));
25026
+ }
24373
25027
  count(main2, view, (line2) => {
25028
+ if (line2.t >= coordinatingFrom)
25029
+ return null;
24374
25030
  const branch = line2.value["gitBranch"];
24375
25031
  return taskOfBranch(typeof branch === "string" ? branch : null, window.taskIds);
24376
25032
  });
24377
25033
  }
24378
- const folder = subagentsFolderOf(main2);
24379
- if (folder === null)
25034
+ const folder2 = subagentsFolderOf(main2);
25035
+ if (folder2 === null)
24380
25036
  continue;
24381
25037
  let names;
24382
25038
  try {
24383
- names = listDir(folder).filter((name) => /^agent-[A-Za-z0-9_-]+\.jsonl$/.test(name)).sort();
25039
+ names = listDir(folder2).filter((name) => /^agent-[A-Za-z0-9_-]+\.jsonl$/.test(name)).sort();
24384
25040
  } catch {
24385
25041
  continue;
24386
25042
  }
24387
25043
  for (const name of names) {
24388
- const path = `${folder}${separatorOf(main2)}${name}`;
25044
+ const path = `${folder2}${separatorOf(main2)}${name}`;
24389
25045
  const view2 = read(path);
25046
+ addToFigure(view2);
24390
25047
  if (!view2.readable)
24391
25048
  continue;
24392
25049
  const agent = name.slice("agent-".length, -".jsonl".length);
24393
25050
  const own = active.filter((lane) => lane.agent === agent && lane.transcript === main2);
24394
25051
  if (own.length === 0)
24395
25052
  continue;
24396
- count(path, view2, (line2) => {
25053
+ const ownAll = lanes.filter((lane) => lane.agent === agent && lane.transcript === main2);
25054
+ let description;
25055
+ count(path, view2, (line2, id) => {
25056
+ const t = line2.t;
24397
25057
  let best;
24398
- for (const lane of own) {
24399
- const latest = lastAtOrBefore(lane.events.map((e) => msOf(e.at)), line2.t);
25058
+ for (const lane2 of own) {
25059
+ const latest = lastAtOrBefore(lane2.events.map((e) => msOf(e.at)), t);
24400
25060
  if (latest !== void 0 && (best === void 0 || latest >= best.t))
24401
- best = { lane, t: latest };
25061
+ best = { lane: lane2, t: latest };
24402
25062
  }
24403
- if (best !== void 0)
24404
- return best.lane.task;
24405
- const first = [...own].sort((a, b) => msOf(a.events[0].at) - msOf(b.events[0].at))[0];
24406
- return first?.task ?? null;
24407
- }, (line2) => builderTaskOf(own, line2.t));
25063
+ const lane = best?.lane ?? [...own].sort((a, b) => msOf(a.events[0].at) - msOf(b.events[0].at))[0];
25064
+ if (lane.task !== null)
25065
+ return lane.task;
25066
+ if (settled2.has(id)) {
25067
+ const task2 = settled2.get(id);
25068
+ return task2 !== null && window.taskIds.includes(task2) ? task2 : null;
25069
+ }
25070
+ const next = nextTaskOf(ownAll, t);
25071
+ if (next !== null)
25072
+ return next;
25073
+ if (!final)
25074
+ return void 0;
25075
+ const before = lane.events.filter((e) => msOf(e.at) <= t);
25076
+ const folder3 = (before[before.length - 1] ?? lane.events[0]).folder;
25077
+ let task = nextTaskInFolderOf(lanes, folder3, t);
25078
+ if (task === null) {
25079
+ if (description === void 0)
25080
+ description = spawnDescriptionOf(path, deps);
25081
+ if (description !== null)
25082
+ task = taskNamedIn(description, window.taskIds);
25083
+ }
25084
+ if (task === null)
25085
+ task = lastTaskOf(ownAll, t);
25086
+ settled2.set(id, task);
25087
+ return task;
25088
+ });
24408
25089
  }
24409
25090
  }
24410
25091
  const codexLanes = active.filter((lane) => lane.rollout !== null && lane.transcript === null);
@@ -24426,6 +25107,8 @@ function readUsage(lanes, window, deps = {}, models) {
24426
25107
  best = { lane, t: latest };
24427
25108
  }
24428
25109
  const bucket = bucketFor(source, (best?.lane ?? first)?.task ?? null, true);
25110
+ if (settling.figure !== void 0)
25111
+ settling.figure.codex += step.inputTokens + step.outputTokens + step.cacheWriteTokens + step.cacheReadTokens;
24429
25112
  bucket.messages += 1;
24430
25113
  bucket.inputTokens += step.inputTokens;
24431
25114
  bucket.outputTokens += step.outputTokens;
@@ -24446,13 +25129,13 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
24446
25129
  function phaseModelsPath(engagementId, home) {
24447
25130
  if (!isEngagementId(engagementId))
24448
25131
  return null;
24449
- return join10(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, PHASE_MODELS_FILENAME);
25132
+ return join11(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, PHASE_MODELS_FILENAME);
24450
25133
  }
24451
25134
  function writePhaseModels(path, phase, openedAt, tally) {
24452
25135
  try {
24453
25136
  let tasks = {};
24454
25137
  try {
24455
- const prior = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync14(path, "utf-8")));
25138
+ const prior = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync15(path, "utf-8")));
24456
25139
  if (prior.success && prior.data.phase === phase && prior.data.openedAt === openedAt)
24457
25140
  tasks = prior.data.tasks;
24458
25141
  } catch {
@@ -24480,14 +25163,14 @@ function timeLogDir(engagementId, home) {
24480
25163
  return path === null ? null : dirname4(path);
24481
25164
  }
24482
25165
  function writeJsonFile(path, value) {
24483
- mkdirSync5(dirname4(path), { recursive: true, mode: 448 });
25166
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
24484
25167
  const tmp = `${path}.${process.pid}.tmp`;
24485
- writeFileSync7(tmp, JSON.stringify(value), { mode: 384 });
24486
- renameSync5(tmp, path);
25168
+ writeFileSync8(tmp, JSON.stringify(value), { mode: 384 });
25169
+ renameSync6(tmp, path);
24487
25170
  }
24488
25171
  function readAttemptedAt(dir) {
24489
25172
  try {
24490
- const parsed = JSON.parse(readFileSync14(join10(dir, TIME_THROTTLE_FILENAME), "utf-8"));
25173
+ const parsed = JSON.parse(readFileSync15(join11(dir, TIME_THROTTLE_FILENAME), "utf-8"));
24491
25174
  return typeof parsed.attemptedAt === "number" ? parsed.attemptedAt : null;
24492
25175
  } catch {
24493
25176
  return null;
@@ -24495,7 +25178,7 @@ function readAttemptedAt(dir) {
24495
25178
  }
24496
25179
  function writeAttemptedAt(dir, at) {
24497
25180
  try {
24498
- writeJsonFile(join10(dir, TIME_THROTTLE_FILENAME), { attemptedAt: at });
25181
+ writeJsonFile(join11(dir, TIME_THROTTLE_FILENAME), { attemptedAt: at });
24499
25182
  } catch {
24500
25183
  }
24501
25184
  }
@@ -24513,7 +25196,7 @@ function stringRecord(value) {
24513
25196
  function readTimeCursor(dir, phase, openedAt) {
24514
25197
  const fresh = { phase, openedAt, sentThrough: null, sentIds: {}, usage: {} };
24515
25198
  try {
24516
- const o = JSON.parse(readFileSync14(join10(dir, TIME_CURSOR_FILENAME), "utf-8"));
25199
+ const o = JSON.parse(readFileSync15(join11(dir, TIME_CURSOR_FILENAME), "utf-8"));
24517
25200
  if (o["phase"] !== phase || o["openedAt"] !== openedAt)
24518
25201
  return fresh;
24519
25202
  const sentThrough = o["sentThrough"];
@@ -24526,6 +25209,29 @@ function readTimeCursor(dir, phase, openedAt) {
24526
25209
  return fresh;
24527
25210
  }
24528
25211
  }
25212
+ function readSettled(dir, phase, openedAt) {
25213
+ const out = /* @__PURE__ */ new Map();
25214
+ try {
25215
+ const o = JSON.parse(readFileSync15(join11(dir, TIME_SETTLED_FILENAME), "utf-8"));
25216
+ if (o["phase"] !== phase || o["openedAt"] !== openedAt)
25217
+ return out;
25218
+ const tasks = o["tasks"];
25219
+ if (typeof tasks !== "object" || tasks === null || Array.isArray(tasks))
25220
+ return out;
25221
+ for (const [id, task] of Object.entries(tasks)) {
25222
+ if (task === null || typeof task === "string")
25223
+ out.set(id, task);
25224
+ }
25225
+ } catch {
25226
+ }
25227
+ return out;
25228
+ }
25229
+ function writeSettled(dir, phase, openedAt, settled2) {
25230
+ try {
25231
+ writeJsonFile(join11(dir, TIME_SETTLED_FILENAME), { phase, openedAt, tasks: Object.fromEntries(settled2) });
25232
+ } catch {
25233
+ }
25234
+ }
24529
25235
  function usageKey(bucket) {
24530
25236
  return `${bucket.source}\0${bucket.task ?? ""}`;
24531
25237
  }
@@ -24612,33 +25318,33 @@ var PRUNE_SETTLE_MS = 50;
24612
25318
  async function pruneTimeLog(path, openedAt) {
24613
25319
  const aside = `${path}.prune-${process.pid}`;
24614
25320
  try {
24615
- renameSync5(path, aside);
25321
+ renameSync6(path, aside);
24616
25322
  } catch {
24617
25323
  return;
24618
25324
  }
24619
25325
  let text = "";
24620
25326
  let written = false;
24621
25327
  try {
24622
- text = readFileSync14(aside, "utf-8");
25328
+ text = readFileSync15(aside, "utf-8");
24623
25329
  const { kept } = prunedTimeLog(text, openedAt);
24624
25330
  if (kept !== "")
24625
- appendFileSync2(path, kept, { mode: 384 });
25331
+ appendFileSync3(path, kept, { mode: 384 });
24626
25332
  written = true;
24627
25333
  let seen = Buffer.byteLength(text, "utf-8");
24628
25334
  for (; ; ) {
24629
- await new Promise((resolve11) => setTimeout(resolve11, PRUNE_SETTLE_MS));
24630
- const after = readFileSync14(aside);
25335
+ await new Promise((resolve12) => setTimeout(resolve12, PRUNE_SETTLE_MS));
25336
+ const after = readFileSync15(aside);
24631
25337
  if (after.length <= seen)
24632
25338
  break;
24633
- appendFileSync2(path, after.subarray(seen), { mode: 384 });
25339
+ appendFileSync3(path, after.subarray(seen), { mode: 384 });
24634
25340
  seen = after.length;
24635
25341
  }
24636
- unlinkSync4(aside);
25342
+ unlinkSync5(aside);
24637
25343
  } catch {
24638
25344
  try {
24639
25345
  if (!written) {
24640
- appendFileSync2(path, readFileSync14(aside, "utf-8"), { mode: 384 });
24641
- unlinkSync4(aside);
25346
+ appendFileSync3(path, readFileSync15(aside, "utf-8"), { mode: 384 });
25347
+ unlinkSync5(aside);
24642
25348
  }
24643
25349
  } catch {
24644
25350
  }
@@ -24653,7 +25359,17 @@ function conflictReason(body) {
24653
25359
  const error51 = body.error;
24654
25360
  return error51 === "basis-moved" || error51 === "phase-closed" ? error51 : null;
24655
25361
  }
24656
- async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
25362
+ async function attemptTimeRecord(config2, controlOrigin, deps = {}, options = {}) {
25363
+ return (await attemptAndTell(config2, controlOrigin, deps, options)).result;
25364
+ }
25365
+ async function attemptAndTell(config2, controlOrigin, deps, options) {
25366
+ let wroteFigure = false;
25367
+ const result = await attemptOnce(config2, controlOrigin, deps, options, () => {
25368
+ wroteFigure = true;
25369
+ });
25370
+ return { result, wroteFigure };
25371
+ }
25372
+ async function attemptOnce(config2, controlOrigin, deps, options, onFigureWritten) {
24657
25373
  const fetchFn = deps.fetchFn ?? globalThis.fetch;
24658
25374
  const base = `${controlOrigin}/engagements/${encodeURIComponent(config2.guardEngagementId)}/time`;
24659
25375
  const authorization = `Bearer ${config2.guardServiceToken}`;
@@ -24671,25 +25387,38 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24671
25387
  const basis = parsedBasis.data;
24672
25388
  if (basis.outcome === "nothing-to-record")
24673
25389
  return { outcome: "not-sent", reason: "nothing-to-record" };
24674
- const logPath = join10(dir, TIME_LOG_FILENAME);
25390
+ const logPath2 = join11(dir, TIME_LOG_FILENAME);
24675
25391
  let text = "";
24676
25392
  try {
24677
- text = readFileSync14(logPath, "utf-8");
25393
+ text = readFileSync15(logPath2, "utf-8");
24678
25394
  } catch {
24679
25395
  text = "";
24680
25396
  }
24681
25397
  const lanes = buildLanes(parseTimeLog(text), basis.taskIds);
24682
- const intervals = buildIntervals(lanes, deps);
25398
+ const offsets = loadReadOffsets(dir, basis.phase, basis.openedAt);
25399
+ const readDeps = { ...deps, offsets };
25400
+ const intervals = buildIntervals(lanes, readDeps);
24683
25401
  const models = /* @__PURE__ */ new Map();
24684
- const usage = readUsage(lanes, { from: basis.openedAt, taskIds: basis.taskIds }, deps, models);
25402
+ const settled2 = readSettled(dir, basis.phase, basis.openedAt);
25403
+ const figure = { claudeCode: 0, codex: 0 };
25404
+ const final = options.final === true;
25405
+ const usage = readUsage(lanes, { from: basis.openedAt, taskIds: basis.taskIds }, readDeps, models, { final, settled: settled2, figure });
25406
+ saveReadOffsets(dir, offsets);
25407
+ const routing = routingFilePath(config2.guardEngagementId, deps.home);
25408
+ if (routing !== null)
25409
+ addCheapStarted(routing, basis.phase, tasksBuiltIn(lanes, usage, { from: basis.openedAt, taskIds: basis.taskIds }));
25410
+ if (final)
25411
+ writeSettled(dir, basis.phase, basis.openedAt, settled2);
24685
25412
  const limitDir = phaseLimitDir(config2.guardEngagementId, deps.home);
24686
25413
  if (limitDir !== null) {
24687
- writePhaseUsage(limitDir, {
25414
+ const wrote = writePhaseUsage(limitDir, {
24688
25415
  phase: basis.phase,
24689
25416
  openedAt: basis.openedAt,
24690
- tokens: phaseTokensOf(usage),
25417
+ tokens: figureOf(figure, options.agent),
24691
25418
  at: new Date((deps.now ?? Date.now)()).toISOString()
24692
25419
  });
25420
+ if (wrote)
25421
+ onFigureWritten();
24693
25422
  }
24694
25423
  const modelsPath = phaseModelsPath(config2.guardEngagementId, deps.home);
24695
25424
  if (modelsPath !== null)
@@ -24698,7 +25427,7 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24698
25427
  const selected = selectTimeReport(cursor, intervals, usage);
24699
25428
  if (selected.intervals.length === 0 && selected.usage.length === 0) {
24700
25429
  if (wouldPrune(text, basis.openedAt))
24701
- await pruneTimeLog(logPath, basis.openedAt);
25430
+ await pruneTimeLog(logPath2, basis.openedAt);
24702
25431
  return { outcome: "recorded" };
24703
25432
  }
24704
25433
  const report = { phase: basis.phase, openedAt: basis.openedAt, ...selected };
@@ -24714,14 +25443,14 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24714
25443
  if (!timeReportResultSchema.safeParse(sent.body).success)
24715
25444
  return { outcome: "not-sent", reason: "refused" };
24716
25445
  try {
24717
- writeJsonFile(join10(dir, TIME_CURSOR_FILENAME), advanceTimeCursor(cursor, selected));
25446
+ writeJsonFile(join11(dir, TIME_CURSOR_FILENAME), advanceTimeCursor(cursor, selected));
24718
25447
  } catch {
24719
25448
  }
24720
25449
  if (wouldPrune(text, basis.openedAt))
24721
- await pruneTimeLog(logPath, basis.openedAt);
25450
+ await pruneTimeLog(logPath2, basis.openedAt);
24722
25451
  return { outcome: "recorded" };
24723
25452
  }
24724
- async function runTimeVerb(deps = {}) {
25453
+ async function runTimeVerb(deps = {}, options = {}) {
24725
25454
  let result;
24726
25455
  try {
24727
25456
  const env = deps.env ?? process.env;
@@ -24731,14 +25460,14 @@ async function runTimeVerb(deps = {}) {
24731
25460
  result = { outcome: "not-sent", reason: "not-configured" };
24732
25461
  } else {
24733
25462
  writeAttemptedAt(dir, (deps.now ?? Date.now)());
24734
- result = await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps);
25463
+ result = await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps, options);
24735
25464
  }
24736
25465
  } catch {
24737
25466
  result = { outcome: "not-sent", reason: "unreachable" };
24738
25467
  }
24739
25468
  return timeRunResultSchema.parse(result);
24740
25469
  }
24741
- async function sendTimeRecord(deps = {}) {
25470
+ async function sendTimeRecord(deps = {}, options = {}) {
24742
25471
  try {
24743
25472
  const env = deps.env ?? process.env;
24744
25473
  const configured = readConfig(env);
@@ -24749,17 +25478,170 @@ async function sendTimeRecord(deps = {}) {
24749
25478
  return;
24750
25479
  const now = (deps.now ?? Date.now)();
24751
25480
  const last = readAttemptedAt(dir);
24752
- if (last !== null && now - last < TIME_MIN_INTERVAL_MS && now >= last)
24753
- return;
24754
- writeAttemptedAt(dir, now);
24755
- await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps);
25481
+ let wroteFigure = false;
25482
+ if (!(last !== null && now - last < TIME_MIN_INTERVAL_MS && now >= last)) {
25483
+ writeAttemptedAt(dir, now);
25484
+ wroteFigure = (await attemptAndTell(configured.config, readControlOrigin(env).origin, deps, { agent: options.agent })).wroteFigure;
25485
+ }
25486
+ if (!wroteFigure)
25487
+ recountPhaseUsage(configured.config, deps, options);
25488
+ } catch {
25489
+ }
25490
+ }
25491
+ function recountPhaseUsage(config2, deps = {}, options = {}) {
25492
+ try {
25493
+ const limitDir = phaseLimitDir(config2.guardEngagementId, deps.home);
25494
+ const dir = timeLogDir(config2.guardEngagementId, deps.home);
25495
+ if (limitDir === null || dir === null)
25496
+ return false;
25497
+ const last = readPhaseUsageFile(limitDir);
25498
+ if (last === null)
25499
+ return false;
25500
+ let text = "";
25501
+ try {
25502
+ text = readFileSync15(join11(dir, TIME_LOG_FILENAME), "utf-8");
25503
+ } catch {
25504
+ text = "";
25505
+ }
25506
+ const lanes = buildLanes(parseTimeLog(text), []);
25507
+ const offsets = loadReadOffsets(dir, last.phase, last.openedAt);
25508
+ const figure = { claudeCode: 0, codex: 0 };
25509
+ readUsage(lanes, { from: last.openedAt, taskIds: [] }, { ...deps, offsets }, void 0, { figure });
25510
+ saveReadOffsets(dir, offsets);
25511
+ return writePhaseUsage(limitDir, {
25512
+ phase: last.phase,
25513
+ openedAt: last.openedAt,
25514
+ tokens: figureOf(figure, options.agent),
25515
+ at: new Date((deps.now ?? Date.now)()).toISOString()
25516
+ });
24756
25517
  } catch {
25518
+ return false;
24757
25519
  }
24758
25520
  }
25521
+ function figureOf(figure, agent) {
25522
+ return Math.max(0, Math.round(agent === "codex" ? figure.codex : figure.claudeCode));
25523
+ }
25524
+ var ROUTING_LOCK_FILENAME = "routing.lock";
25525
+ var ROUTING_LOCK_GIVE_UP_MS = 50;
25526
+ var ROUTING_LOCK_STALE_MS = 5 * 1e3;
25527
+ var ROUTING_LOCK_POLL_MS = 2;
25528
+ function routingFilePath(engagementId, home) {
25529
+ if (!isEngagementId(engagementId))
25530
+ return null;
25531
+ return join11(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ROUTING_FILENAME);
25532
+ }
25533
+ function sleepSync2(ms) {
25534
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
25535
+ }
25536
+ function addCheapStarted(path, phase, tasks, giveUpMs = ROUTING_LOCK_GIVE_UP_MS) {
25537
+ if (tasks.length === 0)
25538
+ return true;
25539
+ try {
25540
+ const lock = join11(dirname4(path), ROUTING_LOCK_FILENAME);
25541
+ const giveUpAt = Date.now() + giveUpMs;
25542
+ while (!tryTakeLockFile(lock, ROUTING_LOCK_STALE_MS)) {
25543
+ if (Date.now() >= giveUpAt)
25544
+ return false;
25545
+ sleepSync2(ROUTING_LOCK_POLL_MS);
25546
+ }
25547
+ try {
25548
+ let started = [];
25549
+ try {
25550
+ const parsed = routingRecordSchema.safeParse(JSON.parse(readFileSync15(path, "utf-8")));
25551
+ if (parsed.success && parsed.data.phase === phase)
25552
+ started = [...parsed.data.cheapStarted];
25553
+ } catch {
25554
+ started = [];
25555
+ }
25556
+ const added = tasks.filter((t, i) => !started.includes(t) && tasks.indexOf(t) === i);
25557
+ if (added.length === 0)
25558
+ return true;
25559
+ const record2 = { phase, cheapStarted: [...started, ...added] };
25560
+ if (!routingRecordSchema.safeParse(record2).success)
25561
+ return false;
25562
+ writeJsonFile(path, record2);
25563
+ return true;
25564
+ } finally {
25565
+ releaseLockFile(lock);
25566
+ }
25567
+ } catch {
25568
+ return false;
25569
+ }
25570
+ }
25571
+ function recordSpawnedTask(toolInput, deps) {
25572
+ try {
25573
+ if (typeof toolInput !== "object" || toolInput === null || Array.isArray(toolInput))
25574
+ return null;
25575
+ const engagementId = readConfig(deps.env ?? process.env).config?.guardEngagementId;
25576
+ if (engagementId === void 0)
25577
+ return null;
25578
+ const path = routingFilePath(engagementId, deps.home);
25579
+ if (path === null)
25580
+ return null;
25581
+ const parsed = taskPlanSchema.safeParse(JSON.parse(readFileSync15(join11(deps.projectDir, TASK_PLAN_FILE), "utf-8")));
25582
+ if (!parsed.success)
25583
+ return null;
25584
+ const plan = parsed.data;
25585
+ const ids = plan.tasks.map((t) => t.id);
25586
+ const textOf = (key) => {
25587
+ const v = toolInput[key];
25588
+ return typeof v === "string" ? v : null;
25589
+ };
25590
+ const description = textOf("description");
25591
+ const prompt = textOf("prompt");
25592
+ const named = (description === null ? null : taskNamedIn(description, ids)) ?? (prompt === null ? null : taskNamedIn(prompt, ids));
25593
+ if (named === null)
25594
+ return null;
25595
+ const task = plan.tasks.find((t) => t.id === named);
25596
+ if (task === void 0 || task.kind !== "build" || task.cheapFirst !== true)
25597
+ return null;
25598
+ return addCheapStarted(path, plan.phase, [named]) ? named : null;
25599
+ } catch {
25600
+ return null;
25601
+ }
25602
+ }
25603
+ function tasksBuiltIn(lanes, usage, window) {
25604
+ const from = Date.parse(window.from);
25605
+ const to = window.to === void 0 || window.to === null ? Infinity : Date.parse(window.to);
25606
+ const inWindow = (t) => t >= from && t <= to;
25607
+ const out = [];
25608
+ const add = (task) => {
25609
+ if (task !== null && window.taskIds.includes(task) && !out.includes(task))
25610
+ out.push(task);
25611
+ };
25612
+ for (const bucket of usage)
25613
+ if (bucket.messages > 0)
25614
+ add(bucket.task);
25615
+ for (const lane of lanes) {
25616
+ if (lane.task === null)
25617
+ continue;
25618
+ const times = lane.events.map((e) => msOf(e.at)).filter(inWindow);
25619
+ if (times.length === 0)
25620
+ continue;
25621
+ const codex = lane.rollout !== null && lane.transcript === null;
25622
+ if (lane.agent !== MAIN_AGENT || codex) {
25623
+ add(lane.task);
25624
+ continue;
25625
+ }
25626
+ let coordinatingFrom = Infinity;
25627
+ for (const other of lanes) {
25628
+ if (other.agent === MAIN_AGENT)
25629
+ continue;
25630
+ const sameSession = lane.session !== null ? other.session === lane.session : lane.transcript !== null && other.transcript === lane.transcript;
25631
+ if (!sameSession)
25632
+ continue;
25633
+ for (const e of other.events)
25634
+ coordinatingFrom = Math.min(coordinatingFrom, msOf(e.at));
25635
+ }
25636
+ if (times.some((t) => t < coordinatingFrom))
25637
+ add(lane.task);
25638
+ }
25639
+ return out;
25640
+ }
24759
25641
 
24760
25642
  // dist/own-work.js
24761
- import { appendFileSync as appendFileSync3, lstatSync as lstatSync2, readFileSync as readFileSync15, realpathSync as realpathSync6 } from "node:fs";
24762
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute2, join as join11, relative as relative5, resolve as resolve4, sep as sep3 } from "node:path";
25643
+ import { appendFileSync as appendFileSync4, lstatSync as lstatSync2, readFileSync as readFileSync16, realpathSync as realpathSync6 } from "node:fs";
25644
+ import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute2, join as join12, relative as relative5, resolve as resolve4, sep as sep3 } from "node:path";
24763
25645
 
24764
25646
  // dist/risky/types.js
24765
25647
  var RISKY_ACTION_TYPES = [
@@ -24793,7 +25675,7 @@ function ownWorkFileName(sessionId) {
24793
25675
  function ownWorkPath(sessionId, dir) {
24794
25676
  if (typeof sessionId !== "string" || !livenessIdOk(sessionId))
24795
25677
  return null;
24796
- return join11(dir ?? markerDir(), ownWorkFileName(sessionId));
25678
+ return join12(dir ?? markerDir(), ownWorkFileName(sessionId));
24797
25679
  }
24798
25680
  function inputString2(input, key) {
24799
25681
  if (typeof input !== "object" || input === null)
@@ -24811,7 +25693,7 @@ function append(sessionId, line2, dir) {
24811
25693
  const path = ownWorkPath(sessionId, dir);
24812
25694
  if (path === null)
24813
25695
  return;
24814
- appendFileSync3(path, `${JSON.stringify(line2)}
25696
+ appendFileSync4(path, `${JSON.stringify(line2)}
24815
25697
  `, { mode: 384 });
24816
25698
  }
24817
25699
  function recordOwnShell(input, at, deps = {}) {
@@ -24857,7 +25739,7 @@ function readOwnWork(sessionId, dir) {
24857
25739
  const path = ownWorkPath(sessionId, dir);
24858
25740
  if (path === null)
24859
25741
  return [];
24860
- return parseOwnWork(readFileSync15(path, "utf-8"));
25742
+ return parseOwnWork(readFileSync16(path, "utf-8"));
24861
25743
  } catch {
24862
25744
  return [];
24863
25745
  }
@@ -24892,7 +25774,7 @@ function realPathOf(path) {
24892
25774
  let at = resolve4(path);
24893
25775
  for (; ; ) {
24894
25776
  try {
24895
- return join11(realpathSync6.native(at), ...missing.reverse());
25777
+ return join12(realpathSync6.native(at), ...missing.reverse());
24896
25778
  } catch {
24897
25779
  const parent = dirname5(at);
24898
25780
  if (parent === at)
@@ -24952,7 +25834,7 @@ function ownChangedFiles(repoRoot, files, sessionId, deps = {}) {
24952
25834
  };
24953
25835
  const root = resolve4(repoRoot);
24954
25836
  return files.filter((rel) => {
24955
- const abs = join11(root, rel);
25837
+ const abs = join12(root, rel);
24956
25838
  if (edited.has(realPathOf(abs)))
24957
25839
  return true;
24958
25840
  if (windows.length === 0)
@@ -24966,7 +25848,7 @@ function ownChangedFiles(repoRoot, files, sessionId, deps = {}) {
24966
25848
 
24967
25849
  // dist/hooks/findings.js
24968
25850
  import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
24969
- import { closeSync as closeSync3, existsSync as existsSync6, openSync as openSync3, readFileSync as readFileSync16, renameSync as renameSync6, statSync as statSync4, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "node:fs";
25851
+ import { closeSync as closeSync4, existsSync as existsSync7, openSync as openSync4, readFileSync as readFileSync17, renameSync as renameSync7, statSync as statSync4, unlinkSync as unlinkSync6, writeFileSync as writeFileSync9 } from "node:fs";
24970
25852
  function findingKey(firing) {
24971
25853
  return createHash4("sha256").update(`${firing.guardId}
24972
25854
  ${firing.path ?? ""}
@@ -25009,7 +25891,7 @@ function readFindings(sessionId) {
25009
25891
  return null;
25010
25892
  const session = sessionId;
25011
25893
  try {
25012
- const parsed = JSON.parse(readFileSync16(path, "utf-8"));
25894
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
25013
25895
  if (parsed["version"] !== 1 || !Array.isArray(parsed["findings"]))
25014
25896
  return emptyFindings(session);
25015
25897
  const findings = [];
@@ -25029,12 +25911,12 @@ function writeFindings(file2) {
25029
25911
  return;
25030
25912
  const temp = `${path.slice(0, -".json".length)}.${process.pid}.tmp.json`;
25031
25913
  try {
25032
- writeFileSync8(temp, `${JSON.stringify(file2)}
25914
+ writeFileSync9(temp, `${JSON.stringify(file2)}
25033
25915
  `, { mode: 384 });
25034
- renameSync6(temp, path);
25916
+ renameSync7(temp, path);
25035
25917
  } catch {
25036
25918
  try {
25037
- unlinkSync5(temp);
25919
+ unlinkSync6(temp);
25038
25920
  } catch {
25039
25921
  }
25040
25922
  }
@@ -25050,7 +25932,7 @@ function pause(ms) {
25050
25932
  function takeOverStaleLock(lock) {
25051
25933
  const aside = `${lock}.${process.pid}.${randomUUID2()}.stale`;
25052
25934
  try {
25053
- renameSync6(lock, aside);
25935
+ renameSync7(lock, aside);
25054
25936
  } catch {
25055
25937
  return false;
25056
25938
  }
@@ -25062,17 +25944,17 @@ function takeOverStaleLock(lock) {
25062
25944
  }
25063
25945
  if (!staleIndeed) {
25064
25946
  try {
25065
- renameSync6(aside, lock);
25947
+ renameSync7(aside, lock);
25066
25948
  } catch {
25067
25949
  try {
25068
- unlinkSync5(aside);
25950
+ unlinkSync6(aside);
25069
25951
  } catch {
25070
25952
  }
25071
25953
  }
25072
25954
  return false;
25073
25955
  }
25074
25956
  try {
25075
- unlinkSync5(aside);
25957
+ unlinkSync6(aside);
25076
25958
  } catch {
25077
25959
  }
25078
25960
  return true;
@@ -25085,7 +25967,7 @@ function withFindingsLock(sessionId, body, waitMs = FINDINGS_LOCK_WAIT_MS) {
25085
25967
  const deadline = Date.now() + waitMs;
25086
25968
  for (; ; ) {
25087
25969
  try {
25088
- closeSync3(openSync3(lock, "wx", 384));
25970
+ closeSync4(openSync4(lock, "wx", 384));
25089
25971
  break;
25090
25972
  } catch (err) {
25091
25973
  if (err.code !== "EEXIST")
@@ -25107,31 +25989,31 @@ function withFindingsLock(sessionId, body, waitMs = FINDINGS_LOCK_WAIT_MS) {
25107
25989
  return body();
25108
25990
  } finally {
25109
25991
  try {
25110
- unlinkSync5(lock);
25992
+ unlinkSync6(lock);
25111
25993
  } catch {
25112
25994
  }
25113
25995
  }
25114
25996
  }
25115
- function takeUndelivered(file2, folder) {
25997
+ function takeUndelivered(file2, folder2) {
25116
25998
  const taken = [];
25117
25999
  const findings = file2.findings.map((f) => {
25118
- if (f.delivered || folder !== null && f.folder !== folder)
26000
+ if (f.delivered || folder2 !== null && f.folder !== folder2)
25119
26001
  return f;
25120
26002
  taken.push(f);
25121
26003
  return { ...f, delivered: true };
25122
26004
  });
25123
26005
  return { file: { ...file2, findings }, taken };
25124
26006
  }
25125
- function deliverFindings(sessionId, folder) {
26007
+ function deliverFindings(sessionId, folder2) {
25126
26008
  const path = findingsPath(sessionId);
25127
- if (path === null || !existsSync6(path))
26009
+ if (path === null || !existsSync7(path))
25128
26010
  return [];
25129
26011
  try {
25130
26012
  const taken = withFindingsLock(sessionId, () => {
25131
26013
  const before = readFindings(sessionId);
25132
26014
  if (before === null)
25133
26015
  return [];
25134
- const { file: file2, taken: given } = takeUndelivered(before, folder);
26016
+ const { file: file2, taken: given } = takeUndelivered(before, folder2);
25135
26017
  if (given.length > 0)
25136
26018
  writeFindings(file2);
25137
26019
  return given;
@@ -25141,11 +26023,11 @@ function deliverFindings(sessionId, folder) {
25141
26023
  return [];
25142
26024
  }
25143
26025
  }
25144
- function turnEndBookkeeping(file2, folder, outcome, topLevel, now) {
26026
+ function turnEndBookkeeping(file2, folder2, outcome, topLevel, now) {
25145
26027
  const recorded = [...file2.findings];
25146
26028
  const firing = /* @__PURE__ */ new Map();
25147
26029
  const notRun = new Set(outcome.kind === "evaluated" ? outcome.notRun : []);
25148
- const observed = (f) => f.folder === folder && !notRun.has(f.patternRef);
26030
+ const observed = (f) => f.folder === folder2 && !notRun.has(f.patternRef);
25149
26031
  if (outcome.kind === "evaluated") {
25150
26032
  for (const g of outcome.fired) {
25151
26033
  if (g.severity === "block")
@@ -25161,13 +26043,13 @@ function turnEndBookkeeping(file2, folder, outcome, topLevel, now) {
25161
26043
  const closes = observed(f) && (outcome.kind === "no-own-changes" || outcome.kind === "evaluated" && !firing.has(f.key));
25162
26044
  (closes ? removed : kept).push(f);
25163
26045
  }
25164
- const known = new Set(kept.filter((f) => f.folder === folder).map((f) => f.key));
26046
+ const known = new Set(kept.filter((f) => f.folder === folder2).map((f) => f.key));
25165
26047
  for (const [key, g] of firing) {
25166
26048
  if (known.has(key))
25167
26049
  continue;
25168
26050
  kept.push({
25169
26051
  key,
25170
- folder,
26052
+ folder: folder2,
25171
26053
  guardId: g.guardId,
25172
26054
  patternRef: g.patternRef,
25173
26055
  severity: g.severity,
@@ -25362,7 +26244,7 @@ function patchHeaderPaths(patch) {
25362
26244
  }
25363
26245
 
25364
26246
  // dist/hooks/post-tool-use.js
25365
- import { isAbsolute as isAbsolute3, join as join12, relative as relative6, resolve as resolve5, sep as sep4 } from "node:path";
26247
+ import { isAbsolute as isAbsolute3, join as join13, relative as relative6, resolve as resolve5, sep as sep4 } from "node:path";
25366
26248
  var HANDLED_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
25367
26249
  async function runPostToolUse(readSeat = readSeatConfiguration, profile = AGENT_PROFILES["claude-code"]) {
25368
26250
  const reply = hookReply("PostToolUse", profile);
@@ -25549,7 +26431,7 @@ function insideRepository(path, repoRoot) {
25549
26431
  const rel = relative6(realPathOf(repoRoot), realPathOf(path));
25550
26432
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep4}`) || isAbsolute3(rel))
25551
26433
  return null;
25552
- return join12(repoRoot, rel);
26434
+ return join13(repoRoot, rel);
25553
26435
  }
25554
26436
  function actOnEnvelope2(envelope, reply) {
25555
26437
  const notRun = notRunReport(envelope.notRun);
@@ -25572,17 +26454,17 @@ ${guardLines}${notRunBlock}`);
25572
26454
  }
25573
26455
  }
25574
26456
  function readStdin() {
25575
- return new Promise((resolve11, reject) => {
26457
+ return new Promise((resolve12, reject) => {
25576
26458
  const chunks = [];
25577
26459
  process.stdin.on("data", (chunk) => chunks.push(chunk));
25578
- process.stdin.on("end", () => resolve11(Buffer.concat(chunks).toString("utf-8")));
26460
+ process.stdin.on("end", () => resolve12(Buffer.concat(chunks).toString("utf-8")));
25579
26461
  process.stdin.on("error", reject);
25580
26462
  });
25581
26463
  }
25582
26464
 
25583
26465
  // dist/liveness.js
25584
- import { closeSync as closeSync4, mkdtempSync, openSync as openSync4, readdirSync as readdirSync7, readFileSync as readFileSync17, renameSync as renameSync7, rmSync as rmSync2, statSync as statSync5, unlinkSync as unlinkSync6, writeFileSync as writeFileSync9 } from "node:fs";
25585
- import { join as join13 } from "node:path";
26466
+ import { closeSync as closeSync5, mkdtempSync, openSync as openSync5, readdirSync as readdirSync7, readFileSync as readFileSync18, renameSync as renameSync8, rmSync as rmSync2, statSync as statSync5, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "node:fs";
26467
+ import { join as join14 } from "node:path";
25586
26468
  var LIVE_WINDOW_MS = 10 * 60 * 1e3;
25587
26469
  function field(input, key) {
25588
26470
  if (typeof input !== "object" || input === null)
@@ -25591,10 +26473,10 @@ function field(input, key) {
25591
26473
  return typeof v === "string" && v !== "" ? v : null;
25592
26474
  }
25593
26475
  function indexEntry(dir, sessionId, name) {
25594
- const folder = join13(dir, livenessIndexDirName(sessionId));
26476
+ const folder2 = join14(dir, livenessIndexDirName(sessionId));
25595
26477
  for (let attempt2 = 0; attempt2 < 3; attempt2++) {
25596
26478
  try {
25597
- closeSync4(openSync4(join13(folder, name), "a", 384));
26479
+ closeSync5(openSync5(join14(folder2, name), "a", 384));
25598
26480
  return true;
25599
26481
  } catch (err) {
25600
26482
  if (err.code !== "ENOENT")
@@ -25602,13 +26484,13 @@ function indexEntry(dir, sessionId, name) {
25602
26484
  }
25603
26485
  let tmp = null;
25604
26486
  try {
25605
- tmp = mkdtempSync(`${folder}.`);
26487
+ tmp = mkdtempSync(`${folder2}.`);
25606
26488
  for (const existing of readdirSync7(dir)) {
25607
26489
  if (parseLivenessFileName(existing)?.sessionId === sessionId)
25608
- closeSync4(openSync4(join13(tmp, existing), "a", 384));
26490
+ closeSync5(openSync5(join14(tmp, existing), "a", 384));
25609
26491
  }
25610
- closeSync4(openSync4(join13(tmp, name), "a", 384));
25611
- renameSync7(tmp, folder);
26492
+ closeSync5(openSync5(join14(tmp, name), "a", 384));
26493
+ renameSync8(tmp, folder2);
25612
26494
  return true;
25613
26495
  } catch {
25614
26496
  if (tmp !== null)
@@ -25633,18 +26515,18 @@ function recordLiveness(input, deps = {}) {
25633
26515
  const name = livenessFileName(livenessKey(copy?.common ?? null), sessionId, agentId);
25634
26516
  if (!indexEntry(dir, sessionId, name))
25635
26517
  return null;
25636
- const path = join13(dir, name);
26518
+ const path = join14(dir, name);
25637
26519
  const text = `${cwd}
25638
26520
  ${copy?.top ?? ""}
25639
26521
  `;
25640
26522
  try {
25641
- writeFileSync9(path, text, { encoding: "utf-8", flag: "wx", mode: 384 });
26523
+ writeFileSync10(path, text, { encoding: "utf-8", flag: "wx", mode: 384 });
25642
26524
  return { firstSeen: true };
25643
26525
  } catch (err) {
25644
26526
  if (err.code !== "EEXIST")
25645
26527
  return null;
25646
26528
  }
25647
- writeFileSync9(path, text, { encoding: "utf-8", mode: 384 });
26529
+ writeFileSync10(path, text, { encoding: "utf-8", mode: 384 });
25648
26530
  return { firstSeen: false };
25649
26531
  } catch {
25650
26532
  return null;
@@ -25663,7 +26545,7 @@ function clearLiveness(input, deps = {}) {
25663
26545
  if (p === null || p.sessionId !== sessionId || p.agentId !== agentId)
25664
26546
  continue;
25665
26547
  try {
25666
- unlinkSync6(join13(dir, name));
26548
+ unlinkSync7(join14(dir, name));
25667
26549
  removed += 1;
25668
26550
  } catch {
25669
26551
  }
@@ -25676,7 +26558,7 @@ function clearLiveness(input, deps = {}) {
25676
26558
  }
25677
26559
  function topOf(path) {
25678
26560
  try {
25679
- const top = readFileSync17(path, "utf-8").split("\n")[1] ?? "";
26561
+ const top = readFileSync18(path, "utf-8").split("\n")[1] ?? "";
25680
26562
  return top === "" ? null : top;
25681
26563
  } catch {
25682
26564
  return null;
@@ -25698,7 +26580,7 @@ function readLiveness(sessionId, cwd, deps = {}) {
25698
26580
  const p = parseLivenessFileName(name);
25699
26581
  if (p === null || p.key !== key || p.sessionId !== sessionId)
25700
26582
  continue;
25701
- const full = join13(dir, name);
26583
+ const full = join14(dir, name);
25702
26584
  let seenAt;
25703
26585
  try {
25704
26586
  seenAt = statSync5(full).mtimeMs;
@@ -25744,168 +26626,8 @@ import nodePath5 from "node:path";
25744
26626
 
25745
26627
  // dist/pace-verb.js
25746
26628
  import { mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as readFileSync19, renameSync as renameSync9, statSync as statSync6, writeFileSync as writeFileSync11 } from "node:fs";
25747
- import { homedir as homedir8 } from "node:os";
26629
+ import { homedir as homedir9 } from "node:os";
25748
26630
  import nodePath4 from "node:path";
25749
-
25750
- // dist/scan-lock.js
25751
- import { spawn } from "node:child_process";
25752
- import { linkSync, mkdirSync as mkdirSync6, readFileSync as readFileSync18, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "node:fs";
25753
- import nodePath3 from "node:path";
25754
- var SCAN_LOCK_FILENAME = "scan.lock";
25755
- var SCAN_LOCK_STALE_MS = 15 * 60 * 1e3;
25756
- var SCAN_LOCK_POLL_MS = 100;
25757
- function scanLockPath(home) {
25758
- return nodePath3.join(pacingDir(home), SCAN_LOCK_FILENAME);
25759
- }
25760
- function readLockBody(path) {
25761
- let text;
25762
- try {
25763
- text = readFileSync18(path, "utf-8");
25764
- } catch (err) {
25765
- return err.code === "ENOENT" ? "missing" : null;
25766
- }
25767
- try {
25768
- const v = JSON.parse(text);
25769
- return typeof v["pid"] === "number" && typeof v["at"] === "number" ? { pid: v["pid"], at: v["at"] } : null;
25770
- } catch {
25771
- return null;
25772
- }
25773
- }
25774
- function processAlive(pid) {
25775
- if (!Number.isInteger(pid) || pid <= 0)
25776
- return false;
25777
- try {
25778
- process.kill(pid, 0);
25779
- return true;
25780
- } catch (err) {
25781
- return err.code === "EPERM";
25782
- }
25783
- }
25784
- function isStale(body, staleMs, deps) {
25785
- if (body === null)
25786
- return true;
25787
- const now = (deps.clock ?? Date.now)();
25788
- if (Math.abs(now - body.at) > staleMs)
25789
- return true;
25790
- return !(deps.alive ?? processAlive)(body.pid);
25791
- }
25792
- function writeLock(path, pid, deps, flag) {
25793
- writeFileSync10(path, JSON.stringify({ pid, at: (deps.clock ?? Date.now)() }), { flag, mode: 384 });
25794
- }
25795
- function tryTakeScanLock(deps = {}) {
25796
- return tryTakeLockFile(scanLockPath(deps.home), SCAN_LOCK_STALE_MS, deps);
25797
- }
25798
- function tryTakeLockFile(path, staleMs, deps = {}) {
25799
- const pid = deps.pid ?? process.pid;
25800
- try {
25801
- mkdirSync6(nodePath3.dirname(path), { recursive: true, mode: 448 });
25802
- } catch {
25803
- return false;
25804
- }
25805
- for (let attempt2 = 0; attempt2 < 2; attempt2++) {
25806
- try {
25807
- writeLock(path, pid, deps, "wx");
25808
- return true;
25809
- } catch (err) {
25810
- if (err.code !== "EEXIST")
25811
- return false;
25812
- }
25813
- const seen = readLockBody(path);
25814
- if (seen === "missing")
25815
- continue;
25816
- if (!isStale(seen, staleMs, deps))
25817
- return false;
25818
- const aside = `${path}.${String(pid)}.${String((deps.clock ?? Date.now)())}.stale`;
25819
- try {
25820
- renameSync8(path, aside);
25821
- } catch {
25822
- continue;
25823
- }
25824
- const taken = readLockBody(aside);
25825
- const same = taken !== "missing" && (seen === null ? taken === null : taken !== null && taken.pid === seen.pid && taken.at === seen.at);
25826
- if (!same && taken !== "missing" && !isStale(taken, staleMs, deps)) {
25827
- try {
25828
- linkSync(aside, path);
25829
- } catch {
25830
- }
25831
- try {
25832
- unlinkSync7(aside);
25833
- } catch {
25834
- }
25835
- return false;
25836
- }
25837
- try {
25838
- unlinkSync7(aside);
25839
- } catch {
25840
- }
25841
- }
25842
- return false;
25843
- }
25844
- function releaseLockFile(path, pid = process.pid) {
25845
- const body = readLockBody(path);
25846
- if (body === "missing" || body === null || body.pid !== pid)
25847
- return;
25848
- try {
25849
- unlinkSync7(path);
25850
- } catch {
25851
- }
25852
- }
25853
- function releaseScanLock(deps = {}) {
25854
- releaseLockFile(scanLockPath(deps.home), deps.pid ?? process.pid);
25855
- }
25856
- function sleepSync(ms) {
25857
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
25858
- }
25859
- function scanUnderLock(key, deps = {}, waitMs = 0) {
25860
- const clock = deps.clock ?? Date.now;
25861
- const giveUpAt = clock() + waitMs;
25862
- while (!tryTakeScanLock(deps)) {
25863
- if (clock() >= giveUpAt)
25864
- return null;
25865
- sleepSync(SCAN_LOCK_POLL_MS);
25866
- }
25867
- try {
25868
- return scanRecords(key, deps);
25869
- } finally {
25870
- releaseScanLock(deps);
25871
- }
25872
- }
25873
- var spawnDetachedReal = (command, args2) => {
25874
- const child = spawn(command, [...args2], { detached: true, stdio: "ignore", windowsHide: true });
25875
- child.on("error", () => void 0);
25876
- child.unref();
25877
- return child.pid;
25878
- };
25879
- function startScanChild(key, binaryPath, deps = {}) {
25880
- if (!tryTakeScanLock(deps))
25881
- return false;
25882
- let childPid;
25883
- try {
25884
- childPid = (deps.spawnDetached ?? spawnDetachedReal)(deps.nodePath ?? process.execPath, [
25885
- binaryPath,
25886
- "pace",
25887
- "--scan",
25888
- "--config-folder",
25889
- key,
25890
- "--lock-held"
25891
- ]);
25892
- } catch {
25893
- childPid = void 0;
25894
- }
25895
- if (childPid === void 0) {
25896
- releaseScanLock(deps);
25897
- return false;
25898
- }
25899
- try {
25900
- writeLock(scanLockPath(deps.home), childPid, deps, "w");
25901
- } catch {
25902
- releaseScanLock(deps);
25903
- return false;
25904
- }
25905
- return true;
25906
- }
25907
-
25908
- // dist/pace-verb.js
25909
26631
  var PACE_DEFAULT_TASK_MINUTES = 30;
25910
26632
  var PACE_DURATION_ROUND_MINUTES = 10;
25911
26633
  var PACE_WINDOW_MINUTES = 300;
@@ -26027,7 +26749,7 @@ function suggestPace(input) {
26027
26749
  function enginePacingDir(engagementId, home) {
26028
26750
  if (!isEngagementId(engagementId))
26029
26751
  return null;
26030
- return nodePath4.join(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
26752
+ return nodePath4.join(home ?? homedir9(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
26031
26753
  }
26032
26754
  function readBuildNumber(engagementId, home) {
26033
26755
  const dir = enginePacingDir(engagementId, home);
@@ -26085,7 +26807,7 @@ var USAGE = "Usage: halfcycle-runner pace [--agent claude-code|codex] [--start <
26085
26807
  function parsePaceArgs(args2) {
26086
26808
  let scan = false;
26087
26809
  let lockHeld = false;
26088
- let folder = null;
26810
+ let folder2 = null;
26089
26811
  let phase = null;
26090
26812
  let agents = null;
26091
26813
  let agent = null;
@@ -26103,8 +26825,8 @@ function parsePaceArgs(args2) {
26103
26825
  else if (a === "--lock-held")
26104
26826
  lockHeld = true;
26105
26827
  else if (a === "--config-folder") {
26106
- folder = value();
26107
- if (folder === null)
26828
+ folder2 = value();
26829
+ if (folder2 === null)
26108
26830
  return { kind: "usage", message: USAGE };
26109
26831
  } else if (a === "--start") {
26110
26832
  phase = value();
@@ -26125,9 +26847,9 @@ function parsePaceArgs(args2) {
26125
26847
  if (scan) {
26126
26848
  if (phase !== null || agents !== null || agent !== null)
26127
26849
  return { kind: "usage", message: USAGE };
26128
- return { kind: "scan", folder, lockHeld };
26850
+ return { kind: "scan", folder: folder2, lockHeld };
26129
26851
  }
26130
- if (folder !== null || lockHeld)
26852
+ if (folder2 !== null || lockHeld)
26131
26853
  return { kind: "usage", message: USAGE };
26132
26854
  if (phase === null)
26133
26855
  return agents === null ? { kind: "now", agent: agent ?? "claude-code" } : { kind: "usage", message: USAGE };
@@ -26210,7 +26932,7 @@ async function runPaceVerb(args2, deps = {}) {
26210
26932
  ...deps.home === void 0 ? {} : { home: deps.home },
26211
26933
  now: () => now.getTime(),
26212
26934
  ...deps.fetchFn === void 0 ? {} : { fetchFn: deps.fetchFn }
26213
- });
26935
+ }, { agent: parsed.agent });
26214
26936
  } catch {
26215
26937
  }
26216
26938
  const remembered = readPaceStateFile(pacingDir2).basis;
@@ -26298,6 +27020,7 @@ async function runPaceVerb(args2, deps = {}) {
26298
27020
  ...decision.until !== null ? { until: decision.until.toISOString() } : {},
26299
27021
  keepFreePercent,
26300
27022
  ...suggestion !== void 0 ? { suggestion } : {},
27023
+ ...limitFile !== null ? { usage: { used: readPhaseUsage(pacingDir2, limitFile.phase), limit: limitFile.limit } } : {},
26301
27024
  ...atLimit ? {
26302
27025
  limit: "reached",
26303
27026
  limitHeld: {
@@ -26445,10 +27168,10 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26445
27168
  const basis = read !== null ? { phase: read.phase, keepFreePercent: read.keepFreePercent } : stored.basis;
26446
27169
  if (basis === null)
26447
27170
  return;
26448
- const write = deps.writeState ?? writePaceState;
27171
+ const write2 = deps.writeState ?? writePaceState;
26449
27172
  if (basis.phase === null) {
26450
27173
  if (before !== null || read !== null)
26451
- write(dir, key, null, read !== null ? basis : null);
27174
+ write2(dir, key, null, read !== null ? basis : null);
26452
27175
  return;
26453
27176
  }
26454
27177
  const working = liveCount(readLiveness(field2(input, "session_id"), field2(input, "cwd") ?? process.cwd(), deps.liveness));
@@ -26475,7 +27198,8 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26475
27198
  }
26476
27199
  }
26477
27200
  const reported = readReportedAccount(key, { home: deps.home });
26478
- const number4 = buildNumberFor(engagementId, basis.phase, deps.home)?.number ?? CAREFUL_START_AGENTS;
27201
+ const build = buildNumberFor(engagementId, basis.phase, deps.home);
27202
+ const number4 = build?.number ?? CAREFUL_START_AGENTS;
26479
27203
  const decision = paceOf({ number: number4, keepFreePercent: basis.keepFreePercent, learned, reported, now });
26480
27204
  const settled2 = {
26481
27205
  pace: decision.pace,
@@ -26489,9 +27213,9 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26489
27213
  };
26490
27214
  const told = before === null || before.told === void 0 ? before : { ...before, ...before.told };
26491
27215
  const text = paceChangeText(told, settled2, working, now, basis.keepFreePercent);
26492
- const offer = text === null ? offerText(settled2, learned, reported, now, basis.keepFreePercent) : null;
27216
+ const offer = text === null && build !== null ? offerText(settled2, learned, reported, now, basis.keepFreePercent) : null;
26493
27217
  const after = offer === null ? settled2 : { ...settled2, offeredFor: offeredForNow(settled2.offeredFor, offer.resetsAt, now) };
26494
- write(dir, key, after, read !== null ? basis : null);
27218
+ write2(dir, key, after, read !== null ? basis : null);
26495
27219
  if (text !== null)
26496
27220
  reply.notice(text);
26497
27221
  else if (offer !== null)
@@ -26542,7 +27266,7 @@ function pruneStarts(file2, nowMs) {
26542
27266
  }
26543
27267
  return out;
26544
27268
  }
26545
- function sleepSync2(ms) {
27269
+ function sleepSync3(ms) {
26546
27270
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
26547
27271
  }
26548
27272
  function underStartsLock(dir, giveUpMs, fn) {
@@ -26551,7 +27275,7 @@ function underStartsLock(dir, giveUpMs, fn) {
26551
27275
  while (!tryTakeLockFile(lock, PACE_STARTS_LOCK_STALE_MS)) {
26552
27276
  if (Date.now() >= giveUpAt)
26553
27277
  return void 0;
26554
- sleepSync2(STARTS_LOCK_POLL_MS);
27278
+ sleepSync3(STARTS_LOCK_POLL_MS);
26555
27279
  }
26556
27280
  try {
26557
27281
  return fn();
@@ -26646,9 +27370,9 @@ function paceHoldText(input, toolInput, deps = {}) {
26646
27370
  }
26647
27371
 
26648
27372
  // dist/risky/facts.js
26649
- import { existsSync as existsSync7, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync21, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
26650
- import { homedir as homedir9, tmpdir as tmpdir2 } from "node:os";
26651
- import { basename as pathBasename, dirname as dirname6, isAbsolute as isAbsolute4, join as join14, relative as relative7, resolve as resolve6, sep as sep5 } from "node:path";
27373
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync21, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
27374
+ import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
27375
+ import { basename as pathBasename, dirname as dirname6, isAbsolute as isAbsolute4, join as join15, relative as relative7, resolve as resolve6, sep as sep5 } from "node:path";
26652
27376
 
26653
27377
  // dist/risky/shell.js
26654
27378
  var MAX_DEPTH = 8;
@@ -27828,7 +28552,7 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27828
28552
  }
27829
28553
  vars.enter(own);
27830
28554
  const simple = expandCommand(raw, vars);
27831
- const folder = folderIn(simple);
28555
+ const folder2 = folderIn(simple);
27832
28556
  for (const m of recogniseSimple(simple)) {
27833
28557
  const trusted = m.type === "shrink-record" && m.change.kind === "saved" && !savedTrusted ? { ...m, change: { kind: "emptied" } } : m;
27834
28558
  let shown2 = trusted;
@@ -27844,7 +28568,7 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27844
28568
  }
27845
28569
  matches.push({
27846
28570
  ...shown2,
27847
- ...folder === void 0 ? {} : { folder },
28571
+ ...folder2 === void 0 ? {} : { folder: folder2 },
27848
28572
  ...made.size === 0 ? {} : { made: new Map(made) },
27849
28573
  ...madeInChain.size === 0 ? {} : { madeInChain: new Set(madeInChain) },
27850
28574
  ...removedInChain.size === 0 ? {} : { removedInChain: new Set(removedInChain) },
@@ -27852,9 +28576,9 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27852
28576
  ...vars.stoppedIf.length === 0 ? {} : { stoppedIf: [...vars.stoppedIf] }
27853
28577
  });
27854
28578
  }
27855
- if (surelyRuns(simple, own) && (folder === void 0 || folder.known)) {
28579
+ if (surelyRuns(simple, own) && (folder2 === void 0 || folder2.known)) {
27856
28580
  for (const p of pathsMade(simple)) {
27857
- const key = madeKey(folder?.dirs ?? [], p.word);
28581
+ const key = madeKey(folder2?.dirs ?? [], p.word);
27858
28582
  made.set(key, p.by);
27859
28583
  if (simple.next === "&&")
27860
28584
  madeInChain.add(key);
@@ -27862,12 +28586,12 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27862
28586
  if (simple.next === "&&" && simple.words[0]?.text === "rmdir") {
27863
28587
  for (const w of simple.words.slice(1))
27864
28588
  if (w.literal && !w.text.startsWith("-"))
27865
- removedInChain.add(madeKey(folder?.dirs ?? [], w));
28589
+ removedInChain.add(madeKey(folder2?.dirs ?? [], w));
27866
28590
  }
27867
28591
  }
27868
28592
  const nextCmd = commands[i + 1];
27869
28593
  const asJoined = simple.next !== void 0 && (nextCmd === void 0 || scopeKey(nextCmd) !== scopeKey(simple)) ? { ...simple, next: void 0 } : simple;
27870
- const after = folderAfter(folder, asJoined, own);
28594
+ const after = folderAfter(folder2, asJoined, own);
27871
28595
  folders.set(scopeKey(simple), after);
27872
28596
  vars.guard = chainCds === null ? null : [...chainCds];
27873
28597
  vars.update(raw, own);
@@ -28860,7 +29584,7 @@ var Env = class {
28860
29584
  constructor(ctx) {
28861
29585
  this.projectDir = realOrSelf(resolve6(ctx.projectDir));
28862
29586
  this.cwd = realOrSelf(resolve6(this.projectDir, ctx.cwd));
28863
- this.home = realOrSelf(resolve6(ctx.home ?? homedir9()));
29587
+ this.home = realOrSelf(resolve6(ctx.home ?? homedir10()));
28864
29588
  this.timeoutMs = ctx.gitTimeoutMs ?? FACTS_GIT_TIMEOUT_MS;
28865
29589
  this.tempDir = realOrSelf(resolve6(ctx.tempDir ?? tmpdir2()));
28866
29590
  const roots = ctx.tempRoots ?? [this.tempDir, tmpdir2(), ...TEMP_ROOTS];
@@ -28884,7 +29608,7 @@ var Env = class {
28884
29608
  return this.description;
28885
29609
  this.description = null;
28886
29610
  try {
28887
- const raw = JSON.parse(readFileSync21(join14(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
29611
+ const raw = JSON.parse(readFileSync21(join15(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
28888
29612
  const checked = checkSystemDescription(raw);
28889
29613
  if (checked.ok)
28890
29614
  this.description = checked.description;
@@ -28948,18 +29672,18 @@ function resolveHome(text, home, base) {
28948
29672
  if (text === "~")
28949
29673
  return home;
28950
29674
  if (text.startsWith("~/"))
28951
- return join14(home, text.slice(2));
29675
+ return join15(home, text.slice(2));
28952
29676
  return resolve6(base, text);
28953
29677
  }
28954
29678
  function realTarget(abs) {
28955
29679
  const parent = dirname6(abs);
28956
29680
  if (parent === abs)
28957
29681
  return abs;
28958
- return join14(realOrSelf(parent), pathBasename(abs));
29682
+ return join15(realOrSelf(parent), pathBasename(abs));
28959
29683
  }
28960
29684
  function wordPath(word, base, env) {
28961
29685
  if (word.temp !== void 0) {
28962
- const root = word.temp.base === "mktemp" ? join14(env.tempDir, ".halfcycle-mktemp-new") : env.tempDir;
29686
+ const root = word.temp.base === "mktemp" ? join15(env.tempDir, ".halfcycle-mktemp-new") : env.tempDir;
28963
29687
  return resolve6(`${root}${word.temp.suffix}`);
28964
29688
  }
28965
29689
  return word.literal ? resolveHome(word.text, env.home, base) : null;
@@ -29105,7 +29829,7 @@ function globPath(abs) {
29105
29829
  const next = [];
29106
29830
  if (!/[*?[]/.test(segment)) {
29107
29831
  for (const c of current)
29108
- next.push(join14(c, segment));
29832
+ next.push(join15(c, segment));
29109
29833
  } else {
29110
29834
  const re = segmentPattern(segment);
29111
29835
  for (const c of current) {
@@ -29119,7 +29843,7 @@ function globPath(abs) {
29119
29843
  if (n.startsWith(".") && !segment.startsWith("."))
29120
29844
  continue;
29121
29845
  if (re.test(n))
29122
- next.push(join14(c, n));
29846
+ next.push(join15(c, n));
29123
29847
  }
29124
29848
  }
29125
29849
  }
@@ -29247,7 +29971,7 @@ function findKeeps(filter, roots, repoRoot) {
29247
29971
  const names = filter.names.map((n) => findPattern(n.pattern, n.fold, false));
29248
29972
  const excluded = filter.excluded.map((p) => findPattern(p, false, true));
29249
29973
  const steps = (rel) => {
29250
- const abs = join14(repoRoot, rel);
29974
+ const abs = join15(repoRoot, rel);
29251
29975
  const out = [];
29252
29976
  for (const r of roots) {
29253
29977
  if (!isInside(abs, r.abs))
@@ -29304,11 +30028,11 @@ function walkRoots(ignoredFolders, specs) {
29304
30028
  const under2 = (child, parent) => parent === "." || child === parent || child.startsWith(`${parent}/`);
29305
30029
  const roots = /* @__PURE__ */ new Set();
29306
30030
  for (const raw of ignoredFolders) {
29307
- const folder = raw.replace(/\/+$/, "");
30031
+ const folder2 = raw.replace(/\/+$/, "");
29308
30032
  for (const spec of specs) {
29309
- if (under2(folder, spec))
29310
- roots.add(folder);
29311
- else if (under2(spec, folder))
30033
+ if (under2(folder2, spec))
30034
+ roots.add(folder2);
30035
+ else if (under2(spec, folder2))
29312
30036
  roots.add(spec);
29313
30037
  }
29314
30038
  }
@@ -29319,9 +30043,9 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
29319
30043
  const out = [];
29320
30044
  const stack = [];
29321
30045
  for (const f of folders) {
29322
- if (isDirectory(join14(repoRoot, f)))
30046
+ if (isDirectory(join15(repoRoot, f)))
29323
30047
  stack.push(f);
29324
- else if (pathExists(join14(repoRoot, f)) && isCredentialPath(f))
30048
+ else if (pathExists(join15(repoRoot, f)) && isCredentialPath(f))
29325
30049
  out.push(f);
29326
30050
  }
29327
30051
  let seen = 0;
@@ -29329,7 +30053,7 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
29329
30053
  const rel = stack.pop();
29330
30054
  let entries;
29331
30055
  try {
29332
- entries = readdirSync9(join14(repoRoot, rel), { withFileTypes: true });
30056
+ entries = readdirSync9(join15(repoRoot, rel), { withFileTypes: true });
29333
30057
  } catch {
29334
30058
  continue;
29335
30059
  }
@@ -29487,7 +30211,7 @@ function closeFoldsAll(targets, env) {
29487
30211
  return false;
29488
30212
  let at = root;
29489
30213
  for (const seg of rel.split("/")) {
29490
- at = join14(at, seg);
30214
+ at = join15(at, seg);
29491
30215
  try {
29492
30216
  if (lstatSync3(at).isSymbolicLink())
29493
30217
  return false;
@@ -29720,7 +30444,7 @@ function worktreeFiles(m, where, env) {
29720
30444
  if (path === null)
29721
30445
  return null;
29722
30446
  const abs = realOrSelf(path);
29723
- if (!existsSync7(abs))
30447
+ if (!existsSync8(abs))
29724
30448
  return [];
29725
30449
  const top = env.repoRoot(abs);
29726
30450
  if (top === null)
@@ -30070,7 +30794,7 @@ function publishFacts(m, env) {
30070
30794
  const start = startFolder(m, env);
30071
30795
  if (start !== null && PACKAGE_JSON_PROGRAMS.has(m.program)) {
30072
30796
  try {
30073
- const pkg = JSON.parse(readFileSync21(join14(start, "package.json"), "utf-8"));
30797
+ const pkg = JSON.parse(readFileSync21(join15(start, "package.json"), "utf-8"));
30074
30798
  if (typeof pkg["name"] === "string" && typeof pkg["version"] === "string" && pkg["name"] !== "" && pkg["version"] !== "") {
30075
30799
  name = pkg["name"];
30076
30800
  version2 = pkg["version"];
@@ -30189,17 +30913,17 @@ function recordFileAt(abs, env) {
30189
30913
  const row = recordPatternFor(rel);
30190
30914
  return row !== void 0 && row.shrinkAsked !== false ? { abs, root, rel } : null;
30191
30915
  }
30192
- function recordFilesUnder(folder, env) {
30193
- const root = recordRoot(folder, env);
30916
+ function recordFilesUnder(folder2, env) {
30917
+ const root = recordRoot(folder2, env);
30194
30918
  if (root === null)
30195
30919
  return [];
30196
- const relFolder = toSlash(relative7(root, folder));
30920
+ const relFolder = toSlash(relative7(root, folder2));
30197
30921
  const starts = /* @__PURE__ */ new Set();
30198
30922
  for (const { pattern } of PROJECT_RECORD_PATTERNS) {
30199
30923
  if (relFolder === "" || patternOwns(relFolder, pattern))
30200
- starts.add(join14(root, pattern));
30924
+ starts.add(join15(root, pattern));
30201
30925
  else if (patternOwns(pattern, relFolder))
30202
- starts.add(folder);
30926
+ starts.add(folder2);
30203
30927
  }
30204
30928
  const out = [];
30205
30929
  const stack = [...starts];
@@ -30223,7 +30947,7 @@ function recordFilesUnder(folder, env) {
30223
30947
  continue;
30224
30948
  }
30225
30949
  for (const n of names)
30226
- stack.push(join14(p, n));
30950
+ stack.push(join15(p, n));
30227
30951
  } else if (st.isFile()) {
30228
30952
  const r = recordFileAt(p, env);
30229
30953
  if (r !== null)
@@ -30306,7 +31030,11 @@ var PERSON_HOLD_LINE = "Halfcycle paused a risky step before it ran. Nothing cha
30306
31030
  var HELD_STEP_OPENER = "The step was: ";
30307
31031
  var PRECAUTION_OPENER = "It was stopped as a precaution: ";
30308
31032
  var ALLOW_REPLY = "allow";
30309
- var HOLD_AGENT_INSTRUCTION = `If the step is not needed, carry on without it; there is no need to mention it. If it is needed, ask the person in chat, in one plain sentence, whether to run it, and tell them to reply "${ALLOW_REPLY}" to run it once, or anything else to skip it. Do not try it again until they answer.`;
31033
+ function holdAgentInstruction(stepNumber) {
31034
+ const reply = stepNumber === null ? ALLOW_REPLY : `${ALLOW_REPLY} ${String(stepNumber)}`;
31035
+ return `If the step is not needed, carry on without it; there is no need to mention it. If it is needed, ask the person in chat, in one plain sentence, whether to run it, and tell them to reply "${reply}" to run it once, or anything else to skip it. Do not try it again until they answer.`;
31036
+ }
31037
+ var HOLD_AGENT_INSTRUCTION = holdAgentInstruction(null);
30310
31038
  var NOTHING_LOST_ON_NO = "Nothing is lost if you say no.";
30311
31039
  var IF_YES = "If you say yes, ";
30312
31040
  var DESCRIBED_TOOLS = /* @__PURE__ */ new Set(["Bash", "Agent"]);
@@ -30342,6 +31070,9 @@ function questionFor(facts, description) {
30342
31070
  const shown2 = staked.slice(0, MAX_LISTED).map((f) => `${whatOf(f)} ${IF_YES}${lostOf(f)}`);
30343
31071
  return `${PAUSED_OPENER.trimEnd().slice(0, -1)}. ${shown2.join(" ")}${moreLine(staked.length - shown2.length)} ${reason} ${NOTHING_LOST_ON_NO}`;
30344
31072
  }
31073
+ function heldAgentText(held, stepNumber) {
31074
+ return `${held.facts} ${holdAgentInstruction(stepNumber)}`;
31075
+ }
30345
31076
  function refusalFor(facts, description) {
30346
31077
  const staked = orderedStaked(facts);
30347
31078
  if (staked.length === 0)
@@ -30352,9 +31083,11 @@ function refusalFor(facts, description) {
30352
31083
  return heldStep(steps, description, shown2.map(whyOf).join("; "));
30353
31084
  }
30354
31085
  function heldStep(steps, description, why) {
31086
+ const facts = `${HELD_STEP_OPENER}${steps}. ${reasonSentence(description)} ${PRECAUTION_OPENER}${why}.`;
30355
31087
  return {
30356
31088
  person: PERSON_HOLD_LINE,
30357
- agent: `${HELD_STEP_OPENER}${steps}. ${reasonSentence(description)} ${PRECAUTION_OPENER}${why}. ${HOLD_AGENT_INSTRUCTION}`,
31089
+ agent: `${facts} ${HOLD_AGENT_INSTRUCTION}`,
31090
+ facts,
30358
31091
  step: steps
30359
31092
  };
30360
31093
  }
@@ -30769,14 +31502,85 @@ function isOneCall(call) {
30769
31502
 
30770
31503
  // dist/risky/allow-once.js
30771
31504
  import { createHash as createHash5 } from "node:crypto";
30772
- import { appendFileSync as appendFileSync4, readFileSync as readFileSync22, renameSync as renameSync11, unlinkSync as unlinkSync9, writeFileSync as writeFileSync13 } from "node:fs";
31505
+ import { appendFileSync as appendFileSync5, readFileSync as readFileSync22, realpathSync as realpathSync8, renameSync as renameSync11, statSync as statSync8, unlinkSync as unlinkSync9, writeFileSync as writeFileSync13 } from "node:fs";
31506
+ import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve7, sep as sep6 } from "node:path";
30773
31507
  var ALLOW_ONCE_MS = 5 * 60 * 1e3;
30774
31508
  var HELD_STEP_MAX_AGE_MS = 60 * 60 * 1e3;
31509
+ var NUMBERS_WAIT_MS = 50;
31510
+ var NUMBERS_LOCK_STALE_MS = 5e3;
31511
+ var NUMBERS_LOCK_POLL_MS = 2;
31512
+ var COPY_GIT_TIMEOUT_MS = 3e3;
31513
+ var ALLOW_NUMBER = new RegExp(`^${ALLOW_REPLY}(?: +([0-9]{1,4}))?$`);
30775
31514
  var STEP_MAX_LENGTH = 1e3;
30776
- function isAllowReply(prompt) {
30777
- return typeof prompt === "string" && prompt.trim().toLowerCase() === ALLOW_REPLY;
31515
+ function allowReplyOf(prompt) {
31516
+ if (typeof prompt !== "string")
31517
+ return null;
31518
+ const m = ALLOW_NUMBER.exec(prompt.trim().toLowerCase());
31519
+ if (m === null)
31520
+ return null;
31521
+ return { number: m[1] === void 0 ? null : Number(m[1]) };
31522
+ }
31523
+ var FOLDER_WORD = String.raw`(?:([^\s'"$\x60\\~*?\[{()<>|&;\-][^\s'"$\x60\\~*?\[{()<>|&;]*)|'([^'"$\x60\\\-][^'"$\x60\\]*)'|"([^'"$\x60\\\-][^'"$\x60\\]*)")`;
31524
+ var CD_PREFIX = new RegExp(String.raw`^\s*cd[ \t]+${FOLDER_WORD}[ \t]*(?:&&|;)`);
31525
+ var GIT_C_PREFIX = new RegExp(String.raw`^\s*git[ \t]+-C[ \t]+${FOLDER_WORD}[ \t]+`);
31526
+ function folderPrefixOf(command) {
31527
+ const cd = CD_PREFIX.exec(command);
31528
+ if (cd !== null) {
31529
+ const rest = command.slice(cd[0].length).replace(/^\s+/, "");
31530
+ const commands = commandsOf(rest);
31531
+ if (commands === null || commands.some((c) => FOLDER_CHANGERS.has(c.words[0]?.text ?? "")))
31532
+ return null;
31533
+ return { folder: folderOf(cd), command: rest };
31534
+ }
31535
+ const gitC = GIT_C_PREFIX.exec(command);
31536
+ if (gitC !== null) {
31537
+ const compared = `git ${command.slice(gitC[0].length)}`;
31538
+ const commands = commandsOf(compared);
31539
+ if (commands === null || commands.filter((c) => c.scope === void 0).length !== 1)
31540
+ return null;
31541
+ return { folder: folderOf(gitC), command: compared };
31542
+ }
31543
+ return null;
30778
31544
  }
30779
- function stepKey(input, staked) {
31545
+ function commandsOf(text) {
31546
+ try {
31547
+ return splitCommand(text);
31548
+ } catch {
31549
+ return null;
31550
+ }
31551
+ }
31552
+ function folderOf(m) {
31553
+ return m[1] ?? m[2] ?? m[3] ?? "";
31554
+ }
31555
+ function actingFolderOf(toolName, cwd, rest) {
31556
+ const command = rest["command"];
31557
+ if (toolName !== "Bash" || typeof command !== "string")
31558
+ return cwd;
31559
+ const prefix = folderPrefixOf(command);
31560
+ if (prefix === null)
31561
+ return cwd;
31562
+ if (!isAbsolute5(prefix.folder) && !isAbsolute5(cwd))
31563
+ return cwd;
31564
+ const named = isAbsolute5(prefix.folder) ? prefix.folder : resolve7(cwd, prefix.folder);
31565
+ if (!isFolder2(named))
31566
+ return cwd;
31567
+ rest["command"] = prefix.command;
31568
+ return named;
31569
+ }
31570
+ function copyOf(folder2, timeoutMs) {
31571
+ if (folder2 === "")
31572
+ return { top: "", within: "" };
31573
+ const real = realOrSelf2(folder2);
31574
+ let top = real;
31575
+ if (isFolder2(real)) {
31576
+ const r = readGitForFacts(["rev-parse", "--show-toplevel"], real, timeoutMs);
31577
+ if (r.ok && r.value.trim() !== "")
31578
+ top = realOrSelf2(r.value.trim());
31579
+ }
31580
+ const within2 = relative8(top, real).split(sep6).join("/");
31581
+ return { top, within: within2 };
31582
+ }
31583
+ function stepKey(input, staked, deps = {}) {
30780
31584
  const toolName = input["tool_name"];
30781
31585
  const toolInput = input["tool_input"];
30782
31586
  if (typeof toolName !== "string" || toolName === "")
@@ -30787,7 +31591,17 @@ function stepKey(input, staked) {
30787
31591
  if (DESCRIBED_TOOLS.has(toolName))
30788
31592
  delete rest["description"];
30789
31593
  const cwd = typeof input["cwd"] === "string" ? input["cwd"] : "";
30790
- return createHash5("sha256").update(canonical([toolName, cwd, rest, staked])).digest("hex");
31594
+ const acting = actingFolderOf(toolName, cwd, rest);
31595
+ const { top, within: within2 } = copyOf(acting, deps.gitTimeoutMs ?? COPY_GIT_TIMEOUT_MS);
31596
+ const facts = staked.map(withoutMatch);
31597
+ return createHash5("sha256").update(canonical([toolName, top, within2, rest, facts])).digest("hex");
31598
+ }
31599
+ function withoutMatch(fact) {
31600
+ if (typeof fact !== "object" || fact === null || Array.isArray(fact))
31601
+ return fact;
31602
+ const { match: _match, ...others } = fact;
31603
+ void _match;
31604
+ return others;
30791
31605
  }
30792
31606
  function canonical(value) {
30793
31607
  if (Array.isArray(value))
@@ -30798,37 +31612,166 @@ function canonical(value) {
30798
31612
  }
30799
31613
  return JSON.stringify(value) ?? "null";
30800
31614
  }
30801
- function recordHeld(sessionId, key, deps = {}, step = null) {
31615
+ function isFolder2(path) {
31616
+ try {
31617
+ return statSync8(path).isDirectory();
31618
+ } catch {
31619
+ return false;
31620
+ }
31621
+ }
31622
+ function realOrSelf2(path) {
31623
+ try {
31624
+ return realpathSync8(path);
31625
+ } catch {
31626
+ return path;
31627
+ }
31628
+ }
31629
+ function numberStep(sessionId, key, deps = {}) {
31630
+ const paths = allowOncePaths(sessionId, deps.dir);
31631
+ if (paths === null)
31632
+ return null;
31633
+ const lock = `${paths.numbers}${STEP_NUMBERS_LOCK_SUFFIX}`;
31634
+ const giveUpAt = Date.now() + NUMBERS_WAIT_MS;
31635
+ while (!tryTakeLockFile(lock, NUMBERS_LOCK_STALE_MS)) {
31636
+ if (Date.now() >= giveUpAt)
31637
+ return null;
31638
+ sleepSync4(NUMBERS_LOCK_POLL_MS);
31639
+ }
31640
+ try {
31641
+ const numbers = readNumbers(paths.numbers);
31642
+ if (numbers === null)
31643
+ return null;
31644
+ const kept = numbers.steps.get(key);
31645
+ if (kept !== void 0)
31646
+ return kept;
31647
+ const n = numbers.next;
31648
+ const steps = new Map(numbers.steps).set(key, n);
31649
+ return writeNumbers(paths.numbers, { next: n + 1, steps }) ? n : null;
31650
+ } catch {
31651
+ return null;
31652
+ } finally {
31653
+ releaseLockFile(lock);
31654
+ }
31655
+ }
31656
+ function numberKept(path, key) {
31657
+ try {
31658
+ return readNumbers(path)?.steps.get(key) ?? null;
31659
+ } catch {
31660
+ return null;
31661
+ }
31662
+ }
31663
+ function readNumbers(path) {
31664
+ let text;
31665
+ try {
31666
+ text = readFileSync22(path, "utf8");
31667
+ } catch (err) {
31668
+ return err.code === "ENOENT" ? { next: 1, steps: /* @__PURE__ */ new Map() } : null;
31669
+ }
31670
+ let parsed;
31671
+ try {
31672
+ parsed = JSON.parse(text);
31673
+ } catch {
31674
+ return null;
31675
+ }
31676
+ if (typeof parsed !== "object" || parsed === null)
31677
+ return null;
31678
+ const o = parsed;
31679
+ const next = o["next"];
31680
+ const steps = o["steps"];
31681
+ if (!isWholeNumber2(next) || typeof steps !== "object" || steps === null || Array.isArray(steps))
31682
+ return null;
31683
+ const out = /* @__PURE__ */ new Map();
31684
+ for (const [k, v] of Object.entries(steps)) {
31685
+ if (!/^[0-9a-f]{64}$/.test(k) || !isWholeNumber2(v) || v >= next)
31686
+ return null;
31687
+ out.set(k, v);
31688
+ }
31689
+ return { next, steps: out };
31690
+ }
31691
+ function writeNumbers(path, numbers) {
31692
+ const body = JSON.stringify({ next: numbers.next, steps: Object.fromEntries(numbers.steps) });
31693
+ return writeWhole2(path, body);
31694
+ }
31695
+ function isWholeNumber2(v) {
31696
+ return typeof v === "number" && Number.isSafeInteger(v) && v >= 1;
31697
+ }
31698
+ function sleepSync4(ms) {
31699
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
31700
+ }
31701
+ function recordHeld(sessionId, key, deps = {}, step = null, stepNumber = null) {
30802
31702
  const paths = allowOncePaths(sessionId, deps.dir);
30803
31703
  if (paths === null)
30804
31704
  return;
30805
- const record2 = { version: 1, session: sessionId, key, at: nowOf(deps), ...isStep(step) ? { step } : {} };
31705
+ const record2 = {
31706
+ version: 1,
31707
+ session: sessionId,
31708
+ key,
31709
+ at: nowOf(deps),
31710
+ ...isStep(step) ? { step } : {},
31711
+ ...isWholeNumber2(stepNumber) ? { number: stepNumber } : {}
31712
+ };
30806
31713
  try {
30807
- appendFileSync4(paths.held, `${JSON.stringify(record2)}
31714
+ appendFileSync5(paths.held, `${JSON.stringify(record2)}
30808
31715
  `, { mode: 384 });
30809
31716
  } catch {
30810
31717
  return;
30811
31718
  }
30812
31719
  }
30813
- function armAllowStep(sessionId, deps = {}) {
31720
+ function armAllowStep(sessionId, deps = {}, stepNumber = null) {
31721
+ const none = (outcome) => ({ outcome, step: null, paused: [] });
30814
31722
  try {
30815
31723
  const paths = allowOncePaths(sessionId, deps.dir);
30816
31724
  if (paths === null)
30817
- return { outcome: "nothing-held", step: null };
31725
+ return none(stepNumber === null ? "nothing-held" : "not-paused");
31726
+ const session = sessionId;
30818
31727
  const now = nowOf(deps);
30819
- const held = claimHeld(paths.held, sessionId, now);
30820
- const keys = [...new Set(held.map((r) => r.key))];
30821
- if (keys.length === 0)
30822
- return { outcome: "nothing-held", step: null };
30823
- if (keys.length > 1)
30824
- return { outcome: "several-held", step: null };
30825
- const step = held[held.length - 1].step ?? null;
30826
- const saved = writeRecord(paths.allow, { version: 1, session: sessionId, key: keys[0], at: now });
30827
- return saved ? { outcome: "armed", step } : { outcome: "not-saved", step: null };
31728
+ const held = claimHeld(paths.held, session, now);
31729
+ const steps = distinctSteps(held, paths.numbers);
31730
+ const arm = (key, step) => {
31731
+ const saved = writeRecord(paths.allow, { version: 1, session, key, at: now });
31732
+ if (!saved) {
31733
+ writeBack(paths.held, held);
31734
+ return none("not-saved");
31735
+ }
31736
+ writeBack(paths.held, held.filter((r) => r.key !== key));
31737
+ return { outcome: "armed", step, paused: [] };
31738
+ };
31739
+ if (stepNumber !== null) {
31740
+ const named = steps.find((s) => s.number === stepNumber);
31741
+ if (named === void 0) {
31742
+ writeBack(paths.held, held);
31743
+ return none("not-paused");
31744
+ }
31745
+ return arm(named.key, named.step);
31746
+ }
31747
+ if (steps.length === 0)
31748
+ return none("nothing-held");
31749
+ if (steps.length === 1)
31750
+ return arm(steps[0].key, steps[0].step);
31751
+ writeBack(paths.held, held);
31752
+ const paused = steps.map((s) => ({ number: s.number ?? numberStep(session, s.key, deps), step: s.step })).sort((a, b) => (a.number ?? Number.MAX_SAFE_INTEGER) - (b.number ?? Number.MAX_SAFE_INTEGER));
31753
+ return { outcome: "several-held", step: null, paused };
30828
31754
  } catch {
30829
- return { outcome: "not-saved", step: null };
31755
+ return none("not-saved");
30830
31756
  }
30831
31757
  }
31758
+ function distinctSteps(held, numbersPath) {
31759
+ const order = [];
31760
+ const latest = /* @__PURE__ */ new Map();
31761
+ const numbered = /* @__PURE__ */ new Map();
31762
+ for (const r of held) {
31763
+ if (!latest.has(r.key))
31764
+ order.push(r.key);
31765
+ latest.set(r.key, r);
31766
+ if (r.number !== void 0)
31767
+ numbered.set(r.key, r.number);
31768
+ }
31769
+ return order.map((key) => ({
31770
+ key,
31771
+ number: numbered.get(key) ?? numberKept(numbersPath, key),
31772
+ step: latest.get(key).step ?? null
31773
+ }));
31774
+ }
30832
31775
  function claimHeld(path, session, now) {
30833
31776
  const claimed = `${path}.${process.pid}.${now}.claim`;
30834
31777
  try {
@@ -30851,6 +31794,16 @@ function claimHeld(path, session, now) {
30851
31794
  }
30852
31795
  return records;
30853
31796
  }
31797
+ function writeBack(path, records) {
31798
+ if (records.length === 0)
31799
+ return;
31800
+ try {
31801
+ appendFileSync5(path, records.map((r) => `${JSON.stringify(r)}
31802
+ `).join(""), { mode: 384 });
31803
+ } catch {
31804
+ return;
31805
+ }
31806
+ }
30854
31807
  function isStep(v) {
30855
31808
  return typeof v === "string" && v !== "" && v.length <= STEP_MAX_LENGTH;
30856
31809
  }
@@ -30905,12 +31858,23 @@ function parseRecord(text, session) {
30905
31858
  if (typeof r["at"] !== "number")
30906
31859
  return null;
30907
31860
  const step = r["step"];
30908
- return { version: 1, session, key: r["key"], at: r["at"], ...isStep(step) ? { step } : {} };
31861
+ const n = r["number"];
31862
+ return {
31863
+ version: 1,
31864
+ session,
31865
+ key: r["key"],
31866
+ at: r["at"],
31867
+ ...isStep(step) ? { step } : {},
31868
+ ...isWholeNumber2(n) ? { number: n } : {}
31869
+ };
30909
31870
  }
30910
31871
  function writeRecord(path, record2) {
31872
+ return writeWhole2(path, JSON.stringify(record2));
31873
+ }
31874
+ function writeWhole2(path, body) {
30911
31875
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
30912
31876
  try {
30913
- writeFileSync13(tmp, JSON.stringify(record2), { mode: 384 });
31877
+ writeFileSync13(tmp, body, { mode: 384 });
30914
31878
  renameSync11(tmp, path);
30915
31879
  return true;
30916
31880
  } catch {
@@ -31008,7 +31972,7 @@ function decideAutonomous(facts, mayPublishNow) {
31008
31972
 
31009
31973
  // dist/risky/own-copy.js
31010
31974
  import { readFileSync as readFileSync23 } from "node:fs";
31011
- import { join as join15, posix as posix2, win32 as win322 } from "node:path";
31975
+ import { join as join16, posix as posix2, win32 as win322 } from "node:path";
31012
31976
  var OWN_COPY_START_TOOL = "Agent";
31013
31977
  var OWN_COPY_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
31014
31978
  var OWN_COPY_ISOLATION = "worktree";
@@ -31069,7 +32033,7 @@ function typeCanChangeFiles(subagentType, deps) {
31069
32033
  return true;
31070
32034
  if (AGENT_FILE_NAME.test(type)) {
31071
32035
  for (const base of [deps.projectDir, deps.home]) {
31072
- const text = readOr(deps, join15(base, ".claude", "agents", `${type}.md`));
32036
+ const text = readOr(deps, join16(base, ".claude", "agents", `${type}.md`));
31073
32037
  if (text === null)
31074
32038
  continue;
31075
32039
  const tools = frontmatterTools(text);
@@ -31175,31 +32139,31 @@ function judgeChange(input, call, deps) {
31175
32139
 
31176
32140
  // dist/risky/save.js
31177
32141
  import { randomBytes } from "node:crypto";
31178
- import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync24, readlinkSync, realpathSync as realpathSync8, renameSync as renameSync12, rmSync as rmSync3, statSync as statSync8, symlinkSync, writeFileSync as writeFileSync14 } from "node:fs";
31179
- import { homedir as homedir10 } from "node:os";
31180
- import { dirname as dirname7, isAbsolute as isAbsolute5, join as join16, resolve as resolve7 } from "node:path";
32142
+ import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync24, readlinkSync, realpathSync as realpathSync9, renameSync as renameSync12, rmSync as rmSync3, statSync as statSync9, symlinkSync, writeFileSync as writeFileSync14 } from "node:fs";
32143
+ import { homedir as homedir11 } from "node:os";
32144
+ import { dirname as dirname7, isAbsolute as isAbsolute6, join as join17, resolve as resolve8 } from "node:path";
31181
32145
  var SAVE_LIMIT_BYTES = 2e8;
31182
32146
  var SAVE_BUDGET_MS = 5e3;
31183
32147
  function notSaved(reason) {
31184
32148
  return { saved: false, reason };
31185
32149
  }
31186
- function savedFolderOf(engagementId, home = homedir10()) {
32150
+ function savedFolderOf(engagementId, home = homedir11()) {
31187
32151
  if (!isEngagementId(engagementId))
31188
32152
  return null;
31189
- return join16(home, HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
32153
+ return join17(home, HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
31190
32154
  }
31191
32155
  function saveFirst(staked, ctx) {
31192
32156
  const now = ctx.now ?? Date.now;
31193
32157
  const deadline = now() + (ctx.budgetMs ?? SAVE_BUDGET_MS);
31194
- const folder = savedFolderOf(ctx.engagementId, ctx.home);
31195
- if (folder === null)
32158
+ const folder2 = savedFolderOf(ctx.engagementId, ctx.home);
32159
+ if (folder2 === null)
31196
32160
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
31197
32161
  const plan = planOf(staked);
31198
32162
  if (!plan.ok)
31199
32163
  return notSaved(plan.reason);
31200
32164
  const g = new SaveGit(plan.root, deadline, now);
31201
32165
  const top = g.run(["rev-parse", "--show-toplevel"]);
31202
- if (!top.ok || realOrSelf2(top.value.trim()) !== plan.root)
32166
+ if (!top.ok || realOrSelf3(top.value.trim()) !== plan.root)
31203
32167
  return notSaved(AUTONOMY_SAVE_NOT_COVERED_REASON);
31204
32168
  const head = g.run(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
31205
32169
  const headCommit2 = head.ok ? head.value.trim() : "";
@@ -31214,23 +32178,23 @@ function saveFirst(staked, ctx) {
31214
32178
  return notSaved(found2.reason);
31215
32179
  let saveDir = null;
31216
32180
  try {
31217
- const id = freshId(folder, ctx.newId);
32181
+ const id = freshId(folder2, ctx.newId);
31218
32182
  if (id === null)
31219
32183
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
31220
- saveDir = join16(folder, id);
32184
+ saveDir = join17(folder2, id);
31221
32185
  const repository = repositoryOf(g);
31222
32186
  if (repository === null)
31223
32187
  return failed(saveDir);
31224
- writeFileSync14(join16(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
32188
+ writeFileSync14(join17(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
31225
32189
  `, { mode: 384 });
31226
- const privateIndex = join16(saveDir, PRIVATE_INDEX_NAME);
32190
+ const privateIndex = join17(saveDir, PRIVATE_INDEX_NAME);
31227
32191
  const commit = saveCommit(id, headCommit2, found2.tracked, plan.lines, privateIndex, g, now());
31228
32192
  rmSync3(privateIndex, { force: true });
31229
32193
  if (commit === null)
31230
32194
  return failed(saveDir);
31231
- const copiesDir = join16(saveDir, SAVED_COPIES_DIR_NAME);
32195
+ const copiesDir = join17(saveDir, SAVED_COPIES_DIR_NAME);
31232
32196
  for (const rel of found2.copies) {
31233
- if (now() > deadline || !copyOne(join16(plan.root, rel), join16(copiesDir, rel)))
32197
+ if (now() > deadline || !copyOne(join17(plan.root, rel), join17(copiesDir, rel)))
31234
32198
  return failed(saveDir);
31235
32199
  }
31236
32200
  const description = savedSchema.safeParse({
@@ -31246,7 +32210,7 @@ function saveFirst(staked, ctx) {
31246
32210
  });
31247
32211
  if (!description.success)
31248
32212
  return failed(saveDir);
31249
- const file2 = join16(saveDir, SAVED_FILE_NAME);
32213
+ const file2 = join17(saveDir, SAVED_FILE_NAME);
31250
32214
  writeFileSync14(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
31251
32215
  `, { mode: 384 });
31252
32216
  renameSync12(`${file2}.part`, file2);
@@ -31277,7 +32241,7 @@ function planOf(staked) {
31277
32241
  const t = f.save;
31278
32242
  if (t === void 0)
31279
32243
  return { ok: false, reason: f.cannotTell === void 0 ? AUTONOMY_SAVE_NOT_COVERED_REASON : AUTONOMY_COULD_NOT_TELL_REASON };
31280
- const r = realOrSelf2(resolve7(t.repoRoot));
32244
+ const r = realOrSelf3(resolve8(t.repoRoot));
31281
32245
  if (root === null)
31282
32246
  root = r;
31283
32247
  else if (root !== r)
@@ -31296,7 +32260,7 @@ function planOf(staked) {
31296
32260
  function isInsideRel(p) {
31297
32261
  if (p === ".")
31298
32262
  return true;
31299
- if (p === "" || isAbsolute5(p) || p.includes("\\"))
32263
+ if (p === "" || isAbsolute6(p) || p.includes("\\"))
31300
32264
  return false;
31301
32265
  return p.split("/").every((s) => s !== "" && s !== "." && s !== "..");
31302
32266
  }
@@ -31326,7 +32290,7 @@ function filesOf(plan, g, deadline) {
31326
32290
  trackedAll.add(path);
31327
32291
  if (!reached(path, plan.specs))
31328
32292
  continue;
31329
- const st = lstatOrNull(join16(plan.root, path));
32293
+ const st = lstatOrNull(join17(plan.root, path));
31330
32294
  if (st !== null && (st.isFile() || st.isSymbolicLink()))
31331
32295
  tracked.add(path);
31332
32296
  }
@@ -31338,7 +32302,7 @@ function filesOf(plan, g, deadline) {
31338
32302
  const rel = stack.pop();
31339
32303
  if (rel === ".git")
31340
32304
  continue;
31341
- const abs = rel === "." ? plan.root : join16(plan.root, rel);
32305
+ const abs = rel === "." ? plan.root : join17(plan.root, rel);
31342
32306
  const st = lstatOrNull(abs);
31343
32307
  if (st === null)
31344
32308
  continue;
@@ -31376,13 +32340,13 @@ function copyOne(src, dst) {
31376
32340
  }
31377
32341
  copyFileSync(src, dst, fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE);
31378
32342
  chmodSync2(dst, st.mode & 4095);
31379
- return statSync8(dst).size === st.size;
32343
+ return statSync9(dst).size === st.size;
31380
32344
  }
31381
32345
  function saveCommit(id, head, tracked, lines2, privateIndex, g, at) {
31382
32346
  const indexPath = g.run(["rev-parse", "--git-path", "index"]);
31383
32347
  if (!indexPath.ok)
31384
32348
  return null;
31385
- const projectIndex = resolve7(g.root, indexPath.value.trim());
32349
+ const projectIndex = resolve8(g.root, indexPath.value.trim());
31386
32350
  const env = commitEnv(privateIndex, at);
31387
32351
  if (lstatOrNull(projectIndex) !== null)
31388
32352
  copyFileSync(projectIndex, privateIndex);
@@ -31444,14 +32408,14 @@ var SaveGit = class {
31444
32408
  });
31445
32409
  }
31446
32410
  };
31447
- function freshId(folder, newId) {
31448
- mkdirSync9(folder, { recursive: true, mode: 448 });
32411
+ function freshId(folder2, newId) {
32412
+ mkdirSync9(folder2, { recursive: true, mode: 448 });
31449
32413
  for (let i = 0; i < 5; i++) {
31450
32414
  const id = newId?.() ?? defaultId();
31451
32415
  if (!savedIdSchema.safeParse(id).success)
31452
32416
  return null;
31453
32417
  try {
31454
- mkdirSync9(join16(folder, id), { mode: 448 });
32418
+ mkdirSync9(join17(folder2, id), { mode: 448 });
31455
32419
  return id;
31456
32420
  } catch (e) {
31457
32421
  if (e.code !== "EEXIST")
@@ -31472,19 +32436,19 @@ function repositoryOf(g) {
31472
32436
  if (!r.ok)
31473
32437
  return null;
31474
32438
  const dir = r.value.trim();
31475
- return dir === "" ? null : realOrSelf2(resolve7(g.root, dir));
32439
+ return dir === "" ? null : realOrSelf3(resolve8(g.root, dir));
31476
32440
  }
31477
32441
  function markedRepository(dir) {
31478
32442
  try {
31479
- const text = readFileSync24(join16(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
32443
+ const text = readFileSync24(join17(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
31480
32444
  return text === "" ? null : text;
31481
32445
  } catch {
31482
32446
  return null;
31483
32447
  }
31484
32448
  }
31485
32449
  function pruneSaves(root, engagementId, ctx = {}) {
31486
- const folder = savedFolderOf(engagementId, ctx.home);
31487
- if (folder === null)
32450
+ const folder2 = savedFolderOf(engagementId, ctx.home);
32451
+ if (folder2 === null)
31488
32452
  return;
31489
32453
  const now = ctx.now ?? Date.now;
31490
32454
  const g = new SaveGit(root, ctx.deadline ?? now() + SAVE_BUDGET_MS, now);
@@ -31501,7 +32465,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
31501
32465
  const repository = repositoryOf(g);
31502
32466
  let names = [];
31503
32467
  try {
31504
- names = readdirSync10(folder);
32468
+ names = readdirSync10(folder2);
31505
32469
  } catch {
31506
32470
  names = [];
31507
32471
  }
@@ -31510,7 +32474,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
31510
32474
  if (!savedIdSchema.safeParse(id).success)
31511
32475
  continue;
31512
32476
  here.add(id);
31513
- const dir = join16(folder, id);
32477
+ const dir = join17(folder2, id);
31514
32478
  const st = lstatOrNull(dir);
31515
32479
  const answer = savedPruneAnswer({
31516
32480
  refMadeAt: refs.get(id) ?? null,
@@ -31539,9 +32503,9 @@ function lstatOrNull(p) {
31539
32503
  return null;
31540
32504
  }
31541
32505
  }
31542
- function realOrSelf2(p) {
32506
+ function realOrSelf3(p) {
31543
32507
  try {
31544
- return realpathSync8(p);
32508
+ return realpathSync9(p);
31545
32509
  } catch {
31546
32510
  return p;
31547
32511
  }
@@ -31550,12 +32514,12 @@ function realOrSelf2(p) {
31550
32514
  // dist/autonomy-state.js
31551
32515
  import { spawnSync as spawnSync2 } from "node:child_process";
31552
32516
  import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync25, renameSync as renameSync13, rmSync as rmSync4, writeFileSync as writeFileSync15 } from "node:fs";
31553
- import { homedir as homedir11, platform as platform2 } from "node:os";
31554
- import { dirname as dirname8, join as join17 } from "node:path";
32517
+ import { homedir as homedir12, platform as platform2 } from "node:os";
32518
+ import { dirname as dirname8, join as join18 } from "node:path";
31555
32519
  var AUTONOMY_STATE_FILENAME = "autonomy.json";
31556
32520
  var AUTONOMY_STATE_DEADLINE_MS = 1500;
31557
32521
  function autonomyStateFilePath(engagementId, home) {
31558
- return join17(home ?? homedir11(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, AUTONOMY_STATE_FILENAME);
32522
+ return join18(home ?? homedir12(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, AUTONOMY_STATE_FILENAME);
31559
32523
  }
31560
32524
  function readCachedAutonomy(engagementId, nowMs, home) {
31561
32525
  if (!isEngagementId(engagementId))
@@ -31583,6 +32547,7 @@ function confirmMayPublishNow(opts) {
31583
32547
  timeout: opts.timeoutMs,
31584
32548
  windowsHide: true
31585
32549
  });
32550
+ recordChildRequest(url2, ["Authorization"], r.status === 0);
31586
32551
  if (r.status !== 0 || typeof r.stdout !== "string")
31587
32552
  return false;
31588
32553
  const parsed = autonomyStateSchema.safeParse(JSON.parse(r.stdout));
@@ -31607,7 +32572,7 @@ function deleteAutonomyStateFile(engagementId, home) {
31607
32572
  }
31608
32573
  function writeAutonomyStateFile(engagementId, file2, home) {
31609
32574
  const target = autonomyStateFilePath(engagementId, home);
31610
- const temp = join17(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
32575
+ const temp = join18(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
31611
32576
  try {
31612
32577
  mkdirSync10(dirname8(target), { recursive: true, mode: 448 });
31613
32578
  writeFileSync15(temp, `${JSON.stringify(file2)}
@@ -31669,7 +32634,7 @@ async function cacheAutonomySetting(opts) {
31669
32634
  }
31670
32635
 
31671
32636
  // dist/risky/patch-calls.js
31672
- import { isAbsolute as isAbsolute6, resolve as resolve8 } from "node:path";
32637
+ import { isAbsolute as isAbsolute7, resolve as resolve9 } from "node:path";
31673
32638
  var SHELL_TOOL = "Bash";
31674
32639
  function toolCallsOf(input, profile) {
31675
32640
  const toolName = input["tool_name"];
@@ -31692,7 +32657,7 @@ function toolCallsOf(input, profile) {
31692
32657
  const ops = patchOperations(command);
31693
32658
  if (ops === null || ops.length === 0)
31694
32659
  return null;
31695
- const cwd = typeof input["cwd"] === "string" && isAbsolute6(input["cwd"]) ? input["cwd"] : null;
32660
+ const cwd = typeof input["cwd"] === "string" && isAbsolute7(input["cwd"]) ? input["cwd"] : null;
31696
32661
  const calls = [];
31697
32662
  for (const op of ops) {
31698
32663
  const made = callsFor(op, cwd);
@@ -31730,9 +32695,9 @@ function callsFor(op, cwd) {
31730
32695
  }
31731
32696
  }
31732
32697
  function placed(path, cwd) {
31733
- if (isAbsolute6(path))
31734
- return resolve8(path);
31735
- return cwd === null ? null : resolve8(cwd, path);
32698
+ if (isAbsolute7(path))
32699
+ return resolve9(path);
32700
+ return cwd === null ? null : resolve9(cwd, path);
31736
32701
  }
31737
32702
  function shellWord(text) {
31738
32703
  return `'${text.split("'").join("'\\''")}'`;
@@ -31740,7 +32705,7 @@ function shellWord(text) {
31740
32705
 
31741
32706
  // dist/hooks/pre-tool-use.js
31742
32707
  import { performance } from "node:perf_hooks";
31743
- import { homedir as homedir12 } from "node:os";
32708
+ import { homedir as homedir13 } from "node:os";
31744
32709
  var UNREADABLE_INPUT_NOTICE = "Halfcycle could not read what the coding agent was about to do, so that step went ahead without being checked for anything that cannot be undone. If this keeps happening, update Halfcycle.";
31745
32710
  var PRE_TOOL_USE_LIMIT_MS = (HALFCYCLE_HOOK_WIRING.find((e) => e.event === "PreToolUse")?.timeout ?? 10) * 1e3;
31746
32711
  var AUTONOMY_REPLY_MARGIN_MS = 1e3;
@@ -31811,6 +32776,11 @@ function runPreToolUse(deps) {
31811
32776
  if (held !== null)
31812
32777
  return `${preToolUseReplyText("refuse", held)}
31813
32778
  `;
32779
+ recordSpawnedTask(call.toolInput, {
32780
+ projectDir: projectDirOf(deps),
32781
+ ...deps.env === void 0 ? {} : { env: deps.env },
32782
+ ...deps.home === void 0 ? {} : { home: deps.home }
32783
+ });
31814
32784
  }
31815
32785
  return own.kind === "unknown" ? ownCopyNotice(input, deps) : "";
31816
32786
  }
@@ -31819,16 +32789,19 @@ function runPreToolUse(deps) {
31819
32789
  return `${preToolUseReplyText("ask", answer.question)}
31820
32790
  `;
31821
32791
  }
32792
+ let agentText = answer.refusal.agent;
31822
32793
  if (allowableByTyping(call, answer.staked)) {
31823
- const key = stepKey(input, answer.staked);
32794
+ const key = stepKey(input, answer.staked, deps.gitTimeoutMs === void 0 ? {} : { gitTimeoutMs: deps.gitTimeoutMs });
31824
32795
  if (key !== null) {
31825
32796
  const allowOnce = { ...deps.liveness?.dir === void 0 ? {} : { dir: deps.liveness.dir }, now: () => at.getTime() };
31826
32797
  if (spendAllow(input["session_id"], key, allowOnce))
31827
32798
  return "";
31828
- recordHeld(input["session_id"], key, allowOnce, answer.refusal.step);
32799
+ const stepNumber = numberStep(input["session_id"], key, allowOnce);
32800
+ recordHeld(input["session_id"], key, allowOnce, answer.refusal.step, stepNumber);
32801
+ agentText = heldAgentText(answer.refusal, stepNumber);
31829
32802
  }
31830
32803
  }
31831
- return `${preToolUseReplyText("refuse", answer.refusal.person, answer.refusal.agent)}
32804
+ return `${preToolUseReplyText("refuse", answer.refusal.person, agentText)}
31832
32805
  `;
31833
32806
  } catch {
31834
32807
  return "";
@@ -31956,7 +32929,7 @@ function ownCopyOf(input, call, deps) {
31956
32929
  const walk2 = deps.walk ?? deps.liveness?.walk;
31957
32930
  return judgeOwnCopy(input, call, {
31958
32931
  projectDir: projectDirOf(deps),
31959
- home: deps.home ?? homedir12(),
32932
+ home: deps.home ?? homedir13(),
31960
32933
  ...walk2 === void 0 ? {} : { walk: walk2 },
31961
32934
  ...deps.liveness === void 0 ? {} : { liveness: deps.liveness }
31962
32935
  });
@@ -31965,8 +32938,8 @@ function ownCopyOf(input, call, deps) {
31965
32938
  }
31966
32939
  }
31967
32940
  function ownCopyNotice(input, deps) {
31968
- const folder = stringOf(input, "cwd") ?? projectDirOf(deps);
31969
- return firstNoticeThisSession("outside-own-copy-unknown", folder, stringOf(input, "session_id"), deps.liveness?.dir) ? `${preToolUseNoticeText(OWN_COPY_UNKNOWN_NOTICE)}
32941
+ const folder2 = stringOf(input, "cwd") ?? projectDirOf(deps);
32942
+ return firstNoticeThisSession("outside-own-copy-unknown", folder2, stringOf(input, "session_id"), deps.liveness?.dir) ? `${preToolUseNoticeText(OWN_COPY_UNKNOWN_NOTICE)}
31970
32943
  ` : "";
31971
32944
  }
31972
32945
  function parseObject2(text) {
@@ -31983,14 +32956,14 @@ function stringOf(input, key) {
31983
32956
  }
31984
32957
  function unreadable(deps, input) {
31985
32958
  const session = (input === null ? void 0 : stringOf(input, "session_id")) ?? /"session_id"\s*:\s*"([^"\\]+)"/.exec(deps.input)?.[1];
31986
- const folder = (input === null ? void 0 : stringOf(input, "cwd")) ?? projectDirOf(deps);
31987
- return firstNoticeThisSession("pre-tool-use-unreadable", folder, session) ? `${preToolUseNoticeText(UNREADABLE_INPUT_NOTICE)}
32959
+ const folder2 = (input === null ? void 0 : stringOf(input, "cwd")) ?? projectDirOf(deps);
32960
+ return firstNoticeThisSession("pre-tool-use-unreadable", folder2, session) ? `${preToolUseNoticeText(UNREADABLE_INPUT_NOTICE)}
31988
32961
  ` : "";
31989
32962
  }
31990
32963
 
31991
32964
  // dist/hooks/session-diff.js
31992
32965
  import { unlinkSync as unlinkSync10 } from "node:fs";
31993
- import { join as join18 } from "node:path";
32966
+ import { join as join19 } from "node:path";
31994
32967
  var STOP_HOOK_EVENT = {
31995
32968
  stop: "Stop",
31996
32969
  "subagent-stop": "SubagentStop"
@@ -32042,7 +33015,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32042
33015
  }
32043
33016
  const repoRoot = repoRootResult.value;
32044
33017
  const sessionId = input.session_id;
32045
- const folder = realPathOf(repoRoot);
33018
+ const folder2 = realPathOf(repoRoot);
32046
33019
  orphanSweep(repoRoot, (ref) => refExists(ref, repoRoot), sessionId, Date.now(), whichCopy(process.cwd())?.common ?? null);
32047
33020
  const base = resolveDiffBase2(repoRoot, sessionId);
32048
33021
  const diffBase = base.kind;
@@ -32055,12 +33028,12 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32055
33028
  const candidates = ownEditedFiles(repoRoot, sessionId).filter((relPath) => !tracked.has(relPath));
32056
33029
  const untracked = getUntrackedFiles(repoRoot, candidates);
32057
33030
  const created = untracked.ok ? untracked.value.filter((relPath) => !tracked.has(relPath)) : [];
32058
- const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join18(repoRoot, relPath), repoRoot);
33031
+ const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join19(repoRoot, relPath), repoRoot);
32059
33032
  const { evaluated } = partitionInstallerOutput(collected.files);
32060
33033
  const { evaluated: createdEvaluated } = partitionInstallerOutput(created);
32061
33034
  const changedFiles2 = [...ownChangedFiles(repoRoot, evaluated, sessionId), ...createdEvaluated];
32062
33035
  if (changedFiles2.length === 0) {
32063
- queueTurnEnd(keepBooks(sessionId, folder, { kind: "no-own-changes" }, kind === "stop"), reply);
33036
+ queueTurnEnd(keepBooks(sessionId, folder2, { kind: "no-own-changes" }, kind === "stop"), reply);
32064
33037
  return;
32065
33038
  }
32066
33039
  const fileDiffEntries = changedFiles2.map((relPath) => ({
@@ -32071,7 +33044,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32071
33044
  const digest = digestChangeState(baseKey, fileDiffEntries);
32072
33045
  const prior = readDedupe(repoRoot, sessionId);
32073
33046
  if (prior && prior.base === baseKey && prior.digest === digest) {
32074
- queueTurnEnd(keepBooks(sessionId, folder, { kind: "unchanged" }, kind === "stop"), reply);
33047
+ queueTurnEnd(keepBooks(sessionId, folder2, { kind: "unchanged" }, kind === "stop"), reply);
32075
33048
  return;
32076
33049
  }
32077
33050
  const context = loadChangeSetContext(repoRoot);
@@ -32151,12 +33124,12 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32151
33124
  warn: (msg) => process.stderr.write(msg + "\n")
32152
33125
  });
32153
33126
  writeDedupe(repoRoot, sessionId, { base: baseKey, digest });
32154
- actOnEnvelope3(clientResult.envelope, kind, input.stop_hook_active === true, reply, sessionId, folder);
33127
+ actOnEnvelope3(clientResult.envelope, kind, input.stop_hook_active === true, reply, sessionId, folder2);
32155
33128
  }
32156
- function keepBooks(sessionId, folder, outcome, topLevel) {
33129
+ function keepBooks(sessionId, folder2, outcome, topLevel) {
32157
33130
  const books = () => {
32158
33131
  const before = readFindings(sessionId);
32159
- const { file: file2, news } = turnEndBookkeeping(before ?? emptyFindings(""), folder, outcome, topLevel, /* @__PURE__ */ new Date());
33132
+ const { file: file2, news } = turnEndBookkeeping(before ?? emptyFindings(""), folder2, outcome, topLevel, /* @__PURE__ */ new Date());
32160
33133
  if (before !== null && JSON.stringify(before) !== JSON.stringify(file2)) {
32161
33134
  if (file2.findings.length > 0)
32162
33135
  writeFindings(file2);
@@ -32181,7 +33154,7 @@ function queueTurnEnd(words, reply) {
32181
33154
  if (words !== null)
32182
33155
  reply.notice(words);
32183
33156
  }
32184
- function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder) {
33157
+ function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder2) {
32185
33158
  const notRun = notRunMessage((envelope.notRun ?? []).map((n) => n.reason));
32186
33159
  const outcome = {
32187
33160
  kind: "evaluated",
@@ -32190,7 +33163,7 @@ function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder) {
32190
33163
  };
32191
33164
  const topLevel = kind === "stop";
32192
33165
  if (envelope.blocking && !neverBlock) {
32193
- keepBooks(sessionId, folder, outcome, false);
33166
+ keepBooks(sessionId, folder2, outcome, false);
32194
33167
  const reason = blockReason(envelope.guardsFired.map((g) => g.explanation));
32195
33168
  reply.block(notRun ? `${reason}
32196
33169
  ${notRun}` : reason, stoppedMessage(blockingExplanations(envelope.guardsFired)));
@@ -32198,7 +33171,7 @@ ${notRun}` : reason, stoppedMessage(blockingExplanations(envelope.guardsFired)))
32198
33171
  if (envelope.blocking && neverBlock) {
32199
33172
  reply.notice(couldNotFixMessage(blockingExplanations(envelope.guardsFired)));
32200
33173
  }
32201
- queueTurnEnd(keepBooks(sessionId, folder, outcome, topLevel), reply);
33174
+ queueTurnEnd(keepBooks(sessionId, folder2, outcome, topLevel), reply);
32202
33175
  if (notRun)
32203
33176
  reply.notice(notRun);
32204
33177
  }
@@ -32219,9 +33192,9 @@ async function readHookStdin() {
32219
33192
  }
32220
33193
  }
32221
33194
  function readStdin2() {
32222
- return new Promise((resolve11) => {
33195
+ return new Promise((resolve12) => {
32223
33196
  if (process.stdin.isTTY) {
32224
- resolve11("");
33197
+ resolve12("");
32225
33198
  return;
32226
33199
  }
32227
33200
  const chunks = [];
@@ -32231,7 +33204,7 @@ function readStdin2() {
32231
33204
  return;
32232
33205
  settled2 = true;
32233
33206
  clearTimeout(timer);
32234
- resolve11(Buffer.concat(chunks).toString("utf-8"));
33207
+ resolve12(Buffer.concat(chunks).toString("utf-8"));
32235
33208
  };
32236
33209
  const timer = setTimeout(done, HOOK_STDIN_GRACE_MS);
32237
33210
  process.stdin.on("data", (chunk) => chunks.push(chunk));
@@ -32258,19 +33231,19 @@ async function runStop(observeDeps = {}, timeDeps = {}, paceDeps = {}, profile =
32258
33231
  recordTimeEvent("turn-end", input);
32259
33232
  await runSessionDiff(input, "stop", readSeatConfiguration, async (reply) => {
32260
33233
  await observeChanges(input.session_id, reply, observeDeps);
32261
- await sendTimeRecord(timeDeps);
33234
+ await sendTimeRecord(timeDeps, { agent: profile.id });
32262
33235
  await paceAtTurnEnd(input, reply, { configFolder: profile.configFolder, ...paceDeps });
32263
33236
  }, profile);
32264
33237
  }
32265
33238
 
32266
33239
  // dist/relayed-choices.js
32267
33240
  import { mkdirSync as mkdirSync11, readFileSync as readFileSync26, renameSync as renameSync14, rmSync as rmSync5, writeFileSync as writeFileSync16 } from "node:fs";
32268
- import { homedir as homedir13 } from "node:os";
32269
- import { dirname as dirname9, join as join19 } from "node:path";
33241
+ import { homedir as homedir14 } from "node:os";
33242
+ import { dirname as dirname9, join as join20 } from "node:path";
32270
33243
  function relayedChoicesFilePath(engagementId, home) {
32271
33244
  if (!isEngagementId(engagementId))
32272
33245
  return null;
32273
- return join19(home ?? homedir13(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, RELAYED_CHOICES_FILENAME);
33246
+ return join20(home ?? homedir14(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, RELAYED_CHOICES_FILENAME);
32274
33247
  }
32275
33248
  function readRelayedChoices(engagementId, phase, home) {
32276
33249
  const path = relayedChoicesFilePath(engagementId, home);
@@ -32306,7 +33279,7 @@ function recordRelayedChoice(opts) {
32306
33279
  });
32307
33280
  if (!file2.success)
32308
33281
  return false;
32309
- const temp = join19(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
33282
+ const temp = join20(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
32310
33283
  try {
32311
33284
  mkdirSync11(dirname9(target), { recursive: true, mode: 448 });
32312
33285
  writeFileSync16(temp, `${JSON.stringify(file2.data)}
@@ -32535,19 +33508,19 @@ function matchReviewPhraseShape(shape, reviews) {
32535
33508
 
32536
33509
  // dist/hooks/shown-record.js
32537
33510
  import { chmodSync as chmodSync4, readFileSync as readFileSync27, renameSync as renameSync15, unlinkSync as unlinkSync11, writeFileSync as writeFileSync17 } from "node:fs";
32538
- import { homedir as homedir14, platform as platform3 } from "node:os";
32539
- import { join as join20 } from "node:path";
33511
+ import { homedir as homedir15, platform as platform3 } from "node:os";
33512
+ import { join as join21 } from "node:path";
32540
33513
  function recordDir(engagementId, home) {
32541
33514
  if (!isEngagementId(engagementId))
32542
33515
  return null;
32543
- return join20(home ?? homedir14(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId);
33516
+ return join21(home ?? homedir15(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId);
32544
33517
  }
32545
33518
  function readShownRecord(engagementId, home) {
32546
33519
  const dir = recordDir(engagementId, home);
32547
33520
  if (dir === null)
32548
33521
  return { entries: [], reviews: [] };
32549
33522
  try {
32550
- return parseShownRecord(readFileSync27(join20(dir, SHOWN_PLANS_FILENAME), "utf-8"));
33523
+ return parseShownRecord(readFileSync27(join21(dir, SHOWN_PLANS_FILENAME), "utf-8"));
32551
33524
  } catch {
32552
33525
  return { entries: [], reviews: [] };
32553
33526
  }
@@ -32568,7 +33541,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
32568
33541
  const dir = recordDir(engagementId, home);
32569
33542
  if (dir === null)
32570
33543
  return false;
32571
- const path = join20(dir, SHOWN_PLANS_FILENAME);
33544
+ const path = join21(dir, SHOWN_PLANS_FILENAME);
32572
33545
  const current = parseShownRecord(readFileSync27(path, "utf-8"));
32573
33546
  const at = current.reviews.findIndex((r) => r.phase === listed.phase && r.shownAt === listed.shownAt && r.reviewVersion === listed.reviewVersion);
32574
33547
  if (at === -1)
@@ -32576,7 +33549,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
32576
33549
  const acts = new Map(decided.map((d) => [d.part, d.act]));
32577
33550
  const reviews = current.reviews.map((review, i) => i === at ? { ...review, reviewVersion, parts: review.parts.map((p) => movedPart(p, acts.get(p.part))) } : review);
32578
33551
  const body = serialiseShownRecord({ entries: current.entries, reviews });
32579
- temp = join20(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
33552
+ temp = join21(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
32580
33553
  writeFileSync17(temp, body, { mode: 384 });
32581
33554
  if (platform3() !== "win32")
32582
33555
  chmodSync4(temp, 384);
@@ -32960,8 +33933,16 @@ function allowArmedLine(step = null) {
32960
33933
  function allowNothingHeldLine() {
32961
33934
  return line(`${NOTHING_ALLOWED}: no step is paused in this session.`);
32962
33935
  }
32963
- function allowSeveralHeldLine() {
32964
- return `${NOTHING_ALLOWED}: more than one step was paused, so Halfcycle could not tell which one you meant. Nothing ran. When your agent tries the step again, type ${ALLOW_REPLY} once more. ${SHOW_AS_WRITTEN}`;
33936
+ var UNNAMED_PAUSED_STEP = "a paused step";
33937
+ function allowSeveralHeldLine(paused) {
33938
+ const listed = paused.filter((p) => p.number !== null).map((p) => `${ALLOW_REPLY} ${String(p.number)} for ${p.step ?? UNNAMED_PAUSED_STEP}`).join("; ");
33939
+ if (listed === "") {
33940
+ return `${NOTHING_ALLOWED}: more than one step is paused, and Halfcycle could not number them on this computer. Nothing ran. ${SHOW_AS_WRITTEN}`;
33941
+ }
33942
+ return `${NOTHING_ALLOWED}: more than one step is paused. Type ${ALLOW_REPLY} and the step's number to run that one once: ${listed}. Nothing ran. ${SHOW_AS_WRITTEN}`;
33943
+ }
33944
+ function allowNotPausedLine(stepNumber) {
33945
+ return line(`${NOTHING_ALLOWED}: step ${String(stepNumber)} is not paused in this session.`);
32965
33946
  }
32966
33947
  function allowNotAttendedLine() {
32967
33948
  return line(`${NOTHING_ALLOWED}: a paused step can be allowed by typing only when a person types it into an open session.`);
@@ -33047,8 +34028,8 @@ function waitLineForAnswer(context, answer) {
33047
34028
  }
33048
34029
  }
33049
34030
  var PACE_NOTHING_CHANGED_OPENING = "Nothing changed";
33050
- function paceAnswerLine(text, agent) {
33051
- return agent === "claude-code" ? line(text) : codexLine(text);
34031
+ function paceAnswerLine(text, _agent) {
34032
+ return codexLine(text);
33052
34033
  }
33053
34034
  function paceNumberDoneText(n, more) {
33054
34035
  const head = `Done. This build now runs up to ${paceAgentsOf(n)} at once.`;
@@ -33156,8 +34137,9 @@ async function decideLine(opts) {
33156
34137
  const review = { recognised: false };
33157
34138
  try {
33158
34139
  const prompt = promptOf(opts.input);
33159
- if (isAllowReply(prompt))
33160
- return `${decideAllow(opts)}
34140
+ const allowReply = allowReplyOf(prompt);
34141
+ if (allowReply !== null)
34142
+ return `${decideAllow(allowReply.number, opts)}
33161
34143
  `;
33162
34144
  const autonomyShape = autonomyPhraseShape(prompt);
33163
34145
  if (autonomyShape !== null) {
@@ -33210,21 +34192,23 @@ async function decideLine(opts) {
33210
34192
  `;
33211
34193
  }
33212
34194
  }
33213
- function decideAllow(opts) {
34195
+ function decideAllow(stepNumber, opts) {
33214
34196
  const env = opts.env ?? process.env;
33215
34197
  if ((opts.agent ?? "claude-code") === "claude-code" && !readSessionAttended(env))
33216
34198
  return allowNotAttendedLine();
33217
- const { outcome, step } = armAllowStep(sessionIdOf(opts.input), {
34199
+ const { outcome, step, paused } = armAllowStep(sessionIdOf(opts.input), {
33218
34200
  ...opts.tempDir === void 0 ? {} : { dir: opts.tempDir },
33219
34201
  ...opts.now === void 0 ? {} : { now: opts.now }
33220
- });
34202
+ }, stepNumber);
33221
34203
  switch (outcome) {
33222
34204
  case "armed":
33223
34205
  return allowArmedLine(step);
33224
34206
  case "nothing-held":
33225
34207
  return allowNothingHeldLine();
34208
+ case "not-paused":
34209
+ return allowNotPausedLine(stepNumber ?? 0);
33226
34210
  case "several-held":
33227
- return allowSeveralHeldLine();
34211
+ return allowSeveralHeldLine(paused);
33228
34212
  case "not-saved":
33229
34213
  return allowNotSavedLine();
33230
34214
  default: {
@@ -33751,14 +34735,14 @@ function runUserPromptFindings(opts) {
33751
34735
  }
33752
34736
 
33753
34737
  // dist/resolve-engagement.js
33754
- import { existsSync as existsSync8, readFileSync as readFileSync28, realpathSync as realpathSync9 } from "node:fs";
33755
- import { join as join21, sep as sep6 } from "node:path";
33756
- import { homedir as homedir15 } from "node:os";
34738
+ import { existsSync as existsSync9, readFileSync as readFileSync28, realpathSync as realpathSync10 } from "node:fs";
34739
+ import { join as join22, sep as sep7 } from "node:path";
34740
+ import { homedir as homedir16 } from "node:os";
33757
34741
  function defaultRegistryPath() {
33758
- return join21(homedir15(), ".halfcycle", "registry.json");
34742
+ return join22(homedir16(), ".halfcycle", "registry.json");
33759
34743
  }
33760
34744
  function readRegistry(registryPath = defaultRegistryPath()) {
33761
- if (!existsSync8(registryPath))
34745
+ if (!existsSync9(registryPath))
33762
34746
  return {};
33763
34747
  try {
33764
34748
  const parsed = JSON.parse(readFileSync28(registryPath, "utf-8"));
@@ -33776,7 +34760,7 @@ function readRegistry(registryPath = defaultRegistryPath()) {
33776
34760
  }
33777
34761
  function canonicalise(p) {
33778
34762
  try {
33779
- return realpathSync9(p);
34763
+ return realpathSync10(p);
33780
34764
  } catch {
33781
34765
  return p;
33782
34766
  }
@@ -33797,7 +34781,7 @@ function resolveEngagement(cwd, registry2) {
33797
34781
  function isSelfOrAncestor(ancestor, target) {
33798
34782
  if (ancestor === target)
33799
34783
  return true;
33800
- const withSep = ancestor.endsWith(sep6) ? ancestor : ancestor + sep6;
34784
+ const withSep = ancestor.endsWith(sep7) ? ancestor : ancestor + sep7;
33801
34785
  return target.startsWith(withSep);
33802
34786
  }
33803
34787
  function runResolveEngagement(cwd, registryPath) {
@@ -33810,12 +34794,12 @@ function runResolveEngagement(cwd, registryPath) {
33810
34794
 
33811
34795
  // dist/refresh-credential.js
33812
34796
  import { readFileSync as readFileSync30 } from "node:fs";
33813
- import { resolve as resolve10 } from "node:path";
34797
+ import { resolve as resolve11 } from "node:path";
33814
34798
 
33815
34799
  // dist/mcp-headers.js
33816
34800
  import { readFileSync as readFileSync29 } from "node:fs";
33817
- import { homedir as homedir16 } from "node:os";
33818
- import { dirname as dirname10, resolve as resolve9 } from "node:path";
34801
+ import { homedir as homedir17 } from "node:os";
34802
+ import { dirname as dirname10, resolve as resolve10 } from "node:path";
33819
34803
  var URL_FLAG = "--url";
33820
34804
  function urlFromArguments(rest) {
33821
34805
  const at = [];
@@ -33842,12 +34826,12 @@ function headerLine(bearer, clientVersion) {
33842
34826
  }
33843
34827
  function projectRootFromBinary(binaryPath) {
33844
34828
  const depth = VENDORED_BIN_REL.split("/").length - 1;
33845
- return resolve9(dirname10(binaryPath), ...Array(depth).fill(".."));
34829
+ return resolve10(dirname10(binaryPath), ...Array(depth).fill(".."));
33846
34830
  }
33847
34831
  function readPinnedVersion(root) {
33848
34832
  let raw;
33849
34833
  try {
33850
- raw = readFileSync29(resolve9(root, BUNDLE_PIN_REL), "utf-8");
34834
+ raw = readFileSync29(resolve10(root, BUNDLE_PIN_REL), "utf-8");
33851
34835
  } catch {
33852
34836
  return void 0;
33853
34837
  }
@@ -33889,7 +34873,7 @@ function sentences(lines2) {
33889
34873
  }
33890
34874
  function badIdSentence(root) {
33891
34875
  return sentences([
33892
- `${resolve9(root, BUNDLE_PIN_REL)} names an engagement id that is not a Halfcycle project id,`,
34876
+ `${resolve10(root, BUNDLE_PIN_REL)} names an engagement id that is not a Halfcycle project id,`,
33893
34877
  'so no credential was read. Delete that file and run "npx halfcycle" again: this repository',
33894
34878
  "is then set up as a new Halfcycle project."
33895
34879
  ]);
@@ -33903,7 +34887,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
33903
34887
  ]);
33904
34888
  case "no-id":
33905
34889
  return sentences([
33906
- `${resolve9(root, BUNDLE_PIN_REL)} names no engagement id.`,
34890
+ `${resolve10(root, BUNDLE_PIN_REL)} names no engagement id.`,
33907
34891
  'run "npx halfcycle" here to rewrite it.'
33908
34892
  ]);
33909
34893
  case "bad-id":
@@ -33920,7 +34904,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
33920
34904
  case "no-credential":
33921
34905
  return sentences([
33922
34906
  `this machine holds no credential for engagement ${engagementId ?? ""}.`,
33923
- `run "npx halfcycle" in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${resolve9(home, HALFCYCLE_DIR_NAME)}, outside this repository.`
34907
+ `run "npx halfcycle" in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${resolve10(home, HALFCYCLE_DIR_NAME)}, outside this repository.`
33924
34908
  ]);
33925
34909
  }
33926
34910
  }
@@ -33943,7 +34927,7 @@ async function refreshWithinDeadline(source, opts, writeErr) {
33943
34927
  }
33944
34928
  async function chooseBearer(opts) {
33945
34929
  const writeErr = opts.writeErr ?? defaultWriteErr;
33946
- const home = opts.home ?? homedir16();
34930
+ const home = opts.home ?? homedir17();
33947
34931
  const root = projectRootFromBinary(opts.binaryPath);
33948
34932
  const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
33949
34933
  const store = resolveStore(root, home);
@@ -34086,8 +35070,8 @@ async function refreshCredentialToken(opts) {
34086
35070
  source
34087
35071
  });
34088
35072
  if (!refreshed.ok) {
34089
- const write = opts?.write ?? ((text) => process.stdout.write(text));
34090
- write(credentialRenewalFailedMessage(refreshed.remedy));
35073
+ const write2 = opts?.write ?? ((text) => process.stdout.write(text));
35074
+ write2(credentialRenewalFailedMessage(refreshed.remedy));
34091
35075
  return { token: void 0 };
34092
35076
  }
34093
35077
  return { token: refreshed.token };
@@ -34109,7 +35093,7 @@ var SESSION_START_PROBE_ATTEMPTS = 2;
34109
35093
  function registeredMcpUrl(projectRoot) {
34110
35094
  let parsed;
34111
35095
  try {
34112
- parsed = JSON.parse(readFileSync30(resolve10(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
35096
+ parsed = JSON.parse(readFileSync30(resolve11(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
34113
35097
  } catch {
34114
35098
  return void 0;
34115
35099
  }
@@ -34140,6 +35124,10 @@ function discardBody(res) {
34140
35124
  res.body?.cancel().catch(() => void 0);
34141
35125
  }
34142
35126
  var CUT_OFF = /* @__PURE__ */ Symbol("cut-off");
35127
+ function reportOf(body) {
35128
+ const instructions = body["result"]["instructions"];
35129
+ return typeof instructions === "string" ? checksReportOfInstructions(instructions) : "none";
35130
+ }
34143
35131
  async function postInitialize(url2, bearer, fetchFn, signal) {
34144
35132
  let res;
34145
35133
  try {
@@ -34173,7 +35161,7 @@ async function postInitialize(url2, bearer, fetchFn, signal) {
34173
35161
  } catch {
34174
35162
  return SERVICE_PROBLEM_SENTENCE;
34175
35163
  }
34176
- return namesItsServer(body) ? void 0 : SERVICE_PROBLEM_SENTENCE;
35164
+ return namesItsServer(body) ? { report: reportOf(body) } : SERVICE_PROBLEM_SENTENCE;
34177
35165
  }
34178
35166
  async function probeDelivery(url2, bearer, fetchFn, deadlineMs, maxAttempts) {
34179
35167
  const controller = new AbortController();
@@ -34207,7 +35195,9 @@ async function checkConnection(renewedToken, opts) {
34207
35195
  return { sentence: PLACEHOLDER_SENTENCES[bearer], url: url2, bearer, connected: false };
34208
35196
  }
34209
35197
  const outcome = await probeDelivery(url2, bearer, opts.fetchFn ?? globalThis.fetch, opts.checkDeadlineMs ?? CONNECTION_CHECK_DEADLINE_MS, opts.maxAttempts ?? SESSION_START_PROBE_ATTEMPTS);
34210
- return { sentence: outcome ?? connectedSentenceFor(profile), url: url2, bearer, connected: outcome === void 0 };
35198
+ if (typeof outcome === "string")
35199
+ return { sentence: outcome, url: url2, bearer, connected: false };
35200
+ return { sentence: connectedSentenceFor(profile), url: url2, bearer, connected: true, report: outcome.report };
34211
35201
  }
34212
35202
  function positionRequestBody(engagementId) {
34213
35203
  return JSON.stringify({
@@ -34358,7 +35348,7 @@ async function runRefreshCredential(opts) {
34358
35348
  import { fileURLToPath } from "node:url";
34359
35349
 
34360
35350
  // dist/capture.js
34361
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, mkdirSync as mkdirSync12, existsSync as existsSync9 } from "node:fs";
35351
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, mkdirSync as mkdirSync12, existsSync as existsSync10 } from "node:fs";
34362
35352
  import { dirname as dirname11 } from "node:path";
34363
35353
  function runCapture(provider, endpoint, opts = {}) {
34364
35354
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -34440,8 +35430,8 @@ function relativeFixturePath(repoRoot, fixture) {
34440
35430
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
34441
35431
  import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
34442
35432
  import { createWriteStream, mkdirSync as mkdirSync13, readdirSync as readdirSync11, readFileSync as readFileSync32, renameSync as renameSync16, unlinkSync as unlinkSync12, writeFileSync as writeFileSync19 } from "node:fs";
34443
- import { homedir as homedir17 } from "node:os";
34444
- import { dirname as dirname12, join as join22 } from "node:path";
35433
+ import { homedir as homedir18 } from "node:os";
35434
+ import { dirname as dirname12, join as join23 } from "node:path";
34445
35435
 
34446
35436
  // dist/test-counts.js
34447
35437
  function sum(values) {
@@ -34550,7 +35540,7 @@ function isProofTestPath(path) {
34550
35540
  function testRunsDir(engagementId, home) {
34551
35541
  if (!isEngagementId(engagementId))
34552
35542
  return null;
34553
- return join22(home ?? homedir17(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TEST_RUNS_DIR_NAME);
35543
+ return join23(home ?? homedir18(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TEST_RUNS_DIR_NAME);
34554
35544
  }
34555
35545
  function commandIdOf(command) {
34556
35546
  return createHash6("sha256").update(command, "utf8").digest("hex").slice(0, 16);
@@ -34646,7 +35636,7 @@ function subjectKey(subject) {
34646
35636
  }
34647
35637
  function keptCheckPath(dir, subject, label) {
34648
35638
  const key = createHash6("sha256").update(JSON.stringify([...subjectKey(subject), label]), "utf8").digest("hex");
34649
- return join22(dir, COMMANDS_DIR_NAME, `${key.slice(0, 32)}.json`);
35639
+ return join23(dir, COMMANDS_DIR_NAME, `${key.slice(0, 32)}.json`);
34650
35640
  }
34651
35641
  function readKeptCheck(dir, subject, label) {
34652
35642
  try {
@@ -34661,18 +35651,18 @@ function readKeptCheck(dir, subject, label) {
34661
35651
  }
34662
35652
  }
34663
35653
  function queuePath(dir, runId) {
34664
- return join22(dir, QUEUE_DIR_NAME, `${runId}.json`);
35654
+ return join23(dir, QUEUE_DIR_NAME, `${runId}.json`);
34665
35655
  }
34666
35656
  function readQueue(dir) {
34667
35657
  let names;
34668
35658
  try {
34669
- names = readdirSync11(join22(dir, QUEUE_DIR_NAME)).filter((n) => n.endsWith(".json"));
35659
+ names = readdirSync11(join23(dir, QUEUE_DIR_NAME)).filter((n) => n.endsWith(".json"));
34670
35660
  } catch {
34671
35661
  return [];
34672
35662
  }
34673
35663
  const reports = [];
34674
35664
  for (const name of names) {
34675
- const path = join22(dir, QUEUE_DIR_NAME, name);
35665
+ const path = join23(dir, QUEUE_DIR_NAME, name);
34676
35666
  let parsed = null;
34677
35667
  try {
34678
35668
  parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync32(path, "utf-8")));
@@ -34745,17 +35735,17 @@ function killTree(child, platform4, signal) {
34745
35735
  }
34746
35736
  }
34747
35737
  }
34748
- function runCommand(command, repoRoot, logPath, env, deps) {
35738
+ function runCommand(command, repoRoot, logPath2, env, deps) {
34749
35739
  const platform4 = deps.platform ?? process.platform;
34750
35740
  const out = deps.stdout ?? process.stdout;
34751
35741
  const err = deps.stderr ?? process.stderr;
34752
35742
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
34753
35743
  const childEnv = { ...env };
34754
35744
  delete childEnv[SERVICE_TOKEN_VAR];
34755
- mkdirSync13(dirname12(logPath), { recursive: true, mode: 448 });
34756
- const log = createWriteStream(logPath, { mode: 384 });
35745
+ mkdirSync13(dirname12(logPath2), { recursive: true, mode: 448 });
35746
+ const log = createWriteStream(logPath2, { mode: 384 });
34757
35747
  const began = process.hrtime.bigint();
34758
- return new Promise((resolve11) => {
35748
+ return new Promise((resolve12) => {
34759
35749
  let timedOut = false;
34760
35750
  let started = true;
34761
35751
  let settled2 = false;
@@ -34790,7 +35780,7 @@ function runCommand(command, repoRoot, logPath, env, deps) {
34790
35780
  process.off("SIGINT", onSignal);
34791
35781
  process.off("SIGTERM", onSignal);
34792
35782
  const durationMs = Number((process.hrtime.bigint() - began) / 1000000n);
34793
- log.end(() => resolve11({ exitCode: timedOut || !started ? null : code, timedOut, started, durationMs }));
35783
+ log.end(() => resolve12({ exitCode: timedOut || !started ? null : code, timedOut, started, durationMs }));
34794
35784
  };
34795
35785
  child.on("error", (e) => {
34796
35786
  started = false;
@@ -34912,13 +35902,13 @@ ${PROVE_USAGE}`);
34912
35902
  if (request.when === "before" && changedOutsideTests > 0)
34913
35903
  say(lateBeforeLine(changedOutsideTests));
34914
35904
  const runId = randomUUID3();
34915
- const logPath = join22(dir, `${runId}.log`);
35905
+ const logPath2 = join23(dir, `${runId}.log`);
34916
35906
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
34917
35907
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
34918
- const end = await runCommand(command, repoRoot, logPath, env, deps);
35908
+ const end = await runCommand(command, repoRoot, logPath2, env, deps);
34919
35909
  let output = "";
34920
35910
  try {
34921
- output = readFileSync32(logPath, "utf-8");
35911
+ output = readFileSync32(logPath2, "utf-8");
34922
35912
  } catch {
34923
35913
  output = "";
34924
35914
  }
@@ -34962,7 +35952,7 @@ ${PROVE_USAGE}`);
34962
35952
  sendLine = mine.outcome === "recorded" ? RECORDED_LINE : mine.outcome === "kept" ? NOT_SENT_YET_LINE : refusedLine(mine.refusal);
34963
35953
  }
34964
35954
  say(`${line2} ${sendLine}`);
34965
- say(`Its output is kept on this computer at ${logPath}`);
35955
+ say(`Its output is kept on this computer at ${logPath2}`);
34966
35956
  return 0;
34967
35957
  }
34968
35958
 
@@ -35030,14 +36020,14 @@ function personCommandEnv(env) {
35030
36020
  }
35031
36021
  return out;
35032
36022
  }
35033
- var runOwnCommandReal = (command, input, env) => new Promise((resolve11) => {
36023
+ var runOwnCommandReal = (command, input, env) => new Promise((resolve12) => {
35034
36024
  let out = "";
35035
36025
  let settled2 = false;
35036
36026
  const done = (v) => {
35037
36027
  if (settled2)
35038
36028
  return;
35039
36029
  settled2 = true;
35040
- resolve11(v);
36030
+ resolve12(v);
35041
36031
  };
35042
36032
  try {
35043
36033
  const child = spawn3(command, {
@@ -35126,7 +36116,8 @@ async function checkConnectionLine(opts) {
35126
36116
  });
35127
36117
  if (result === void 0)
35128
36118
  return { kind: "not-registered", sentence: null };
35129
- return { kind: connectionKind(result.bearer, result.sentence), sentence: result.sentence };
36119
+ const line2 = { kind: connectionKind(result.bearer, result.sentence), sentence: result.sentence };
36120
+ return result.report === void 0 || result.report === "none" ? line2 : { ...line2, report: result.report };
35130
36121
  }
35131
36122
  async function runCheckConnection(opts) {
35132
36123
  const line2 = await checkConnectionLine(opts);
@@ -35185,7 +36176,7 @@ async function main() {
35185
36176
  if (cmd === "time") {
35186
36177
  let line2;
35187
36178
  try {
35188
- line2 = JSON.stringify(await runTimeVerb());
36179
+ line2 = JSON.stringify(await runTimeVerb({}, { final: args.slice(1).includes("--final") }));
35189
36180
  } catch {
35190
36181
  line2 = JSON.stringify({ outcome: "not-sent", reason: "unreachable" });
35191
36182
  }
@@ -35351,7 +36342,7 @@ function unknownCommand(name = args.join(" ")) {
35351
36342
  halfcycle-runner mcp-headers [--url <address>]
35352
36343
  halfcycle-runner observe
35353
36344
  halfcycle-runner observe --change <sortingId>
35354
- halfcycle-runner time
36345
+ halfcycle-runner time [--final]
35355
36346
  halfcycle-runner prove <phase> <task> --label <label> --before|--after [-- <command>]
35356
36347
  halfcycle-runner pace [--start <phase> [--agents <n>]]
35357
36348
  halfcycle-runner pace --scan [--config-folder <dir>]
@@ -35362,10 +36353,10 @@ function unknownCommand(name = args.join(" ")) {
35362
36353
  process.exit(1);
35363
36354
  }
35364
36355
  function readStdin3() {
35365
- return new Promise((resolve11, reject) => {
36356
+ return new Promise((resolve12, reject) => {
35366
36357
  const chunks = [];
35367
36358
  process.stdin.on("data", (chunk) => chunks.push(chunk));
35368
- process.stdin.on("end", () => resolve11(Buffer.concat(chunks).toString("utf-8")));
36359
+ process.stdin.on("end", () => resolve12(Buffer.concat(chunks).toString("utf-8")));
35369
36360
  process.stdin.on("error", reject);
35370
36361
  });
35371
36362
  }