halfcycle 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)
@@ -19784,7 +20043,7 @@ async function attempt(url2, token, request, fetchFn, retry) {
19784
20043
 
19785
20044
  // dist/git.js
19786
20045
  import { spawnSync } from "node:child_process";
19787
- import { existsSync, readFileSync } from "node:fs";
20046
+ import { existsSync as existsSync2, readFileSync } from "node:fs";
19788
20047
  import { relative } from "node:path";
19789
20048
  var COLOR_AWARE_SUBCOMMANDS = /* @__PURE__ */ new Set(["diff", "show", "log"]);
19790
20049
  function spawnGit(args2, cwd, timeoutMs, extra) {
@@ -19801,7 +20060,7 @@ function spawnGit(args2, cwd, timeoutMs, extra) {
19801
20060
  function git(args2, cwd, timeoutMs, extra) {
19802
20061
  const result = spawnGit(args2, cwd, timeoutMs, extra);
19803
20062
  if (result.error) {
19804
- const missing = result.error.code === "ENOENT" && existsSync(cwd);
20063
+ const missing = result.error.code === "ENOENT" && existsSync2(cwd);
19805
20064
  return { ok: false, message: `git error: ${result.error.message}`, ...missing ? { gitMissing: true } : {} };
19806
20065
  }
19807
20066
  if (result.status !== 0) {
@@ -20315,14 +20574,14 @@ function stripComments(code) {
20315
20574
  function extractSqlWrites(diffContent) {
20316
20575
  const writes = [];
20317
20576
  const seen = /* @__PURE__ */ new Set();
20318
- const push = (write) => {
20319
- if (!write)
20577
+ const push = (write2) => {
20578
+ if (!write2)
20320
20579
  return;
20321
- const key = `${write.kind}:${write.table}`;
20580
+ const key = `${write2.kind}:${write2.table}`;
20322
20581
  if (seen.has(key))
20323
20582
  return;
20324
20583
  seen.add(key);
20325
- writes.push(write);
20584
+ writes.push(write2);
20326
20585
  };
20327
20586
  for (const line2 of diffContent.split("\n")) {
20328
20587
  if (isRemovedLine(line2) || isContextLine(line2) || isDiffHeader(line2))
@@ -20633,7 +20892,7 @@ function detectBlastRadiusMarkers(filePath, diffContent) {
20633
20892
 
20634
20893
  // dist/extract/numeric-columns.js
20635
20894
  import { readFileSync as readFileSync2, readdirSync } from "node:fs";
20636
- import { join } from "node:path";
20895
+ import { join as join2 } from "node:path";
20637
20896
  function extractNumericColumns(diffContent, context = {}) {
20638
20897
  const addedLines = [];
20639
20898
  for (const line2 of diffContent.split("\n")) {
@@ -20700,12 +20959,12 @@ function walkForRegistrations(dir, repoRoot, acc) {
20700
20959
  if (entry.isDirectory()) {
20701
20960
  if (EXCLUDED_DIRS.has(entry.name))
20702
20961
  continue;
20703
- walkForRegistrations(join(dir, entry.name), repoRoot, acc);
20962
+ walkForRegistrations(join2(dir, entry.name), repoRoot, acc);
20704
20963
  continue;
20705
20964
  }
20706
20965
  if (!entry.isFile())
20707
20966
  continue;
20708
- const absPath = join(dir, entry.name);
20967
+ const absPath = join2(dir, entry.name);
20709
20968
  if (!isCodeFile(absPath))
20710
20969
  continue;
20711
20970
  let content;
@@ -20847,13 +21106,13 @@ function resolveWireFields(lines2, decls) {
20847
21106
  }
20848
21107
 
20849
21108
  // 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";
21109
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, renameSync, unlinkSync, writeFileSync } from "node:fs";
21110
+ import { dirname, join as join3 } from "node:path";
20852
21111
  function manifestPath(repoRoot) {
20853
- return join2(repoRoot, "test", "fixtures", "captured", "manifest.json");
21112
+ return join3(repoRoot, "test", "fixtures", "captured", "manifest.json");
20854
21113
  }
20855
21114
  function fixturePath(repoRoot, provider, slug) {
20856
- return join2(repoRoot, "test", "fixtures", "captured", provider, `${slug}.json`);
21115
+ return join3(repoRoot, "test", "fixtures", "captured", provider, `${slug}.json`);
20857
21116
  }
20858
21117
  function loadManifest(repoRoot) {
20859
21118
  if (!repoRoot)
@@ -20886,7 +21145,7 @@ function upsertManifest(repoRoot, entry) {
20886
21145
  }
20887
21146
  function removeFixtureIfExists(path) {
20888
21147
  try {
20889
- if (existsSync2(path))
21148
+ if (existsSync3(path))
20890
21149
  unlinkSync(path);
20891
21150
  } catch {
20892
21151
  }
@@ -21134,7 +21393,7 @@ function toAstShapes(markers) {
21134
21393
 
21135
21394
  // dist/extract/context.js
21136
21395
  import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
21137
- import { join as join3, relative as relative2, sep } from "node:path";
21396
+ import { join as join4, relative as relative2, sep } from "node:path";
21138
21397
  var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set([
21139
21398
  "node_modules",
21140
21399
  ".git",
@@ -21168,7 +21427,7 @@ function walk(dir, repoRoot, acc) {
21168
21427
  if (entry.isDirectory()) {
21169
21428
  if (EXCLUDED_DIRS2.has(entry.name))
21170
21429
  continue;
21171
- walk(join3(dir, entry.name), repoRoot, acc);
21430
+ walk(join4(dir, entry.name), repoRoot, acc);
21172
21431
  continue;
21173
21432
  }
21174
21433
  if (!entry.isFile())
@@ -21176,7 +21435,7 @@ function walk(dir, repoRoot, acc) {
21176
21435
  const kind = classifyFile(entry.name);
21177
21436
  if (!kind)
21178
21437
  continue;
21179
- const absPath = join3(dir, entry.name);
21438
+ const absPath = join4(dir, entry.name);
21180
21439
  let content;
21181
21440
  try {
21182
21441
  content = readFileSync4(absPath, "utf-8");
@@ -21199,9 +21458,9 @@ function loadChangeSetContext(repoRoot) {
21199
21458
  }
21200
21459
 
21201
21460
  // 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";
21461
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
21462
+ import { join as join5, dirname as dirname2 } from "node:path";
21463
+ import { homedir as homedir2 } from "node:os";
21205
21464
  import { randomUUID } from "node:crypto";
21206
21465
  var TELEMETRY_TIMEOUT_MS = 5e3;
21207
21466
  async function emitTelemetry(run, config2, logger, fetchFn = globalThis.fetch) {
@@ -21211,23 +21470,23 @@ async function emitTelemetry(run, config2, logger, fetchFn = globalThis.fetch) {
21211
21470
  }
21212
21471
  }
21213
21472
  function localLogPath(run, config2) {
21214
- const logDir = config2.guardEvalLogDir ?? join4(homedir(), ".halfcycle", "guard-eval-log");
21473
+ const logDir = config2.guardEvalLogDir ?? join5(homedir2(), ".halfcycle", "guard-eval-log");
21215
21474
  const phase = run.phase;
21216
21475
  const fileName = phase === void 0 || phase === "" ? "unphased.jsonl" : `${renderPhaseSegment(phase)}.jsonl`;
21217
- return join4(logDir, fileName);
21476
+ return join5(logDir, fileName);
21218
21477
  }
21219
21478
  function appendToLocalLog(run, config2, logger) {
21220
- let logPath;
21479
+ let logPath2;
21221
21480
  try {
21222
- logPath = localLogPath(run, config2);
21481
+ logPath2 = localLogPath(run, config2);
21223
21482
  } catch (err) {
21224
21483
  const msg = err instanceof Error ? err.message : String(err);
21225
21484
  logger.warn(`[Halfcycle telemetry] Phase name refused, nothing written (dropped): ${msg}`);
21226
21485
  return;
21227
21486
  }
21228
21487
  try {
21229
- mkdirSync2(dirname2(logPath), { recursive: true });
21230
- appendFileSync(logPath, JSON.stringify(run) + "\n", "utf-8");
21488
+ mkdirSync2(dirname2(logPath2), { recursive: true });
21489
+ appendFileSync2(logPath2, JSON.stringify(run) + "\n", "utf-8");
21231
21490
  } catch (err) {
21232
21491
  const msg = err instanceof Error ? err.message : String(err);
21233
21492
  logger.warn(`[Halfcycle telemetry] Local guard-eval log write failed (dropped): ${msg}`);
@@ -21536,21 +21795,21 @@ function projectFolder(env = process.env) {
21536
21795
  }
21537
21796
 
21538
21797
  // 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";
21798
+ import { chmodSync, existsSync as existsSync4, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2, realpathSync } from "node:fs";
21799
+ import { homedir as homedir3, platform } from "node:os";
21800
+ import { dirname as dirname3, join as join6 } from "node:path";
21542
21801
  var REFRESHED_TOKEN_KEYS = ["HALFCYCLE_TOKEN", "GUARD_SERVICE_TOKEN"];
21543
21802
  var TOKEN_READ_KEY = "GUARD_SERVICE_TOKEN";
21544
21803
  function composeEngagementEnvPath(engagementId, home) {
21545
- return join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ENGAGEMENT_ENV_FILENAME);
21804
+ return join6(home ?? homedir3(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ENGAGEMENT_ENV_FILENAME);
21546
21805
  }
21547
21806
  function hasNotThisAccountMarker(engagementId, home) {
21548
- return existsSync3(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, NOT_THIS_ACCOUNT_MARKER_FILENAME));
21807
+ return existsSync4(join6(home ?? homedir3(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, NOT_THIS_ACCOUNT_MARKER_FILENAME));
21549
21808
  }
21550
21809
  function readAccountCredential(controlOrigin, home) {
21551
21810
  let raw;
21552
21811
  try {
21553
- raw = readFileSync6(join5(home ?? homedir2(), HALFCYCLE_DIR_NAME, ACCOUNT_STORE_FILENAME), "utf-8");
21812
+ raw = readFileSync6(join6(home ?? homedir3(), HALFCYCLE_DIR_NAME, ACCOUNT_STORE_FILENAME), "utf-8");
21554
21813
  } catch {
21555
21814
  return void 0;
21556
21815
  }
@@ -21602,7 +21861,7 @@ function writeRefreshedToken(opts) {
21602
21861
  existing = null;
21603
21862
  }
21604
21863
  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`);
21864
+ const temp = join6(dirname3(declaredReal), `.${ENGAGEMENT_ENV_FILENAME}.${process.pid}.tmp`);
21606
21865
  try {
21607
21866
  writeFileSync2(temp, body, { mode: 384 });
21608
21867
  if (platform() !== "win32")
@@ -21703,15 +21962,15 @@ async function refreshEngagementCredential(opts) {
21703
21962
  const minted = await mintThroughJoin(controlOrigin, opts.engagementId, accountCredential, opts.fetchFn ?? globalThis.fetch);
21704
21963
  if (!minted.ok)
21705
21964
  return minted;
21706
- const write = writeRefreshedToken({
21965
+ const write2 = writeRefreshedToken({
21707
21966
  envFilePath,
21708
21967
  engagementId: opts.engagementId,
21709
21968
  token: minted.token,
21710
21969
  home: opts.home
21711
21970
  });
21712
- if (!write.written) {
21971
+ if (!write2.written) {
21713
21972
  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}.
21973
+ warn(`[Halfcycle] This engagement's credential was renewed for this run only \u2014 ${write2.reason}.
21715
21974
  `);
21716
21975
  }
21717
21976
  return minted;
@@ -21740,9 +21999,9 @@ async function evaluateWithRefresh(opts) {
21740
21999
 
21741
22000
  // dist/hooks/marker.js
21742
22001
  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";
22002
+ import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3, readdirSync as readdirSync3, rmdirSync, statSync } from "node:fs";
21744
22003
  import { tmpdir } from "node:os";
21745
- import { join as join6 } from "node:path";
22004
+ import { join as join7 } from "node:path";
21746
22005
  var MARKER_TTL_MS = 24 * 60 * 60 * 1e3;
21747
22006
  var MARKER_PREFIX = "halfcycle-session-";
21748
22007
  var MARKER_SUFFIX = ".ref";
@@ -21756,18 +22015,21 @@ var FINDINGS_ID = new RegExp(FINDINGS_ID_SOURCE);
21756
22015
  function findingsPath(sessionId) {
21757
22016
  if (typeof sessionId !== "string" || !FINDINGS_ID.test(sessionId))
21758
22017
  return null;
21759
- return join6(markerDir(), `${FINDINGS_PREFIX}${sessionId}${FINDINGS_SUFFIX}`);
22018
+ return join7(markerDir(), `${FINDINGS_PREFIX}${sessionId}${FINDINGS_SUFFIX}`);
21760
22019
  }
21761
22020
  var HELD_STEP_PREFIX = "halfcycle-held-";
21762
22021
  var ALLOW_PREFIX = "halfcycle-allow-";
22022
+ var STEP_NUMBERS_PREFIX = "halfcycle-step-numbers-";
21763
22023
  var ALLOW_ONCE_SUFFIX = ".json";
22024
+ var STEP_NUMBERS_LOCK_SUFFIX = ".lock";
21764
22025
  function allowOncePaths(sessionId, dir) {
21765
22026
  if (typeof sessionId !== "string" || !FINDINGS_ID.test(sessionId))
21766
22027
  return null;
21767
22028
  const base = dir ?? markerDir();
21768
22029
  return {
21769
- held: join6(base, `${HELD_STEP_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`),
21770
- allow: join6(base, `${ALLOW_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`)
22030
+ held: join7(base, `${HELD_STEP_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`),
22031
+ allow: join7(base, `${ALLOW_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`),
22032
+ numbers: join7(base, `${STEP_NUMBERS_PREFIX}${sessionId}${ALLOW_ONCE_SUFFIX}`)
21771
22033
  };
21772
22034
  }
21773
22035
  function markerDir() {
@@ -21783,16 +22045,16 @@ function markerFileName(repoRoot, sessionId) {
21783
22045
  return `${MARKER_PREFIX}${key}${MARKER_SUFFIX}`;
21784
22046
  }
21785
22047
  function markerPath(repoRoot, sessionId) {
21786
- return join6(markerDir(), markerFileName(repoRoot, sessionId));
22048
+ return join7(markerDir(), markerFileName(repoRoot, sessionId));
21787
22049
  }
21788
22050
  function dedupePath(repoRoot, sessionId) {
21789
22051
  const hash2 = repoHash(repoRoot);
21790
22052
  const key = sessionId && sessionId.length > 0 ? `${hash2}-${sessionId}` : hash2;
21791
- return join6(markerDir(), `${MARKER_PREFIX}${key}${DEDUPE_SUFFIX}`);
22053
+ return join7(markerDir(), `${MARKER_PREFIX}${key}${DEDUPE_SUFFIX}`);
21792
22054
  }
21793
22055
  function readMarker(repoRoot, sessionId) {
21794
22056
  const path = markerPath(repoRoot, sessionId);
21795
- if (!existsSync4(path))
22057
+ if (!existsSync5(path))
21796
22058
  return null;
21797
22059
  try {
21798
22060
  return readFileSync7(path, "utf-8").trim() || null;
@@ -21803,7 +22065,7 @@ function readMarker(repoRoot, sessionId) {
21803
22065
  function deleteMarker(repoRoot, sessionId) {
21804
22066
  for (const path of [markerPath(repoRoot, sessionId), dedupePath(repoRoot, sessionId)]) {
21805
22067
  try {
21806
- if (existsSync4(path))
22068
+ if (existsSync5(path))
21807
22069
  unlinkSync3(path);
21808
22070
  } catch {
21809
22071
  }
@@ -21820,11 +22082,11 @@ var SESSION_NOTICE_SUFFIX = {
21820
22082
  "pre-tool-use-unreadable": ".pretool-unreadable",
21821
22083
  "outside-own-copy-unknown": ".owncopy-unknown"
21822
22084
  };
21823
- function firstNoticeThisSession(notice, folder, sessionId, dir = markerDir()) {
22085
+ function firstNoticeThisSession(notice, folder2, sessionId, dir = markerDir()) {
21824
22086
  if (!sessionId)
21825
22087
  return true;
21826
22088
  const suffix = SESSION_NOTICE_SUFFIX[notice];
21827
- const path = join6(dir, `${MARKER_PREFIX}${repoHash(folder)}-${sessionId}${suffix}`);
22089
+ const path = join7(dir, `${MARKER_PREFIX}${repoHash(folder2)}-${sessionId}${suffix}`);
21828
22090
  try {
21829
22091
  writeFileSync3(path, "", { encoding: "utf-8", flag: "wx" });
21830
22092
  return true;
@@ -21832,18 +22094,18 @@ function firstNoticeThisSession(notice, folder, sessionId, dir = markerDir()) {
21832
22094
  return err.code !== "EEXIST";
21833
22095
  }
21834
22096
  }
21835
- function warnOnceThisSession(notice, folder, sessionId) {
22097
+ function warnOnceThisSession(notice, folder2, sessionId) {
21836
22098
  return (message) => {
21837
- if (firstNoticeThisSession(notice, folder, sessionId))
22099
+ if (firstNoticeThisSession(notice, folder2, sessionId))
21838
22100
  process.stderr.write(message);
21839
22101
  };
21840
22102
  }
21841
- function firstNoRepoNoticeThisSession(folder, sessionId) {
21842
- return firstNoticeThisSession("no-repository", folder, sessionId);
22103
+ function firstNoRepoNoticeThisSession(folder2, sessionId) {
22104
+ return firstNoticeThisSession("no-repository", folder2, sessionId);
21843
22105
  }
21844
22106
  function readDedupe(repoRoot, sessionId) {
21845
22107
  const path = dedupePath(repoRoot, sessionId);
21846
- if (!existsSync4(path))
22108
+ if (!existsSync5(path))
21847
22109
  return null;
21848
22110
  try {
21849
22111
  const parsed = JSON.parse(readFileSync7(path, "utf-8"));
@@ -21891,7 +22153,7 @@ function orphanSweep(repoRoot, refKnown, keepSessionId, now = Date.now(), livene
21891
22153
  continue;
21892
22154
  if (name === keepName)
21893
22155
  continue;
21894
- const full = join6(dir, name);
22156
+ const full = join7(dir, name);
21895
22157
  let unusable = false;
21896
22158
  try {
21897
22159
  const ref = readFileSync7(full, "utf-8").trim();
@@ -21928,7 +22190,9 @@ function sweepSessionFiles(keepSessionId, now) {
21928
22190
  [FINDINGS_PREFIX, FINDINGS_SUFFIX],
21929
22191
  [OWN_WORK_PREFIX, OWN_WORK_SUFFIX],
21930
22192
  [HELD_STEP_PREFIX, ALLOW_ONCE_SUFFIX],
21931
- [ALLOW_PREFIX, ALLOW_ONCE_SUFFIX]
22193
+ [ALLOW_PREFIX, ALLOW_ONCE_SUFFIX],
22194
+ [STEP_NUMBERS_PREFIX, ALLOW_ONCE_SUFFIX],
22195
+ [STEP_NUMBERS_PREFIX, `${ALLOW_ONCE_SUFFIX}${STEP_NUMBERS_LOCK_SUFFIX}`]
21932
22196
  ];
21933
22197
  for (const name of names) {
21934
22198
  for (const [prefix, suffix] of kinds) {
@@ -21936,7 +22200,7 @@ function sweepSessionFiles(keepSessionId, now) {
21936
22200
  continue;
21937
22201
  if (keepSessionId !== void 0 && name === `${prefix}${keepSessionId}${suffix}`)
21938
22202
  continue;
21939
- const full = join6(dir, name);
22203
+ const full = join7(dir, name);
21940
22204
  try {
21941
22205
  if (now - statSync(full).mtimeMs > MARKER_TTL_MS)
21942
22206
  unlinkSync3(full);
@@ -21945,13 +22209,13 @@ function sweepSessionFiles(keepSessionId, now) {
21945
22209
  }
21946
22210
  }
21947
22211
  }
21948
- function observeThrottlePath(folder) {
21949
- return join6(markerDir(), `${MARKER_PREFIX}${repoHash(folder)}.observe-throttle`);
22212
+ function observeThrottlePath(folder2) {
22213
+ return join7(markerDir(), `${MARKER_PREFIX}${repoHash(folder2)}.observe-throttle`);
21950
22214
  }
21951
- function observeNoticesPath(folder, sessionId) {
22215
+ function observeNoticesPath(folder2, sessionId) {
21952
22216
  if (!sessionId)
21953
22217
  return null;
21954
- return join6(markerDir(), `${MARKER_PREFIX}${repoHash(folder)}-${sessionId}.observe-said`);
22218
+ return join7(markerDir(), `${MARKER_PREFIX}${repoHash(folder2)}-${sessionId}.observe-said`);
21955
22219
  }
21956
22220
  var LIVENESS_SUFFIX = ".live";
21957
22221
  var LIVENESS_NO_REPOSITORY_KEY = "norepository";
@@ -22005,7 +22269,7 @@ function sweepLiveness(common, now) {
22005
22269
  const parsed = parseLivenessFileName(name);
22006
22270
  if (parsed === null || !keys.has(parsed.key))
22007
22271
  continue;
22008
- const full = join6(dir, name);
22272
+ const full = join7(dir, name);
22009
22273
  try {
22010
22274
  if (now - statSync(full).mtimeMs > MARKER_TTL_MS) {
22011
22275
  unlinkSync3(full);
@@ -22016,22 +22280,22 @@ function sweepLiveness(common, now) {
22016
22280
  }
22017
22281
  }
22018
22282
  function removeLivenessIndexEntry(dir, sessionId, name) {
22019
- const folder = join6(dir, livenessIndexDirName(sessionId));
22283
+ const folder2 = join7(dir, livenessIndexDirName(sessionId));
22020
22284
  try {
22021
- unlinkSync3(join6(folder, name));
22285
+ unlinkSync3(join7(folder2, name));
22022
22286
  } catch {
22023
22287
  return;
22024
22288
  }
22025
22289
  try {
22026
- rmdirSync(folder);
22290
+ rmdirSync(folder2);
22027
22291
  } catch {
22028
22292
  }
22029
22293
  }
22030
22294
 
22031
22295
  // 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";
22296
+ import { existsSync as existsSync6, readFileSync as readFileSync8, realpathSync as realpathSync2 } from "node:fs";
22297
+ import { homedir as homedir4 } from "node:os";
22298
+ import { join as join8, relative as relative3, resolve as resolve2 } from "node:path";
22035
22299
  function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration, profile = AGENT_PROFILES["claude-code"]) {
22036
22300
  try {
22037
22301
  const facts = read(repoRoot, profile);
@@ -22046,7 +22310,7 @@ function gatherSeatConfiguration(repoRoot, read = readSeatConfiguration, profile
22046
22310
  function readSeatConfiguration(repoRoot, profile = AGENT_PROFILES["claude-code"]) {
22047
22311
  const given = profile.projectRoot === "git-toplevel" ? resolve2(repoRoot) : resolve2(process.env["CLAUDE_PROJECT_DIR"] || process.cwd());
22048
22312
  const projectDir = realpathSync2.native(given);
22049
- const home = homedir3();
22313
+ const home = homedir4();
22050
22314
  let realHome = home;
22051
22315
  try {
22052
22316
  realHome = realpathSync2.native(home);
@@ -22067,7 +22331,7 @@ function isPlainObject3(value) {
22067
22331
  function readSettingsFile(projectDir, rel, ours) {
22068
22332
  let raw;
22069
22333
  try {
22070
- raw = readFileSync8(join7(projectDir, rel), "utf-8");
22334
+ raw = readFileSync8(join8(projectDir, rel), "utf-8");
22071
22335
  } catch (err) {
22072
22336
  const code = err.code;
22073
22337
  const state = code === "ENOENT" || code === "ENOTDIR" ? "absent" : "unreadable";
@@ -22139,7 +22403,7 @@ function readPathStates(projectDir, repoRoot, wiringPaths) {
22139
22403
  const status = getWiringStatus(wiringPaths, projectDir);
22140
22404
  if (!status.ok)
22141
22405
  return unknown2();
22142
- const mapped = mapWiringStatus(status.value, prefix, wiringPaths, (rel) => existsSync5(join7(projectDir, rel)));
22406
+ const mapped = mapWiringStatus(status.value, prefix, wiringPaths, (rel) => existsSync6(join8(projectDir, rel)));
22143
22407
  return mapped ?? unknown2();
22144
22408
  }
22145
22409
  function mapWiringStatus(output, prefix, paths, onDisk) {
@@ -22182,28 +22446,28 @@ function mapWiringStatus(output, prefix, paths, onDisk) {
22182
22446
 
22183
22447
  // dist/time-record.js
22184
22448
  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";
22449
+ 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";
22450
+ import { homedir as homedir8 } from "node:os";
22451
+ import { dirname as dirname4, join as join11 } from "node:path";
22188
22452
 
22189
22453
  // dist/codex-usage.js
22190
22454
  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";
22455
+ import { homedir as homedir5 } from "node:os";
22456
+ import { basename, isAbsolute, join as join9, relative as relative4, resolve as resolve3, sep as sep2 } from "node:path";
22193
22457
  var CODEX_WINDOW_FRESH_MS = 10 * 60 * 1e3;
22194
22458
  var CODEX_DAYS_BACK = 7;
22195
22459
  var CODEX_FILES_MAX = 20;
22196
22460
  var CODEX_TAIL_BYTES = 256 * 1024;
22197
22461
  var CODEX_SAME_RESET_SECONDS = 60;
22198
22462
  var ROLLOUT_NAME = /^rollout-.+\.jsonl$/;
22199
- function codexHomeOf(env = process.env, home = homedir4()) {
22463
+ function codexHomeOf(env = process.env, home = homedir5()) {
22200
22464
  const set2 = env["CODEX_HOME"];
22201
- const folder = typeof set2 === "string" && set2 !== "" ? set2 : join8(home, ".codex");
22202
- if (folder === "~")
22465
+ const folder2 = typeof set2 === "string" && set2 !== "" ? set2 : join9(home, ".codex");
22466
+ if (folder2 === "~")
22203
22467
  return resolve3(home);
22204
- if (folder.startsWith("~/") || folder.startsWith("~\\"))
22205
- return resolve3(join8(home, folder.slice(2)));
22206
- return resolve3(folder);
22468
+ if (folder2.startsWith("~/") || folder2.startsWith("~\\"))
22469
+ return resolve3(join9(home, folder2.slice(2)));
22470
+ return resolve3(folder2);
22207
22471
  }
22208
22472
  function objectOf(v) {
22209
22473
  return typeof v === "object" && v !== null && !Array.isArray(v) ? v : null;
@@ -22287,20 +22551,20 @@ function dayFolders(sessions, now) {
22287
22551
  const out = [];
22288
22552
  for (let back = 0; back < CODEX_DAYS_BACK; back++) {
22289
22553
  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())));
22554
+ out.push(join9(sessions, String(d.getFullYear()), twoDigits(d.getMonth() + 1), twoDigits(d.getDate())));
22291
22555
  }
22292
22556
  return out;
22293
22557
  }
22294
22558
  function recentRollouts(sessions, now) {
22295
22559
  const found2 = [];
22296
- for (const folder of dayFolders(sessions, now)) {
22560
+ for (const folder2 of dayFolders(sessions, now)) {
22297
22561
  try {
22298
- if (!lstatSync(folder).isDirectory())
22562
+ if (!lstatSync(folder2).isDirectory())
22299
22563
  continue;
22300
- for (const name of readdirSync4(folder)) {
22564
+ for (const name of readdirSync4(folder2)) {
22301
22565
  if (!ROLLOUT_NAME.test(name))
22302
22566
  continue;
22303
- const path = join8(folder, name);
22567
+ const path = join9(folder2, name);
22304
22568
  try {
22305
22569
  const st = lstatSync(path);
22306
22570
  if (!st.isFile())
@@ -22319,7 +22583,7 @@ function recentRollouts(sessions, now) {
22319
22583
  function readCodexWindows(codexHome, now) {
22320
22584
  try {
22321
22585
  const candidates = [];
22322
- for (const path of recentRollouts(join8(codexHome, "sessions"), now)) {
22586
+ for (const path of recentRollouts(join9(codexHome, "sessions"), now)) {
22323
22587
  try {
22324
22588
  candidates.push(...newestWindowsIn(path, now));
22325
22589
  } catch {
@@ -22349,7 +22613,7 @@ function isCodexSessionFile(path, codexHome) {
22349
22613
  return false;
22350
22614
  if (!lstatSync(path).isFile())
22351
22615
  return false;
22352
- const rel = relative4(realpathSync3(join8(codexHome, "sessions")), realpathSync3(path));
22616
+ const rel = relative4(realpathSync3(join9(codexHome, "sessions")), realpathSync3(path));
22353
22617
  return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep2}`) && !isAbsolute(rel);
22354
22618
  } catch {
22355
22619
  return false;
@@ -22444,18 +22708,18 @@ function firstLine(text) {
22444
22708
  const line2 = text.split("\n", 1)[0]?.replace(/[ \t\r]+$/, "") ?? "";
22445
22709
  return line2 === "" ? null : line2;
22446
22710
  }
22447
- function whichCopy(folder, deps = {}) {
22711
+ function whichCopy(folder2, deps = {}) {
22448
22712
  try {
22449
22713
  const rules = deps.rules ?? machineRules();
22450
22714
  const p = pathsFor(rules);
22451
22715
  const files = deps.files ?? DISK_FILES;
22452
22716
  const realpath = deps.realpath ?? ((x) => realpathSync4.native(x));
22453
22717
  const oneForm = rules === "windows" ? (x) => x.toLowerCase() : (x) => x;
22454
- if (!p.isAbsolute(folder))
22718
+ if (!p.isAbsolute(folder2))
22455
22719
  return null;
22456
- if (files.kind(p.resolve(folder)) !== "folder")
22720
+ if (files.kind(p.resolve(folder2)) !== "folder")
22457
22721
  return null;
22458
- let dir = realpath(p.resolve(folder));
22722
+ let dir = realpath(p.resolve(folder2));
22459
22723
  for (; ; ) {
22460
22724
  const dotGit = p.join(dir, ".git");
22461
22725
  const found2 = files.kind(dotGit);
@@ -22530,7 +22794,7 @@ function copyHolding(file2, deps = {}) {
22530
22794
 
22531
22795
  // dist/observed.js
22532
22796
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
22533
- import { join as join9 } from "node:path";
22797
+ import { join as join10 } from "node:path";
22534
22798
  function emptyTally() {
22535
22799
  return { added: 0, changed: 0, removed: 0, testFilesAdded: 0 };
22536
22800
  }
@@ -22662,9 +22926,9 @@ async function attemptObservation(opts) {
22662
22926
  return { outcome: "not-sent", reason: "refused" };
22663
22927
  return answer.data.outcome === "kept" ? { outcome: "kept" } : { outcome: "recorded", answer: answer.data };
22664
22928
  }
22665
- function readThrottle(folder) {
22929
+ function readThrottle(folder2) {
22666
22930
  try {
22667
- const parsed = JSON.parse(readFileSync11(observeThrottlePath(folder), "utf-8"));
22931
+ const parsed = JSON.parse(readFileSync11(observeThrottlePath(folder2), "utf-8"));
22668
22932
  if (typeof parsed !== "object" || parsed === null)
22669
22933
  return null;
22670
22934
  const { attemptedAt, accepted } = parsed;
@@ -22677,9 +22941,9 @@ function readThrottle(folder) {
22677
22941
  return null;
22678
22942
  }
22679
22943
  }
22680
- function writeThrottle(folder, state) {
22944
+ function writeThrottle(folder2, state) {
22681
22945
  try {
22682
- writeFileSync4(observeThrottlePath(folder), JSON.stringify(state), "utf-8");
22946
+ writeFileSync4(observeThrottlePath(folder2), JSON.stringify(state), "utf-8");
22683
22947
  } catch {
22684
22948
  }
22685
22949
  }
@@ -22700,9 +22964,9 @@ function locate(cwd) {
22700
22964
  const read = repoRoot === null ? null : headCommit(repoRoot);
22701
22965
  return { repoRoot, folder: repoRoot ?? cwd, head: read !== null && read.ok ? read.value : null };
22702
22966
  }
22703
- function recordAttempt(folder, prior, head, at, attempt2) {
22967
+ function recordAttempt(folder2, prior, head, at, attempt2) {
22704
22968
  const accepted = (attempt2.outcome === "recorded" || attempt2.outcome === "kept") && head !== null ? { head, at } : prior?.accepted ?? null;
22705
- writeThrottle(folder, { attemptedAt: at, accepted });
22969
+ writeThrottle(folder2, { attemptedAt: at, accepted });
22706
22970
  }
22707
22971
  async function runObserve(deps = {}) {
22708
22972
  let result;
@@ -22731,7 +22995,7 @@ async function runObserve(deps = {}) {
22731
22995
  }
22732
22996
  function readLocalSystemDescription(repoRoot) {
22733
22997
  try {
22734
- const raw = JSON.parse(readFileSync11(join9(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
22998
+ const raw = JSON.parse(readFileSync11(join10(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
22735
22999
  const checked = checkSystemDescription(raw);
22736
23000
  return checked.ok ? checked.description : null;
22737
23001
  } catch {
@@ -22905,23 +23169,23 @@ function writeSaid(path, state) {
22905
23169
  function reasonsOf(entry) {
22906
23170
  return entry.said.filter((task) => task.whyOutsidePlan !== void 0).map((task) => `${task.title}\0${task.whyOutsidePlan}`);
22907
23171
  }
22908
- function noticesFor(attempt2, folder, sessionId) {
23172
+ function noticesFor(attempt2, folder2, sessionId) {
22909
23173
  if (attempt2.outcome === "not-sent") {
22910
23174
  if (attempt2.reason === "start-not-here") {
22911
- return firstNoticeThisSession("observe-no-start-here", folder, sessionId) ? [R1_START_NOT_HERE] : [];
23175
+ return firstNoticeThisSession("observe-no-start-here", folder2, sessionId) ? [R1_START_NOT_HERE] : [];
22912
23176
  }
22913
23177
  if (attempt2.reason === "start-not-on-branch") {
22914
- return firstNoticeThisSession("observe-not-on-branch", folder, sessionId) ? [R2_START_NOT_ON_BRANCH] : [];
23178
+ return firstNoticeThisSession("observe-not-on-branch", folder2, sessionId) ? [R2_START_NOT_ON_BRANCH] : [];
22915
23179
  }
22916
23180
  if (attempt2.reason === "cannot-count" && attempt2.phase !== void 0) {
22917
- return firstNoticeThisSession("observe-no-start-recorded", folder, sessionId) ? [r3NoStartRecorded(attempt2.phase)] : [];
23181
+ return firstNoticeThisSession("observe-no-start-recorded", folder2, sessionId) ? [r3NoStartRecorded(attempt2.phase)] : [];
22918
23182
  }
22919
23183
  return [];
22920
23184
  }
22921
23185
  if (attempt2.outcome === "kept")
22922
23186
  return [];
22923
23187
  const { notInPlan, outside } = attempt2.answer;
22924
- const path = observeNoticesPath(folder, sessionId);
23188
+ const path = observeNoticesPath(folder2, sessionId);
22925
23189
  const prior = readSaid(path);
22926
23190
  const notices = [];
22927
23191
  const parts = {};
@@ -22949,7 +23213,7 @@ function noticesFor(attempt2, folder, sessionId) {
22949
23213
  // dist/pacing.js
22950
23214
  import { createHash as createHash2 } from "node:crypto";
22951
23215
  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";
23216
+ import { homedir as homedir6 } from "node:os";
22953
23217
  import nodePath from "node:path";
22954
23218
  var DEFAULT_CONFIG_FOLDER_NAME = ".claude";
22955
23219
  var CODEX_DEFAULT_FOLDER_NAME = ".codex";
@@ -22967,10 +23231,10 @@ function hookInputForRule(input, rule) {
22967
23231
  delete copy["rollout_path"];
22968
23232
  return copy;
22969
23233
  }
22970
- function normaliseFolder(folder, input = {}) {
23234
+ function normaliseFolder(folder2, input = {}) {
22971
23235
  const p = input.path ?? nodePath;
22972
- const home = input.home ?? homedir5();
22973
- let f = folder;
23236
+ const home = input.home ?? homedir6();
23237
+ let f = folder2;
22974
23238
  if (f === "~")
22975
23239
  f = home;
22976
23240
  else if (f.startsWith("~/") || f.startsWith("~\\"))
@@ -22984,7 +23248,7 @@ function configFolderKey(input = {}) {
22984
23248
  const codexHome = env["CODEX_HOME"];
22985
23249
  if (typeof codexHome === "string" && codexHome !== "")
22986
23250
  return normaliseFolder(codexHome, input);
22987
- return normaliseFolder(p.join(input.home ?? homedir5(), CODEX_DEFAULT_FOLDER_NAME), input);
23251
+ return normaliseFolder(p.join(input.home ?? homedir6(), CODEX_DEFAULT_FOLDER_NAME), input);
22988
23252
  }
22989
23253
  const fromEnv = env["CLAUDE_CONFIG_DIR"];
22990
23254
  if (typeof fromEnv === "string" && fromEnv !== "")
@@ -22993,7 +23257,7 @@ function configFolderKey(input = {}) {
22993
23257
  if (typeof transcript === "string" && transcript !== "") {
22994
23258
  return normaliseFolder(p.dirname(p.dirname(p.dirname(normaliseFolder(transcript, input)))), input);
22995
23259
  }
22996
- return normaliseFolder(p.join(input.home ?? homedir5(), DEFAULT_CONFIG_FOLDER_NAME), input);
23260
+ return normaliseFolder(p.join(input.home ?? homedir6(), DEFAULT_CONFIG_FOLDER_NAME), input);
22997
23261
  }
22998
23262
  function recordsFolderOf(key, path = nodePath) {
22999
23263
  return path.join(key, RECORDS_FOLDER_NAME);
@@ -23013,7 +23277,7 @@ var PACING_DIR_NAME = "pacing";
23013
23277
  var LEARNED_FILENAME = "learned.json";
23014
23278
  var REPORTED_FILENAME = "reported.json";
23015
23279
  function pacingDir(home) {
23016
- return nodePath.join(home ?? homedir5(), HALFCYCLE_DIR_NAME, PACING_DIR_NAME);
23280
+ return nodePath.join(home ?? homedir6(), HALFCYCLE_DIR_NAME, PACING_DIR_NAME);
23017
23281
  }
23018
23282
  function readJsonObject(path) {
23019
23283
  try {
@@ -23048,6 +23312,19 @@ function accountKeysOf(path) {
23048
23312
  return typeof accounts === "object" && accounts !== null && !Array.isArray(accounts) ? Object.keys(accounts) : [];
23049
23313
  }
23050
23314
  var PACE_STATE_FILENAME = "state.json";
23315
+ function toldOf(v) {
23316
+ if (typeof v !== "object" || v === null || Array.isArray(v))
23317
+ return null;
23318
+ const o = v;
23319
+ const pace = paceStateSchema.safeParse(o["pace"]);
23320
+ const kind = o["windowKind"] === null ? null : paceWindowKindSchema.safeParse(o["windowKind"]);
23321
+ const allowance = o["allowance"];
23322
+ if (!pace.success || kind !== null && !kind.success)
23323
+ return null;
23324
+ if (typeof allowance !== "number" || !Number.isInteger(allowance) || allowance < 0)
23325
+ return null;
23326
+ return { pace: pace.data, allowance, windowKind: kind === null ? null : kind.data };
23327
+ }
23051
23328
  function paceStateEntryOf(v) {
23052
23329
  if (typeof v !== "object" || v === null || Array.isArray(v))
23053
23330
  return null;
@@ -23069,7 +23346,17 @@ function paceStateEntryOf(v) {
23069
23346
  if (typeof at !== "string" || Number.isNaN(Date.parse(at)))
23070
23347
  return null;
23071
23348
  const offered = Array.isArray(o["offeredFor"]) ? o["offeredFor"].filter((t) => typeof t === "string" && !Number.isNaN(Date.parse(t))) : [];
23072
- return { pace: pace.data, allowance, number: number4, windowKind: kind === null ? null : kind.data, until, at, ...offered.length > 0 ? { offeredFor: offered } : {} };
23349
+ const told = toldOf(o["told"]);
23350
+ return {
23351
+ pace: pace.data,
23352
+ allowance,
23353
+ number: number4,
23354
+ windowKind: kind === null ? null : kind.data,
23355
+ until,
23356
+ at,
23357
+ ...offered.length > 0 ? { offeredFor: offered } : {},
23358
+ ...told === null ? {} : { told }
23359
+ };
23073
23360
  }
23074
23361
  function rememberedBasisOf(v) {
23075
23362
  if (typeof v !== "object" || v === null || Array.isArray(v))
@@ -23100,8 +23387,14 @@ function writePaceState(dir, key, entry, basis) {
23100
23387
  if (entry === null)
23101
23388
  delete accounts[key];
23102
23389
  else {
23103
- const kept = entry.offeredFor ?? current.accounts[key]?.offeredFor;
23104
- accounts[key] = kept === void 0 ? entry : { ...entry, offeredFor: kept };
23390
+ const last = current.accounts[key];
23391
+ const kept = entry.offeredFor ?? last?.offeredFor;
23392
+ const told = entry.told ?? last?.told ?? (last === void 0 ? { pace: "full", allowance: entry.allowance, windowKind: null } : void 0);
23393
+ accounts[key] = {
23394
+ ...entry,
23395
+ ...kept === void 0 ? {} : { offeredFor: kept },
23396
+ ...told === void 0 ? {} : { told }
23397
+ };
23105
23398
  }
23106
23399
  writeJsonWhole(nodePath.join(dir, PACE_STATE_FILENAME), { accounts, basis: basis ?? current.basis });
23107
23400
  }
@@ -23406,7 +23699,7 @@ function resolvedRecords(key, path) {
23406
23699
  }
23407
23700
  }
23408
23701
  function timeLogTranscripts(home) {
23409
- const engagements = nodePath.join(home ?? homedir5(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
23702
+ const engagements = nodePath.join(home ?? homedir6(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME);
23410
23703
  const out = [];
23411
23704
  for (const e of dirents(engagements)) {
23412
23705
  if (!e.isDirectory())
@@ -23425,7 +23718,7 @@ function timeLogTranscripts(home) {
23425
23718
  }
23426
23719
  function teacherOf(key, deps = {}) {
23427
23720
  const path = deps.path ?? nodePath;
23428
- const home = deps.home ?? homedir5();
23721
+ const home = deps.home ?? homedir6();
23429
23722
  const ours = resolvedRecords(key, path);
23430
23723
  if (ours === null)
23431
23724
  return { shared: false, teaches: () => false };
@@ -23595,13 +23888,13 @@ function knownWindowOf(learned, reported, now) {
23595
23888
 
23596
23889
  // dist/phase-limit.js
23597
23890
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync as renameSync4, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
23598
- import { homedir as homedir6 } from "node:os";
23891
+ import { homedir as homedir7 } from "node:os";
23599
23892
  import nodePath2 from "node:path";
23600
23893
  var PHASE_USAGE_FILENAME = "phase-usage.json";
23601
23894
  function phaseLimitDir(engagementId, home) {
23602
23895
  if (!isEngagementId(engagementId))
23603
23896
  return null;
23604
- return nodePath2.join(home ?? homedir6(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
23897
+ return nodePath2.join(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
23605
23898
  }
23606
23899
  function readJsonFile(path) {
23607
23900
  try {
@@ -23649,16 +23942,14 @@ function readPhaseUsage(dir, phase, openedAt = null) {
23649
23942
  return 0;
23650
23943
  return parsed.data.tokens;
23651
23944
  }
23945
+ function readPhaseUsageFile(dir) {
23946
+ const parsed = phaseUsageFileSchema.safeParse(readJsonFile(nodePath2.join(dir, PHASE_USAGE_FILENAME)));
23947
+ return parsed.success ? parsed.data : null;
23948
+ }
23652
23949
  function writePhaseUsage(dir, file2) {
23653
23950
  if (!phaseUsageFileSchema.safeParse(file2).success)
23654
- return;
23655
- writeWhole(nodePath2.join(dir, PHASE_USAGE_FILENAME), file2);
23656
- }
23657
- function phaseTokensOf(buckets) {
23658
- let total = 0;
23659
- for (const b of buckets)
23660
- total += b.inputTokens + b.outputTokens + b.cacheWriteTokens + b.cacheReadTokens;
23661
- return Math.max(0, Math.round(total));
23951
+ return false;
23952
+ return writeWhole(nodePath2.join(dir, PHASE_USAGE_FILENAME), file2);
23662
23953
  }
23663
23954
  function roundTokens(n) {
23664
23955
  return Math.max(LIMIT_ROUND_TOKENS, Math.round(n / LIMIT_ROUND_TOKENS) * LIMIT_ROUND_TOKENS);
@@ -23836,6 +24127,164 @@ function setLimit(file2, limit, multiple, used, by, at) {
23836
24127
  };
23837
24128
  }
23838
24129
 
24130
+ // dist/scan-lock.js
24131
+ import { spawn } from "node:child_process";
24132
+ import { linkSync, mkdirSync as mkdirSync5, readFileSync as readFileSync14, renameSync as renameSync5, unlinkSync as unlinkSync4, writeFileSync as writeFileSync7 } from "node:fs";
24133
+ import nodePath3 from "node:path";
24134
+ var SCAN_LOCK_FILENAME = "scan.lock";
24135
+ var SCAN_LOCK_STALE_MS = 15 * 60 * 1e3;
24136
+ var SCAN_LOCK_POLL_MS = 100;
24137
+ function scanLockPath(home) {
24138
+ return nodePath3.join(pacingDir(home), SCAN_LOCK_FILENAME);
24139
+ }
24140
+ function readLockBody(path) {
24141
+ let text;
24142
+ try {
24143
+ text = readFileSync14(path, "utf-8");
24144
+ } catch (err) {
24145
+ return err.code === "ENOENT" ? "missing" : null;
24146
+ }
24147
+ try {
24148
+ const v = JSON.parse(text);
24149
+ return typeof v["pid"] === "number" && typeof v["at"] === "number" ? { pid: v["pid"], at: v["at"] } : null;
24150
+ } catch {
24151
+ return null;
24152
+ }
24153
+ }
24154
+ function processAlive(pid) {
24155
+ if (!Number.isInteger(pid) || pid <= 0)
24156
+ return false;
24157
+ try {
24158
+ process.kill(pid, 0);
24159
+ return true;
24160
+ } catch (err) {
24161
+ return err.code === "EPERM";
24162
+ }
24163
+ }
24164
+ function isStale(body, staleMs, deps) {
24165
+ if (body === null)
24166
+ return true;
24167
+ const now = (deps.clock ?? Date.now)();
24168
+ if (Math.abs(now - body.at) > staleMs)
24169
+ return true;
24170
+ return !(deps.alive ?? processAlive)(body.pid);
24171
+ }
24172
+ function writeLock(path, pid, deps, flag) {
24173
+ writeFileSync7(path, JSON.stringify({ pid, at: (deps.clock ?? Date.now)() }), { flag, mode: 384 });
24174
+ }
24175
+ function tryTakeScanLock(deps = {}) {
24176
+ return tryTakeLockFile(scanLockPath(deps.home), SCAN_LOCK_STALE_MS, deps);
24177
+ }
24178
+ function tryTakeLockFile(path, staleMs, deps = {}) {
24179
+ const pid = deps.pid ?? process.pid;
24180
+ try {
24181
+ mkdirSync5(nodePath3.dirname(path), { recursive: true, mode: 448 });
24182
+ } catch {
24183
+ return false;
24184
+ }
24185
+ for (let attempt2 = 0; attempt2 < 2; attempt2++) {
24186
+ try {
24187
+ writeLock(path, pid, deps, "wx");
24188
+ return true;
24189
+ } catch (err) {
24190
+ if (err.code !== "EEXIST")
24191
+ return false;
24192
+ }
24193
+ const seen = readLockBody(path);
24194
+ if (seen === "missing")
24195
+ continue;
24196
+ if (!isStale(seen, staleMs, deps))
24197
+ return false;
24198
+ const aside = `${path}.${String(pid)}.${String((deps.clock ?? Date.now)())}.stale`;
24199
+ try {
24200
+ renameSync5(path, aside);
24201
+ } catch {
24202
+ continue;
24203
+ }
24204
+ const taken = readLockBody(aside);
24205
+ const same = taken !== "missing" && (seen === null ? taken === null : taken !== null && taken.pid === seen.pid && taken.at === seen.at);
24206
+ if (!same && taken !== "missing" && !isStale(taken, staleMs, deps)) {
24207
+ try {
24208
+ linkSync(aside, path);
24209
+ } catch {
24210
+ }
24211
+ try {
24212
+ unlinkSync4(aside);
24213
+ } catch {
24214
+ }
24215
+ return false;
24216
+ }
24217
+ try {
24218
+ unlinkSync4(aside);
24219
+ } catch {
24220
+ }
24221
+ }
24222
+ return false;
24223
+ }
24224
+ function releaseLockFile(path, pid = process.pid) {
24225
+ const body = readLockBody(path);
24226
+ if (body === "missing" || body === null || body.pid !== pid)
24227
+ return;
24228
+ try {
24229
+ unlinkSync4(path);
24230
+ } catch {
24231
+ }
24232
+ }
24233
+ function releaseScanLock(deps = {}) {
24234
+ releaseLockFile(scanLockPath(deps.home), deps.pid ?? process.pid);
24235
+ }
24236
+ function sleepSync(ms) {
24237
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
24238
+ }
24239
+ function scanUnderLock(key, deps = {}, waitMs = 0) {
24240
+ const clock = deps.clock ?? Date.now;
24241
+ const giveUpAt = clock() + waitMs;
24242
+ while (!tryTakeScanLock(deps)) {
24243
+ if (clock() >= giveUpAt)
24244
+ return null;
24245
+ sleepSync(SCAN_LOCK_POLL_MS);
24246
+ }
24247
+ try {
24248
+ return scanRecords(key, deps);
24249
+ } finally {
24250
+ releaseScanLock(deps);
24251
+ }
24252
+ }
24253
+ var spawnDetachedReal = (command, args2) => {
24254
+ const child = spawn(command, [...args2], { detached: true, stdio: "ignore", windowsHide: true });
24255
+ child.on("error", () => void 0);
24256
+ child.unref();
24257
+ return child.pid;
24258
+ };
24259
+ function startScanChild(key, binaryPath, deps = {}) {
24260
+ if (!tryTakeScanLock(deps))
24261
+ return false;
24262
+ let childPid;
24263
+ try {
24264
+ childPid = (deps.spawnDetached ?? spawnDetachedReal)(deps.nodePath ?? process.execPath, [
24265
+ binaryPath,
24266
+ "pace",
24267
+ "--scan",
24268
+ "--config-folder",
24269
+ key,
24270
+ "--lock-held"
24271
+ ]);
24272
+ } catch {
24273
+ childPid = void 0;
24274
+ }
24275
+ if (childPid === void 0) {
24276
+ releaseScanLock(deps);
24277
+ return false;
24278
+ }
24279
+ try {
24280
+ writeLock(scanLockPath(deps.home), childPid, deps, "w");
24281
+ } catch {
24282
+ releaseScanLock(deps);
24283
+ return false;
24284
+ }
24285
+ return true;
24286
+ }
24287
+
23839
24288
  // dist/time-record.js
23840
24289
  var TIME_EVENT_KINDS = ["action", "asked", "edit", "turn-end", "lock-wait-start", "lock-wait-end"];
23841
24290
  function isLockWaitEvent(event) {
@@ -23849,25 +24298,25 @@ function inputString(input, key) {
23849
24298
  const value = input[key];
23850
24299
  return typeof value === "string" && value !== "" ? value : null;
23851
24300
  }
23852
- function readBranch(folder, deps = {}) {
23853
- const copy = whichCopy(folder, deps);
24301
+ function readBranch(folder2, deps = {}) {
24302
+ const copy = whichCopy(folder2, deps);
23854
24303
  return copy === null ? null : copyBranch(copy, deps);
23855
24304
  }
23856
24305
  function timeLogPath(engagementId, home) {
23857
24306
  if (!isEngagementId(engagementId))
23858
24307
  return null;
23859
- return join10(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TIME_LOG_DIR_NAME, TIME_LOG_FILENAME);
24308
+ return join11(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TIME_LOG_DIR_NAME, TIME_LOG_FILENAME);
23860
24309
  }
23861
24310
  function timeEventLine(event, input, deps = {}) {
23862
24311
  const at = (deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
23863
- const folder = inputString(input, "cwd") ?? (deps.cwd ?? (() => process.cwd()))();
24312
+ const folder2 = inputString(input, "cwd") ?? (deps.cwd ?? (() => process.cwd()))();
23864
24313
  return {
23865
24314
  at,
23866
24315
  event,
23867
24316
  session: inputString(input, "session_id"),
23868
24317
  agent: inputString(input, "agent_id"),
23869
- folder,
23870
- branch: readBranch(folder),
24318
+ folder: folder2,
24319
+ branch: readBranch(folder2),
23871
24320
  transcript: inputString(input, "transcript_path"),
23872
24321
  toolUse: inputString(input, "tool_use_id"),
23873
24322
  rollout: inputString(input, "rollout_path")
@@ -23885,12 +24334,12 @@ function recordTimeEvent(event, input, deps = {}) {
23885
24334
  const text = `${JSON.stringify(line2)}
23886
24335
  `;
23887
24336
  try {
23888
- appendFileSync2(path, text, { mode: 384 });
24337
+ appendFileSync3(path, text, { mode: 384 });
23889
24338
  } catch (err) {
23890
24339
  if (err.code !== "ENOENT")
23891
24340
  return;
23892
- mkdirSync5(dirname4(path), { recursive: true, mode: 448 });
23893
- appendFileSync2(path, text, { mode: 384 });
24341
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
24342
+ appendFileSync3(path, text, { mode: 384 });
23894
24343
  }
23895
24344
  } catch {
23896
24345
  }
@@ -23949,7 +24398,7 @@ function readSessionTimeLines(sessionId, deps = {}) {
23949
24398
  if (path === null)
23950
24399
  return [];
23951
24400
  const needle = JSON.stringify(sessionId);
23952
- const kept = readFileSync14(path, "utf-8").split("\n").filter((raw) => raw.includes(needle));
24401
+ const kept = readFileSync15(path, "utf-8").split("\n").filter((raw) => raw.includes(needle));
23953
24402
  return parseTimeLog(kept.join("\n")).filter((line2) => line2.session === sessionId);
23954
24403
  } catch {
23955
24404
  return [];
@@ -23976,8 +24425,8 @@ function separatorOf(path) {
23976
24425
  function subagentsFolderOf(transcript) {
23977
24426
  if (!transcript.endsWith(".jsonl") || transcript.length === ".jsonl".length)
23978
24427
  return null;
23979
- const sep7 = separatorOf(transcript);
23980
- return `${transcript.slice(0, -".jsonl".length)}${sep7}subagents`;
24428
+ const sep8 = separatorOf(transcript);
24429
+ return `${transcript.slice(0, -".jsonl".length)}${sep8}subagents`;
23981
24430
  }
23982
24431
  function subagentRecordName(agentId) {
23983
24432
  return /^[A-Za-z0-9_-]+$/.test(agentId) ? `agent-${agentId}.jsonl` : null;
@@ -23985,12 +24434,12 @@ function subagentRecordName(agentId) {
23985
24434
  function subagentRecordPath(transcript, agentId) {
23986
24435
  if (transcript === null)
23987
24436
  return null;
23988
- const folder = subagentsFolderOf(transcript);
24437
+ const folder2 = subagentsFolderOf(transcript);
23989
24438
  const name = subagentRecordName(agentId);
23990
- return folder === null || name === null ? null : `${folder}${separatorOf(transcript)}${name}`;
24439
+ return folder2 === null || name === null ? null : `${folder2}${separatorOf(transcript)}${name}`;
23991
24440
  }
23992
- function folderParts(folder) {
23993
- return folder.split(/[\\/]+/).filter((part, i) => part !== "" || i === 0);
24441
+ function folderParts(folder2) {
24442
+ return folder2.split(/[\\/]+/).filter((part, i) => part !== "" || i === 0);
23994
24443
  }
23995
24444
  function folderHolds(outer, inner) {
23996
24445
  const o = folderParts(outer);
@@ -24043,15 +24492,52 @@ function buildLanes(events, taskIds) {
24043
24492
  });
24044
24493
  }
24045
24494
  var UNREADABLE2 = { readable: false, lines: [] };
24046
- function viewRecord(path, deps) {
24047
- if (path === null)
24048
- return UNREADABLE2;
24049
- let text;
24050
- try {
24051
- text = (deps.readFile ?? ((p) => readFileSync14(p, "utf-8")))(path);
24052
- } catch {
24053
- return UNREADABLE2;
24495
+ function plainObject(v) {
24496
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : null;
24497
+ }
24498
+ var USAGE_KEYS = ["input_tokens", "output_tokens", "cache_creation_input_tokens", "cache_read_input_tokens"];
24499
+ function reducedLine(o) {
24500
+ const out = { type: o["type"] };
24501
+ if (typeof o["timestamp"] === "string")
24502
+ out["timestamp"] = o["timestamp"];
24503
+ if (typeof o["gitBranch"] === "string")
24504
+ out["gitBranch"] = o["gitBranch"];
24505
+ if ("toolUseResult" in o)
24506
+ out["toolUseResult"] = true;
24507
+ if (o["isApiErrorMessage"] === true)
24508
+ out["isApiErrorMessage"] = true;
24509
+ if (typeof o["error"] === "string")
24510
+ out["error"] = o["error"];
24511
+ const message = plainObject(o["message"]);
24512
+ if (message !== null) {
24513
+ const m = {};
24514
+ if (typeof message["id"] === "string")
24515
+ m["id"] = message["id"];
24516
+ if (typeof message["model"] === "string")
24517
+ m["model"] = message["model"];
24518
+ const usage = plainObject(message["usage"]);
24519
+ if (usage !== null) {
24520
+ const u = {};
24521
+ for (const key of USAGE_KEYS)
24522
+ if (key in usage)
24523
+ u[key] = usage[key];
24524
+ m["usage"] = u;
24525
+ }
24526
+ const content = message["content"];
24527
+ if (Array.isArray(content) && content.some((block) => plainObject(block)?.["type"] === "tool_result"))
24528
+ m["content"] = [{ type: "tool_result" }];
24529
+ out["message"] = m;
24054
24530
  }
24531
+ return out;
24532
+ }
24533
+ function recordLineOfValue(value) {
24534
+ const o = plainObject(value);
24535
+ if (o === null || typeof o["type"] !== "string")
24536
+ return null;
24537
+ const t = typeof o["timestamp"] === "string" ? Date.parse(o["timestamp"]) : NaN;
24538
+ return { value: o, t: Number.isNaN(t) ? null : t };
24539
+ }
24540
+ function recordLinesOf(text) {
24055
24541
  const lines2 = [];
24056
24542
  for (const raw of text.split("\n")) {
24057
24543
  if (raw.trim() === "")
@@ -24062,29 +24548,129 @@ function viewRecord(path, deps) {
24062
24548
  } catch {
24063
24549
  continue;
24064
24550
  }
24065
- if (typeof value !== "object" || value === null || Array.isArray(value))
24066
- continue;
24067
- const o = value;
24068
- if (typeof o["type"] !== "string")
24551
+ const o = plainObject(value);
24552
+ if (o === null || typeof o["type"] !== "string")
24069
24553
  continue;
24070
- const t = typeof o["timestamp"] === "string" ? Date.parse(o["timestamp"]) : NaN;
24071
- lines2.push({ value: o, t: Number.isNaN(t) ? null : t });
24554
+ lines2.push(recordLineOfValue(reducedLine(o)));
24555
+ }
24556
+ return lines2;
24557
+ }
24558
+ function viewRecord(path, deps) {
24559
+ let text;
24560
+ try {
24561
+ text = (deps.readFile ?? ((p) => readFileSync15(p, "utf-8")))(path);
24562
+ } catch {
24563
+ return UNREADABLE2;
24564
+ }
24565
+ const lines2 = recordLinesOf(text);
24566
+ return lines2.length === 0 ? UNREADABLE2 : { readable: true, lines: lines2 };
24567
+ }
24568
+ function readFileFrom(path, offset) {
24569
+ const fd = openSync3(path, "r");
24570
+ try {
24571
+ const size = fstatSync2(fd).size;
24572
+ if (size < offset)
24573
+ return null;
24574
+ const buf = Buffer.alloc(size - offset);
24575
+ let got = 0;
24576
+ while (got < buf.length) {
24577
+ const n = readSync3(fd, buf, got, buf.length - got, offset + got);
24578
+ if (n === 0)
24579
+ break;
24580
+ got += n;
24581
+ }
24582
+ return buf.subarray(0, got);
24583
+ } finally {
24584
+ closeSync3(fd);
24585
+ }
24586
+ }
24587
+ function readFromOf(deps) {
24588
+ if (deps.readFrom !== void 0)
24589
+ return deps.readFrom;
24590
+ const readFile = deps.readFile;
24591
+ if (readFile === void 0)
24592
+ return readFileFrom;
24593
+ return (path, offset) => {
24594
+ const bytes = Buffer.from(readFile(path), "utf-8");
24595
+ return bytes.length < offset ? null : bytes.subarray(offset);
24596
+ };
24597
+ }
24598
+ function viewRecordFrom(path, deps, offsets) {
24599
+ const readFrom = readFromOf(deps);
24600
+ const stored = offsets.files.get(path);
24601
+ let start = stored?.offset ?? 0;
24602
+ let prior = stored?.lines ?? [];
24603
+ let bytes;
24604
+ try {
24605
+ bytes = readFrom(path, start);
24606
+ if (bytes === null && start > 0) {
24607
+ start = 0;
24608
+ prior = [];
24609
+ bytes = readFrom(path, 0);
24610
+ }
24611
+ } catch {
24612
+ return UNREADABLE2;
24072
24613
  }
24614
+ if (bytes === null)
24615
+ return UNREADABLE2;
24616
+ const buf = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
24617
+ const end = buf.lastIndexOf(10) + 1;
24618
+ const fresh = end === 0 ? [] : recordLinesOf(buf.subarray(0, end).toString("utf-8"));
24619
+ const values = [...prior, ...fresh.map((l) => l.value)];
24620
+ offsets.files.set(path, { offset: start + end, lines: values });
24621
+ const lines2 = values.map((v) => recordLineOfValue(v));
24073
24622
  return lines2.length === 0 ? UNREADABLE2 : { readable: true, lines: lines2 };
24074
24623
  }
24624
+ var viewsOfOffsets = /* @__PURE__ */ new WeakMap();
24075
24625
  function recordReader(deps) {
24076
- const cache2 = /* @__PURE__ */ new Map();
24626
+ let cache2 = /* @__PURE__ */ new Map();
24627
+ if (deps.offsets !== void 0) {
24628
+ cache2 = viewsOfOffsets.get(deps.offsets) ?? /* @__PURE__ */ new Map();
24629
+ viewsOfOffsets.set(deps.offsets, cache2);
24630
+ }
24077
24631
  return (path) => {
24078
24632
  if (path === null)
24079
24633
  return UNREADABLE2;
24080
24634
  const hit = cache2.get(path);
24081
24635
  if (hit !== void 0)
24082
24636
  return hit;
24083
- const view = viewRecord(path, deps);
24637
+ const view = deps.offsets === void 0 ? viewRecord(path, deps) : viewRecordFrom(path, deps, deps.offsets);
24084
24638
  cache2.set(path, view);
24085
24639
  return view;
24086
24640
  };
24087
24641
  }
24642
+ var TIME_READ_OFFSETS_FILENAME = "read-offsets.json";
24643
+ function loadReadOffsets(dir, phase, openedAt) {
24644
+ const fresh = { phase, openedAt, files: /* @__PURE__ */ new Map() };
24645
+ try {
24646
+ const o = plainObject(JSON.parse(readFileSync15(join11(dir, TIME_READ_OFFSETS_FILENAME), "utf-8")));
24647
+ if (o === null || o["phase"] !== phase || o["openedAt"] !== openedAt)
24648
+ return fresh;
24649
+ const files = plainObject(o["files"]);
24650
+ if (files === null)
24651
+ return fresh;
24652
+ const out = /* @__PURE__ */ new Map();
24653
+ for (const [path, entry] of Object.entries(files)) {
24654
+ const e = plainObject(entry);
24655
+ const offset = e?.["offset"];
24656
+ const lines2 = e?.["lines"];
24657
+ if (typeof offset !== "number" || !Number.isSafeInteger(offset) || offset < 0 || !Array.isArray(lines2))
24658
+ return fresh;
24659
+ if (!lines2.every((line2) => recordLineOfValue(line2) !== null))
24660
+ return fresh;
24661
+ out.set(path, { offset, lines: lines2.map((line2) => reducedLine(line2)) });
24662
+ }
24663
+ return { phase, openedAt, files: out };
24664
+ } catch {
24665
+ return fresh;
24666
+ }
24667
+ }
24668
+ function saveReadOffsets(dir, offsets) {
24669
+ try {
24670
+ writeJsonFile(join11(dir, TIME_READ_OFFSETS_FILENAME), { phase: offsets.phase, openedAt: offsets.openedAt, files: Object.fromEntries(offsets.files) });
24671
+ } catch {
24672
+ }
24673
+ }
24088
24674
  function isPrompt(line2) {
24089
24675
  const o = line2.value;
24090
24676
  if (o["type"] !== "user" || line2.t === null || "toolUseResult" in o)
@@ -24275,7 +24861,76 @@ function tokensOf(usage) {
24275
24861
  }
24276
24862
  return out;
24277
24863
  }
24278
- function readUsage(lanes, window, deps = {}, models) {
24864
+ var TIME_SETTLED_FILENAME = "settled.json";
24865
+ function taskNamedIn(text, taskIds) {
24866
+ let named = null;
24867
+ for (const id of new Set(taskIds)) {
24868
+ if (id === "")
24869
+ continue;
24870
+ const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24871
+ if (!new RegExp(`(?<![A-Za-z0-9_-])${escaped}(?![A-Za-z0-9_-])`).test(text))
24872
+ continue;
24873
+ if (named !== null)
24874
+ return null;
24875
+ named = id;
24876
+ }
24877
+ return named;
24878
+ }
24879
+ function spawnDescriptionOf(recordPath, deps) {
24880
+ if (!recordPath.endsWith(".jsonl"))
24881
+ return null;
24882
+ try {
24883
+ const text = (deps.readFile ?? ((p) => readFileSync15(p, "utf-8")))(`${recordPath.slice(0, -".jsonl".length)}.meta.json`);
24884
+ const value = JSON.parse(text);
24885
+ if (typeof value !== "object" || value === null || Array.isArray(value))
24886
+ return null;
24887
+ const description = value["description"];
24888
+ return typeof description === "string" ? description : null;
24889
+ } catch {
24890
+ return null;
24891
+ }
24892
+ }
24893
+ function sameFolder(a, b) {
24894
+ const pa = folderParts(a);
24895
+ const pb = folderParts(b);
24896
+ return pa.length === pb.length && pa.every((part, k) => part === pb[k]);
24897
+ }
24898
+ function nextTaskOf(own, t) {
24899
+ let next;
24900
+ for (const lane of own) {
24901
+ if (lane.task === null)
24902
+ continue;
24903
+ const after = lane.events.map((e) => msOf(e.at)).find((at) => at > t);
24904
+ if (after !== void 0 && (next === void 0 || after < next.t))
24905
+ next = { task: lane.task, t: after };
24906
+ }
24907
+ return next?.task ?? null;
24908
+ }
24909
+ function lastTaskOf(own, t) {
24910
+ let last;
24911
+ for (const lane of own) {
24912
+ if (lane.task === null)
24913
+ continue;
24914
+ const before = lastAtOrBefore(lane.events.map((e) => msOf(e.at)), t);
24915
+ if (before !== void 0 && (last === void 0 || before >= last.t))
24916
+ last = { task: lane.task, t: before };
24917
+ }
24918
+ return last?.task ?? null;
24919
+ }
24920
+ function nextTaskInFolderOf(lanes, folder2, t) {
24921
+ let next;
24922
+ for (const lane of lanes) {
24923
+ if (lane.task === null)
24924
+ continue;
24925
+ for (const e of lane.events) {
24926
+ const at = msOf(e.at);
24927
+ if (at > t && sameFolder(e.folder, folder2) && (next === void 0 || at < next.t))
24928
+ next = { task: lane.task, t: at };
24929
+ }
24930
+ }
24931
+ return next?.task ?? null;
24932
+ }
24933
+ function readUsage(lanes, window, deps = {}, models, settling = {}) {
24279
24934
  const from = Date.parse(window.from);
24280
24935
  const to = window.to === void 0 || window.to === null ? Infinity : Date.parse(window.to);
24281
24936
  const inWindow = (t) => t >= from && t <= to;
@@ -24291,6 +24946,28 @@ function readUsage(lanes, window, deps = {}, models) {
24291
24946
  return buckets.get(key) ?? buckets.set(key, { source, task, readable, messages: 0, inputTokens: 0, outputTokens: 0, cacheWriteTokens: 0, cacheReadTokens: 0 }).get(key);
24292
24947
  };
24293
24948
  const seen = /* @__PURE__ */ new Set();
24949
+ const held = /* @__PURE__ */ new Set();
24950
+ const settled2 = settling.settled ?? /* @__PURE__ */ new Map();
24951
+ const final = settling.final === true;
24952
+ const figureIds = /* @__PURE__ */ new Set();
24953
+ const addToFigure = (view) => {
24954
+ const figure = settling.figure;
24955
+ if (figure === void 0)
24956
+ return;
24957
+ for (const line2 of view.lines) {
24958
+ if (line2.value["type"] !== "assistant" || line2.t === null || !inWindow(line2.t))
24959
+ continue;
24960
+ const message = plainObject(line2.value["message"]);
24961
+ const id = message?.["id"];
24962
+ if (typeof id !== "string" || id === "" || figureIds.has(id))
24963
+ continue;
24964
+ const tokens = tokensOf(message["usage"]);
24965
+ if (tokens === null)
24966
+ continue;
24967
+ figureIds.add(id);
24968
+ figure.claudeCode += tokens[0] + tokens[1] + tokens[2] + tokens[3];
24969
+ }
24970
+ };
24294
24971
  const count = (path, view, taskAt) => {
24295
24972
  const source = recordSourceOf(path);
24296
24973
  for (const line2 of view.lines) {
@@ -24301,13 +24978,17 @@ function readUsage(lanes, window, deps = {}, models) {
24301
24978
  if (typeof message !== "object" || message === null)
24302
24979
  continue;
24303
24980
  const id = message["id"];
24304
- if (typeof id !== "string" || id === "" || seen.has(id))
24981
+ if (typeof id !== "string" || id === "" || seen.has(id) || held.has(id))
24305
24982
  continue;
24306
24983
  const tokens = tokensOf(message["usage"]);
24307
24984
  if (tokens === null)
24308
24985
  continue;
24986
+ const task = taskAt(line2, id);
24987
+ if (task === void 0) {
24988
+ held.add(id);
24989
+ continue;
24990
+ }
24309
24991
  seen.add(id);
24310
- const task = taskAt(line2);
24311
24992
  const bucket = bucketFor(source, task, true);
24312
24993
  bucket.messages += 1;
24313
24994
  const model = message["model"];
@@ -24323,41 +25004,76 @@ function readUsage(lanes, window, deps = {}, models) {
24323
25004
  };
24324
25005
  for (const main2 of mains) {
24325
25006
  const view = read(main2);
25007
+ addToFigure(view);
24326
25008
  if (view.readable) {
25009
+ let coordinatingFrom = Infinity;
25010
+ for (const lane of lanes) {
25011
+ if (lane.agent === MAIN_AGENT || lane.transcript !== main2)
25012
+ continue;
25013
+ for (const e of lane.events)
25014
+ coordinatingFrom = Math.min(coordinatingFrom, msOf(e.at));
25015
+ }
24327
25016
  count(main2, view, (line2) => {
25017
+ if (line2.t >= coordinatingFrom)
25018
+ return null;
24328
25019
  const branch = line2.value["gitBranch"];
24329
25020
  return taskOfBranch(typeof branch === "string" ? branch : null, window.taskIds);
24330
25021
  });
24331
25022
  }
24332
- const folder = subagentsFolderOf(main2);
24333
- if (folder === null)
25023
+ const folder2 = subagentsFolderOf(main2);
25024
+ if (folder2 === null)
24334
25025
  continue;
24335
25026
  let names;
24336
25027
  try {
24337
- names = listDir(folder).filter((name) => /^agent-[A-Za-z0-9_-]+\.jsonl$/.test(name)).sort();
25028
+ names = listDir(folder2).filter((name) => /^agent-[A-Za-z0-9_-]+\.jsonl$/.test(name)).sort();
24338
25029
  } catch {
24339
25030
  continue;
24340
25031
  }
24341
25032
  for (const name of names) {
24342
- const path = `${folder}${separatorOf(main2)}${name}`;
25033
+ const path = `${folder2}${separatorOf(main2)}${name}`;
24343
25034
  const view2 = read(path);
25035
+ addToFigure(view2);
24344
25036
  if (!view2.readable)
24345
25037
  continue;
24346
25038
  const agent = name.slice("agent-".length, -".jsonl".length);
24347
25039
  const own = active.filter((lane) => lane.agent === agent && lane.transcript === main2);
24348
25040
  if (own.length === 0)
24349
25041
  continue;
24350
- count(path, view2, (line2) => {
25042
+ const ownAll = lanes.filter((lane) => lane.agent === agent && lane.transcript === main2);
25043
+ let description;
25044
+ count(path, view2, (line2, id) => {
25045
+ const t = line2.t;
24351
25046
  let best;
24352
- for (const lane of own) {
24353
- const latest = lastAtOrBefore(lane.events.map((e) => msOf(e.at)), line2.t);
25047
+ for (const lane2 of own) {
25048
+ const latest = lastAtOrBefore(lane2.events.map((e) => msOf(e.at)), t);
24354
25049
  if (latest !== void 0 && (best === void 0 || latest >= best.t))
24355
- best = { lane, t: latest };
25050
+ best = { lane: lane2, t: latest };
25051
+ }
25052
+ const lane = best?.lane ?? [...own].sort((a, b) => msOf(a.events[0].at) - msOf(b.events[0].at))[0];
25053
+ if (lane.task !== null)
25054
+ return lane.task;
25055
+ if (settled2.has(id)) {
25056
+ const task2 = settled2.get(id);
25057
+ return task2 !== null && window.taskIds.includes(task2) ? task2 : null;
25058
+ }
25059
+ const next = nextTaskOf(ownAll, t);
25060
+ if (next !== null)
25061
+ return next;
25062
+ if (!final)
25063
+ return void 0;
25064
+ const before = lane.events.filter((e) => msOf(e.at) <= t);
25065
+ const folder3 = (before[before.length - 1] ?? lane.events[0]).folder;
25066
+ let task = nextTaskInFolderOf(lanes, folder3, t);
25067
+ if (task === null) {
25068
+ if (description === void 0)
25069
+ description = spawnDescriptionOf(path, deps);
25070
+ if (description !== null)
25071
+ task = taskNamedIn(description, window.taskIds);
24356
25072
  }
24357
- if (best !== void 0)
24358
- return best.lane.task;
24359
- const first = [...own].sort((a, b) => msOf(a.events[0].at) - msOf(b.events[0].at))[0];
24360
- return first?.task ?? null;
25073
+ if (task === null)
25074
+ task = lastTaskOf(ownAll, t);
25075
+ settled2.set(id, task);
25076
+ return task;
24361
25077
  });
24362
25078
  }
24363
25079
  }
@@ -24380,6 +25096,8 @@ function readUsage(lanes, window, deps = {}, models) {
24380
25096
  best = { lane, t: latest };
24381
25097
  }
24382
25098
  const bucket = bucketFor(source, (best?.lane ?? first)?.task ?? null, true);
25099
+ if (settling.figure !== void 0)
25100
+ settling.figure.codex += step.inputTokens + step.outputTokens + step.cacheWriteTokens + step.cacheReadTokens;
24383
25101
  bucket.messages += 1;
24384
25102
  bucket.inputTokens += step.inputTokens;
24385
25103
  bucket.outputTokens += step.outputTokens;
@@ -24400,13 +25118,13 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
24400
25118
  function phaseModelsPath(engagementId, home) {
24401
25119
  if (!isEngagementId(engagementId))
24402
25120
  return null;
24403
- return join10(home ?? homedir7(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, PHASE_MODELS_FILENAME);
25121
+ return join11(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, PHASE_MODELS_FILENAME);
24404
25122
  }
24405
25123
  function writePhaseModels(path, phase, openedAt, tally) {
24406
25124
  try {
24407
25125
  let tasks = {};
24408
25126
  try {
24409
- const prior = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync14(path, "utf-8")));
25127
+ const prior = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync15(path, "utf-8")));
24410
25128
  if (prior.success && prior.data.phase === phase && prior.data.openedAt === openedAt)
24411
25129
  tasks = prior.data.tasks;
24412
25130
  } catch {
@@ -24434,14 +25152,14 @@ function timeLogDir(engagementId, home) {
24434
25152
  return path === null ? null : dirname4(path);
24435
25153
  }
24436
25154
  function writeJsonFile(path, value) {
24437
- mkdirSync5(dirname4(path), { recursive: true, mode: 448 });
25155
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
24438
25156
  const tmp = `${path}.${process.pid}.tmp`;
24439
- writeFileSync7(tmp, JSON.stringify(value), { mode: 384 });
24440
- renameSync5(tmp, path);
25157
+ writeFileSync8(tmp, JSON.stringify(value), { mode: 384 });
25158
+ renameSync6(tmp, path);
24441
25159
  }
24442
25160
  function readAttemptedAt(dir) {
24443
25161
  try {
24444
- const parsed = JSON.parse(readFileSync14(join10(dir, TIME_THROTTLE_FILENAME), "utf-8"));
25162
+ const parsed = JSON.parse(readFileSync15(join11(dir, TIME_THROTTLE_FILENAME), "utf-8"));
24445
25163
  return typeof parsed.attemptedAt === "number" ? parsed.attemptedAt : null;
24446
25164
  } catch {
24447
25165
  return null;
@@ -24449,7 +25167,7 @@ function readAttemptedAt(dir) {
24449
25167
  }
24450
25168
  function writeAttemptedAt(dir, at) {
24451
25169
  try {
24452
- writeJsonFile(join10(dir, TIME_THROTTLE_FILENAME), { attemptedAt: at });
25170
+ writeJsonFile(join11(dir, TIME_THROTTLE_FILENAME), { attemptedAt: at });
24453
25171
  } catch {
24454
25172
  }
24455
25173
  }
@@ -24467,7 +25185,7 @@ function stringRecord(value) {
24467
25185
  function readTimeCursor(dir, phase, openedAt) {
24468
25186
  const fresh = { phase, openedAt, sentThrough: null, sentIds: {}, usage: {} };
24469
25187
  try {
24470
- const o = JSON.parse(readFileSync14(join10(dir, TIME_CURSOR_FILENAME), "utf-8"));
25188
+ const o = JSON.parse(readFileSync15(join11(dir, TIME_CURSOR_FILENAME), "utf-8"));
24471
25189
  if (o["phase"] !== phase || o["openedAt"] !== openedAt)
24472
25190
  return fresh;
24473
25191
  const sentThrough = o["sentThrough"];
@@ -24480,6 +25198,29 @@ function readTimeCursor(dir, phase, openedAt) {
24480
25198
  return fresh;
24481
25199
  }
24482
25200
  }
25201
+ function readSettled(dir, phase, openedAt) {
25202
+ const out = /* @__PURE__ */ new Map();
25203
+ try {
25204
+ const o = JSON.parse(readFileSync15(join11(dir, TIME_SETTLED_FILENAME), "utf-8"));
25205
+ if (o["phase"] !== phase || o["openedAt"] !== openedAt)
25206
+ return out;
25207
+ const tasks = o["tasks"];
25208
+ if (typeof tasks !== "object" || tasks === null || Array.isArray(tasks))
25209
+ return out;
25210
+ for (const [id, task] of Object.entries(tasks)) {
25211
+ if (task === null || typeof task === "string")
25212
+ out.set(id, task);
25213
+ }
25214
+ } catch {
25215
+ }
25216
+ return out;
25217
+ }
25218
+ function writeSettled(dir, phase, openedAt, settled2) {
25219
+ try {
25220
+ writeJsonFile(join11(dir, TIME_SETTLED_FILENAME), { phase, openedAt, tasks: Object.fromEntries(settled2) });
25221
+ } catch {
25222
+ }
25223
+ }
24483
25224
  function usageKey(bucket) {
24484
25225
  return `${bucket.source}\0${bucket.task ?? ""}`;
24485
25226
  }
@@ -24566,33 +25307,33 @@ var PRUNE_SETTLE_MS = 50;
24566
25307
  async function pruneTimeLog(path, openedAt) {
24567
25308
  const aside = `${path}.prune-${process.pid}`;
24568
25309
  try {
24569
- renameSync5(path, aside);
25310
+ renameSync6(path, aside);
24570
25311
  } catch {
24571
25312
  return;
24572
25313
  }
24573
25314
  let text = "";
24574
25315
  let written = false;
24575
25316
  try {
24576
- text = readFileSync14(aside, "utf-8");
25317
+ text = readFileSync15(aside, "utf-8");
24577
25318
  const { kept } = prunedTimeLog(text, openedAt);
24578
25319
  if (kept !== "")
24579
- appendFileSync2(path, kept, { mode: 384 });
25320
+ appendFileSync3(path, kept, { mode: 384 });
24580
25321
  written = true;
24581
25322
  let seen = Buffer.byteLength(text, "utf-8");
24582
25323
  for (; ; ) {
24583
- await new Promise((resolve11) => setTimeout(resolve11, PRUNE_SETTLE_MS));
24584
- const after = readFileSync14(aside);
25324
+ await new Promise((resolve12) => setTimeout(resolve12, PRUNE_SETTLE_MS));
25325
+ const after = readFileSync15(aside);
24585
25326
  if (after.length <= seen)
24586
25327
  break;
24587
- appendFileSync2(path, after.subarray(seen), { mode: 384 });
25328
+ appendFileSync3(path, after.subarray(seen), { mode: 384 });
24588
25329
  seen = after.length;
24589
25330
  }
24590
- unlinkSync4(aside);
25331
+ unlinkSync5(aside);
24591
25332
  } catch {
24592
25333
  try {
24593
25334
  if (!written) {
24594
- appendFileSync2(path, readFileSync14(aside, "utf-8"), { mode: 384 });
24595
- unlinkSync4(aside);
25335
+ appendFileSync3(path, readFileSync15(aside, "utf-8"), { mode: 384 });
25336
+ unlinkSync5(aside);
24596
25337
  }
24597
25338
  } catch {
24598
25339
  }
@@ -24607,7 +25348,17 @@ function conflictReason(body) {
24607
25348
  const error51 = body.error;
24608
25349
  return error51 === "basis-moved" || error51 === "phase-closed" ? error51 : null;
24609
25350
  }
24610
- async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
25351
+ async function attemptTimeRecord(config2, controlOrigin, deps = {}, options = {}) {
25352
+ return (await attemptAndTell(config2, controlOrigin, deps, options)).result;
25353
+ }
25354
+ async function attemptAndTell(config2, controlOrigin, deps, options) {
25355
+ let wroteFigure = false;
25356
+ const result = await attemptOnce(config2, controlOrigin, deps, options, () => {
25357
+ wroteFigure = true;
25358
+ });
25359
+ return { result, wroteFigure };
25360
+ }
25361
+ async function attemptOnce(config2, controlOrigin, deps, options, onFigureWritten) {
24611
25362
  const fetchFn = deps.fetchFn ?? globalThis.fetch;
24612
25363
  const base = `${controlOrigin}/engagements/${encodeURIComponent(config2.guardEngagementId)}/time`;
24613
25364
  const authorization = `Bearer ${config2.guardServiceToken}`;
@@ -24625,25 +25376,38 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24625
25376
  const basis = parsedBasis.data;
24626
25377
  if (basis.outcome === "nothing-to-record")
24627
25378
  return { outcome: "not-sent", reason: "nothing-to-record" };
24628
- const logPath = join10(dir, TIME_LOG_FILENAME);
25379
+ const logPath2 = join11(dir, TIME_LOG_FILENAME);
24629
25380
  let text = "";
24630
25381
  try {
24631
- text = readFileSync14(logPath, "utf-8");
25382
+ text = readFileSync15(logPath2, "utf-8");
24632
25383
  } catch {
24633
25384
  text = "";
24634
25385
  }
24635
25386
  const lanes = buildLanes(parseTimeLog(text), basis.taskIds);
24636
- const intervals = buildIntervals(lanes, deps);
25387
+ const offsets = loadReadOffsets(dir, basis.phase, basis.openedAt);
25388
+ const readDeps = { ...deps, offsets };
25389
+ const intervals = buildIntervals(lanes, readDeps);
24637
25390
  const models = /* @__PURE__ */ new Map();
24638
- const usage = readUsage(lanes, { from: basis.openedAt, taskIds: basis.taskIds }, deps, models);
25391
+ const settled2 = readSettled(dir, basis.phase, basis.openedAt);
25392
+ const figure = { claudeCode: 0, codex: 0 };
25393
+ const final = options.final === true;
25394
+ const usage = readUsage(lanes, { from: basis.openedAt, taskIds: basis.taskIds }, readDeps, models, { final, settled: settled2, figure });
25395
+ saveReadOffsets(dir, offsets);
25396
+ const routing = routingFilePath(config2.guardEngagementId, deps.home);
25397
+ if (routing !== null)
25398
+ addCheapStarted(routing, basis.phase, tasksBuiltIn(lanes, usage, { from: basis.openedAt, taskIds: basis.taskIds }));
25399
+ if (final)
25400
+ writeSettled(dir, basis.phase, basis.openedAt, settled2);
24639
25401
  const limitDir = phaseLimitDir(config2.guardEngagementId, deps.home);
24640
25402
  if (limitDir !== null) {
24641
- writePhaseUsage(limitDir, {
25403
+ const wrote = writePhaseUsage(limitDir, {
24642
25404
  phase: basis.phase,
24643
25405
  openedAt: basis.openedAt,
24644
- tokens: phaseTokensOf(usage),
25406
+ tokens: figureOf(figure, options.agent),
24645
25407
  at: new Date((deps.now ?? Date.now)()).toISOString()
24646
25408
  });
25409
+ if (wrote)
25410
+ onFigureWritten();
24647
25411
  }
24648
25412
  const modelsPath = phaseModelsPath(config2.guardEngagementId, deps.home);
24649
25413
  if (modelsPath !== null)
@@ -24652,7 +25416,7 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24652
25416
  const selected = selectTimeReport(cursor, intervals, usage);
24653
25417
  if (selected.intervals.length === 0 && selected.usage.length === 0) {
24654
25418
  if (wouldPrune(text, basis.openedAt))
24655
- await pruneTimeLog(logPath, basis.openedAt);
25419
+ await pruneTimeLog(logPath2, basis.openedAt);
24656
25420
  return { outcome: "recorded" };
24657
25421
  }
24658
25422
  const report = { phase: basis.phase, openedAt: basis.openedAt, ...selected };
@@ -24668,14 +25432,14 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24668
25432
  if (!timeReportResultSchema.safeParse(sent.body).success)
24669
25433
  return { outcome: "not-sent", reason: "refused" };
24670
25434
  try {
24671
- writeJsonFile(join10(dir, TIME_CURSOR_FILENAME), advanceTimeCursor(cursor, selected));
25435
+ writeJsonFile(join11(dir, TIME_CURSOR_FILENAME), advanceTimeCursor(cursor, selected));
24672
25436
  } catch {
24673
25437
  }
24674
25438
  if (wouldPrune(text, basis.openedAt))
24675
- await pruneTimeLog(logPath, basis.openedAt);
25439
+ await pruneTimeLog(logPath2, basis.openedAt);
24676
25440
  return { outcome: "recorded" };
24677
25441
  }
24678
- async function runTimeVerb(deps = {}) {
25442
+ async function runTimeVerb(deps = {}, options = {}) {
24679
25443
  let result;
24680
25444
  try {
24681
25445
  const env = deps.env ?? process.env;
@@ -24685,14 +25449,14 @@ async function runTimeVerb(deps = {}) {
24685
25449
  result = { outcome: "not-sent", reason: "not-configured" };
24686
25450
  } else {
24687
25451
  writeAttemptedAt(dir, (deps.now ?? Date.now)());
24688
- result = await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps);
25452
+ result = await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps, options);
24689
25453
  }
24690
25454
  } catch {
24691
25455
  result = { outcome: "not-sent", reason: "unreachable" };
24692
25456
  }
24693
25457
  return timeRunResultSchema.parse(result);
24694
25458
  }
24695
- async function sendTimeRecord(deps = {}) {
25459
+ async function sendTimeRecord(deps = {}, options = {}) {
24696
25460
  try {
24697
25461
  const env = deps.env ?? process.env;
24698
25462
  const configured = readConfig(env);
@@ -24703,17 +25467,170 @@ async function sendTimeRecord(deps = {}) {
24703
25467
  return;
24704
25468
  const now = (deps.now ?? Date.now)();
24705
25469
  const last = readAttemptedAt(dir);
24706
- if (last !== null && now - last < TIME_MIN_INTERVAL_MS && now >= last)
24707
- return;
24708
- writeAttemptedAt(dir, now);
24709
- await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps);
25470
+ let wroteFigure = false;
25471
+ if (!(last !== null && now - last < TIME_MIN_INTERVAL_MS && now >= last)) {
25472
+ writeAttemptedAt(dir, now);
25473
+ wroteFigure = (await attemptAndTell(configured.config, readControlOrigin(env).origin, deps, { agent: options.agent })).wroteFigure;
25474
+ }
25475
+ if (!wroteFigure)
25476
+ recountPhaseUsage(configured.config, deps, options);
25477
+ } catch {
25478
+ }
25479
+ }
25480
+ function recountPhaseUsage(config2, deps = {}, options = {}) {
25481
+ try {
25482
+ const limitDir = phaseLimitDir(config2.guardEngagementId, deps.home);
25483
+ const dir = timeLogDir(config2.guardEngagementId, deps.home);
25484
+ if (limitDir === null || dir === null)
25485
+ return false;
25486
+ const last = readPhaseUsageFile(limitDir);
25487
+ if (last === null)
25488
+ return false;
25489
+ let text = "";
25490
+ try {
25491
+ text = readFileSync15(join11(dir, TIME_LOG_FILENAME), "utf-8");
25492
+ } catch {
25493
+ text = "";
25494
+ }
25495
+ const lanes = buildLanes(parseTimeLog(text), []);
25496
+ const offsets = loadReadOffsets(dir, last.phase, last.openedAt);
25497
+ const figure = { claudeCode: 0, codex: 0 };
25498
+ readUsage(lanes, { from: last.openedAt, taskIds: [] }, { ...deps, offsets }, void 0, { figure });
25499
+ saveReadOffsets(dir, offsets);
25500
+ return writePhaseUsage(limitDir, {
25501
+ phase: last.phase,
25502
+ openedAt: last.openedAt,
25503
+ tokens: figureOf(figure, options.agent),
25504
+ at: new Date((deps.now ?? Date.now)()).toISOString()
25505
+ });
25506
+ } catch {
25507
+ return false;
25508
+ }
25509
+ }
25510
+ function figureOf(figure, agent) {
25511
+ return Math.max(0, Math.round(agent === "codex" ? figure.codex : figure.claudeCode));
25512
+ }
25513
+ var ROUTING_LOCK_FILENAME = "routing.lock";
25514
+ var ROUTING_LOCK_GIVE_UP_MS = 50;
25515
+ var ROUTING_LOCK_STALE_MS = 5 * 1e3;
25516
+ var ROUTING_LOCK_POLL_MS = 2;
25517
+ function routingFilePath(engagementId, home) {
25518
+ if (!isEngagementId(engagementId))
25519
+ return null;
25520
+ return join11(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, ROUTING_FILENAME);
25521
+ }
25522
+ function sleepSync2(ms) {
25523
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
25524
+ }
25525
+ function addCheapStarted(path, phase, tasks, giveUpMs = ROUTING_LOCK_GIVE_UP_MS) {
25526
+ if (tasks.length === 0)
25527
+ return true;
25528
+ try {
25529
+ const lock = join11(dirname4(path), ROUTING_LOCK_FILENAME);
25530
+ const giveUpAt = Date.now() + giveUpMs;
25531
+ while (!tryTakeLockFile(lock, ROUTING_LOCK_STALE_MS)) {
25532
+ if (Date.now() >= giveUpAt)
25533
+ return false;
25534
+ sleepSync2(ROUTING_LOCK_POLL_MS);
25535
+ }
25536
+ try {
25537
+ let started = [];
25538
+ try {
25539
+ const parsed = routingRecordSchema.safeParse(JSON.parse(readFileSync15(path, "utf-8")));
25540
+ if (parsed.success && parsed.data.phase === phase)
25541
+ started = [...parsed.data.cheapStarted];
25542
+ } catch {
25543
+ started = [];
25544
+ }
25545
+ const added = tasks.filter((t, i) => !started.includes(t) && tasks.indexOf(t) === i);
25546
+ if (added.length === 0)
25547
+ return true;
25548
+ const record2 = { phase, cheapStarted: [...started, ...added] };
25549
+ if (!routingRecordSchema.safeParse(record2).success)
25550
+ return false;
25551
+ writeJsonFile(path, record2);
25552
+ return true;
25553
+ } finally {
25554
+ releaseLockFile(lock);
25555
+ }
25556
+ } catch {
25557
+ return false;
25558
+ }
25559
+ }
25560
+ function recordSpawnedTask(toolInput, deps) {
25561
+ try {
25562
+ if (typeof toolInput !== "object" || toolInput === null || Array.isArray(toolInput))
25563
+ return null;
25564
+ const engagementId = readConfig(deps.env ?? process.env).config?.guardEngagementId;
25565
+ if (engagementId === void 0)
25566
+ return null;
25567
+ const path = routingFilePath(engagementId, deps.home);
25568
+ if (path === null)
25569
+ return null;
25570
+ const parsed = taskPlanSchema.safeParse(JSON.parse(readFileSync15(join11(deps.projectDir, TASK_PLAN_FILE), "utf-8")));
25571
+ if (!parsed.success)
25572
+ return null;
25573
+ const plan = parsed.data;
25574
+ const ids = plan.tasks.map((t) => t.id);
25575
+ const textOf = (key) => {
25576
+ const v = toolInput[key];
25577
+ return typeof v === "string" ? v : null;
25578
+ };
25579
+ const description = textOf("description");
25580
+ const prompt = textOf("prompt");
25581
+ const named = (description === null ? null : taskNamedIn(description, ids)) ?? (prompt === null ? null : taskNamedIn(prompt, ids));
25582
+ if (named === null)
25583
+ return null;
25584
+ const task = plan.tasks.find((t) => t.id === named);
25585
+ if (task === void 0 || task.kind !== "build" || task.cheapFirst !== true)
25586
+ return null;
25587
+ return addCheapStarted(path, plan.phase, [named]) ? named : null;
24710
25588
  } catch {
25589
+ return null;
24711
25590
  }
24712
25591
  }
25592
+ function tasksBuiltIn(lanes, usage, window) {
25593
+ const from = Date.parse(window.from);
25594
+ const to = window.to === void 0 || window.to === null ? Infinity : Date.parse(window.to);
25595
+ const inWindow = (t) => t >= from && t <= to;
25596
+ const out = [];
25597
+ const add = (task) => {
25598
+ if (task !== null && window.taskIds.includes(task) && !out.includes(task))
25599
+ out.push(task);
25600
+ };
25601
+ for (const bucket of usage)
25602
+ if (bucket.messages > 0)
25603
+ add(bucket.task);
25604
+ for (const lane of lanes) {
25605
+ if (lane.task === null)
25606
+ continue;
25607
+ const times = lane.events.map((e) => msOf(e.at)).filter(inWindow);
25608
+ if (times.length === 0)
25609
+ continue;
25610
+ const codex = lane.rollout !== null && lane.transcript === null;
25611
+ if (lane.agent !== MAIN_AGENT || codex) {
25612
+ add(lane.task);
25613
+ continue;
25614
+ }
25615
+ let coordinatingFrom = Infinity;
25616
+ for (const other of lanes) {
25617
+ if (other.agent === MAIN_AGENT)
25618
+ continue;
25619
+ const sameSession = lane.session !== null ? other.session === lane.session : lane.transcript !== null && other.transcript === lane.transcript;
25620
+ if (!sameSession)
25621
+ continue;
25622
+ for (const e of other.events)
25623
+ coordinatingFrom = Math.min(coordinatingFrom, msOf(e.at));
25624
+ }
25625
+ if (times.some((t) => t < coordinatingFrom))
25626
+ add(lane.task);
25627
+ }
25628
+ return out;
25629
+ }
24713
25630
 
24714
25631
  // dist/own-work.js
24715
- import { appendFileSync as appendFileSync3, lstatSync as lstatSync2, readFileSync as readFileSync15, realpathSync as realpathSync6 } from "node:fs";
24716
- 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";
25632
+ import { appendFileSync as appendFileSync4, lstatSync as lstatSync2, readFileSync as readFileSync16, realpathSync as realpathSync6 } from "node:fs";
25633
+ 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";
24717
25634
 
24718
25635
  // dist/risky/types.js
24719
25636
  var RISKY_ACTION_TYPES = [
@@ -24747,7 +25664,7 @@ function ownWorkFileName(sessionId) {
24747
25664
  function ownWorkPath(sessionId, dir) {
24748
25665
  if (typeof sessionId !== "string" || !livenessIdOk(sessionId))
24749
25666
  return null;
24750
- return join11(dir ?? markerDir(), ownWorkFileName(sessionId));
25667
+ return join12(dir ?? markerDir(), ownWorkFileName(sessionId));
24751
25668
  }
24752
25669
  function inputString2(input, key) {
24753
25670
  if (typeof input !== "object" || input === null)
@@ -24765,7 +25682,7 @@ function append(sessionId, line2, dir) {
24765
25682
  const path = ownWorkPath(sessionId, dir);
24766
25683
  if (path === null)
24767
25684
  return;
24768
- appendFileSync3(path, `${JSON.stringify(line2)}
25685
+ appendFileSync4(path, `${JSON.stringify(line2)}
24769
25686
  `, { mode: 384 });
24770
25687
  }
24771
25688
  function recordOwnShell(input, at, deps = {}) {
@@ -24811,7 +25728,7 @@ function readOwnWork(sessionId, dir) {
24811
25728
  const path = ownWorkPath(sessionId, dir);
24812
25729
  if (path === null)
24813
25730
  return [];
24814
- return parseOwnWork(readFileSync15(path, "utf-8"));
25731
+ return parseOwnWork(readFileSync16(path, "utf-8"));
24815
25732
  } catch {
24816
25733
  return [];
24817
25734
  }
@@ -24846,7 +25763,7 @@ function realPathOf(path) {
24846
25763
  let at = resolve4(path);
24847
25764
  for (; ; ) {
24848
25765
  try {
24849
- return join11(realpathSync6.native(at), ...missing.reverse());
25766
+ return join12(realpathSync6.native(at), ...missing.reverse());
24850
25767
  } catch {
24851
25768
  const parent = dirname5(at);
24852
25769
  if (parent === at)
@@ -24906,7 +25823,7 @@ function ownChangedFiles(repoRoot, files, sessionId, deps = {}) {
24906
25823
  };
24907
25824
  const root = resolve4(repoRoot);
24908
25825
  return files.filter((rel) => {
24909
- const abs = join11(root, rel);
25826
+ const abs = join12(root, rel);
24910
25827
  if (edited.has(realPathOf(abs)))
24911
25828
  return true;
24912
25829
  if (windows.length === 0)
@@ -24920,7 +25837,7 @@ function ownChangedFiles(repoRoot, files, sessionId, deps = {}) {
24920
25837
 
24921
25838
  // dist/hooks/findings.js
24922
25839
  import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
24923
- 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";
25840
+ 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";
24924
25841
  function findingKey(firing) {
24925
25842
  return createHash4("sha256").update(`${firing.guardId}
24926
25843
  ${firing.path ?? ""}
@@ -24963,7 +25880,7 @@ function readFindings(sessionId) {
24963
25880
  return null;
24964
25881
  const session = sessionId;
24965
25882
  try {
24966
- const parsed = JSON.parse(readFileSync16(path, "utf-8"));
25883
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
24967
25884
  if (parsed["version"] !== 1 || !Array.isArray(parsed["findings"]))
24968
25885
  return emptyFindings(session);
24969
25886
  const findings = [];
@@ -24983,12 +25900,12 @@ function writeFindings(file2) {
24983
25900
  return;
24984
25901
  const temp = `${path.slice(0, -".json".length)}.${process.pid}.tmp.json`;
24985
25902
  try {
24986
- writeFileSync8(temp, `${JSON.stringify(file2)}
25903
+ writeFileSync9(temp, `${JSON.stringify(file2)}
24987
25904
  `, { mode: 384 });
24988
- renameSync6(temp, path);
25905
+ renameSync7(temp, path);
24989
25906
  } catch {
24990
25907
  try {
24991
- unlinkSync5(temp);
25908
+ unlinkSync6(temp);
24992
25909
  } catch {
24993
25910
  }
24994
25911
  }
@@ -25004,7 +25921,7 @@ function pause(ms) {
25004
25921
  function takeOverStaleLock(lock) {
25005
25922
  const aside = `${lock}.${process.pid}.${randomUUID2()}.stale`;
25006
25923
  try {
25007
- renameSync6(lock, aside);
25924
+ renameSync7(lock, aside);
25008
25925
  } catch {
25009
25926
  return false;
25010
25927
  }
@@ -25016,17 +25933,17 @@ function takeOverStaleLock(lock) {
25016
25933
  }
25017
25934
  if (!staleIndeed) {
25018
25935
  try {
25019
- renameSync6(aside, lock);
25936
+ renameSync7(aside, lock);
25020
25937
  } catch {
25021
25938
  try {
25022
- unlinkSync5(aside);
25939
+ unlinkSync6(aside);
25023
25940
  } catch {
25024
25941
  }
25025
25942
  }
25026
25943
  return false;
25027
25944
  }
25028
25945
  try {
25029
- unlinkSync5(aside);
25946
+ unlinkSync6(aside);
25030
25947
  } catch {
25031
25948
  }
25032
25949
  return true;
@@ -25039,7 +25956,7 @@ function withFindingsLock(sessionId, body, waitMs = FINDINGS_LOCK_WAIT_MS) {
25039
25956
  const deadline = Date.now() + waitMs;
25040
25957
  for (; ; ) {
25041
25958
  try {
25042
- closeSync3(openSync3(lock, "wx", 384));
25959
+ closeSync4(openSync4(lock, "wx", 384));
25043
25960
  break;
25044
25961
  } catch (err) {
25045
25962
  if (err.code !== "EEXIST")
@@ -25061,31 +25978,31 @@ function withFindingsLock(sessionId, body, waitMs = FINDINGS_LOCK_WAIT_MS) {
25061
25978
  return body();
25062
25979
  } finally {
25063
25980
  try {
25064
- unlinkSync5(lock);
25981
+ unlinkSync6(lock);
25065
25982
  } catch {
25066
25983
  }
25067
25984
  }
25068
25985
  }
25069
- function takeUndelivered(file2, folder) {
25986
+ function takeUndelivered(file2, folder2) {
25070
25987
  const taken = [];
25071
25988
  const findings = file2.findings.map((f) => {
25072
- if (f.delivered || folder !== null && f.folder !== folder)
25989
+ if (f.delivered || folder2 !== null && f.folder !== folder2)
25073
25990
  return f;
25074
25991
  taken.push(f);
25075
25992
  return { ...f, delivered: true };
25076
25993
  });
25077
25994
  return { file: { ...file2, findings }, taken };
25078
25995
  }
25079
- function deliverFindings(sessionId, folder) {
25996
+ function deliverFindings(sessionId, folder2) {
25080
25997
  const path = findingsPath(sessionId);
25081
- if (path === null || !existsSync6(path))
25998
+ if (path === null || !existsSync7(path))
25082
25999
  return [];
25083
26000
  try {
25084
26001
  const taken = withFindingsLock(sessionId, () => {
25085
26002
  const before = readFindings(sessionId);
25086
26003
  if (before === null)
25087
26004
  return [];
25088
- const { file: file2, taken: given } = takeUndelivered(before, folder);
26005
+ const { file: file2, taken: given } = takeUndelivered(before, folder2);
25089
26006
  if (given.length > 0)
25090
26007
  writeFindings(file2);
25091
26008
  return given;
@@ -25095,11 +26012,11 @@ function deliverFindings(sessionId, folder) {
25095
26012
  return [];
25096
26013
  }
25097
26014
  }
25098
- function turnEndBookkeeping(file2, folder, outcome, topLevel, now) {
26015
+ function turnEndBookkeeping(file2, folder2, outcome, topLevel, now) {
25099
26016
  const recorded = [...file2.findings];
25100
26017
  const firing = /* @__PURE__ */ new Map();
25101
26018
  const notRun = new Set(outcome.kind === "evaluated" ? outcome.notRun : []);
25102
- const observed = (f) => f.folder === folder && !notRun.has(f.patternRef);
26019
+ const observed = (f) => f.folder === folder2 && !notRun.has(f.patternRef);
25103
26020
  if (outcome.kind === "evaluated") {
25104
26021
  for (const g of outcome.fired) {
25105
26022
  if (g.severity === "block")
@@ -25115,13 +26032,13 @@ function turnEndBookkeeping(file2, folder, outcome, topLevel, now) {
25115
26032
  const closes = observed(f) && (outcome.kind === "no-own-changes" || outcome.kind === "evaluated" && !firing.has(f.key));
25116
26033
  (closes ? removed : kept).push(f);
25117
26034
  }
25118
- const known = new Set(kept.filter((f) => f.folder === folder).map((f) => f.key));
26035
+ const known = new Set(kept.filter((f) => f.folder === folder2).map((f) => f.key));
25119
26036
  for (const [key, g] of firing) {
25120
26037
  if (known.has(key))
25121
26038
  continue;
25122
26039
  kept.push({
25123
26040
  key,
25124
- folder,
26041
+ folder: folder2,
25125
26042
  guardId: g.guardId,
25126
26043
  patternRef: g.patternRef,
25127
26044
  severity: g.severity,
@@ -25316,7 +26233,7 @@ function patchHeaderPaths(patch) {
25316
26233
  }
25317
26234
 
25318
26235
  // dist/hooks/post-tool-use.js
25319
- import { isAbsolute as isAbsolute3, join as join12, relative as relative6, resolve as resolve5, sep as sep4 } from "node:path";
26236
+ import { isAbsolute as isAbsolute3, join as join13, relative as relative6, resolve as resolve5, sep as sep4 } from "node:path";
25320
26237
  var HANDLED_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
25321
26238
  async function runPostToolUse(readSeat = readSeatConfiguration, profile = AGENT_PROFILES["claude-code"]) {
25322
26239
  const reply = hookReply("PostToolUse", profile);
@@ -25503,7 +26420,7 @@ function insideRepository(path, repoRoot) {
25503
26420
  const rel = relative6(realPathOf(repoRoot), realPathOf(path));
25504
26421
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep4}`) || isAbsolute3(rel))
25505
26422
  return null;
25506
- return join12(repoRoot, rel);
26423
+ return join13(repoRoot, rel);
25507
26424
  }
25508
26425
  function actOnEnvelope2(envelope, reply) {
25509
26426
  const notRun = notRunReport(envelope.notRun);
@@ -25526,17 +26443,17 @@ ${guardLines}${notRunBlock}`);
25526
26443
  }
25527
26444
  }
25528
26445
  function readStdin() {
25529
- return new Promise((resolve11, reject) => {
26446
+ return new Promise((resolve12, reject) => {
25530
26447
  const chunks = [];
25531
26448
  process.stdin.on("data", (chunk) => chunks.push(chunk));
25532
- process.stdin.on("end", () => resolve11(Buffer.concat(chunks).toString("utf-8")));
26449
+ process.stdin.on("end", () => resolve12(Buffer.concat(chunks).toString("utf-8")));
25533
26450
  process.stdin.on("error", reject);
25534
26451
  });
25535
26452
  }
25536
26453
 
25537
26454
  // dist/liveness.js
25538
- 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";
25539
- import { join as join13 } from "node:path";
26455
+ 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";
26456
+ import { join as join14 } from "node:path";
25540
26457
  var LIVE_WINDOW_MS = 10 * 60 * 1e3;
25541
26458
  function field(input, key) {
25542
26459
  if (typeof input !== "object" || input === null)
@@ -25545,10 +26462,10 @@ function field(input, key) {
25545
26462
  return typeof v === "string" && v !== "" ? v : null;
25546
26463
  }
25547
26464
  function indexEntry(dir, sessionId, name) {
25548
- const folder = join13(dir, livenessIndexDirName(sessionId));
26465
+ const folder2 = join14(dir, livenessIndexDirName(sessionId));
25549
26466
  for (let attempt2 = 0; attempt2 < 3; attempt2++) {
25550
26467
  try {
25551
- closeSync4(openSync4(join13(folder, name), "a", 384));
26468
+ closeSync5(openSync5(join14(folder2, name), "a", 384));
25552
26469
  return true;
25553
26470
  } catch (err) {
25554
26471
  if (err.code !== "ENOENT")
@@ -25556,13 +26473,13 @@ function indexEntry(dir, sessionId, name) {
25556
26473
  }
25557
26474
  let tmp = null;
25558
26475
  try {
25559
- tmp = mkdtempSync(`${folder}.`);
26476
+ tmp = mkdtempSync(`${folder2}.`);
25560
26477
  for (const existing of readdirSync7(dir)) {
25561
26478
  if (parseLivenessFileName(existing)?.sessionId === sessionId)
25562
- closeSync4(openSync4(join13(tmp, existing), "a", 384));
26479
+ closeSync5(openSync5(join14(tmp, existing), "a", 384));
25563
26480
  }
25564
- closeSync4(openSync4(join13(tmp, name), "a", 384));
25565
- renameSync7(tmp, folder);
26481
+ closeSync5(openSync5(join14(tmp, name), "a", 384));
26482
+ renameSync8(tmp, folder2);
25566
26483
  return true;
25567
26484
  } catch {
25568
26485
  if (tmp !== null)
@@ -25587,18 +26504,18 @@ function recordLiveness(input, deps = {}) {
25587
26504
  const name = livenessFileName(livenessKey(copy?.common ?? null), sessionId, agentId);
25588
26505
  if (!indexEntry(dir, sessionId, name))
25589
26506
  return null;
25590
- const path = join13(dir, name);
26507
+ const path = join14(dir, name);
25591
26508
  const text = `${cwd}
25592
26509
  ${copy?.top ?? ""}
25593
26510
  `;
25594
26511
  try {
25595
- writeFileSync9(path, text, { encoding: "utf-8", flag: "wx", mode: 384 });
26512
+ writeFileSync10(path, text, { encoding: "utf-8", flag: "wx", mode: 384 });
25596
26513
  return { firstSeen: true };
25597
26514
  } catch (err) {
25598
26515
  if (err.code !== "EEXIST")
25599
26516
  return null;
25600
26517
  }
25601
- writeFileSync9(path, text, { encoding: "utf-8", mode: 384 });
26518
+ writeFileSync10(path, text, { encoding: "utf-8", mode: 384 });
25602
26519
  return { firstSeen: false };
25603
26520
  } catch {
25604
26521
  return null;
@@ -25617,7 +26534,7 @@ function clearLiveness(input, deps = {}) {
25617
26534
  if (p === null || p.sessionId !== sessionId || p.agentId !== agentId)
25618
26535
  continue;
25619
26536
  try {
25620
- unlinkSync6(join13(dir, name));
26537
+ unlinkSync7(join14(dir, name));
25621
26538
  removed += 1;
25622
26539
  } catch {
25623
26540
  }
@@ -25630,7 +26547,7 @@ function clearLiveness(input, deps = {}) {
25630
26547
  }
25631
26548
  function topOf(path) {
25632
26549
  try {
25633
- const top = readFileSync17(path, "utf-8").split("\n")[1] ?? "";
26550
+ const top = readFileSync18(path, "utf-8").split("\n")[1] ?? "";
25634
26551
  return top === "" ? null : top;
25635
26552
  } catch {
25636
26553
  return null;
@@ -25652,7 +26569,7 @@ function readLiveness(sessionId, cwd, deps = {}) {
25652
26569
  const p = parseLivenessFileName(name);
25653
26570
  if (p === null || p.key !== key || p.sessionId !== sessionId)
25654
26571
  continue;
25655
- const full = join13(dir, name);
26572
+ const full = join14(dir, name);
25656
26573
  let seenAt;
25657
26574
  try {
25658
26575
  seenAt = statSync5(full).mtimeMs;
@@ -25698,168 +26615,8 @@ import nodePath5 from "node:path";
25698
26615
 
25699
26616
  // dist/pace-verb.js
25700
26617
  import { mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as readFileSync19, renameSync as renameSync9, statSync as statSync6, writeFileSync as writeFileSync11 } from "node:fs";
25701
- import { homedir as homedir8 } from "node:os";
26618
+ import { homedir as homedir9 } from "node:os";
25702
26619
  import nodePath4 from "node:path";
25703
-
25704
- // dist/scan-lock.js
25705
- import { spawn } from "node:child_process";
25706
- import { linkSync, mkdirSync as mkdirSync6, readFileSync as readFileSync18, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "node:fs";
25707
- import nodePath3 from "node:path";
25708
- var SCAN_LOCK_FILENAME = "scan.lock";
25709
- var SCAN_LOCK_STALE_MS = 15 * 60 * 1e3;
25710
- var SCAN_LOCK_POLL_MS = 100;
25711
- function scanLockPath(home) {
25712
- return nodePath3.join(pacingDir(home), SCAN_LOCK_FILENAME);
25713
- }
25714
- function readLockBody(path) {
25715
- let text;
25716
- try {
25717
- text = readFileSync18(path, "utf-8");
25718
- } catch (err) {
25719
- return err.code === "ENOENT" ? "missing" : null;
25720
- }
25721
- try {
25722
- const v = JSON.parse(text);
25723
- return typeof v["pid"] === "number" && typeof v["at"] === "number" ? { pid: v["pid"], at: v["at"] } : null;
25724
- } catch {
25725
- return null;
25726
- }
25727
- }
25728
- function processAlive(pid) {
25729
- if (!Number.isInteger(pid) || pid <= 0)
25730
- return false;
25731
- try {
25732
- process.kill(pid, 0);
25733
- return true;
25734
- } catch (err) {
25735
- return err.code === "EPERM";
25736
- }
25737
- }
25738
- function isStale(body, staleMs, deps) {
25739
- if (body === null)
25740
- return true;
25741
- const now = (deps.clock ?? Date.now)();
25742
- if (Math.abs(now - body.at) > staleMs)
25743
- return true;
25744
- return !(deps.alive ?? processAlive)(body.pid);
25745
- }
25746
- function writeLock(path, pid, deps, flag) {
25747
- writeFileSync10(path, JSON.stringify({ pid, at: (deps.clock ?? Date.now)() }), { flag, mode: 384 });
25748
- }
25749
- function tryTakeScanLock(deps = {}) {
25750
- return tryTakeLockFile(scanLockPath(deps.home), SCAN_LOCK_STALE_MS, deps);
25751
- }
25752
- function tryTakeLockFile(path, staleMs, deps = {}) {
25753
- const pid = deps.pid ?? process.pid;
25754
- try {
25755
- mkdirSync6(nodePath3.dirname(path), { recursive: true, mode: 448 });
25756
- } catch {
25757
- return false;
25758
- }
25759
- for (let attempt2 = 0; attempt2 < 2; attempt2++) {
25760
- try {
25761
- writeLock(path, pid, deps, "wx");
25762
- return true;
25763
- } catch (err) {
25764
- if (err.code !== "EEXIST")
25765
- return false;
25766
- }
25767
- const seen = readLockBody(path);
25768
- if (seen === "missing")
25769
- continue;
25770
- if (!isStale(seen, staleMs, deps))
25771
- return false;
25772
- const aside = `${path}.${String(pid)}.${String((deps.clock ?? Date.now)())}.stale`;
25773
- try {
25774
- renameSync8(path, aside);
25775
- } catch {
25776
- continue;
25777
- }
25778
- const taken = readLockBody(aside);
25779
- const same = taken !== "missing" && (seen === null ? taken === null : taken !== null && taken.pid === seen.pid && taken.at === seen.at);
25780
- if (!same && taken !== "missing" && !isStale(taken, staleMs, deps)) {
25781
- try {
25782
- linkSync(aside, path);
25783
- } catch {
25784
- }
25785
- try {
25786
- unlinkSync7(aside);
25787
- } catch {
25788
- }
25789
- return false;
25790
- }
25791
- try {
25792
- unlinkSync7(aside);
25793
- } catch {
25794
- }
25795
- }
25796
- return false;
25797
- }
25798
- function releaseLockFile(path, pid = process.pid) {
25799
- const body = readLockBody(path);
25800
- if (body === "missing" || body === null || body.pid !== pid)
25801
- return;
25802
- try {
25803
- unlinkSync7(path);
25804
- } catch {
25805
- }
25806
- }
25807
- function releaseScanLock(deps = {}) {
25808
- releaseLockFile(scanLockPath(deps.home), deps.pid ?? process.pid);
25809
- }
25810
- function sleepSync(ms) {
25811
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
25812
- }
25813
- function scanUnderLock(key, deps = {}, waitMs = 0) {
25814
- const clock = deps.clock ?? Date.now;
25815
- const giveUpAt = clock() + waitMs;
25816
- while (!tryTakeScanLock(deps)) {
25817
- if (clock() >= giveUpAt)
25818
- return null;
25819
- sleepSync(SCAN_LOCK_POLL_MS);
25820
- }
25821
- try {
25822
- return scanRecords(key, deps);
25823
- } finally {
25824
- releaseScanLock(deps);
25825
- }
25826
- }
25827
- var spawnDetachedReal = (command, args2) => {
25828
- const child = spawn(command, [...args2], { detached: true, stdio: "ignore", windowsHide: true });
25829
- child.on("error", () => void 0);
25830
- child.unref();
25831
- return child.pid;
25832
- };
25833
- function startScanChild(key, binaryPath, deps = {}) {
25834
- if (!tryTakeScanLock(deps))
25835
- return false;
25836
- let childPid;
25837
- try {
25838
- childPid = (deps.spawnDetached ?? spawnDetachedReal)(deps.nodePath ?? process.execPath, [
25839
- binaryPath,
25840
- "pace",
25841
- "--scan",
25842
- "--config-folder",
25843
- key,
25844
- "--lock-held"
25845
- ]);
25846
- } catch {
25847
- childPid = void 0;
25848
- }
25849
- if (childPid === void 0) {
25850
- releaseScanLock(deps);
25851
- return false;
25852
- }
25853
- try {
25854
- writeLock(scanLockPath(deps.home), childPid, deps, "w");
25855
- } catch {
25856
- releaseScanLock(deps);
25857
- return false;
25858
- }
25859
- return true;
25860
- }
25861
-
25862
- // dist/pace-verb.js
25863
26620
  var PACE_DEFAULT_TASK_MINUTES = 30;
25864
26621
  var PACE_DURATION_ROUND_MINUTES = 10;
25865
26622
  var PACE_WINDOW_MINUTES = 300;
@@ -25981,7 +26738,7 @@ function suggestPace(input) {
25981
26738
  function enginePacingDir(engagementId, home) {
25982
26739
  if (!isEngagementId(engagementId))
25983
26740
  return null;
25984
- return nodePath4.join(home ?? homedir8(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
26741
+ return nodePath4.join(home ?? homedir9(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
25985
26742
  }
25986
26743
  function readBuildNumber(engagementId, home) {
25987
26744
  const dir = enginePacingDir(engagementId, home);
@@ -26039,7 +26796,7 @@ var USAGE = "Usage: halfcycle-runner pace [--agent claude-code|codex] [--start <
26039
26796
  function parsePaceArgs(args2) {
26040
26797
  let scan = false;
26041
26798
  let lockHeld = false;
26042
- let folder = null;
26799
+ let folder2 = null;
26043
26800
  let phase = null;
26044
26801
  let agents = null;
26045
26802
  let agent = null;
@@ -26057,8 +26814,8 @@ function parsePaceArgs(args2) {
26057
26814
  else if (a === "--lock-held")
26058
26815
  lockHeld = true;
26059
26816
  else if (a === "--config-folder") {
26060
- folder = value();
26061
- if (folder === null)
26817
+ folder2 = value();
26818
+ if (folder2 === null)
26062
26819
  return { kind: "usage", message: USAGE };
26063
26820
  } else if (a === "--start") {
26064
26821
  phase = value();
@@ -26079,9 +26836,9 @@ function parsePaceArgs(args2) {
26079
26836
  if (scan) {
26080
26837
  if (phase !== null || agents !== null || agent !== null)
26081
26838
  return { kind: "usage", message: USAGE };
26082
- return { kind: "scan", folder, lockHeld };
26839
+ return { kind: "scan", folder: folder2, lockHeld };
26083
26840
  }
26084
- if (folder !== null || lockHeld)
26841
+ if (folder2 !== null || lockHeld)
26085
26842
  return { kind: "usage", message: USAGE };
26086
26843
  if (phase === null)
26087
26844
  return agents === null ? { kind: "now", agent: agent ?? "claude-code" } : { kind: "usage", message: USAGE };
@@ -26164,7 +26921,7 @@ async function runPaceVerb(args2, deps = {}) {
26164
26921
  ...deps.home === void 0 ? {} : { home: deps.home },
26165
26922
  now: () => now.getTime(),
26166
26923
  ...deps.fetchFn === void 0 ? {} : { fetchFn: deps.fetchFn }
26167
- });
26924
+ }, { agent: parsed.agent });
26168
26925
  } catch {
26169
26926
  }
26170
26927
  const remembered = readPaceStateFile(pacingDir2).basis;
@@ -26252,6 +27009,7 @@ async function runPaceVerb(args2, deps = {}) {
26252
27009
  ...decision.until !== null ? { until: decision.until.toISOString() } : {},
26253
27010
  keepFreePercent,
26254
27011
  ...suggestion !== void 0 ? { suggestion } : {},
27012
+ ...limitFile !== null ? { usage: { used: readPhaseUsage(pacingDir2, limitFile.phase), limit: limitFile.limit } } : {},
26255
27013
  ...atLimit ? {
26256
27014
  limit: "reached",
26257
27015
  limitHeld: {
@@ -26399,10 +27157,10 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26399
27157
  const basis = read !== null ? { phase: read.phase, keepFreePercent: read.keepFreePercent } : stored.basis;
26400
27158
  if (basis === null)
26401
27159
  return;
26402
- const write = deps.writeState ?? writePaceState;
27160
+ const write2 = deps.writeState ?? writePaceState;
26403
27161
  if (basis.phase === null) {
26404
27162
  if (before !== null || read !== null)
26405
- write(dir, key, null, read !== null ? basis : null);
27163
+ write2(dir, key, null, read !== null ? basis : null);
26406
27164
  return;
26407
27165
  }
26408
27166
  const working = liveCount(readLiveness(field2(input, "session_id"), field2(input, "cwd") ?? process.cwd(), deps.liveness));
@@ -26429,7 +27187,8 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26429
27187
  }
26430
27188
  }
26431
27189
  const reported = readReportedAccount(key, { home: deps.home });
26432
- const number4 = buildNumberFor(engagementId, basis.phase, deps.home)?.number ?? CAREFUL_START_AGENTS;
27190
+ const build = buildNumberFor(engagementId, basis.phase, deps.home);
27191
+ const number4 = build?.number ?? CAREFUL_START_AGENTS;
26433
27192
  const decision = paceOf({ number: number4, keepFreePercent: basis.keepFreePercent, learned, reported, now });
26434
27193
  const settled2 = {
26435
27194
  pace: decision.pace,
@@ -26438,12 +27197,14 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26438
27197
  windowKind: decision.windowKind,
26439
27198
  until: decision.until === null ? null : decision.until.toISOString(),
26440
27199
  at: now.toISOString(),
26441
- offeredFor: offeredForNow(stored.accounts[key]?.offeredFor, null, now)
27200
+ offeredFor: offeredForNow(stored.accounts[key]?.offeredFor, null, now),
27201
+ told: { pace: decision.pace, allowance: decision.allowance, windowKind: decision.windowKind }
26442
27202
  };
26443
- const text = paceChangeText(before, settled2, working, now, basis.keepFreePercent);
26444
- const offer = text === null ? offerText(settled2, learned, reported, now, basis.keepFreePercent) : null;
27203
+ const told = before === null || before.told === void 0 ? before : { ...before, ...before.told };
27204
+ const text = paceChangeText(told, settled2, working, now, basis.keepFreePercent);
27205
+ const offer = text === null && build !== null ? offerText(settled2, learned, reported, now, basis.keepFreePercent) : null;
26445
27206
  const after = offer === null ? settled2 : { ...settled2, offeredFor: offeredForNow(settled2.offeredFor, offer.resetsAt, now) };
26446
- write(dir, key, after, read !== null ? basis : null);
27207
+ write2(dir, key, after, read !== null ? basis : null);
26447
27208
  if (text !== null)
26448
27209
  reply.notice(text);
26449
27210
  else if (offer !== null)
@@ -26494,7 +27255,7 @@ function pruneStarts(file2, nowMs) {
26494
27255
  }
26495
27256
  return out;
26496
27257
  }
26497
- function sleepSync2(ms) {
27258
+ function sleepSync3(ms) {
26498
27259
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
26499
27260
  }
26500
27261
  function underStartsLock(dir, giveUpMs, fn) {
@@ -26503,7 +27264,7 @@ function underStartsLock(dir, giveUpMs, fn) {
26503
27264
  while (!tryTakeLockFile(lock, PACE_STARTS_LOCK_STALE_MS)) {
26504
27265
  if (Date.now() >= giveUpAt)
26505
27266
  return void 0;
26506
- sleepSync2(STARTS_LOCK_POLL_MS);
27267
+ sleepSync3(STARTS_LOCK_POLL_MS);
26507
27268
  }
26508
27269
  try {
26509
27270
  return fn();
@@ -26598,9 +27359,9 @@ function paceHoldText(input, toolInput, deps = {}) {
26598
27359
  }
26599
27360
 
26600
27361
  // dist/risky/facts.js
26601
- import { existsSync as existsSync7, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync21, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
26602
- import { homedir as homedir9, tmpdir as tmpdir2 } from "node:os";
26603
- 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";
27362
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync21, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
27363
+ import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
27364
+ 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";
26604
27365
 
26605
27366
  // dist/risky/shell.js
26606
27367
  var MAX_DEPTH = 8;
@@ -27780,7 +28541,7 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27780
28541
  }
27781
28542
  vars.enter(own);
27782
28543
  const simple = expandCommand(raw, vars);
27783
- const folder = folderIn(simple);
28544
+ const folder2 = folderIn(simple);
27784
28545
  for (const m of recogniseSimple(simple)) {
27785
28546
  const trusted = m.type === "shrink-record" && m.change.kind === "saved" && !savedTrusted ? { ...m, change: { kind: "emptied" } } : m;
27786
28547
  let shown2 = trusted;
@@ -27796,7 +28557,7 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27796
28557
  }
27797
28558
  matches.push({
27798
28559
  ...shown2,
27799
- ...folder === void 0 ? {} : { folder },
28560
+ ...folder2 === void 0 ? {} : { folder: folder2 },
27800
28561
  ...made.size === 0 ? {} : { made: new Map(made) },
27801
28562
  ...madeInChain.size === 0 ? {} : { madeInChain: new Set(madeInChain) },
27802
28563
  ...removedInChain.size === 0 ? {} : { removedInChain: new Set(removedInChain) },
@@ -27804,9 +28565,9 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27804
28565
  ...vars.stoppedIf.length === 0 ? {} : { stoppedIf: [...vars.stoppedIf] }
27805
28566
  });
27806
28567
  }
27807
- if (surelyRuns(simple, own) && (folder === void 0 || folder.known)) {
28568
+ if (surelyRuns(simple, own) && (folder2 === void 0 || folder2.known)) {
27808
28569
  for (const p of pathsMade(simple)) {
27809
- const key = madeKey(folder?.dirs ?? [], p.word);
28570
+ const key = madeKey(folder2?.dirs ?? [], p.word);
27810
28571
  made.set(key, p.by);
27811
28572
  if (simple.next === "&&")
27812
28573
  madeInChain.add(key);
@@ -27814,12 +28575,12 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27814
28575
  if (simple.next === "&&" && simple.words[0]?.text === "rmdir") {
27815
28576
  for (const w of simple.words.slice(1))
27816
28577
  if (w.literal && !w.text.startsWith("-"))
27817
- removedInChain.add(madeKey(folder?.dirs ?? [], w));
28578
+ removedInChain.add(madeKey(folder2?.dirs ?? [], w));
27818
28579
  }
27819
28580
  }
27820
28581
  const nextCmd = commands[i + 1];
27821
28582
  const asJoined = simple.next !== void 0 && (nextCmd === void 0 || scopeKey(nextCmd) !== scopeKey(simple)) ? { ...simple, next: void 0 } : simple;
27822
- const after = folderAfter(folder, asJoined, own);
28583
+ const after = folderAfter(folder2, asJoined, own);
27823
28584
  folders.set(scopeKey(simple), after);
27824
28585
  vars.guard = chainCds === null ? null : [...chainCds];
27825
28586
  vars.update(raw, own);
@@ -28812,7 +29573,7 @@ var Env = class {
28812
29573
  constructor(ctx) {
28813
29574
  this.projectDir = realOrSelf(resolve6(ctx.projectDir));
28814
29575
  this.cwd = realOrSelf(resolve6(this.projectDir, ctx.cwd));
28815
- this.home = realOrSelf(resolve6(ctx.home ?? homedir9()));
29576
+ this.home = realOrSelf(resolve6(ctx.home ?? homedir10()));
28816
29577
  this.timeoutMs = ctx.gitTimeoutMs ?? FACTS_GIT_TIMEOUT_MS;
28817
29578
  this.tempDir = realOrSelf(resolve6(ctx.tempDir ?? tmpdir2()));
28818
29579
  const roots = ctx.tempRoots ?? [this.tempDir, tmpdir2(), ...TEMP_ROOTS];
@@ -28836,7 +29597,7 @@ var Env = class {
28836
29597
  return this.description;
28837
29598
  this.description = null;
28838
29599
  try {
28839
- const raw = JSON.parse(readFileSync21(join14(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
29600
+ const raw = JSON.parse(readFileSync21(join15(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
28840
29601
  const checked = checkSystemDescription(raw);
28841
29602
  if (checked.ok)
28842
29603
  this.description = checked.description;
@@ -28900,18 +29661,18 @@ function resolveHome(text, home, base) {
28900
29661
  if (text === "~")
28901
29662
  return home;
28902
29663
  if (text.startsWith("~/"))
28903
- return join14(home, text.slice(2));
29664
+ return join15(home, text.slice(2));
28904
29665
  return resolve6(base, text);
28905
29666
  }
28906
29667
  function realTarget(abs) {
28907
29668
  const parent = dirname6(abs);
28908
29669
  if (parent === abs)
28909
29670
  return abs;
28910
- return join14(realOrSelf(parent), pathBasename(abs));
29671
+ return join15(realOrSelf(parent), pathBasename(abs));
28911
29672
  }
28912
29673
  function wordPath(word, base, env) {
28913
29674
  if (word.temp !== void 0) {
28914
- const root = word.temp.base === "mktemp" ? join14(env.tempDir, ".halfcycle-mktemp-new") : env.tempDir;
29675
+ const root = word.temp.base === "mktemp" ? join15(env.tempDir, ".halfcycle-mktemp-new") : env.tempDir;
28915
29676
  return resolve6(`${root}${word.temp.suffix}`);
28916
29677
  }
28917
29678
  return word.literal ? resolveHome(word.text, env.home, base) : null;
@@ -29057,7 +29818,7 @@ function globPath(abs) {
29057
29818
  const next = [];
29058
29819
  if (!/[*?[]/.test(segment)) {
29059
29820
  for (const c of current)
29060
- next.push(join14(c, segment));
29821
+ next.push(join15(c, segment));
29061
29822
  } else {
29062
29823
  const re = segmentPattern(segment);
29063
29824
  for (const c of current) {
@@ -29071,7 +29832,7 @@ function globPath(abs) {
29071
29832
  if (n.startsWith(".") && !segment.startsWith("."))
29072
29833
  continue;
29073
29834
  if (re.test(n))
29074
- next.push(join14(c, n));
29835
+ next.push(join15(c, n));
29075
29836
  }
29076
29837
  }
29077
29838
  }
@@ -29199,7 +29960,7 @@ function findKeeps(filter, roots, repoRoot) {
29199
29960
  const names = filter.names.map((n) => findPattern(n.pattern, n.fold, false));
29200
29961
  const excluded = filter.excluded.map((p) => findPattern(p, false, true));
29201
29962
  const steps = (rel) => {
29202
- const abs = join14(repoRoot, rel);
29963
+ const abs = join15(repoRoot, rel);
29203
29964
  const out = [];
29204
29965
  for (const r of roots) {
29205
29966
  if (!isInside(abs, r.abs))
@@ -29256,11 +30017,11 @@ function walkRoots(ignoredFolders, specs) {
29256
30017
  const under2 = (child, parent) => parent === "." || child === parent || child.startsWith(`${parent}/`);
29257
30018
  const roots = /* @__PURE__ */ new Set();
29258
30019
  for (const raw of ignoredFolders) {
29259
- const folder = raw.replace(/\/+$/, "");
30020
+ const folder2 = raw.replace(/\/+$/, "");
29260
30021
  for (const spec of specs) {
29261
- if (under2(folder, spec))
29262
- roots.add(folder);
29263
- else if (under2(spec, folder))
30022
+ if (under2(folder2, spec))
30023
+ roots.add(folder2);
30024
+ else if (under2(spec, folder2))
29264
30025
  roots.add(spec);
29265
30026
  }
29266
30027
  }
@@ -29271,9 +30032,9 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
29271
30032
  const out = [];
29272
30033
  const stack = [];
29273
30034
  for (const f of folders) {
29274
- if (isDirectory(join14(repoRoot, f)))
30035
+ if (isDirectory(join15(repoRoot, f)))
29275
30036
  stack.push(f);
29276
- else if (pathExists(join14(repoRoot, f)) && isCredentialPath(f))
30037
+ else if (pathExists(join15(repoRoot, f)) && isCredentialPath(f))
29277
30038
  out.push(f);
29278
30039
  }
29279
30040
  let seen = 0;
@@ -29281,7 +30042,7 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
29281
30042
  const rel = stack.pop();
29282
30043
  let entries;
29283
30044
  try {
29284
- entries = readdirSync9(join14(repoRoot, rel), { withFileTypes: true });
30045
+ entries = readdirSync9(join15(repoRoot, rel), { withFileTypes: true });
29285
30046
  } catch {
29286
30047
  continue;
29287
30048
  }
@@ -29439,7 +30200,7 @@ function closeFoldsAll(targets, env) {
29439
30200
  return false;
29440
30201
  let at = root;
29441
30202
  for (const seg of rel.split("/")) {
29442
- at = join14(at, seg);
30203
+ at = join15(at, seg);
29443
30204
  try {
29444
30205
  if (lstatSync3(at).isSymbolicLink())
29445
30206
  return false;
@@ -29672,7 +30433,7 @@ function worktreeFiles(m, where, env) {
29672
30433
  if (path === null)
29673
30434
  return null;
29674
30435
  const abs = realOrSelf(path);
29675
- if (!existsSync7(abs))
30436
+ if (!existsSync8(abs))
29676
30437
  return [];
29677
30438
  const top = env.repoRoot(abs);
29678
30439
  if (top === null)
@@ -30022,7 +30783,7 @@ function publishFacts(m, env) {
30022
30783
  const start = startFolder(m, env);
30023
30784
  if (start !== null && PACKAGE_JSON_PROGRAMS.has(m.program)) {
30024
30785
  try {
30025
- const pkg = JSON.parse(readFileSync21(join14(start, "package.json"), "utf-8"));
30786
+ const pkg = JSON.parse(readFileSync21(join15(start, "package.json"), "utf-8"));
30026
30787
  if (typeof pkg["name"] === "string" && typeof pkg["version"] === "string" && pkg["name"] !== "" && pkg["version"] !== "") {
30027
30788
  name = pkg["name"];
30028
30789
  version2 = pkg["version"];
@@ -30141,17 +30902,17 @@ function recordFileAt(abs, env) {
30141
30902
  const row = recordPatternFor(rel);
30142
30903
  return row !== void 0 && row.shrinkAsked !== false ? { abs, root, rel } : null;
30143
30904
  }
30144
- function recordFilesUnder(folder, env) {
30145
- const root = recordRoot(folder, env);
30905
+ function recordFilesUnder(folder2, env) {
30906
+ const root = recordRoot(folder2, env);
30146
30907
  if (root === null)
30147
30908
  return [];
30148
- const relFolder = toSlash(relative7(root, folder));
30909
+ const relFolder = toSlash(relative7(root, folder2));
30149
30910
  const starts = /* @__PURE__ */ new Set();
30150
30911
  for (const { pattern } of PROJECT_RECORD_PATTERNS) {
30151
30912
  if (relFolder === "" || patternOwns(relFolder, pattern))
30152
- starts.add(join14(root, pattern));
30913
+ starts.add(join15(root, pattern));
30153
30914
  else if (patternOwns(pattern, relFolder))
30154
- starts.add(folder);
30915
+ starts.add(folder2);
30155
30916
  }
30156
30917
  const out = [];
30157
30918
  const stack = [...starts];
@@ -30175,7 +30936,7 @@ function recordFilesUnder(folder, env) {
30175
30936
  continue;
30176
30937
  }
30177
30938
  for (const n of names)
30178
- stack.push(join14(p, n));
30939
+ stack.push(join15(p, n));
30179
30940
  } else if (st.isFile()) {
30180
30941
  const r = recordFileAt(p, env);
30181
30942
  if (r !== null)
@@ -30258,7 +31019,11 @@ var PERSON_HOLD_LINE = "Halfcycle paused a risky step before it ran. Nothing cha
30258
31019
  var HELD_STEP_OPENER = "The step was: ";
30259
31020
  var PRECAUTION_OPENER = "It was stopped as a precaution: ";
30260
31021
  var ALLOW_REPLY = "allow";
30261
- 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.`;
31022
+ function holdAgentInstruction(stepNumber) {
31023
+ const reply = stepNumber === null ? ALLOW_REPLY : `${ALLOW_REPLY} ${String(stepNumber)}`;
31024
+ 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.`;
31025
+ }
31026
+ var HOLD_AGENT_INSTRUCTION = holdAgentInstruction(null);
30262
31027
  var NOTHING_LOST_ON_NO = "Nothing is lost if you say no.";
30263
31028
  var IF_YES = "If you say yes, ";
30264
31029
  var DESCRIBED_TOOLS = /* @__PURE__ */ new Set(["Bash", "Agent"]);
@@ -30294,6 +31059,9 @@ function questionFor(facts, description) {
30294
31059
  const shown2 = staked.slice(0, MAX_LISTED).map((f) => `${whatOf(f)} ${IF_YES}${lostOf(f)}`);
30295
31060
  return `${PAUSED_OPENER.trimEnd().slice(0, -1)}. ${shown2.join(" ")}${moreLine(staked.length - shown2.length)} ${reason} ${NOTHING_LOST_ON_NO}`;
30296
31061
  }
31062
+ function heldAgentText(held, stepNumber) {
31063
+ return `${held.facts} ${holdAgentInstruction(stepNumber)}`;
31064
+ }
30297
31065
  function refusalFor(facts, description) {
30298
31066
  const staked = orderedStaked(facts);
30299
31067
  if (staked.length === 0)
@@ -30304,9 +31072,11 @@ function refusalFor(facts, description) {
30304
31072
  return heldStep(steps, description, shown2.map(whyOf).join("; "));
30305
31073
  }
30306
31074
  function heldStep(steps, description, why) {
31075
+ const facts = `${HELD_STEP_OPENER}${steps}. ${reasonSentence(description)} ${PRECAUTION_OPENER}${why}.`;
30307
31076
  return {
30308
31077
  person: PERSON_HOLD_LINE,
30309
- agent: `${HELD_STEP_OPENER}${steps}. ${reasonSentence(description)} ${PRECAUTION_OPENER}${why}. ${HOLD_AGENT_INSTRUCTION}`,
31078
+ agent: `${facts} ${HOLD_AGENT_INSTRUCTION}`,
31079
+ facts,
30310
31080
  step: steps
30311
31081
  };
30312
31082
  }
@@ -30721,14 +31491,85 @@ function isOneCall(call) {
30721
31491
 
30722
31492
  // dist/risky/allow-once.js
30723
31493
  import { createHash as createHash5 } from "node:crypto";
30724
- import { appendFileSync as appendFileSync4, readFileSync as readFileSync22, renameSync as renameSync11, unlinkSync as unlinkSync9, writeFileSync as writeFileSync13 } from "node:fs";
31494
+ 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";
31495
+ import { isAbsolute as isAbsolute5, relative as relative8, resolve as resolve7, sep as sep6 } from "node:path";
30725
31496
  var ALLOW_ONCE_MS = 5 * 60 * 1e3;
30726
31497
  var HELD_STEP_MAX_AGE_MS = 60 * 60 * 1e3;
31498
+ var NUMBERS_WAIT_MS = 50;
31499
+ var NUMBERS_LOCK_STALE_MS = 5e3;
31500
+ var NUMBERS_LOCK_POLL_MS = 2;
31501
+ var COPY_GIT_TIMEOUT_MS = 3e3;
31502
+ var ALLOW_NUMBER = new RegExp(`^${ALLOW_REPLY}(?: +([0-9]{1,4}))?$`);
30727
31503
  var STEP_MAX_LENGTH = 1e3;
30728
- function isAllowReply(prompt) {
30729
- return typeof prompt === "string" && prompt.trim().toLowerCase() === ALLOW_REPLY;
31504
+ function allowReplyOf(prompt) {
31505
+ if (typeof prompt !== "string")
31506
+ return null;
31507
+ const m = ALLOW_NUMBER.exec(prompt.trim().toLowerCase());
31508
+ if (m === null)
31509
+ return null;
31510
+ return { number: m[1] === void 0 ? null : Number(m[1]) };
31511
+ }
31512
+ var FOLDER_WORD = String.raw`(?:([^\s'"$\x60\\~*?\[{()<>|&;\-][^\s'"$\x60\\~*?\[{()<>|&;]*)|'([^'"$\x60\\\-][^'"$\x60\\]*)'|"([^'"$\x60\\\-][^'"$\x60\\]*)")`;
31513
+ var CD_PREFIX = new RegExp(String.raw`^\s*cd[ \t]+${FOLDER_WORD}[ \t]*(?:&&|;)`);
31514
+ var GIT_C_PREFIX = new RegExp(String.raw`^\s*git[ \t]+-C[ \t]+${FOLDER_WORD}[ \t]+`);
31515
+ function folderPrefixOf(command) {
31516
+ const cd = CD_PREFIX.exec(command);
31517
+ if (cd !== null) {
31518
+ const rest = command.slice(cd[0].length).replace(/^\s+/, "");
31519
+ const commands = commandsOf(rest);
31520
+ if (commands === null || commands.some((c) => FOLDER_CHANGERS.has(c.words[0]?.text ?? "")))
31521
+ return null;
31522
+ return { folder: folderOf(cd), command: rest };
31523
+ }
31524
+ const gitC = GIT_C_PREFIX.exec(command);
31525
+ if (gitC !== null) {
31526
+ const compared = `git ${command.slice(gitC[0].length)}`;
31527
+ const commands = commandsOf(compared);
31528
+ if (commands === null || commands.filter((c) => c.scope === void 0).length !== 1)
31529
+ return null;
31530
+ return { folder: folderOf(gitC), command: compared };
31531
+ }
31532
+ return null;
31533
+ }
31534
+ function commandsOf(text) {
31535
+ try {
31536
+ return splitCommand(text);
31537
+ } catch {
31538
+ return null;
31539
+ }
30730
31540
  }
30731
- function stepKey(input, staked) {
31541
+ function folderOf(m) {
31542
+ return m[1] ?? m[2] ?? m[3] ?? "";
31543
+ }
31544
+ function actingFolderOf(toolName, cwd, rest) {
31545
+ const command = rest["command"];
31546
+ if (toolName !== "Bash" || typeof command !== "string")
31547
+ return cwd;
31548
+ const prefix = folderPrefixOf(command);
31549
+ if (prefix === null)
31550
+ return cwd;
31551
+ if (!isAbsolute5(prefix.folder) && !isAbsolute5(cwd))
31552
+ return cwd;
31553
+ const named = isAbsolute5(prefix.folder) ? prefix.folder : resolve7(cwd, prefix.folder);
31554
+ if (!isFolder2(named))
31555
+ return cwd;
31556
+ rest["command"] = prefix.command;
31557
+ return named;
31558
+ }
31559
+ function copyOf(folder2, timeoutMs) {
31560
+ if (folder2 === "")
31561
+ return { top: "", within: "" };
31562
+ const real = realOrSelf2(folder2);
31563
+ let top = real;
31564
+ if (isFolder2(real)) {
31565
+ const r = readGitForFacts(["rev-parse", "--show-toplevel"], real, timeoutMs);
31566
+ if (r.ok && r.value.trim() !== "")
31567
+ top = realOrSelf2(r.value.trim());
31568
+ }
31569
+ const within2 = relative8(top, real).split(sep6).join("/");
31570
+ return { top, within: within2 };
31571
+ }
31572
+ function stepKey(input, staked, deps = {}) {
30732
31573
  const toolName = input["tool_name"];
30733
31574
  const toolInput = input["tool_input"];
30734
31575
  if (typeof toolName !== "string" || toolName === "")
@@ -30739,7 +31580,17 @@ function stepKey(input, staked) {
30739
31580
  if (DESCRIBED_TOOLS.has(toolName))
30740
31581
  delete rest["description"];
30741
31582
  const cwd = typeof input["cwd"] === "string" ? input["cwd"] : "";
30742
- return createHash5("sha256").update(canonical([toolName, cwd, rest, staked])).digest("hex");
31583
+ const acting = actingFolderOf(toolName, cwd, rest);
31584
+ const { top, within: within2 } = copyOf(acting, deps.gitTimeoutMs ?? COPY_GIT_TIMEOUT_MS);
31585
+ const facts = staked.map(withoutMatch);
31586
+ return createHash5("sha256").update(canonical([toolName, top, within2, rest, facts])).digest("hex");
31587
+ }
31588
+ function withoutMatch(fact) {
31589
+ if (typeof fact !== "object" || fact === null || Array.isArray(fact))
31590
+ return fact;
31591
+ const { match: _match, ...others } = fact;
31592
+ void _match;
31593
+ return others;
30743
31594
  }
30744
31595
  function canonical(value) {
30745
31596
  if (Array.isArray(value))
@@ -30750,37 +31601,166 @@ function canonical(value) {
30750
31601
  }
30751
31602
  return JSON.stringify(value) ?? "null";
30752
31603
  }
30753
- function recordHeld(sessionId, key, deps = {}, step = null) {
31604
+ function isFolder2(path) {
31605
+ try {
31606
+ return statSync8(path).isDirectory();
31607
+ } catch {
31608
+ return false;
31609
+ }
31610
+ }
31611
+ function realOrSelf2(path) {
31612
+ try {
31613
+ return realpathSync8(path);
31614
+ } catch {
31615
+ return path;
31616
+ }
31617
+ }
31618
+ function numberStep(sessionId, key, deps = {}) {
31619
+ const paths = allowOncePaths(sessionId, deps.dir);
31620
+ if (paths === null)
31621
+ return null;
31622
+ const lock = `${paths.numbers}${STEP_NUMBERS_LOCK_SUFFIX}`;
31623
+ const giveUpAt = Date.now() + NUMBERS_WAIT_MS;
31624
+ while (!tryTakeLockFile(lock, NUMBERS_LOCK_STALE_MS)) {
31625
+ if (Date.now() >= giveUpAt)
31626
+ return null;
31627
+ sleepSync4(NUMBERS_LOCK_POLL_MS);
31628
+ }
31629
+ try {
31630
+ const numbers = readNumbers(paths.numbers);
31631
+ if (numbers === null)
31632
+ return null;
31633
+ const kept = numbers.steps.get(key);
31634
+ if (kept !== void 0)
31635
+ return kept;
31636
+ const n = numbers.next;
31637
+ const steps = new Map(numbers.steps).set(key, n);
31638
+ return writeNumbers(paths.numbers, { next: n + 1, steps }) ? n : null;
31639
+ } catch {
31640
+ return null;
31641
+ } finally {
31642
+ releaseLockFile(lock);
31643
+ }
31644
+ }
31645
+ function numberKept(path, key) {
31646
+ try {
31647
+ return readNumbers(path)?.steps.get(key) ?? null;
31648
+ } catch {
31649
+ return null;
31650
+ }
31651
+ }
31652
+ function readNumbers(path) {
31653
+ let text;
31654
+ try {
31655
+ text = readFileSync22(path, "utf8");
31656
+ } catch (err) {
31657
+ return err.code === "ENOENT" ? { next: 1, steps: /* @__PURE__ */ new Map() } : null;
31658
+ }
31659
+ let parsed;
31660
+ try {
31661
+ parsed = JSON.parse(text);
31662
+ } catch {
31663
+ return null;
31664
+ }
31665
+ if (typeof parsed !== "object" || parsed === null)
31666
+ return null;
31667
+ const o = parsed;
31668
+ const next = o["next"];
31669
+ const steps = o["steps"];
31670
+ if (!isWholeNumber2(next) || typeof steps !== "object" || steps === null || Array.isArray(steps))
31671
+ return null;
31672
+ const out = /* @__PURE__ */ new Map();
31673
+ for (const [k, v] of Object.entries(steps)) {
31674
+ if (!/^[0-9a-f]{64}$/.test(k) || !isWholeNumber2(v) || v >= next)
31675
+ return null;
31676
+ out.set(k, v);
31677
+ }
31678
+ return { next, steps: out };
31679
+ }
31680
+ function writeNumbers(path, numbers) {
31681
+ const body = JSON.stringify({ next: numbers.next, steps: Object.fromEntries(numbers.steps) });
31682
+ return writeWhole2(path, body);
31683
+ }
31684
+ function isWholeNumber2(v) {
31685
+ return typeof v === "number" && Number.isSafeInteger(v) && v >= 1;
31686
+ }
31687
+ function sleepSync4(ms) {
31688
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
31689
+ }
31690
+ function recordHeld(sessionId, key, deps = {}, step = null, stepNumber = null) {
30754
31691
  const paths = allowOncePaths(sessionId, deps.dir);
30755
31692
  if (paths === null)
30756
31693
  return;
30757
- const record2 = { version: 1, session: sessionId, key, at: nowOf(deps), ...isStep(step) ? { step } : {} };
31694
+ const record2 = {
31695
+ version: 1,
31696
+ session: sessionId,
31697
+ key,
31698
+ at: nowOf(deps),
31699
+ ...isStep(step) ? { step } : {},
31700
+ ...isWholeNumber2(stepNumber) ? { number: stepNumber } : {}
31701
+ };
30758
31702
  try {
30759
- appendFileSync4(paths.held, `${JSON.stringify(record2)}
31703
+ appendFileSync5(paths.held, `${JSON.stringify(record2)}
30760
31704
  `, { mode: 384 });
30761
31705
  } catch {
30762
31706
  return;
30763
31707
  }
30764
31708
  }
30765
- function armAllowStep(sessionId, deps = {}) {
31709
+ function armAllowStep(sessionId, deps = {}, stepNumber = null) {
31710
+ const none = (outcome) => ({ outcome, step: null, paused: [] });
30766
31711
  try {
30767
31712
  const paths = allowOncePaths(sessionId, deps.dir);
30768
31713
  if (paths === null)
30769
- return { outcome: "nothing-held", step: null };
31714
+ return none(stepNumber === null ? "nothing-held" : "not-paused");
31715
+ const session = sessionId;
30770
31716
  const now = nowOf(deps);
30771
- const held = claimHeld(paths.held, sessionId, now);
30772
- const keys = [...new Set(held.map((r) => r.key))];
30773
- if (keys.length === 0)
30774
- return { outcome: "nothing-held", step: null };
30775
- if (keys.length > 1)
30776
- return { outcome: "several-held", step: null };
30777
- const step = held[held.length - 1].step ?? null;
30778
- const saved = writeRecord(paths.allow, { version: 1, session: sessionId, key: keys[0], at: now });
30779
- return saved ? { outcome: "armed", step } : { outcome: "not-saved", step: null };
31717
+ const held = claimHeld(paths.held, session, now);
31718
+ const steps = distinctSteps(held, paths.numbers);
31719
+ const arm = (key, step) => {
31720
+ const saved = writeRecord(paths.allow, { version: 1, session, key, at: now });
31721
+ if (!saved) {
31722
+ writeBack(paths.held, held);
31723
+ return none("not-saved");
31724
+ }
31725
+ writeBack(paths.held, held.filter((r) => r.key !== key));
31726
+ return { outcome: "armed", step, paused: [] };
31727
+ };
31728
+ if (stepNumber !== null) {
31729
+ const named = steps.find((s) => s.number === stepNumber);
31730
+ if (named === void 0) {
31731
+ writeBack(paths.held, held);
31732
+ return none("not-paused");
31733
+ }
31734
+ return arm(named.key, named.step);
31735
+ }
31736
+ if (steps.length === 0)
31737
+ return none("nothing-held");
31738
+ if (steps.length === 1)
31739
+ return arm(steps[0].key, steps[0].step);
31740
+ writeBack(paths.held, held);
31741
+ 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));
31742
+ return { outcome: "several-held", step: null, paused };
30780
31743
  } catch {
30781
- return { outcome: "not-saved", step: null };
31744
+ return none("not-saved");
30782
31745
  }
30783
31746
  }
31747
+ function distinctSteps(held, numbersPath) {
31748
+ const order = [];
31749
+ const latest = /* @__PURE__ */ new Map();
31750
+ const numbered = /* @__PURE__ */ new Map();
31751
+ for (const r of held) {
31752
+ if (!latest.has(r.key))
31753
+ order.push(r.key);
31754
+ latest.set(r.key, r);
31755
+ if (r.number !== void 0)
31756
+ numbered.set(r.key, r.number);
31757
+ }
31758
+ return order.map((key) => ({
31759
+ key,
31760
+ number: numbered.get(key) ?? numberKept(numbersPath, key),
31761
+ step: latest.get(key).step ?? null
31762
+ }));
31763
+ }
30784
31764
  function claimHeld(path, session, now) {
30785
31765
  const claimed = `${path}.${process.pid}.${now}.claim`;
30786
31766
  try {
@@ -30803,6 +31783,16 @@ function claimHeld(path, session, now) {
30803
31783
  }
30804
31784
  return records;
30805
31785
  }
31786
+ function writeBack(path, records) {
31787
+ if (records.length === 0)
31788
+ return;
31789
+ try {
31790
+ appendFileSync5(path, records.map((r) => `${JSON.stringify(r)}
31791
+ `).join(""), { mode: 384 });
31792
+ } catch {
31793
+ return;
31794
+ }
31795
+ }
30806
31796
  function isStep(v) {
30807
31797
  return typeof v === "string" && v !== "" && v.length <= STEP_MAX_LENGTH;
30808
31798
  }
@@ -30857,12 +31847,23 @@ function parseRecord(text, session) {
30857
31847
  if (typeof r["at"] !== "number")
30858
31848
  return null;
30859
31849
  const step = r["step"];
30860
- return { version: 1, session, key: r["key"], at: r["at"], ...isStep(step) ? { step } : {} };
31850
+ const n = r["number"];
31851
+ return {
31852
+ version: 1,
31853
+ session,
31854
+ key: r["key"],
31855
+ at: r["at"],
31856
+ ...isStep(step) ? { step } : {},
31857
+ ...isWholeNumber2(n) ? { number: n } : {}
31858
+ };
30861
31859
  }
30862
31860
  function writeRecord(path, record2) {
31861
+ return writeWhole2(path, JSON.stringify(record2));
31862
+ }
31863
+ function writeWhole2(path, body) {
30863
31864
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
30864
31865
  try {
30865
- writeFileSync13(tmp, JSON.stringify(record2), { mode: 384 });
31866
+ writeFileSync13(tmp, body, { mode: 384 });
30866
31867
  renameSync11(tmp, path);
30867
31868
  return true;
30868
31869
  } catch {
@@ -30960,7 +31961,7 @@ function decideAutonomous(facts, mayPublishNow) {
30960
31961
 
30961
31962
  // dist/risky/own-copy.js
30962
31963
  import { readFileSync as readFileSync23 } from "node:fs";
30963
- import { join as join15, posix as posix2, win32 as win322 } from "node:path";
31964
+ import { join as join16, posix as posix2, win32 as win322 } from "node:path";
30964
31965
  var OWN_COPY_START_TOOL = "Agent";
30965
31966
  var OWN_COPY_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
30966
31967
  var OWN_COPY_ISOLATION = "worktree";
@@ -31021,7 +32022,7 @@ function typeCanChangeFiles(subagentType, deps) {
31021
32022
  return true;
31022
32023
  if (AGENT_FILE_NAME.test(type)) {
31023
32024
  for (const base of [deps.projectDir, deps.home]) {
31024
- const text = readOr(deps, join15(base, ".claude", "agents", `${type}.md`));
32025
+ const text = readOr(deps, join16(base, ".claude", "agents", `${type}.md`));
31025
32026
  if (text === null)
31026
32027
  continue;
31027
32028
  const tools = frontmatterTools(text);
@@ -31127,31 +32128,31 @@ function judgeChange(input, call, deps) {
31127
32128
 
31128
32129
  // dist/risky/save.js
31129
32130
  import { randomBytes } from "node:crypto";
31130
- 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";
31131
- import { homedir as homedir10 } from "node:os";
31132
- import { dirname as dirname7, isAbsolute as isAbsolute5, join as join16, resolve as resolve7 } from "node:path";
32131
+ 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";
32132
+ import { homedir as homedir11 } from "node:os";
32133
+ import { dirname as dirname7, isAbsolute as isAbsolute6, join as join17, resolve as resolve8 } from "node:path";
31133
32134
  var SAVE_LIMIT_BYTES = 2e8;
31134
32135
  var SAVE_BUDGET_MS = 5e3;
31135
32136
  function notSaved(reason) {
31136
32137
  return { saved: false, reason };
31137
32138
  }
31138
- function savedFolderOf(engagementId, home = homedir10()) {
32139
+ function savedFolderOf(engagementId, home = homedir11()) {
31139
32140
  if (!isEngagementId(engagementId))
31140
32141
  return null;
31141
- return join16(home, HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
32142
+ return join17(home, HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, SAVED_DIR_NAME);
31142
32143
  }
31143
32144
  function saveFirst(staked, ctx) {
31144
32145
  const now = ctx.now ?? Date.now;
31145
32146
  const deadline = now() + (ctx.budgetMs ?? SAVE_BUDGET_MS);
31146
- const folder = savedFolderOf(ctx.engagementId, ctx.home);
31147
- if (folder === null)
32147
+ const folder2 = savedFolderOf(ctx.engagementId, ctx.home);
32148
+ if (folder2 === null)
31148
32149
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
31149
32150
  const plan = planOf(staked);
31150
32151
  if (!plan.ok)
31151
32152
  return notSaved(plan.reason);
31152
32153
  const g = new SaveGit(plan.root, deadline, now);
31153
32154
  const top = g.run(["rev-parse", "--show-toplevel"]);
31154
- if (!top.ok || realOrSelf2(top.value.trim()) !== plan.root)
32155
+ if (!top.ok || realOrSelf3(top.value.trim()) !== plan.root)
31155
32156
  return notSaved(AUTONOMY_SAVE_NOT_COVERED_REASON);
31156
32157
  const head = g.run(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
31157
32158
  const headCommit2 = head.ok ? head.value.trim() : "";
@@ -31166,23 +32167,23 @@ function saveFirst(staked, ctx) {
31166
32167
  return notSaved(found2.reason);
31167
32168
  let saveDir = null;
31168
32169
  try {
31169
- const id = freshId(folder, ctx.newId);
32170
+ const id = freshId(folder2, ctx.newId);
31170
32171
  if (id === null)
31171
32172
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
31172
- saveDir = join16(folder, id);
32173
+ saveDir = join17(folder2, id);
31173
32174
  const repository = repositoryOf(g);
31174
32175
  if (repository === null)
31175
32176
  return failed(saveDir);
31176
- writeFileSync14(join16(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
32177
+ writeFileSync14(join17(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
31177
32178
  `, { mode: 384 });
31178
- const privateIndex = join16(saveDir, PRIVATE_INDEX_NAME);
32179
+ const privateIndex = join17(saveDir, PRIVATE_INDEX_NAME);
31179
32180
  const commit = saveCommit(id, headCommit2, found2.tracked, plan.lines, privateIndex, g, now());
31180
32181
  rmSync3(privateIndex, { force: true });
31181
32182
  if (commit === null)
31182
32183
  return failed(saveDir);
31183
- const copiesDir = join16(saveDir, SAVED_COPIES_DIR_NAME);
32184
+ const copiesDir = join17(saveDir, SAVED_COPIES_DIR_NAME);
31184
32185
  for (const rel of found2.copies) {
31185
- if (now() > deadline || !copyOne(join16(plan.root, rel), join16(copiesDir, rel)))
32186
+ if (now() > deadline || !copyOne(join17(plan.root, rel), join17(copiesDir, rel)))
31186
32187
  return failed(saveDir);
31187
32188
  }
31188
32189
  const description = savedSchema.safeParse({
@@ -31198,7 +32199,7 @@ function saveFirst(staked, ctx) {
31198
32199
  });
31199
32200
  if (!description.success)
31200
32201
  return failed(saveDir);
31201
- const file2 = join16(saveDir, SAVED_FILE_NAME);
32202
+ const file2 = join17(saveDir, SAVED_FILE_NAME);
31202
32203
  writeFileSync14(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
31203
32204
  `, { mode: 384 });
31204
32205
  renameSync12(`${file2}.part`, file2);
@@ -31229,7 +32230,7 @@ function planOf(staked) {
31229
32230
  const t = f.save;
31230
32231
  if (t === void 0)
31231
32232
  return { ok: false, reason: f.cannotTell === void 0 ? AUTONOMY_SAVE_NOT_COVERED_REASON : AUTONOMY_COULD_NOT_TELL_REASON };
31232
- const r = realOrSelf2(resolve7(t.repoRoot));
32233
+ const r = realOrSelf3(resolve8(t.repoRoot));
31233
32234
  if (root === null)
31234
32235
  root = r;
31235
32236
  else if (root !== r)
@@ -31248,7 +32249,7 @@ function planOf(staked) {
31248
32249
  function isInsideRel(p) {
31249
32250
  if (p === ".")
31250
32251
  return true;
31251
- if (p === "" || isAbsolute5(p) || p.includes("\\"))
32252
+ if (p === "" || isAbsolute6(p) || p.includes("\\"))
31252
32253
  return false;
31253
32254
  return p.split("/").every((s) => s !== "" && s !== "." && s !== "..");
31254
32255
  }
@@ -31278,7 +32279,7 @@ function filesOf(plan, g, deadline) {
31278
32279
  trackedAll.add(path);
31279
32280
  if (!reached(path, plan.specs))
31280
32281
  continue;
31281
- const st = lstatOrNull(join16(plan.root, path));
32282
+ const st = lstatOrNull(join17(plan.root, path));
31282
32283
  if (st !== null && (st.isFile() || st.isSymbolicLink()))
31283
32284
  tracked.add(path);
31284
32285
  }
@@ -31290,7 +32291,7 @@ function filesOf(plan, g, deadline) {
31290
32291
  const rel = stack.pop();
31291
32292
  if (rel === ".git")
31292
32293
  continue;
31293
- const abs = rel === "." ? plan.root : join16(plan.root, rel);
32294
+ const abs = rel === "." ? plan.root : join17(plan.root, rel);
31294
32295
  const st = lstatOrNull(abs);
31295
32296
  if (st === null)
31296
32297
  continue;
@@ -31328,13 +32329,13 @@ function copyOne(src, dst) {
31328
32329
  }
31329
32330
  copyFileSync(src, dst, fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE);
31330
32331
  chmodSync2(dst, st.mode & 4095);
31331
- return statSync8(dst).size === st.size;
32332
+ return statSync9(dst).size === st.size;
31332
32333
  }
31333
32334
  function saveCommit(id, head, tracked, lines2, privateIndex, g, at) {
31334
32335
  const indexPath = g.run(["rev-parse", "--git-path", "index"]);
31335
32336
  if (!indexPath.ok)
31336
32337
  return null;
31337
- const projectIndex = resolve7(g.root, indexPath.value.trim());
32338
+ const projectIndex = resolve8(g.root, indexPath.value.trim());
31338
32339
  const env = commitEnv(privateIndex, at);
31339
32340
  if (lstatOrNull(projectIndex) !== null)
31340
32341
  copyFileSync(projectIndex, privateIndex);
@@ -31396,14 +32397,14 @@ var SaveGit = class {
31396
32397
  });
31397
32398
  }
31398
32399
  };
31399
- function freshId(folder, newId) {
31400
- mkdirSync9(folder, { recursive: true, mode: 448 });
32400
+ function freshId(folder2, newId) {
32401
+ mkdirSync9(folder2, { recursive: true, mode: 448 });
31401
32402
  for (let i = 0; i < 5; i++) {
31402
32403
  const id = newId?.() ?? defaultId();
31403
32404
  if (!savedIdSchema.safeParse(id).success)
31404
32405
  return null;
31405
32406
  try {
31406
- mkdirSync9(join16(folder, id), { mode: 448 });
32407
+ mkdirSync9(join17(folder2, id), { mode: 448 });
31407
32408
  return id;
31408
32409
  } catch (e) {
31409
32410
  if (e.code !== "EEXIST")
@@ -31424,19 +32425,19 @@ function repositoryOf(g) {
31424
32425
  if (!r.ok)
31425
32426
  return null;
31426
32427
  const dir = r.value.trim();
31427
- return dir === "" ? null : realOrSelf2(resolve7(g.root, dir));
32428
+ return dir === "" ? null : realOrSelf3(resolve8(g.root, dir));
31428
32429
  }
31429
32430
  function markedRepository(dir) {
31430
32431
  try {
31431
- const text = readFileSync24(join16(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
32432
+ const text = readFileSync24(join17(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
31432
32433
  return text === "" ? null : text;
31433
32434
  } catch {
31434
32435
  return null;
31435
32436
  }
31436
32437
  }
31437
32438
  function pruneSaves(root, engagementId, ctx = {}) {
31438
- const folder = savedFolderOf(engagementId, ctx.home);
31439
- if (folder === null)
32439
+ const folder2 = savedFolderOf(engagementId, ctx.home);
32440
+ if (folder2 === null)
31440
32441
  return;
31441
32442
  const now = ctx.now ?? Date.now;
31442
32443
  const g = new SaveGit(root, ctx.deadline ?? now() + SAVE_BUDGET_MS, now);
@@ -31453,7 +32454,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
31453
32454
  const repository = repositoryOf(g);
31454
32455
  let names = [];
31455
32456
  try {
31456
- names = readdirSync10(folder);
32457
+ names = readdirSync10(folder2);
31457
32458
  } catch {
31458
32459
  names = [];
31459
32460
  }
@@ -31462,7 +32463,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
31462
32463
  if (!savedIdSchema.safeParse(id).success)
31463
32464
  continue;
31464
32465
  here.add(id);
31465
- const dir = join16(folder, id);
32466
+ const dir = join17(folder2, id);
31466
32467
  const st = lstatOrNull(dir);
31467
32468
  const answer = savedPruneAnswer({
31468
32469
  refMadeAt: refs.get(id) ?? null,
@@ -31491,9 +32492,9 @@ function lstatOrNull(p) {
31491
32492
  return null;
31492
32493
  }
31493
32494
  }
31494
- function realOrSelf2(p) {
32495
+ function realOrSelf3(p) {
31495
32496
  try {
31496
- return realpathSync8(p);
32497
+ return realpathSync9(p);
31497
32498
  } catch {
31498
32499
  return p;
31499
32500
  }
@@ -31502,12 +32503,12 @@ function realOrSelf2(p) {
31502
32503
  // dist/autonomy-state.js
31503
32504
  import { spawnSync as spawnSync2 } from "node:child_process";
31504
32505
  import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync25, renameSync as renameSync13, rmSync as rmSync4, writeFileSync as writeFileSync15 } from "node:fs";
31505
- import { homedir as homedir11, platform as platform2 } from "node:os";
31506
- import { dirname as dirname8, join as join17 } from "node:path";
32506
+ import { homedir as homedir12, platform as platform2 } from "node:os";
32507
+ import { dirname as dirname8, join as join18 } from "node:path";
31507
32508
  var AUTONOMY_STATE_FILENAME = "autonomy.json";
31508
32509
  var AUTONOMY_STATE_DEADLINE_MS = 1500;
31509
32510
  function autonomyStateFilePath(engagementId, home) {
31510
- return join17(home ?? homedir11(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, AUTONOMY_STATE_FILENAME);
32511
+ return join18(home ?? homedir12(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, AUTONOMY_STATE_FILENAME);
31511
32512
  }
31512
32513
  function readCachedAutonomy(engagementId, nowMs, home) {
31513
32514
  if (!isEngagementId(engagementId))
@@ -31535,6 +32536,7 @@ function confirmMayPublishNow(opts) {
31535
32536
  timeout: opts.timeoutMs,
31536
32537
  windowsHide: true
31537
32538
  });
32539
+ recordChildRequest(url2, ["Authorization"], r.status === 0);
31538
32540
  if (r.status !== 0 || typeof r.stdout !== "string")
31539
32541
  return false;
31540
32542
  const parsed = autonomyStateSchema.safeParse(JSON.parse(r.stdout));
@@ -31559,7 +32561,7 @@ function deleteAutonomyStateFile(engagementId, home) {
31559
32561
  }
31560
32562
  function writeAutonomyStateFile(engagementId, file2, home) {
31561
32563
  const target = autonomyStateFilePath(engagementId, home);
31562
- const temp = join17(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
32564
+ const temp = join18(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
31563
32565
  try {
31564
32566
  mkdirSync10(dirname8(target), { recursive: true, mode: 448 });
31565
32567
  writeFileSync15(temp, `${JSON.stringify(file2)}
@@ -31621,7 +32623,7 @@ async function cacheAutonomySetting(opts) {
31621
32623
  }
31622
32624
 
31623
32625
  // dist/risky/patch-calls.js
31624
- import { isAbsolute as isAbsolute6, resolve as resolve8 } from "node:path";
32626
+ import { isAbsolute as isAbsolute7, resolve as resolve9 } from "node:path";
31625
32627
  var SHELL_TOOL = "Bash";
31626
32628
  function toolCallsOf(input, profile) {
31627
32629
  const toolName = input["tool_name"];
@@ -31644,7 +32646,7 @@ function toolCallsOf(input, profile) {
31644
32646
  const ops = patchOperations(command);
31645
32647
  if (ops === null || ops.length === 0)
31646
32648
  return null;
31647
- const cwd = typeof input["cwd"] === "string" && isAbsolute6(input["cwd"]) ? input["cwd"] : null;
32649
+ const cwd = typeof input["cwd"] === "string" && isAbsolute7(input["cwd"]) ? input["cwd"] : null;
31648
32650
  const calls = [];
31649
32651
  for (const op of ops) {
31650
32652
  const made = callsFor(op, cwd);
@@ -31682,9 +32684,9 @@ function callsFor(op, cwd) {
31682
32684
  }
31683
32685
  }
31684
32686
  function placed(path, cwd) {
31685
- if (isAbsolute6(path))
31686
- return resolve8(path);
31687
- return cwd === null ? null : resolve8(cwd, path);
32687
+ if (isAbsolute7(path))
32688
+ return resolve9(path);
32689
+ return cwd === null ? null : resolve9(cwd, path);
31688
32690
  }
31689
32691
  function shellWord(text) {
31690
32692
  return `'${text.split("'").join("'\\''")}'`;
@@ -31692,7 +32694,7 @@ function shellWord(text) {
31692
32694
 
31693
32695
  // dist/hooks/pre-tool-use.js
31694
32696
  import { performance } from "node:perf_hooks";
31695
- import { homedir as homedir12 } from "node:os";
32697
+ import { homedir as homedir13 } from "node:os";
31696
32698
  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.";
31697
32699
  var PRE_TOOL_USE_LIMIT_MS = (HALFCYCLE_HOOK_WIRING.find((e) => e.event === "PreToolUse")?.timeout ?? 10) * 1e3;
31698
32700
  var AUTONOMY_REPLY_MARGIN_MS = 1e3;
@@ -31763,6 +32765,11 @@ function runPreToolUse(deps) {
31763
32765
  if (held !== null)
31764
32766
  return `${preToolUseReplyText("refuse", held)}
31765
32767
  `;
32768
+ recordSpawnedTask(call.toolInput, {
32769
+ projectDir: projectDirOf(deps),
32770
+ ...deps.env === void 0 ? {} : { env: deps.env },
32771
+ ...deps.home === void 0 ? {} : { home: deps.home }
32772
+ });
31766
32773
  }
31767
32774
  return own.kind === "unknown" ? ownCopyNotice(input, deps) : "";
31768
32775
  }
@@ -31771,16 +32778,19 @@ function runPreToolUse(deps) {
31771
32778
  return `${preToolUseReplyText("ask", answer.question)}
31772
32779
  `;
31773
32780
  }
32781
+ let agentText = answer.refusal.agent;
31774
32782
  if (allowableByTyping(call, answer.staked)) {
31775
- const key = stepKey(input, answer.staked);
32783
+ const key = stepKey(input, answer.staked, deps.gitTimeoutMs === void 0 ? {} : { gitTimeoutMs: deps.gitTimeoutMs });
31776
32784
  if (key !== null) {
31777
32785
  const allowOnce = { ...deps.liveness?.dir === void 0 ? {} : { dir: deps.liveness.dir }, now: () => at.getTime() };
31778
32786
  if (spendAllow(input["session_id"], key, allowOnce))
31779
32787
  return "";
31780
- recordHeld(input["session_id"], key, allowOnce, answer.refusal.step);
32788
+ const stepNumber = numberStep(input["session_id"], key, allowOnce);
32789
+ recordHeld(input["session_id"], key, allowOnce, answer.refusal.step, stepNumber);
32790
+ agentText = heldAgentText(answer.refusal, stepNumber);
31781
32791
  }
31782
32792
  }
31783
- return `${preToolUseReplyText("refuse", answer.refusal.person, answer.refusal.agent)}
32793
+ return `${preToolUseReplyText("refuse", answer.refusal.person, agentText)}
31784
32794
  `;
31785
32795
  } catch {
31786
32796
  return "";
@@ -31908,7 +32918,7 @@ function ownCopyOf(input, call, deps) {
31908
32918
  const walk2 = deps.walk ?? deps.liveness?.walk;
31909
32919
  return judgeOwnCopy(input, call, {
31910
32920
  projectDir: projectDirOf(deps),
31911
- home: deps.home ?? homedir12(),
32921
+ home: deps.home ?? homedir13(),
31912
32922
  ...walk2 === void 0 ? {} : { walk: walk2 },
31913
32923
  ...deps.liveness === void 0 ? {} : { liveness: deps.liveness }
31914
32924
  });
@@ -31917,8 +32927,8 @@ function ownCopyOf(input, call, deps) {
31917
32927
  }
31918
32928
  }
31919
32929
  function ownCopyNotice(input, deps) {
31920
- const folder = stringOf(input, "cwd") ?? projectDirOf(deps);
31921
- return firstNoticeThisSession("outside-own-copy-unknown", folder, stringOf(input, "session_id"), deps.liveness?.dir) ? `${preToolUseNoticeText(OWN_COPY_UNKNOWN_NOTICE)}
32930
+ const folder2 = stringOf(input, "cwd") ?? projectDirOf(deps);
32931
+ return firstNoticeThisSession("outside-own-copy-unknown", folder2, stringOf(input, "session_id"), deps.liveness?.dir) ? `${preToolUseNoticeText(OWN_COPY_UNKNOWN_NOTICE)}
31922
32932
  ` : "";
31923
32933
  }
31924
32934
  function parseObject2(text) {
@@ -31935,14 +32945,14 @@ function stringOf(input, key) {
31935
32945
  }
31936
32946
  function unreadable(deps, input) {
31937
32947
  const session = (input === null ? void 0 : stringOf(input, "session_id")) ?? /"session_id"\s*:\s*"([^"\\]+)"/.exec(deps.input)?.[1];
31938
- const folder = (input === null ? void 0 : stringOf(input, "cwd")) ?? projectDirOf(deps);
31939
- return firstNoticeThisSession("pre-tool-use-unreadable", folder, session) ? `${preToolUseNoticeText(UNREADABLE_INPUT_NOTICE)}
32948
+ const folder2 = (input === null ? void 0 : stringOf(input, "cwd")) ?? projectDirOf(deps);
32949
+ return firstNoticeThisSession("pre-tool-use-unreadable", folder2, session) ? `${preToolUseNoticeText(UNREADABLE_INPUT_NOTICE)}
31940
32950
  ` : "";
31941
32951
  }
31942
32952
 
31943
32953
  // dist/hooks/session-diff.js
31944
32954
  import { unlinkSync as unlinkSync10 } from "node:fs";
31945
- import { join as join18 } from "node:path";
32955
+ import { join as join19 } from "node:path";
31946
32956
  var STOP_HOOK_EVENT = {
31947
32957
  stop: "Stop",
31948
32958
  "subagent-stop": "SubagentStop"
@@ -31994,7 +33004,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
31994
33004
  }
31995
33005
  const repoRoot = repoRootResult.value;
31996
33006
  const sessionId = input.session_id;
31997
- const folder = realPathOf(repoRoot);
33007
+ const folder2 = realPathOf(repoRoot);
31998
33008
  orphanSweep(repoRoot, (ref) => refExists(ref, repoRoot), sessionId, Date.now(), whichCopy(process.cwd())?.common ?? null);
31999
33009
  const base = resolveDiffBase2(repoRoot, sessionId);
32000
33010
  const diffBase = base.kind;
@@ -32007,12 +33017,12 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32007
33017
  const candidates = ownEditedFiles(repoRoot, sessionId).filter((relPath) => !tracked.has(relPath));
32008
33018
  const untracked = getUntrackedFiles(repoRoot, candidates);
32009
33019
  const created = untracked.ok ? untracked.value.filter((relPath) => !tracked.has(relPath)) : [];
32010
- const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join18(repoRoot, relPath), repoRoot);
33020
+ const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join19(repoRoot, relPath), repoRoot);
32011
33021
  const { evaluated } = partitionInstallerOutput(collected.files);
32012
33022
  const { evaluated: createdEvaluated } = partitionInstallerOutput(created);
32013
33023
  const changedFiles2 = [...ownChangedFiles(repoRoot, evaluated, sessionId), ...createdEvaluated];
32014
33024
  if (changedFiles2.length === 0) {
32015
- queueTurnEnd(keepBooks(sessionId, folder, { kind: "no-own-changes" }, kind === "stop"), reply);
33025
+ queueTurnEnd(keepBooks(sessionId, folder2, { kind: "no-own-changes" }, kind === "stop"), reply);
32016
33026
  return;
32017
33027
  }
32018
33028
  const fileDiffEntries = changedFiles2.map((relPath) => ({
@@ -32023,7 +33033,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32023
33033
  const digest = digestChangeState(baseKey, fileDiffEntries);
32024
33034
  const prior = readDedupe(repoRoot, sessionId);
32025
33035
  if (prior && prior.base === baseKey && prior.digest === digest) {
32026
- queueTurnEnd(keepBooks(sessionId, folder, { kind: "unchanged" }, kind === "stop"), reply);
33036
+ queueTurnEnd(keepBooks(sessionId, folder2, { kind: "unchanged" }, kind === "stop"), reply);
32027
33037
  return;
32028
33038
  }
32029
33039
  const context = loadChangeSetContext(repoRoot);
@@ -32103,12 +33113,12 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32103
33113
  warn: (msg) => process.stderr.write(msg + "\n")
32104
33114
  });
32105
33115
  writeDedupe(repoRoot, sessionId, { base: baseKey, digest });
32106
- actOnEnvelope3(clientResult.envelope, kind, input.stop_hook_active === true, reply, sessionId, folder);
33116
+ actOnEnvelope3(clientResult.envelope, kind, input.stop_hook_active === true, reply, sessionId, folder2);
32107
33117
  }
32108
- function keepBooks(sessionId, folder, outcome, topLevel) {
33118
+ function keepBooks(sessionId, folder2, outcome, topLevel) {
32109
33119
  const books = () => {
32110
33120
  const before = readFindings(sessionId);
32111
- const { file: file2, news } = turnEndBookkeeping(before ?? emptyFindings(""), folder, outcome, topLevel, /* @__PURE__ */ new Date());
33121
+ const { file: file2, news } = turnEndBookkeeping(before ?? emptyFindings(""), folder2, outcome, topLevel, /* @__PURE__ */ new Date());
32112
33122
  if (before !== null && JSON.stringify(before) !== JSON.stringify(file2)) {
32113
33123
  if (file2.findings.length > 0)
32114
33124
  writeFindings(file2);
@@ -32133,7 +33143,7 @@ function queueTurnEnd(words, reply) {
32133
33143
  if (words !== null)
32134
33144
  reply.notice(words);
32135
33145
  }
32136
- function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder) {
33146
+ function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder2) {
32137
33147
  const notRun = notRunMessage((envelope.notRun ?? []).map((n) => n.reason));
32138
33148
  const outcome = {
32139
33149
  kind: "evaluated",
@@ -32142,7 +33152,7 @@ function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder) {
32142
33152
  };
32143
33153
  const topLevel = kind === "stop";
32144
33154
  if (envelope.blocking && !neverBlock) {
32145
- keepBooks(sessionId, folder, outcome, false);
33155
+ keepBooks(sessionId, folder2, outcome, false);
32146
33156
  const reason = blockReason(envelope.guardsFired.map((g) => g.explanation));
32147
33157
  reply.block(notRun ? `${reason}
32148
33158
  ${notRun}` : reason, stoppedMessage(blockingExplanations(envelope.guardsFired)));
@@ -32150,7 +33160,7 @@ ${notRun}` : reason, stoppedMessage(blockingExplanations(envelope.guardsFired)))
32150
33160
  if (envelope.blocking && neverBlock) {
32151
33161
  reply.notice(couldNotFixMessage(blockingExplanations(envelope.guardsFired)));
32152
33162
  }
32153
- queueTurnEnd(keepBooks(sessionId, folder, outcome, topLevel), reply);
33163
+ queueTurnEnd(keepBooks(sessionId, folder2, outcome, topLevel), reply);
32154
33164
  if (notRun)
32155
33165
  reply.notice(notRun);
32156
33166
  }
@@ -32171,9 +33181,9 @@ async function readHookStdin() {
32171
33181
  }
32172
33182
  }
32173
33183
  function readStdin2() {
32174
- return new Promise((resolve11) => {
33184
+ return new Promise((resolve12) => {
32175
33185
  if (process.stdin.isTTY) {
32176
- resolve11("");
33186
+ resolve12("");
32177
33187
  return;
32178
33188
  }
32179
33189
  const chunks = [];
@@ -32183,7 +33193,7 @@ function readStdin2() {
32183
33193
  return;
32184
33194
  settled2 = true;
32185
33195
  clearTimeout(timer);
32186
- resolve11(Buffer.concat(chunks).toString("utf-8"));
33196
+ resolve12(Buffer.concat(chunks).toString("utf-8"));
32187
33197
  };
32188
33198
  const timer = setTimeout(done, HOOK_STDIN_GRACE_MS);
32189
33199
  process.stdin.on("data", (chunk) => chunks.push(chunk));
@@ -32210,19 +33220,19 @@ async function runStop(observeDeps = {}, timeDeps = {}, paceDeps = {}, profile =
32210
33220
  recordTimeEvent("turn-end", input);
32211
33221
  await runSessionDiff(input, "stop", readSeatConfiguration, async (reply) => {
32212
33222
  await observeChanges(input.session_id, reply, observeDeps);
32213
- await sendTimeRecord(timeDeps);
33223
+ await sendTimeRecord(timeDeps, { agent: profile.id });
32214
33224
  await paceAtTurnEnd(input, reply, { configFolder: profile.configFolder, ...paceDeps });
32215
33225
  }, profile);
32216
33226
  }
32217
33227
 
32218
33228
  // dist/relayed-choices.js
32219
33229
  import { mkdirSync as mkdirSync11, readFileSync as readFileSync26, renameSync as renameSync14, rmSync as rmSync5, writeFileSync as writeFileSync16 } from "node:fs";
32220
- import { homedir as homedir13 } from "node:os";
32221
- import { dirname as dirname9, join as join19 } from "node:path";
33230
+ import { homedir as homedir14 } from "node:os";
33231
+ import { dirname as dirname9, join as join20 } from "node:path";
32222
33232
  function relayedChoicesFilePath(engagementId, home) {
32223
33233
  if (!isEngagementId(engagementId))
32224
33234
  return null;
32225
- return join19(home ?? homedir13(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, RELAYED_CHOICES_FILENAME);
33235
+ return join20(home ?? homedir14(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME, RELAYED_CHOICES_FILENAME);
32226
33236
  }
32227
33237
  function readRelayedChoices(engagementId, phase, home) {
32228
33238
  const path = relayedChoicesFilePath(engagementId, home);
@@ -32258,7 +33268,7 @@ function recordRelayedChoice(opts) {
32258
33268
  });
32259
33269
  if (!file2.success)
32260
33270
  return false;
32261
- const temp = join19(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
33271
+ const temp = join20(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
32262
33272
  try {
32263
33273
  mkdirSync11(dirname9(target), { recursive: true, mode: 448 });
32264
33274
  writeFileSync16(temp, `${JSON.stringify(file2.data)}
@@ -32487,19 +33497,19 @@ function matchReviewPhraseShape(shape, reviews) {
32487
33497
 
32488
33498
  // dist/hooks/shown-record.js
32489
33499
  import { chmodSync as chmodSync4, readFileSync as readFileSync27, renameSync as renameSync15, unlinkSync as unlinkSync11, writeFileSync as writeFileSync17 } from "node:fs";
32490
- import { homedir as homedir14, platform as platform3 } from "node:os";
32491
- import { join as join20 } from "node:path";
33500
+ import { homedir as homedir15, platform as platform3 } from "node:os";
33501
+ import { join as join21 } from "node:path";
32492
33502
  function recordDir(engagementId, home) {
32493
33503
  if (!isEngagementId(engagementId))
32494
33504
  return null;
32495
- return join20(home ?? homedir14(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId);
33505
+ return join21(home ?? homedir15(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId);
32496
33506
  }
32497
33507
  function readShownRecord(engagementId, home) {
32498
33508
  const dir = recordDir(engagementId, home);
32499
33509
  if (dir === null)
32500
33510
  return { entries: [], reviews: [] };
32501
33511
  try {
32502
- return parseShownRecord(readFileSync27(join20(dir, SHOWN_PLANS_FILENAME), "utf-8"));
33512
+ return parseShownRecord(readFileSync27(join21(dir, SHOWN_PLANS_FILENAME), "utf-8"));
32503
33513
  } catch {
32504
33514
  return { entries: [], reviews: [] };
32505
33515
  }
@@ -32520,7 +33530,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
32520
33530
  const dir = recordDir(engagementId, home);
32521
33531
  if (dir === null)
32522
33532
  return false;
32523
- const path = join20(dir, SHOWN_PLANS_FILENAME);
33533
+ const path = join21(dir, SHOWN_PLANS_FILENAME);
32524
33534
  const current = parseShownRecord(readFileSync27(path, "utf-8"));
32525
33535
  const at = current.reviews.findIndex((r) => r.phase === listed.phase && r.shownAt === listed.shownAt && r.reviewVersion === listed.reviewVersion);
32526
33536
  if (at === -1)
@@ -32528,7 +33538,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
32528
33538
  const acts = new Map(decided.map((d) => [d.part, d.act]));
32529
33539
  const reviews = current.reviews.map((review, i) => i === at ? { ...review, reviewVersion, parts: review.parts.map((p) => movedPart(p, acts.get(p.part))) } : review);
32530
33540
  const body = serialiseShownRecord({ entries: current.entries, reviews });
32531
- temp = join20(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
33541
+ temp = join21(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
32532
33542
  writeFileSync17(temp, body, { mode: 384 });
32533
33543
  if (platform3() !== "win32")
32534
33544
  chmodSync4(temp, 384);
@@ -32912,8 +33922,16 @@ function allowArmedLine(step = null) {
32912
33922
  function allowNothingHeldLine() {
32913
33923
  return line(`${NOTHING_ALLOWED}: no step is paused in this session.`);
32914
33924
  }
32915
- function allowSeveralHeldLine() {
32916
- 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}`;
33925
+ var UNNAMED_PAUSED_STEP = "a paused step";
33926
+ function allowSeveralHeldLine(paused) {
33927
+ const listed = paused.filter((p) => p.number !== null).map((p) => `${ALLOW_REPLY} ${String(p.number)} for ${p.step ?? UNNAMED_PAUSED_STEP}`).join("; ");
33928
+ if (listed === "") {
33929
+ return `${NOTHING_ALLOWED}: more than one step is paused, and Halfcycle could not number them on this computer. Nothing ran. ${SHOW_AS_WRITTEN}`;
33930
+ }
33931
+ 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}`;
33932
+ }
33933
+ function allowNotPausedLine(stepNumber) {
33934
+ return line(`${NOTHING_ALLOWED}: step ${String(stepNumber)} is not paused in this session.`);
32917
33935
  }
32918
33936
  function allowNotAttendedLine() {
32919
33937
  return line(`${NOTHING_ALLOWED}: a paused step can be allowed by typing only when a person types it into an open session.`);
@@ -32999,8 +34017,8 @@ function waitLineForAnswer(context, answer) {
32999
34017
  }
33000
34018
  }
33001
34019
  var PACE_NOTHING_CHANGED_OPENING = "Nothing changed";
33002
- function paceAnswerLine(text, agent) {
33003
- return agent === "claude-code" ? line(text) : codexLine(text);
34020
+ function paceAnswerLine(text, _agent) {
34021
+ return codexLine(text);
33004
34022
  }
33005
34023
  function paceNumberDoneText(n, more) {
33006
34024
  const head = `Done. This build now runs up to ${paceAgentsOf(n)} at once.`;
@@ -33108,8 +34126,9 @@ async function decideLine(opts) {
33108
34126
  const review = { recognised: false };
33109
34127
  try {
33110
34128
  const prompt = promptOf(opts.input);
33111
- if (isAllowReply(prompt))
33112
- return `${decideAllow(opts)}
34129
+ const allowReply = allowReplyOf(prompt);
34130
+ if (allowReply !== null)
34131
+ return `${decideAllow(allowReply.number, opts)}
33113
34132
  `;
33114
34133
  const autonomyShape = autonomyPhraseShape(prompt);
33115
34134
  if (autonomyShape !== null) {
@@ -33162,21 +34181,23 @@ async function decideLine(opts) {
33162
34181
  `;
33163
34182
  }
33164
34183
  }
33165
- function decideAllow(opts) {
34184
+ function decideAllow(stepNumber, opts) {
33166
34185
  const env = opts.env ?? process.env;
33167
34186
  if ((opts.agent ?? "claude-code") === "claude-code" && !readSessionAttended(env))
33168
34187
  return allowNotAttendedLine();
33169
- const { outcome, step } = armAllowStep(sessionIdOf(opts.input), {
34188
+ const { outcome, step, paused } = armAllowStep(sessionIdOf(opts.input), {
33170
34189
  ...opts.tempDir === void 0 ? {} : { dir: opts.tempDir },
33171
34190
  ...opts.now === void 0 ? {} : { now: opts.now }
33172
- });
34191
+ }, stepNumber);
33173
34192
  switch (outcome) {
33174
34193
  case "armed":
33175
34194
  return allowArmedLine(step);
33176
34195
  case "nothing-held":
33177
34196
  return allowNothingHeldLine();
34197
+ case "not-paused":
34198
+ return allowNotPausedLine(stepNumber ?? 0);
33178
34199
  case "several-held":
33179
- return allowSeveralHeldLine();
34200
+ return allowSeveralHeldLine(paused);
33180
34201
  case "not-saved":
33181
34202
  return allowNotSavedLine();
33182
34203
  default: {
@@ -33703,14 +34724,14 @@ function runUserPromptFindings(opts) {
33703
34724
  }
33704
34725
 
33705
34726
  // dist/resolve-engagement.js
33706
- import { existsSync as existsSync8, readFileSync as readFileSync28, realpathSync as realpathSync9 } from "node:fs";
33707
- import { join as join21, sep as sep6 } from "node:path";
33708
- import { homedir as homedir15 } from "node:os";
34727
+ import { existsSync as existsSync9, readFileSync as readFileSync28, realpathSync as realpathSync10 } from "node:fs";
34728
+ import { join as join22, sep as sep7 } from "node:path";
34729
+ import { homedir as homedir16 } from "node:os";
33709
34730
  function defaultRegistryPath() {
33710
- return join21(homedir15(), ".halfcycle", "registry.json");
34731
+ return join22(homedir16(), ".halfcycle", "registry.json");
33711
34732
  }
33712
34733
  function readRegistry(registryPath = defaultRegistryPath()) {
33713
- if (!existsSync8(registryPath))
34734
+ if (!existsSync9(registryPath))
33714
34735
  return {};
33715
34736
  try {
33716
34737
  const parsed = JSON.parse(readFileSync28(registryPath, "utf-8"));
@@ -33728,7 +34749,7 @@ function readRegistry(registryPath = defaultRegistryPath()) {
33728
34749
  }
33729
34750
  function canonicalise(p) {
33730
34751
  try {
33731
- return realpathSync9(p);
34752
+ return realpathSync10(p);
33732
34753
  } catch {
33733
34754
  return p;
33734
34755
  }
@@ -33749,7 +34770,7 @@ function resolveEngagement(cwd, registry2) {
33749
34770
  function isSelfOrAncestor(ancestor, target) {
33750
34771
  if (ancestor === target)
33751
34772
  return true;
33752
- const withSep = ancestor.endsWith(sep6) ? ancestor : ancestor + sep6;
34773
+ const withSep = ancestor.endsWith(sep7) ? ancestor : ancestor + sep7;
33753
34774
  return target.startsWith(withSep);
33754
34775
  }
33755
34776
  function runResolveEngagement(cwd, registryPath) {
@@ -33762,12 +34783,12 @@ function runResolveEngagement(cwd, registryPath) {
33762
34783
 
33763
34784
  // dist/refresh-credential.js
33764
34785
  import { readFileSync as readFileSync30 } from "node:fs";
33765
- import { resolve as resolve10 } from "node:path";
34786
+ import { resolve as resolve11 } from "node:path";
33766
34787
 
33767
34788
  // dist/mcp-headers.js
33768
34789
  import { readFileSync as readFileSync29 } from "node:fs";
33769
- import { homedir as homedir16 } from "node:os";
33770
- import { dirname as dirname10, resolve as resolve9 } from "node:path";
34790
+ import { homedir as homedir17 } from "node:os";
34791
+ import { dirname as dirname10, resolve as resolve10 } from "node:path";
33771
34792
  var URL_FLAG = "--url";
33772
34793
  function urlFromArguments(rest) {
33773
34794
  const at = [];
@@ -33794,12 +34815,12 @@ function headerLine(bearer, clientVersion) {
33794
34815
  }
33795
34816
  function projectRootFromBinary(binaryPath) {
33796
34817
  const depth = VENDORED_BIN_REL.split("/").length - 1;
33797
- return resolve9(dirname10(binaryPath), ...Array(depth).fill(".."));
34818
+ return resolve10(dirname10(binaryPath), ...Array(depth).fill(".."));
33798
34819
  }
33799
34820
  function readPinnedVersion(root) {
33800
34821
  let raw;
33801
34822
  try {
33802
- raw = readFileSync29(resolve9(root, BUNDLE_PIN_REL), "utf-8");
34823
+ raw = readFileSync29(resolve10(root, BUNDLE_PIN_REL), "utf-8");
33803
34824
  } catch {
33804
34825
  return void 0;
33805
34826
  }
@@ -33841,7 +34862,7 @@ function sentences(lines2) {
33841
34862
  }
33842
34863
  function badIdSentence(root) {
33843
34864
  return sentences([
33844
- `${resolve9(root, BUNDLE_PIN_REL)} names an engagement id that is not a Halfcycle project id,`,
34865
+ `${resolve10(root, BUNDLE_PIN_REL)} names an engagement id that is not a Halfcycle project id,`,
33845
34866
  'so no credential was read. Delete that file and run "npx halfcycle" again: this repository',
33846
34867
  "is then set up as a new Halfcycle project."
33847
34868
  ]);
@@ -33855,7 +34876,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
33855
34876
  ]);
33856
34877
  case "no-id":
33857
34878
  return sentences([
33858
- `${resolve9(root, BUNDLE_PIN_REL)} names no engagement id.`,
34879
+ `${resolve10(root, BUNDLE_PIN_REL)} names no engagement id.`,
33859
34880
  'run "npx halfcycle" here to rewrite it.'
33860
34881
  ]);
33861
34882
  case "bad-id":
@@ -33872,7 +34893,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
33872
34893
  case "no-credential":
33873
34894
  return sentences([
33874
34895
  `this machine holds no credential for engagement ${engagementId ?? ""}.`,
33875
- `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.`
34896
+ `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.`
33876
34897
  ]);
33877
34898
  }
33878
34899
  }
@@ -33895,7 +34916,7 @@ async function refreshWithinDeadline(source, opts, writeErr) {
33895
34916
  }
33896
34917
  async function chooseBearer(opts) {
33897
34918
  const writeErr = opts.writeErr ?? defaultWriteErr;
33898
- const home = opts.home ?? homedir16();
34919
+ const home = opts.home ?? homedir17();
33899
34920
  const root = projectRootFromBinary(opts.binaryPath);
33900
34921
  const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
33901
34922
  const store = resolveStore(root, home);
@@ -34038,8 +35059,8 @@ async function refreshCredentialToken(opts) {
34038
35059
  source
34039
35060
  });
34040
35061
  if (!refreshed.ok) {
34041
- const write = opts?.write ?? ((text) => process.stdout.write(text));
34042
- write(credentialRenewalFailedMessage(refreshed.remedy));
35062
+ const write2 = opts?.write ?? ((text) => process.stdout.write(text));
35063
+ write2(credentialRenewalFailedMessage(refreshed.remedy));
34043
35064
  return { token: void 0 };
34044
35065
  }
34045
35066
  return { token: refreshed.token };
@@ -34061,7 +35082,7 @@ var SESSION_START_PROBE_ATTEMPTS = 2;
34061
35082
  function registeredMcpUrl(projectRoot) {
34062
35083
  let parsed;
34063
35084
  try {
34064
- parsed = JSON.parse(readFileSync30(resolve10(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
35085
+ parsed = JSON.parse(readFileSync30(resolve11(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
34065
35086
  } catch {
34066
35087
  return void 0;
34067
35088
  }
@@ -34310,7 +35331,7 @@ async function runRefreshCredential(opts) {
34310
35331
  import { fileURLToPath } from "node:url";
34311
35332
 
34312
35333
  // dist/capture.js
34313
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, mkdirSync as mkdirSync12, existsSync as existsSync9 } from "node:fs";
35334
+ import { readFileSync as readFileSync31, writeFileSync as writeFileSync18, mkdirSync as mkdirSync12, existsSync as existsSync10 } from "node:fs";
34314
35335
  import { dirname as dirname11 } from "node:path";
34315
35336
  function runCapture(provider, endpoint, opts = {}) {
34316
35337
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -34392,8 +35413,8 @@ function relativeFixturePath(repoRoot, fixture) {
34392
35413
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
34393
35414
  import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
34394
35415
  import { createWriteStream, mkdirSync as mkdirSync13, readdirSync as readdirSync11, readFileSync as readFileSync32, renameSync as renameSync16, unlinkSync as unlinkSync12, writeFileSync as writeFileSync19 } from "node:fs";
34395
- import { homedir as homedir17 } from "node:os";
34396
- import { dirname as dirname12, join as join22 } from "node:path";
35416
+ import { homedir as homedir18 } from "node:os";
35417
+ import { dirname as dirname12, join as join23 } from "node:path";
34397
35418
 
34398
35419
  // dist/test-counts.js
34399
35420
  function sum(values) {
@@ -34502,7 +35523,7 @@ function isProofTestPath(path) {
34502
35523
  function testRunsDir(engagementId, home) {
34503
35524
  if (!isEngagementId(engagementId))
34504
35525
  return null;
34505
- return join22(home ?? homedir17(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TEST_RUNS_DIR_NAME);
35526
+ return join23(home ?? homedir18(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, TEST_RUNS_DIR_NAME);
34506
35527
  }
34507
35528
  function commandIdOf(command) {
34508
35529
  return createHash6("sha256").update(command, "utf8").digest("hex").slice(0, 16);
@@ -34598,7 +35619,7 @@ function subjectKey(subject) {
34598
35619
  }
34599
35620
  function keptCheckPath(dir, subject, label) {
34600
35621
  const key = createHash6("sha256").update(JSON.stringify([...subjectKey(subject), label]), "utf8").digest("hex");
34601
- return join22(dir, COMMANDS_DIR_NAME, `${key.slice(0, 32)}.json`);
35622
+ return join23(dir, COMMANDS_DIR_NAME, `${key.slice(0, 32)}.json`);
34602
35623
  }
34603
35624
  function readKeptCheck(dir, subject, label) {
34604
35625
  try {
@@ -34613,18 +35634,18 @@ function readKeptCheck(dir, subject, label) {
34613
35634
  }
34614
35635
  }
34615
35636
  function queuePath(dir, runId) {
34616
- return join22(dir, QUEUE_DIR_NAME, `${runId}.json`);
35637
+ return join23(dir, QUEUE_DIR_NAME, `${runId}.json`);
34617
35638
  }
34618
35639
  function readQueue(dir) {
34619
35640
  let names;
34620
35641
  try {
34621
- names = readdirSync11(join22(dir, QUEUE_DIR_NAME)).filter((n) => n.endsWith(".json"));
35642
+ names = readdirSync11(join23(dir, QUEUE_DIR_NAME)).filter((n) => n.endsWith(".json"));
34622
35643
  } catch {
34623
35644
  return [];
34624
35645
  }
34625
35646
  const reports = [];
34626
35647
  for (const name of names) {
34627
- const path = join22(dir, QUEUE_DIR_NAME, name);
35648
+ const path = join23(dir, QUEUE_DIR_NAME, name);
34628
35649
  let parsed = null;
34629
35650
  try {
34630
35651
  parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync32(path, "utf-8")));
@@ -34697,17 +35718,17 @@ function killTree(child, platform4, signal) {
34697
35718
  }
34698
35719
  }
34699
35720
  }
34700
- function runCommand(command, repoRoot, logPath, env, deps) {
35721
+ function runCommand(command, repoRoot, logPath2, env, deps) {
34701
35722
  const platform4 = deps.platform ?? process.platform;
34702
35723
  const out = deps.stdout ?? process.stdout;
34703
35724
  const err = deps.stderr ?? process.stderr;
34704
35725
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
34705
35726
  const childEnv = { ...env };
34706
35727
  delete childEnv[SERVICE_TOKEN_VAR];
34707
- mkdirSync13(dirname12(logPath), { recursive: true, mode: 448 });
34708
- const log = createWriteStream(logPath, { mode: 384 });
35728
+ mkdirSync13(dirname12(logPath2), { recursive: true, mode: 448 });
35729
+ const log = createWriteStream(logPath2, { mode: 384 });
34709
35730
  const began = process.hrtime.bigint();
34710
- return new Promise((resolve11) => {
35731
+ return new Promise((resolve12) => {
34711
35732
  let timedOut = false;
34712
35733
  let started = true;
34713
35734
  let settled2 = false;
@@ -34742,7 +35763,7 @@ function runCommand(command, repoRoot, logPath, env, deps) {
34742
35763
  process.off("SIGINT", onSignal);
34743
35764
  process.off("SIGTERM", onSignal);
34744
35765
  const durationMs = Number((process.hrtime.bigint() - began) / 1000000n);
34745
- log.end(() => resolve11({ exitCode: timedOut || !started ? null : code, timedOut, started, durationMs }));
35766
+ log.end(() => resolve12({ exitCode: timedOut || !started ? null : code, timedOut, started, durationMs }));
34746
35767
  };
34747
35768
  child.on("error", (e) => {
34748
35769
  started = false;
@@ -34864,13 +35885,13 @@ ${PROVE_USAGE}`);
34864
35885
  if (request.when === "before" && changedOutsideTests > 0)
34865
35886
  say(lateBeforeLine(changedOutsideTests));
34866
35887
  const runId = randomUUID3();
34867
- const logPath = join22(dir, `${runId}.log`);
35888
+ const logPath2 = join23(dir, `${runId}.log`);
34868
35889
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
34869
35890
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
34870
- const end = await runCommand(command, repoRoot, logPath, env, deps);
35891
+ const end = await runCommand(command, repoRoot, logPath2, env, deps);
34871
35892
  let output = "";
34872
35893
  try {
34873
- output = readFileSync32(logPath, "utf-8");
35894
+ output = readFileSync32(logPath2, "utf-8");
34874
35895
  } catch {
34875
35896
  output = "";
34876
35897
  }
@@ -34914,7 +35935,7 @@ ${PROVE_USAGE}`);
34914
35935
  sendLine = mine.outcome === "recorded" ? RECORDED_LINE : mine.outcome === "kept" ? NOT_SENT_YET_LINE : refusedLine(mine.refusal);
34915
35936
  }
34916
35937
  say(`${line2} ${sendLine}`);
34917
- say(`Its output is kept on this computer at ${logPath}`);
35938
+ say(`Its output is kept on this computer at ${logPath2}`);
34918
35939
  return 0;
34919
35940
  }
34920
35941
 
@@ -34982,14 +36003,14 @@ function personCommandEnv(env) {
34982
36003
  }
34983
36004
  return out;
34984
36005
  }
34985
- var runOwnCommandReal = (command, input, env) => new Promise((resolve11) => {
36006
+ var runOwnCommandReal = (command, input, env) => new Promise((resolve12) => {
34986
36007
  let out = "";
34987
36008
  let settled2 = false;
34988
36009
  const done = (v) => {
34989
36010
  if (settled2)
34990
36011
  return;
34991
36012
  settled2 = true;
34992
- resolve11(v);
36013
+ resolve12(v);
34993
36014
  };
34994
36015
  try {
34995
36016
  const child = spawn3(command, {
@@ -35137,7 +36158,7 @@ async function main() {
35137
36158
  if (cmd === "time") {
35138
36159
  let line2;
35139
36160
  try {
35140
- line2 = JSON.stringify(await runTimeVerb());
36161
+ line2 = JSON.stringify(await runTimeVerb({}, { final: args.slice(1).includes("--final") }));
35141
36162
  } catch {
35142
36163
  line2 = JSON.stringify({ outcome: "not-sent", reason: "unreachable" });
35143
36164
  }
@@ -35303,7 +36324,7 @@ function unknownCommand(name = args.join(" ")) {
35303
36324
  halfcycle-runner mcp-headers [--url <address>]
35304
36325
  halfcycle-runner observe
35305
36326
  halfcycle-runner observe --change <sortingId>
35306
- halfcycle-runner time
36327
+ halfcycle-runner time [--final]
35307
36328
  halfcycle-runner prove <phase> <task> --label <label> --before|--after [-- <command>]
35308
36329
  halfcycle-runner pace [--start <phase> [--agents <n>]]
35309
36330
  halfcycle-runner pace --scan [--config-folder <dir>]
@@ -35314,10 +36335,10 @@ function unknownCommand(name = args.join(" ")) {
35314
36335
  process.exit(1);
35315
36336
  }
35316
36337
  function readStdin3() {
35317
- return new Promise((resolve11, reject) => {
36338
+ return new Promise((resolve12, reject) => {
35318
36339
  const chunks = [];
35319
36340
  process.stdin.on("data", (chunk) => chunks.push(chunk));
35320
- process.stdin.on("end", () => resolve11(Buffer.concat(chunks).toString("utf-8")));
36341
+ process.stdin.on("end", () => resolve12(Buffer.concat(chunks).toString("utf-8")));
35321
36342
  process.stdin.on("error", reject);
35322
36343
  });
35323
36344
  }