halfcycle 0.7.1 → 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 {
@@ -23435,7 +23699,7 @@ function resolvedRecords(key, path) {
23435
23699
  }
23436
23700
  }
23437
23701
  function timeLogTranscripts(home) {
23438
- 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);
23439
23703
  const out = [];
23440
23704
  for (const e of dirents(engagements)) {
23441
23705
  if (!e.isDirectory())
@@ -23454,7 +23718,7 @@ function timeLogTranscripts(home) {
23454
23718
  }
23455
23719
  function teacherOf(key, deps = {}) {
23456
23720
  const path = deps.path ?? nodePath;
23457
- const home = deps.home ?? homedir5();
23721
+ const home = deps.home ?? homedir6();
23458
23722
  const ours = resolvedRecords(key, path);
23459
23723
  if (ours === null)
23460
23724
  return { shared: false, teaches: () => false };
@@ -23624,13 +23888,13 @@ function knownWindowOf(learned, reported, now) {
23624
23888
 
23625
23889
  // dist/phase-limit.js
23626
23890
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync as renameSync4, rmSync, writeFileSync as writeFileSync6 } from "node:fs";
23627
- import { homedir as homedir6 } from "node:os";
23891
+ import { homedir as homedir7 } from "node:os";
23628
23892
  import nodePath2 from "node:path";
23629
23893
  var PHASE_USAGE_FILENAME = "phase-usage.json";
23630
23894
  function phaseLimitDir(engagementId, home) {
23631
23895
  if (!isEngagementId(engagementId))
23632
23896
  return null;
23633
- 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);
23634
23898
  }
23635
23899
  function readJsonFile(path) {
23636
23900
  try {
@@ -23678,16 +23942,14 @@ function readPhaseUsage(dir, phase, openedAt = null) {
23678
23942
  return 0;
23679
23943
  return parsed.data.tokens;
23680
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
+ }
23681
23949
  function writePhaseUsage(dir, file2) {
23682
23950
  if (!phaseUsageFileSchema.safeParse(file2).success)
23683
- return;
23684
- writeWhole(nodePath2.join(dir, PHASE_USAGE_FILENAME), file2);
23685
- }
23686
- function phaseTokensOf(buckets) {
23687
- let total = 0;
23688
- for (const b of buckets)
23689
- total += b.inputTokens + b.outputTokens + b.cacheWriteTokens + b.cacheReadTokens;
23690
- return Math.max(0, Math.round(total));
23951
+ return false;
23952
+ return writeWhole(nodePath2.join(dir, PHASE_USAGE_FILENAME), file2);
23691
23953
  }
23692
23954
  function roundTokens(n) {
23693
23955
  return Math.max(LIMIT_ROUND_TOKENS, Math.round(n / LIMIT_ROUND_TOKENS) * LIMIT_ROUND_TOKENS);
@@ -23865,6 +24127,164 @@ function setLimit(file2, limit, multiple, used, by, at) {
23865
24127
  };
23866
24128
  }
23867
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
+
23868
24288
  // dist/time-record.js
23869
24289
  var TIME_EVENT_KINDS = ["action", "asked", "edit", "turn-end", "lock-wait-start", "lock-wait-end"];
23870
24290
  function isLockWaitEvent(event) {
@@ -23878,25 +24298,25 @@ function inputString(input, key) {
23878
24298
  const value = input[key];
23879
24299
  return typeof value === "string" && value !== "" ? value : null;
23880
24300
  }
23881
- function readBranch(folder, deps = {}) {
23882
- const copy = whichCopy(folder, deps);
24301
+ function readBranch(folder2, deps = {}) {
24302
+ const copy = whichCopy(folder2, deps);
23883
24303
  return copy === null ? null : copyBranch(copy, deps);
23884
24304
  }
23885
24305
  function timeLogPath(engagementId, home) {
23886
24306
  if (!isEngagementId(engagementId))
23887
24307
  return null;
23888
- 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);
23889
24309
  }
23890
24310
  function timeEventLine(event, input, deps = {}) {
23891
24311
  const at = (deps.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
23892
- const folder = inputString(input, "cwd") ?? (deps.cwd ?? (() => process.cwd()))();
24312
+ const folder2 = inputString(input, "cwd") ?? (deps.cwd ?? (() => process.cwd()))();
23893
24313
  return {
23894
24314
  at,
23895
24315
  event,
23896
24316
  session: inputString(input, "session_id"),
23897
24317
  agent: inputString(input, "agent_id"),
23898
- folder,
23899
- branch: readBranch(folder),
24318
+ folder: folder2,
24319
+ branch: readBranch(folder2),
23900
24320
  transcript: inputString(input, "transcript_path"),
23901
24321
  toolUse: inputString(input, "tool_use_id"),
23902
24322
  rollout: inputString(input, "rollout_path")
@@ -23914,12 +24334,12 @@ function recordTimeEvent(event, input, deps = {}) {
23914
24334
  const text = `${JSON.stringify(line2)}
23915
24335
  `;
23916
24336
  try {
23917
- appendFileSync2(path, text, { mode: 384 });
24337
+ appendFileSync3(path, text, { mode: 384 });
23918
24338
  } catch (err) {
23919
24339
  if (err.code !== "ENOENT")
23920
24340
  return;
23921
- mkdirSync5(dirname4(path), { recursive: true, mode: 448 });
23922
- appendFileSync2(path, text, { mode: 384 });
24341
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
24342
+ appendFileSync3(path, text, { mode: 384 });
23923
24343
  }
23924
24344
  } catch {
23925
24345
  }
@@ -23978,7 +24398,7 @@ function readSessionTimeLines(sessionId, deps = {}) {
23978
24398
  if (path === null)
23979
24399
  return [];
23980
24400
  const needle = JSON.stringify(sessionId);
23981
- 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));
23982
24402
  return parseTimeLog(kept.join("\n")).filter((line2) => line2.session === sessionId);
23983
24403
  } catch {
23984
24404
  return [];
@@ -24005,8 +24425,8 @@ function separatorOf(path) {
24005
24425
  function subagentsFolderOf(transcript) {
24006
24426
  if (!transcript.endsWith(".jsonl") || transcript.length === ".jsonl".length)
24007
24427
  return null;
24008
- const sep7 = separatorOf(transcript);
24009
- return `${transcript.slice(0, -".jsonl".length)}${sep7}subagents`;
24428
+ const sep8 = separatorOf(transcript);
24429
+ return `${transcript.slice(0, -".jsonl".length)}${sep8}subagents`;
24010
24430
  }
24011
24431
  function subagentRecordName(agentId) {
24012
24432
  return /^[A-Za-z0-9_-]+$/.test(agentId) ? `agent-${agentId}.jsonl` : null;
@@ -24014,12 +24434,12 @@ function subagentRecordName(agentId) {
24014
24434
  function subagentRecordPath(transcript, agentId) {
24015
24435
  if (transcript === null)
24016
24436
  return null;
24017
- const folder = subagentsFolderOf(transcript);
24437
+ const folder2 = subagentsFolderOf(transcript);
24018
24438
  const name = subagentRecordName(agentId);
24019
- return folder === null || name === null ? null : `${folder}${separatorOf(transcript)}${name}`;
24439
+ return folder2 === null || name === null ? null : `${folder2}${separatorOf(transcript)}${name}`;
24020
24440
  }
24021
- function folderParts(folder) {
24022
- return folder.split(/[\\/]+/).filter((part, i) => part !== "" || i === 0);
24441
+ function folderParts(folder2) {
24442
+ return folder2.split(/[\\/]+/).filter((part, i) => part !== "" || i === 0);
24023
24443
  }
24024
24444
  function folderHolds(outer, inner) {
24025
24445
  const o = folderParts(outer);
@@ -24072,15 +24492,52 @@ function buildLanes(events, taskIds) {
24072
24492
  });
24073
24493
  }
24074
24494
  var UNREADABLE2 = { readable: false, lines: [] };
24075
- function viewRecord(path, deps) {
24076
- if (path === null)
24077
- return UNREADABLE2;
24078
- let text;
24079
- try {
24080
- text = (deps.readFile ?? ((p) => readFileSync14(p, "utf-8")))(path);
24081
- } catch {
24082
- return UNREADABLE2;
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;
24083
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) {
24084
24541
  const lines2 = [];
24085
24542
  for (const raw of text.split("\n")) {
24086
24543
  if (raw.trim() === "")
@@ -24091,29 +24548,129 @@ function viewRecord(path, deps) {
24091
24548
  } catch {
24092
24549
  continue;
24093
24550
  }
24094
- if (typeof value !== "object" || value === null || Array.isArray(value))
24551
+ const o = plainObject(value);
24552
+ if (o === null || typeof o["type"] !== "string")
24095
24553
  continue;
24096
- const o = value;
24097
- if (typeof o["type"] !== "string")
24098
- continue;
24099
- const t = typeof o["timestamp"] === "string" ? Date.parse(o["timestamp"]) : NaN;
24100
- lines2.push({ value: o, t: Number.isNaN(t) ? null : t });
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;
24101
24564
  }
24565
+ const lines2 = recordLinesOf(text);
24102
24566
  return lines2.length === 0 ? UNREADABLE2 : { readable: true, lines: lines2 };
24103
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;
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));
24622
+ return lines2.length === 0 ? UNREADABLE2 : { readable: true, lines: lines2 };
24623
+ }
24624
+ var viewsOfOffsets = /* @__PURE__ */ new WeakMap();
24104
24625
  function recordReader(deps) {
24105
- 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
+ }
24106
24631
  return (path) => {
24107
24632
  if (path === null)
24108
24633
  return UNREADABLE2;
24109
24634
  const hit = cache2.get(path);
24110
24635
  if (hit !== void 0)
24111
24636
  return hit;
24112
- const view = viewRecord(path, deps);
24637
+ const view = deps.offsets === void 0 ? viewRecord(path, deps) : viewRecordFrom(path, deps, deps.offsets);
24113
24638
  cache2.set(path, view);
24114
24639
  return view;
24115
24640
  };
24116
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
+ }
24117
24674
  function isPrompt(line2) {
24118
24675
  const o = line2.value;
24119
24676
  if (o["type"] !== "user" || line2.t === null || "toolUseResult" in o)
@@ -24304,23 +24861,76 @@ function tokensOf(usage) {
24304
24861
  }
24305
24862
  return out;
24306
24863
  }
24307
- function builderTaskOf(own, t) {
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) {
24308
24899
  let next;
24309
- let last;
24310
24900
  for (const lane of own) {
24311
24901
  if (lane.task === null)
24312
24902
  continue;
24313
- const times = lane.events.map((e) => msOf(e.at));
24314
- const after = times.find((at) => at > t);
24903
+ const after = lane.events.map((e) => msOf(e.at)).find((at) => at > t);
24315
24904
  if (after !== void 0 && (next === void 0 || after < next.t))
24316
24905
  next = { task: lane.task, t: after };
24317
- const before = lastAtOrBefore(times, t);
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);
24318
24915
  if (before !== void 0 && (last === void 0 || before >= last.t))
24319
24916
  last = { task: lane.task, t: before };
24320
24917
  }
24321
- return next?.task ?? last?.task ?? null;
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;
24322
24932
  }
24323
- function readUsage(lanes, window, deps = {}, models) {
24933
+ function readUsage(lanes, window, deps = {}, models, settling = {}) {
24324
24934
  const from = Date.parse(window.from);
24325
24935
  const to = window.to === void 0 || window.to === null ? Infinity : Date.parse(window.to);
24326
24936
  const inWindow = (t) => t >= from && t <= to;
@@ -24336,7 +24946,29 @@ function readUsage(lanes, window, deps = {}, models) {
24336
24946
  return buckets.get(key) ?? buckets.set(key, { source, task, readable, messages: 0, inputTokens: 0, outputTokens: 0, cacheWriteTokens: 0, cacheReadTokens: 0 }).get(key);
24337
24947
  };
24338
24948
  const seen = /* @__PURE__ */ new Set();
24339
- const count = (path, view, taskAt, builderTaskAt = () => null) => {
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
+ };
24971
+ const count = (path, view, taskAt) => {
24340
24972
  const source = recordSourceOf(path);
24341
24973
  for (const line2 of view.lines) {
24342
24974
  const o = line2.value;
@@ -24346,19 +24978,22 @@ function readUsage(lanes, window, deps = {}, models) {
24346
24978
  if (typeof message !== "object" || message === null)
24347
24979
  continue;
24348
24980
  const id = message["id"];
24349
- if (typeof id !== "string" || id === "" || seen.has(id))
24981
+ if (typeof id !== "string" || id === "" || seen.has(id) || held.has(id))
24350
24982
  continue;
24351
24983
  const tokens = tokensOf(message["usage"]);
24352
24984
  if (tokens === null)
24353
24985
  continue;
24986
+ const task = taskAt(line2, id);
24987
+ if (task === void 0) {
24988
+ held.add(id);
24989
+ continue;
24990
+ }
24354
24991
  seen.add(id);
24355
- const task = taskAt(line2);
24356
24992
  const bucket = bucketFor(source, task, true);
24357
24993
  bucket.messages += 1;
24358
24994
  const model = message["model"];
24359
- const modelTask = models === void 0 ? null : task ?? builderTaskAt(line2);
24360
- if (models !== void 0 && modelTask !== null && isTalliedModel(model)) {
24361
- const ofTask = models.get(modelTask) ?? models.set(modelTask, /* @__PURE__ */ new Map()).get(modelTask);
24995
+ if (models !== void 0 && task !== null && isTalliedModel(model)) {
24996
+ const ofTask = models.get(task) ?? models.set(task, /* @__PURE__ */ new Map()).get(task);
24362
24997
  ofTask.set(model, (ofTask.get(model) ?? 0) + 1);
24363
24998
  }
24364
24999
  bucket.inputTokens += tokens[0];
@@ -24369,42 +25004,77 @@ function readUsage(lanes, window, deps = {}, models) {
24369
25004
  };
24370
25005
  for (const main2 of mains) {
24371
25006
  const view = read(main2);
25007
+ addToFigure(view);
24372
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
+ }
24373
25016
  count(main2, view, (line2) => {
25017
+ if (line2.t >= coordinatingFrom)
25018
+ return null;
24374
25019
  const branch = line2.value["gitBranch"];
24375
25020
  return taskOfBranch(typeof branch === "string" ? branch : null, window.taskIds);
24376
25021
  });
24377
25022
  }
24378
- const folder = subagentsFolderOf(main2);
24379
- if (folder === null)
25023
+ const folder2 = subagentsFolderOf(main2);
25024
+ if (folder2 === null)
24380
25025
  continue;
24381
25026
  let names;
24382
25027
  try {
24383
- 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();
24384
25029
  } catch {
24385
25030
  continue;
24386
25031
  }
24387
25032
  for (const name of names) {
24388
- const path = `${folder}${separatorOf(main2)}${name}`;
25033
+ const path = `${folder2}${separatorOf(main2)}${name}`;
24389
25034
  const view2 = read(path);
25035
+ addToFigure(view2);
24390
25036
  if (!view2.readable)
24391
25037
  continue;
24392
25038
  const agent = name.slice("agent-".length, -".jsonl".length);
24393
25039
  const own = active.filter((lane) => lane.agent === agent && lane.transcript === main2);
24394
25040
  if (own.length === 0)
24395
25041
  continue;
24396
- 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;
24397
25046
  let best;
24398
- for (const lane of own) {
24399
- 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);
24400
25049
  if (latest !== void 0 && (best === void 0 || latest >= best.t))
24401
- 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);
24402
25072
  }
24403
- if (best !== void 0)
24404
- return best.lane.task;
24405
- const first = [...own].sort((a, b) => msOf(a.events[0].at) - msOf(b.events[0].at))[0];
24406
- return first?.task ?? null;
24407
- }, (line2) => builderTaskOf(own, line2.t));
25073
+ if (task === null)
25074
+ task = lastTaskOf(ownAll, t);
25075
+ settled2.set(id, task);
25076
+ return task;
25077
+ });
24408
25078
  }
24409
25079
  }
24410
25080
  const codexLanes = active.filter((lane) => lane.rollout !== null && lane.transcript === null);
@@ -24426,6 +25096,8 @@ function readUsage(lanes, window, deps = {}, models) {
24426
25096
  best = { lane, t: latest };
24427
25097
  }
24428
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;
24429
25101
  bucket.messages += 1;
24430
25102
  bucket.inputTokens += step.inputTokens;
24431
25103
  bucket.outputTokens += step.outputTokens;
@@ -24446,13 +25118,13 @@ var PHASE_MODELS_FILENAME = "phase-models.json";
24446
25118
  function phaseModelsPath(engagementId, home) {
24447
25119
  if (!isEngagementId(engagementId))
24448
25120
  return null;
24449
- 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);
24450
25122
  }
24451
25123
  function writePhaseModels(path, phase, openedAt, tally) {
24452
25124
  try {
24453
25125
  let tasks = {};
24454
25126
  try {
24455
- const prior = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync14(path, "utf-8")));
25127
+ const prior = phaseModelsFileSchema.safeParse(JSON.parse(readFileSync15(path, "utf-8")));
24456
25128
  if (prior.success && prior.data.phase === phase && prior.data.openedAt === openedAt)
24457
25129
  tasks = prior.data.tasks;
24458
25130
  } catch {
@@ -24480,14 +25152,14 @@ function timeLogDir(engagementId, home) {
24480
25152
  return path === null ? null : dirname4(path);
24481
25153
  }
24482
25154
  function writeJsonFile(path, value) {
24483
- mkdirSync5(dirname4(path), { recursive: true, mode: 448 });
25155
+ mkdirSync6(dirname4(path), { recursive: true, mode: 448 });
24484
25156
  const tmp = `${path}.${process.pid}.tmp`;
24485
- writeFileSync7(tmp, JSON.stringify(value), { mode: 384 });
24486
- renameSync5(tmp, path);
25157
+ writeFileSync8(tmp, JSON.stringify(value), { mode: 384 });
25158
+ renameSync6(tmp, path);
24487
25159
  }
24488
25160
  function readAttemptedAt(dir) {
24489
25161
  try {
24490
- 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"));
24491
25163
  return typeof parsed.attemptedAt === "number" ? parsed.attemptedAt : null;
24492
25164
  } catch {
24493
25165
  return null;
@@ -24495,7 +25167,7 @@ function readAttemptedAt(dir) {
24495
25167
  }
24496
25168
  function writeAttemptedAt(dir, at) {
24497
25169
  try {
24498
- writeJsonFile(join10(dir, TIME_THROTTLE_FILENAME), { attemptedAt: at });
25170
+ writeJsonFile(join11(dir, TIME_THROTTLE_FILENAME), { attemptedAt: at });
24499
25171
  } catch {
24500
25172
  }
24501
25173
  }
@@ -24513,7 +25185,7 @@ function stringRecord(value) {
24513
25185
  function readTimeCursor(dir, phase, openedAt) {
24514
25186
  const fresh = { phase, openedAt, sentThrough: null, sentIds: {}, usage: {} };
24515
25187
  try {
24516
- 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"));
24517
25189
  if (o["phase"] !== phase || o["openedAt"] !== openedAt)
24518
25190
  return fresh;
24519
25191
  const sentThrough = o["sentThrough"];
@@ -24526,6 +25198,29 @@ function readTimeCursor(dir, phase, openedAt) {
24526
25198
  return fresh;
24527
25199
  }
24528
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
+ }
24529
25224
  function usageKey(bucket) {
24530
25225
  return `${bucket.source}\0${bucket.task ?? ""}`;
24531
25226
  }
@@ -24612,33 +25307,33 @@ var PRUNE_SETTLE_MS = 50;
24612
25307
  async function pruneTimeLog(path, openedAt) {
24613
25308
  const aside = `${path}.prune-${process.pid}`;
24614
25309
  try {
24615
- renameSync5(path, aside);
25310
+ renameSync6(path, aside);
24616
25311
  } catch {
24617
25312
  return;
24618
25313
  }
24619
25314
  let text = "";
24620
25315
  let written = false;
24621
25316
  try {
24622
- text = readFileSync14(aside, "utf-8");
25317
+ text = readFileSync15(aside, "utf-8");
24623
25318
  const { kept } = prunedTimeLog(text, openedAt);
24624
25319
  if (kept !== "")
24625
- appendFileSync2(path, kept, { mode: 384 });
25320
+ appendFileSync3(path, kept, { mode: 384 });
24626
25321
  written = true;
24627
25322
  let seen = Buffer.byteLength(text, "utf-8");
24628
25323
  for (; ; ) {
24629
- await new Promise((resolve11) => setTimeout(resolve11, PRUNE_SETTLE_MS));
24630
- const after = readFileSync14(aside);
25324
+ await new Promise((resolve12) => setTimeout(resolve12, PRUNE_SETTLE_MS));
25325
+ const after = readFileSync15(aside);
24631
25326
  if (after.length <= seen)
24632
25327
  break;
24633
- appendFileSync2(path, after.subarray(seen), { mode: 384 });
25328
+ appendFileSync3(path, after.subarray(seen), { mode: 384 });
24634
25329
  seen = after.length;
24635
25330
  }
24636
- unlinkSync4(aside);
25331
+ unlinkSync5(aside);
24637
25332
  } catch {
24638
25333
  try {
24639
25334
  if (!written) {
24640
- appendFileSync2(path, readFileSync14(aside, "utf-8"), { mode: 384 });
24641
- unlinkSync4(aside);
25335
+ appendFileSync3(path, readFileSync15(aside, "utf-8"), { mode: 384 });
25336
+ unlinkSync5(aside);
24642
25337
  }
24643
25338
  } catch {
24644
25339
  }
@@ -24653,7 +25348,17 @@ function conflictReason(body) {
24653
25348
  const error51 = body.error;
24654
25349
  return error51 === "basis-moved" || error51 === "phase-closed" ? error51 : null;
24655
25350
  }
24656
- 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) {
24657
25362
  const fetchFn = deps.fetchFn ?? globalThis.fetch;
24658
25363
  const base = `${controlOrigin}/engagements/${encodeURIComponent(config2.guardEngagementId)}/time`;
24659
25364
  const authorization = `Bearer ${config2.guardServiceToken}`;
@@ -24671,25 +25376,38 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24671
25376
  const basis = parsedBasis.data;
24672
25377
  if (basis.outcome === "nothing-to-record")
24673
25378
  return { outcome: "not-sent", reason: "nothing-to-record" };
24674
- const logPath = join10(dir, TIME_LOG_FILENAME);
25379
+ const logPath2 = join11(dir, TIME_LOG_FILENAME);
24675
25380
  let text = "";
24676
25381
  try {
24677
- text = readFileSync14(logPath, "utf-8");
25382
+ text = readFileSync15(logPath2, "utf-8");
24678
25383
  } catch {
24679
25384
  text = "";
24680
25385
  }
24681
25386
  const lanes = buildLanes(parseTimeLog(text), basis.taskIds);
24682
- 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);
24683
25390
  const models = /* @__PURE__ */ new Map();
24684
- 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);
24685
25401
  const limitDir = phaseLimitDir(config2.guardEngagementId, deps.home);
24686
25402
  if (limitDir !== null) {
24687
- writePhaseUsage(limitDir, {
25403
+ const wrote = writePhaseUsage(limitDir, {
24688
25404
  phase: basis.phase,
24689
25405
  openedAt: basis.openedAt,
24690
- tokens: phaseTokensOf(usage),
25406
+ tokens: figureOf(figure, options.agent),
24691
25407
  at: new Date((deps.now ?? Date.now)()).toISOString()
24692
25408
  });
25409
+ if (wrote)
25410
+ onFigureWritten();
24693
25411
  }
24694
25412
  const modelsPath = phaseModelsPath(config2.guardEngagementId, deps.home);
24695
25413
  if (modelsPath !== null)
@@ -24698,7 +25416,7 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24698
25416
  const selected = selectTimeReport(cursor, intervals, usage);
24699
25417
  if (selected.intervals.length === 0 && selected.usage.length === 0) {
24700
25418
  if (wouldPrune(text, basis.openedAt))
24701
- await pruneTimeLog(logPath, basis.openedAt);
25419
+ await pruneTimeLog(logPath2, basis.openedAt);
24702
25420
  return { outcome: "recorded" };
24703
25421
  }
24704
25422
  const report = { phase: basis.phase, openedAt: basis.openedAt, ...selected };
@@ -24714,14 +25432,14 @@ async function attemptTimeRecord(config2, controlOrigin, deps = {}) {
24714
25432
  if (!timeReportResultSchema.safeParse(sent.body).success)
24715
25433
  return { outcome: "not-sent", reason: "refused" };
24716
25434
  try {
24717
- writeJsonFile(join10(dir, TIME_CURSOR_FILENAME), advanceTimeCursor(cursor, selected));
25435
+ writeJsonFile(join11(dir, TIME_CURSOR_FILENAME), advanceTimeCursor(cursor, selected));
24718
25436
  } catch {
24719
25437
  }
24720
25438
  if (wouldPrune(text, basis.openedAt))
24721
- await pruneTimeLog(logPath, basis.openedAt);
25439
+ await pruneTimeLog(logPath2, basis.openedAt);
24722
25440
  return { outcome: "recorded" };
24723
25441
  }
24724
- async function runTimeVerb(deps = {}) {
25442
+ async function runTimeVerb(deps = {}, options = {}) {
24725
25443
  let result;
24726
25444
  try {
24727
25445
  const env = deps.env ?? process.env;
@@ -24731,14 +25449,14 @@ async function runTimeVerb(deps = {}) {
24731
25449
  result = { outcome: "not-sent", reason: "not-configured" };
24732
25450
  } else {
24733
25451
  writeAttemptedAt(dir, (deps.now ?? Date.now)());
24734
- result = await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps);
25452
+ result = await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps, options);
24735
25453
  }
24736
25454
  } catch {
24737
25455
  result = { outcome: "not-sent", reason: "unreachable" };
24738
25456
  }
24739
25457
  return timeRunResultSchema.parse(result);
24740
25458
  }
24741
- async function sendTimeRecord(deps = {}) {
25459
+ async function sendTimeRecord(deps = {}, options = {}) {
24742
25460
  try {
24743
25461
  const env = deps.env ?? process.env;
24744
25462
  const configured = readConfig(env);
@@ -24749,17 +25467,170 @@ async function sendTimeRecord(deps = {}) {
24749
25467
  return;
24750
25468
  const now = (deps.now ?? Date.now)();
24751
25469
  const last = readAttemptedAt(dir);
24752
- if (last !== null && now - last < TIME_MIN_INTERVAL_MS && now >= last)
24753
- return;
24754
- writeAttemptedAt(dir, now);
24755
- await attemptTimeRecord(configured.config, readControlOrigin(env).origin, deps);
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;
24756
25588
  } catch {
25589
+ return null;
25590
+ }
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);
24757
25627
  }
25628
+ return out;
24758
25629
  }
24759
25630
 
24760
25631
  // dist/own-work.js
24761
- import { appendFileSync as appendFileSync3, lstatSync as lstatSync2, readFileSync as readFileSync15, realpathSync as realpathSync6 } from "node:fs";
24762
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute2, join as join11, relative as relative5, resolve as resolve4, sep as sep3 } from "node:path";
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";
24763
25634
 
24764
25635
  // dist/risky/types.js
24765
25636
  var RISKY_ACTION_TYPES = [
@@ -24793,7 +25664,7 @@ function ownWorkFileName(sessionId) {
24793
25664
  function ownWorkPath(sessionId, dir) {
24794
25665
  if (typeof sessionId !== "string" || !livenessIdOk(sessionId))
24795
25666
  return null;
24796
- return join11(dir ?? markerDir(), ownWorkFileName(sessionId));
25667
+ return join12(dir ?? markerDir(), ownWorkFileName(sessionId));
24797
25668
  }
24798
25669
  function inputString2(input, key) {
24799
25670
  if (typeof input !== "object" || input === null)
@@ -24811,7 +25682,7 @@ function append(sessionId, line2, dir) {
24811
25682
  const path = ownWorkPath(sessionId, dir);
24812
25683
  if (path === null)
24813
25684
  return;
24814
- appendFileSync3(path, `${JSON.stringify(line2)}
25685
+ appendFileSync4(path, `${JSON.stringify(line2)}
24815
25686
  `, { mode: 384 });
24816
25687
  }
24817
25688
  function recordOwnShell(input, at, deps = {}) {
@@ -24857,7 +25728,7 @@ function readOwnWork(sessionId, dir) {
24857
25728
  const path = ownWorkPath(sessionId, dir);
24858
25729
  if (path === null)
24859
25730
  return [];
24860
- return parseOwnWork(readFileSync15(path, "utf-8"));
25731
+ return parseOwnWork(readFileSync16(path, "utf-8"));
24861
25732
  } catch {
24862
25733
  return [];
24863
25734
  }
@@ -24892,7 +25763,7 @@ function realPathOf(path) {
24892
25763
  let at = resolve4(path);
24893
25764
  for (; ; ) {
24894
25765
  try {
24895
- return join11(realpathSync6.native(at), ...missing.reverse());
25766
+ return join12(realpathSync6.native(at), ...missing.reverse());
24896
25767
  } catch {
24897
25768
  const parent = dirname5(at);
24898
25769
  if (parent === at)
@@ -24952,7 +25823,7 @@ function ownChangedFiles(repoRoot, files, sessionId, deps = {}) {
24952
25823
  };
24953
25824
  const root = resolve4(repoRoot);
24954
25825
  return files.filter((rel) => {
24955
- const abs = join11(root, rel);
25826
+ const abs = join12(root, rel);
24956
25827
  if (edited.has(realPathOf(abs)))
24957
25828
  return true;
24958
25829
  if (windows.length === 0)
@@ -24966,7 +25837,7 @@ function ownChangedFiles(repoRoot, files, sessionId, deps = {}) {
24966
25837
 
24967
25838
  // dist/hooks/findings.js
24968
25839
  import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
24969
- import { closeSync as closeSync3, existsSync as existsSync6, openSync as openSync3, readFileSync as readFileSync16, renameSync as renameSync6, statSync as statSync4, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "node:fs";
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";
24970
25841
  function findingKey(firing) {
24971
25842
  return createHash4("sha256").update(`${firing.guardId}
24972
25843
  ${firing.path ?? ""}
@@ -25009,7 +25880,7 @@ function readFindings(sessionId) {
25009
25880
  return null;
25010
25881
  const session = sessionId;
25011
25882
  try {
25012
- const parsed = JSON.parse(readFileSync16(path, "utf-8"));
25883
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
25013
25884
  if (parsed["version"] !== 1 || !Array.isArray(parsed["findings"]))
25014
25885
  return emptyFindings(session);
25015
25886
  const findings = [];
@@ -25029,12 +25900,12 @@ function writeFindings(file2) {
25029
25900
  return;
25030
25901
  const temp = `${path.slice(0, -".json".length)}.${process.pid}.tmp.json`;
25031
25902
  try {
25032
- writeFileSync8(temp, `${JSON.stringify(file2)}
25903
+ writeFileSync9(temp, `${JSON.stringify(file2)}
25033
25904
  `, { mode: 384 });
25034
- renameSync6(temp, path);
25905
+ renameSync7(temp, path);
25035
25906
  } catch {
25036
25907
  try {
25037
- unlinkSync5(temp);
25908
+ unlinkSync6(temp);
25038
25909
  } catch {
25039
25910
  }
25040
25911
  }
@@ -25050,7 +25921,7 @@ function pause(ms) {
25050
25921
  function takeOverStaleLock(lock) {
25051
25922
  const aside = `${lock}.${process.pid}.${randomUUID2()}.stale`;
25052
25923
  try {
25053
- renameSync6(lock, aside);
25924
+ renameSync7(lock, aside);
25054
25925
  } catch {
25055
25926
  return false;
25056
25927
  }
@@ -25062,17 +25933,17 @@ function takeOverStaleLock(lock) {
25062
25933
  }
25063
25934
  if (!staleIndeed) {
25064
25935
  try {
25065
- renameSync6(aside, lock);
25936
+ renameSync7(aside, lock);
25066
25937
  } catch {
25067
25938
  try {
25068
- unlinkSync5(aside);
25939
+ unlinkSync6(aside);
25069
25940
  } catch {
25070
25941
  }
25071
25942
  }
25072
25943
  return false;
25073
25944
  }
25074
25945
  try {
25075
- unlinkSync5(aside);
25946
+ unlinkSync6(aside);
25076
25947
  } catch {
25077
25948
  }
25078
25949
  return true;
@@ -25085,7 +25956,7 @@ function withFindingsLock(sessionId, body, waitMs = FINDINGS_LOCK_WAIT_MS) {
25085
25956
  const deadline = Date.now() + waitMs;
25086
25957
  for (; ; ) {
25087
25958
  try {
25088
- closeSync3(openSync3(lock, "wx", 384));
25959
+ closeSync4(openSync4(lock, "wx", 384));
25089
25960
  break;
25090
25961
  } catch (err) {
25091
25962
  if (err.code !== "EEXIST")
@@ -25107,31 +25978,31 @@ function withFindingsLock(sessionId, body, waitMs = FINDINGS_LOCK_WAIT_MS) {
25107
25978
  return body();
25108
25979
  } finally {
25109
25980
  try {
25110
- unlinkSync5(lock);
25981
+ unlinkSync6(lock);
25111
25982
  } catch {
25112
25983
  }
25113
25984
  }
25114
25985
  }
25115
- function takeUndelivered(file2, folder) {
25986
+ function takeUndelivered(file2, folder2) {
25116
25987
  const taken = [];
25117
25988
  const findings = file2.findings.map((f) => {
25118
- if (f.delivered || folder !== null && f.folder !== folder)
25989
+ if (f.delivered || folder2 !== null && f.folder !== folder2)
25119
25990
  return f;
25120
25991
  taken.push(f);
25121
25992
  return { ...f, delivered: true };
25122
25993
  });
25123
25994
  return { file: { ...file2, findings }, taken };
25124
25995
  }
25125
- function deliverFindings(sessionId, folder) {
25996
+ function deliverFindings(sessionId, folder2) {
25126
25997
  const path = findingsPath(sessionId);
25127
- if (path === null || !existsSync6(path))
25998
+ if (path === null || !existsSync7(path))
25128
25999
  return [];
25129
26000
  try {
25130
26001
  const taken = withFindingsLock(sessionId, () => {
25131
26002
  const before = readFindings(sessionId);
25132
26003
  if (before === null)
25133
26004
  return [];
25134
- const { file: file2, taken: given } = takeUndelivered(before, folder);
26005
+ const { file: file2, taken: given } = takeUndelivered(before, folder2);
25135
26006
  if (given.length > 0)
25136
26007
  writeFindings(file2);
25137
26008
  return given;
@@ -25141,11 +26012,11 @@ function deliverFindings(sessionId, folder) {
25141
26012
  return [];
25142
26013
  }
25143
26014
  }
25144
- function turnEndBookkeeping(file2, folder, outcome, topLevel, now) {
26015
+ function turnEndBookkeeping(file2, folder2, outcome, topLevel, now) {
25145
26016
  const recorded = [...file2.findings];
25146
26017
  const firing = /* @__PURE__ */ new Map();
25147
26018
  const notRun = new Set(outcome.kind === "evaluated" ? outcome.notRun : []);
25148
- const observed = (f) => f.folder === folder && !notRun.has(f.patternRef);
26019
+ const observed = (f) => f.folder === folder2 && !notRun.has(f.patternRef);
25149
26020
  if (outcome.kind === "evaluated") {
25150
26021
  for (const g of outcome.fired) {
25151
26022
  if (g.severity === "block")
@@ -25161,13 +26032,13 @@ function turnEndBookkeeping(file2, folder, outcome, topLevel, now) {
25161
26032
  const closes = observed(f) && (outcome.kind === "no-own-changes" || outcome.kind === "evaluated" && !firing.has(f.key));
25162
26033
  (closes ? removed : kept).push(f);
25163
26034
  }
25164
- 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));
25165
26036
  for (const [key, g] of firing) {
25166
26037
  if (known.has(key))
25167
26038
  continue;
25168
26039
  kept.push({
25169
26040
  key,
25170
- folder,
26041
+ folder: folder2,
25171
26042
  guardId: g.guardId,
25172
26043
  patternRef: g.patternRef,
25173
26044
  severity: g.severity,
@@ -25362,7 +26233,7 @@ function patchHeaderPaths(patch) {
25362
26233
  }
25363
26234
 
25364
26235
  // dist/hooks/post-tool-use.js
25365
- import { isAbsolute as isAbsolute3, join as join12, relative as relative6, resolve as resolve5, sep as sep4 } from "node:path";
26236
+ import { isAbsolute as isAbsolute3, join as join13, relative as relative6, resolve as resolve5, sep as sep4 } from "node:path";
25366
26237
  var HANDLED_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
25367
26238
  async function runPostToolUse(readSeat = readSeatConfiguration, profile = AGENT_PROFILES["claude-code"]) {
25368
26239
  const reply = hookReply("PostToolUse", profile);
@@ -25549,7 +26420,7 @@ function insideRepository(path, repoRoot) {
25549
26420
  const rel = relative6(realPathOf(repoRoot), realPathOf(path));
25550
26421
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep4}`) || isAbsolute3(rel))
25551
26422
  return null;
25552
- return join12(repoRoot, rel);
26423
+ return join13(repoRoot, rel);
25553
26424
  }
25554
26425
  function actOnEnvelope2(envelope, reply) {
25555
26426
  const notRun = notRunReport(envelope.notRun);
@@ -25572,17 +26443,17 @@ ${guardLines}${notRunBlock}`);
25572
26443
  }
25573
26444
  }
25574
26445
  function readStdin() {
25575
- return new Promise((resolve11, reject) => {
26446
+ return new Promise((resolve12, reject) => {
25576
26447
  const chunks = [];
25577
26448
  process.stdin.on("data", (chunk) => chunks.push(chunk));
25578
- process.stdin.on("end", () => resolve11(Buffer.concat(chunks).toString("utf-8")));
26449
+ process.stdin.on("end", () => resolve12(Buffer.concat(chunks).toString("utf-8")));
25579
26450
  process.stdin.on("error", reject);
25580
26451
  });
25581
26452
  }
25582
26453
 
25583
26454
  // dist/liveness.js
25584
- import { closeSync as closeSync4, mkdtempSync, openSync as openSync4, readdirSync as readdirSync7, readFileSync as readFileSync17, renameSync as renameSync7, rmSync as rmSync2, statSync as statSync5, unlinkSync as unlinkSync6, writeFileSync as writeFileSync9 } from "node:fs";
25585
- import { join as join13 } from "node:path";
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";
25586
26457
  var LIVE_WINDOW_MS = 10 * 60 * 1e3;
25587
26458
  function field(input, key) {
25588
26459
  if (typeof input !== "object" || input === null)
@@ -25591,10 +26462,10 @@ function field(input, key) {
25591
26462
  return typeof v === "string" && v !== "" ? v : null;
25592
26463
  }
25593
26464
  function indexEntry(dir, sessionId, name) {
25594
- const folder = join13(dir, livenessIndexDirName(sessionId));
26465
+ const folder2 = join14(dir, livenessIndexDirName(sessionId));
25595
26466
  for (let attempt2 = 0; attempt2 < 3; attempt2++) {
25596
26467
  try {
25597
- closeSync4(openSync4(join13(folder, name), "a", 384));
26468
+ closeSync5(openSync5(join14(folder2, name), "a", 384));
25598
26469
  return true;
25599
26470
  } catch (err) {
25600
26471
  if (err.code !== "ENOENT")
@@ -25602,13 +26473,13 @@ function indexEntry(dir, sessionId, name) {
25602
26473
  }
25603
26474
  let tmp = null;
25604
26475
  try {
25605
- tmp = mkdtempSync(`${folder}.`);
26476
+ tmp = mkdtempSync(`${folder2}.`);
25606
26477
  for (const existing of readdirSync7(dir)) {
25607
26478
  if (parseLivenessFileName(existing)?.sessionId === sessionId)
25608
- closeSync4(openSync4(join13(tmp, existing), "a", 384));
26479
+ closeSync5(openSync5(join14(tmp, existing), "a", 384));
25609
26480
  }
25610
- closeSync4(openSync4(join13(tmp, name), "a", 384));
25611
- renameSync7(tmp, folder);
26481
+ closeSync5(openSync5(join14(tmp, name), "a", 384));
26482
+ renameSync8(tmp, folder2);
25612
26483
  return true;
25613
26484
  } catch {
25614
26485
  if (tmp !== null)
@@ -25633,18 +26504,18 @@ function recordLiveness(input, deps = {}) {
25633
26504
  const name = livenessFileName(livenessKey(copy?.common ?? null), sessionId, agentId);
25634
26505
  if (!indexEntry(dir, sessionId, name))
25635
26506
  return null;
25636
- const path = join13(dir, name);
26507
+ const path = join14(dir, name);
25637
26508
  const text = `${cwd}
25638
26509
  ${copy?.top ?? ""}
25639
26510
  `;
25640
26511
  try {
25641
- writeFileSync9(path, text, { encoding: "utf-8", flag: "wx", mode: 384 });
26512
+ writeFileSync10(path, text, { encoding: "utf-8", flag: "wx", mode: 384 });
25642
26513
  return { firstSeen: true };
25643
26514
  } catch (err) {
25644
26515
  if (err.code !== "EEXIST")
25645
26516
  return null;
25646
26517
  }
25647
- writeFileSync9(path, text, { encoding: "utf-8", mode: 384 });
26518
+ writeFileSync10(path, text, { encoding: "utf-8", mode: 384 });
25648
26519
  return { firstSeen: false };
25649
26520
  } catch {
25650
26521
  return null;
@@ -25663,7 +26534,7 @@ function clearLiveness(input, deps = {}) {
25663
26534
  if (p === null || p.sessionId !== sessionId || p.agentId !== agentId)
25664
26535
  continue;
25665
26536
  try {
25666
- unlinkSync6(join13(dir, name));
26537
+ unlinkSync7(join14(dir, name));
25667
26538
  removed += 1;
25668
26539
  } catch {
25669
26540
  }
@@ -25676,7 +26547,7 @@ function clearLiveness(input, deps = {}) {
25676
26547
  }
25677
26548
  function topOf(path) {
25678
26549
  try {
25679
- const top = readFileSync17(path, "utf-8").split("\n")[1] ?? "";
26550
+ const top = readFileSync18(path, "utf-8").split("\n")[1] ?? "";
25680
26551
  return top === "" ? null : top;
25681
26552
  } catch {
25682
26553
  return null;
@@ -25698,7 +26569,7 @@ function readLiveness(sessionId, cwd, deps = {}) {
25698
26569
  const p = parseLivenessFileName(name);
25699
26570
  if (p === null || p.key !== key || p.sessionId !== sessionId)
25700
26571
  continue;
25701
- const full = join13(dir, name);
26572
+ const full = join14(dir, name);
25702
26573
  let seenAt;
25703
26574
  try {
25704
26575
  seenAt = statSync5(full).mtimeMs;
@@ -25744,168 +26615,8 @@ import nodePath5 from "node:path";
25744
26615
 
25745
26616
  // dist/pace-verb.js
25746
26617
  import { mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as readFileSync19, renameSync as renameSync9, statSync as statSync6, writeFileSync as writeFileSync11 } from "node:fs";
25747
- import { homedir as homedir8 } from "node:os";
26618
+ import { homedir as homedir9 } from "node:os";
25748
26619
  import nodePath4 from "node:path";
25749
-
25750
- // dist/scan-lock.js
25751
- import { spawn } from "node:child_process";
25752
- import { linkSync, mkdirSync as mkdirSync6, readFileSync as readFileSync18, renameSync as renameSync8, unlinkSync as unlinkSync7, writeFileSync as writeFileSync10 } from "node:fs";
25753
- import nodePath3 from "node:path";
25754
- var SCAN_LOCK_FILENAME = "scan.lock";
25755
- var SCAN_LOCK_STALE_MS = 15 * 60 * 1e3;
25756
- var SCAN_LOCK_POLL_MS = 100;
25757
- function scanLockPath(home) {
25758
- return nodePath3.join(pacingDir(home), SCAN_LOCK_FILENAME);
25759
- }
25760
- function readLockBody(path) {
25761
- let text;
25762
- try {
25763
- text = readFileSync18(path, "utf-8");
25764
- } catch (err) {
25765
- return err.code === "ENOENT" ? "missing" : null;
25766
- }
25767
- try {
25768
- const v = JSON.parse(text);
25769
- return typeof v["pid"] === "number" && typeof v["at"] === "number" ? { pid: v["pid"], at: v["at"] } : null;
25770
- } catch {
25771
- return null;
25772
- }
25773
- }
25774
- function processAlive(pid) {
25775
- if (!Number.isInteger(pid) || pid <= 0)
25776
- return false;
25777
- try {
25778
- process.kill(pid, 0);
25779
- return true;
25780
- } catch (err) {
25781
- return err.code === "EPERM";
25782
- }
25783
- }
25784
- function isStale(body, staleMs, deps) {
25785
- if (body === null)
25786
- return true;
25787
- const now = (deps.clock ?? Date.now)();
25788
- if (Math.abs(now - body.at) > staleMs)
25789
- return true;
25790
- return !(deps.alive ?? processAlive)(body.pid);
25791
- }
25792
- function writeLock(path, pid, deps, flag) {
25793
- writeFileSync10(path, JSON.stringify({ pid, at: (deps.clock ?? Date.now)() }), { flag, mode: 384 });
25794
- }
25795
- function tryTakeScanLock(deps = {}) {
25796
- return tryTakeLockFile(scanLockPath(deps.home), SCAN_LOCK_STALE_MS, deps);
25797
- }
25798
- function tryTakeLockFile(path, staleMs, deps = {}) {
25799
- const pid = deps.pid ?? process.pid;
25800
- try {
25801
- mkdirSync6(nodePath3.dirname(path), { recursive: true, mode: 448 });
25802
- } catch {
25803
- return false;
25804
- }
25805
- for (let attempt2 = 0; attempt2 < 2; attempt2++) {
25806
- try {
25807
- writeLock(path, pid, deps, "wx");
25808
- return true;
25809
- } catch (err) {
25810
- if (err.code !== "EEXIST")
25811
- return false;
25812
- }
25813
- const seen = readLockBody(path);
25814
- if (seen === "missing")
25815
- continue;
25816
- if (!isStale(seen, staleMs, deps))
25817
- return false;
25818
- const aside = `${path}.${String(pid)}.${String((deps.clock ?? Date.now)())}.stale`;
25819
- try {
25820
- renameSync8(path, aside);
25821
- } catch {
25822
- continue;
25823
- }
25824
- const taken = readLockBody(aside);
25825
- const same = taken !== "missing" && (seen === null ? taken === null : taken !== null && taken.pid === seen.pid && taken.at === seen.at);
25826
- if (!same && taken !== "missing" && !isStale(taken, staleMs, deps)) {
25827
- try {
25828
- linkSync(aside, path);
25829
- } catch {
25830
- }
25831
- try {
25832
- unlinkSync7(aside);
25833
- } catch {
25834
- }
25835
- return false;
25836
- }
25837
- try {
25838
- unlinkSync7(aside);
25839
- } catch {
25840
- }
25841
- }
25842
- return false;
25843
- }
25844
- function releaseLockFile(path, pid = process.pid) {
25845
- const body = readLockBody(path);
25846
- if (body === "missing" || body === null || body.pid !== pid)
25847
- return;
25848
- try {
25849
- unlinkSync7(path);
25850
- } catch {
25851
- }
25852
- }
25853
- function releaseScanLock(deps = {}) {
25854
- releaseLockFile(scanLockPath(deps.home), deps.pid ?? process.pid);
25855
- }
25856
- function sleepSync(ms) {
25857
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
25858
- }
25859
- function scanUnderLock(key, deps = {}, waitMs = 0) {
25860
- const clock = deps.clock ?? Date.now;
25861
- const giveUpAt = clock() + waitMs;
25862
- while (!tryTakeScanLock(deps)) {
25863
- if (clock() >= giveUpAt)
25864
- return null;
25865
- sleepSync(SCAN_LOCK_POLL_MS);
25866
- }
25867
- try {
25868
- return scanRecords(key, deps);
25869
- } finally {
25870
- releaseScanLock(deps);
25871
- }
25872
- }
25873
- var spawnDetachedReal = (command, args2) => {
25874
- const child = spawn(command, [...args2], { detached: true, stdio: "ignore", windowsHide: true });
25875
- child.on("error", () => void 0);
25876
- child.unref();
25877
- return child.pid;
25878
- };
25879
- function startScanChild(key, binaryPath, deps = {}) {
25880
- if (!tryTakeScanLock(deps))
25881
- return false;
25882
- let childPid;
25883
- try {
25884
- childPid = (deps.spawnDetached ?? spawnDetachedReal)(deps.nodePath ?? process.execPath, [
25885
- binaryPath,
25886
- "pace",
25887
- "--scan",
25888
- "--config-folder",
25889
- key,
25890
- "--lock-held"
25891
- ]);
25892
- } catch {
25893
- childPid = void 0;
25894
- }
25895
- if (childPid === void 0) {
25896
- releaseScanLock(deps);
25897
- return false;
25898
- }
25899
- try {
25900
- writeLock(scanLockPath(deps.home), childPid, deps, "w");
25901
- } catch {
25902
- releaseScanLock(deps);
25903
- return false;
25904
- }
25905
- return true;
25906
- }
25907
-
25908
- // dist/pace-verb.js
25909
26620
  var PACE_DEFAULT_TASK_MINUTES = 30;
25910
26621
  var PACE_DURATION_ROUND_MINUTES = 10;
25911
26622
  var PACE_WINDOW_MINUTES = 300;
@@ -26027,7 +26738,7 @@ function suggestPace(input) {
26027
26738
  function enginePacingDir(engagementId, home) {
26028
26739
  if (!isEngagementId(engagementId))
26029
26740
  return null;
26030
- 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);
26031
26742
  }
26032
26743
  function readBuildNumber(engagementId, home) {
26033
26744
  const dir = enginePacingDir(engagementId, home);
@@ -26085,7 +26796,7 @@ var USAGE = "Usage: halfcycle-runner pace [--agent claude-code|codex] [--start <
26085
26796
  function parsePaceArgs(args2) {
26086
26797
  let scan = false;
26087
26798
  let lockHeld = false;
26088
- let folder = null;
26799
+ let folder2 = null;
26089
26800
  let phase = null;
26090
26801
  let agents = null;
26091
26802
  let agent = null;
@@ -26103,8 +26814,8 @@ function parsePaceArgs(args2) {
26103
26814
  else if (a === "--lock-held")
26104
26815
  lockHeld = true;
26105
26816
  else if (a === "--config-folder") {
26106
- folder = value();
26107
- if (folder === null)
26817
+ folder2 = value();
26818
+ if (folder2 === null)
26108
26819
  return { kind: "usage", message: USAGE };
26109
26820
  } else if (a === "--start") {
26110
26821
  phase = value();
@@ -26125,9 +26836,9 @@ function parsePaceArgs(args2) {
26125
26836
  if (scan) {
26126
26837
  if (phase !== null || agents !== null || agent !== null)
26127
26838
  return { kind: "usage", message: USAGE };
26128
- return { kind: "scan", folder, lockHeld };
26839
+ return { kind: "scan", folder: folder2, lockHeld };
26129
26840
  }
26130
- if (folder !== null || lockHeld)
26841
+ if (folder2 !== null || lockHeld)
26131
26842
  return { kind: "usage", message: USAGE };
26132
26843
  if (phase === null)
26133
26844
  return agents === null ? { kind: "now", agent: agent ?? "claude-code" } : { kind: "usage", message: USAGE };
@@ -26210,7 +26921,7 @@ async function runPaceVerb(args2, deps = {}) {
26210
26921
  ...deps.home === void 0 ? {} : { home: deps.home },
26211
26922
  now: () => now.getTime(),
26212
26923
  ...deps.fetchFn === void 0 ? {} : { fetchFn: deps.fetchFn }
26213
- });
26924
+ }, { agent: parsed.agent });
26214
26925
  } catch {
26215
26926
  }
26216
26927
  const remembered = readPaceStateFile(pacingDir2).basis;
@@ -26298,6 +27009,7 @@ async function runPaceVerb(args2, deps = {}) {
26298
27009
  ...decision.until !== null ? { until: decision.until.toISOString() } : {},
26299
27010
  keepFreePercent,
26300
27011
  ...suggestion !== void 0 ? { suggestion } : {},
27012
+ ...limitFile !== null ? { usage: { used: readPhaseUsage(pacingDir2, limitFile.phase), limit: limitFile.limit } } : {},
26301
27013
  ...atLimit ? {
26302
27014
  limit: "reached",
26303
27015
  limitHeld: {
@@ -26445,10 +27157,10 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26445
27157
  const basis = read !== null ? { phase: read.phase, keepFreePercent: read.keepFreePercent } : stored.basis;
26446
27158
  if (basis === null)
26447
27159
  return;
26448
- const write = deps.writeState ?? writePaceState;
27160
+ const write2 = deps.writeState ?? writePaceState;
26449
27161
  if (basis.phase === null) {
26450
27162
  if (before !== null || read !== null)
26451
- write(dir, key, null, read !== null ? basis : null);
27163
+ write2(dir, key, null, read !== null ? basis : null);
26452
27164
  return;
26453
27165
  }
26454
27166
  const working = liveCount(readLiveness(field2(input, "session_id"), field2(input, "cwd") ?? process.cwd(), deps.liveness));
@@ -26475,7 +27187,8 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26475
27187
  }
26476
27188
  }
26477
27189
  const reported = readReportedAccount(key, { home: deps.home });
26478
- 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;
26479
27192
  const decision = paceOf({ number: number4, keepFreePercent: basis.keepFreePercent, learned, reported, now });
26480
27193
  const settled2 = {
26481
27194
  pace: decision.pace,
@@ -26489,9 +27202,9 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
26489
27202
  };
26490
27203
  const told = before === null || before.told === void 0 ? before : { ...before, ...before.told };
26491
27204
  const text = paceChangeText(told, settled2, working, now, basis.keepFreePercent);
26492
- const offer = text === null ? offerText(settled2, learned, reported, now, basis.keepFreePercent) : null;
27205
+ const offer = text === null && build !== null ? offerText(settled2, learned, reported, now, basis.keepFreePercent) : null;
26493
27206
  const after = offer === null ? settled2 : { ...settled2, offeredFor: offeredForNow(settled2.offeredFor, offer.resetsAt, now) };
26494
- write(dir, key, after, read !== null ? basis : null);
27207
+ write2(dir, key, after, read !== null ? basis : null);
26495
27208
  if (text !== null)
26496
27209
  reply.notice(text);
26497
27210
  else if (offer !== null)
@@ -26542,7 +27255,7 @@ function pruneStarts(file2, nowMs) {
26542
27255
  }
26543
27256
  return out;
26544
27257
  }
26545
- function sleepSync2(ms) {
27258
+ function sleepSync3(ms) {
26546
27259
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
26547
27260
  }
26548
27261
  function underStartsLock(dir, giveUpMs, fn) {
@@ -26551,7 +27264,7 @@ function underStartsLock(dir, giveUpMs, fn) {
26551
27264
  while (!tryTakeLockFile(lock, PACE_STARTS_LOCK_STALE_MS)) {
26552
27265
  if (Date.now() >= giveUpAt)
26553
27266
  return void 0;
26554
- sleepSync2(STARTS_LOCK_POLL_MS);
27267
+ sleepSync3(STARTS_LOCK_POLL_MS);
26555
27268
  }
26556
27269
  try {
26557
27270
  return fn();
@@ -26646,9 +27359,9 @@ function paceHoldText(input, toolInput, deps = {}) {
26646
27359
  }
26647
27360
 
26648
27361
  // dist/risky/facts.js
26649
- import { existsSync as existsSync7, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync21, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
26650
- import { homedir as homedir9, tmpdir as tmpdir2 } from "node:os";
26651
- import { basename as pathBasename, dirname as dirname6, isAbsolute as isAbsolute4, join as join14, relative as relative7, resolve as resolve6, sep as sep5 } from "node:path";
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";
26652
27365
 
26653
27366
  // dist/risky/shell.js
26654
27367
  var MAX_DEPTH = 8;
@@ -27828,7 +28541,7 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27828
28541
  }
27829
28542
  vars.enter(own);
27830
28543
  const simple = expandCommand(raw, vars);
27831
- const folder = folderIn(simple);
28544
+ const folder2 = folderIn(simple);
27832
28545
  for (const m of recogniseSimple(simple)) {
27833
28546
  const trusted = m.type === "shrink-record" && m.change.kind === "saved" && !savedTrusted ? { ...m, change: { kind: "emptied" } } : m;
27834
28547
  let shown2 = trusted;
@@ -27844,7 +28557,7 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27844
28557
  }
27845
28558
  matches.push({
27846
28559
  ...shown2,
27847
- ...folder === void 0 ? {} : { folder },
28560
+ ...folder2 === void 0 ? {} : { folder: folder2 },
27848
28561
  ...made.size === 0 ? {} : { made: new Map(made) },
27849
28562
  ...madeInChain.size === 0 ? {} : { madeInChain: new Set(madeInChain) },
27850
28563
  ...removedInChain.size === 0 ? {} : { removedInChain: new Set(removedInChain) },
@@ -27852,9 +28565,9 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27852
28565
  ...vars.stoppedIf.length === 0 ? {} : { stoppedIf: [...vars.stoppedIf] }
27853
28566
  });
27854
28567
  }
27855
- if (surelyRuns(simple, own) && (folder === void 0 || folder.known)) {
28568
+ if (surelyRuns(simple, own) && (folder2 === void 0 || folder2.known)) {
27856
28569
  for (const p of pathsMade(simple)) {
27857
- const key = madeKey(folder?.dirs ?? [], p.word);
28570
+ const key = madeKey(folder2?.dirs ?? [], p.word);
27858
28571
  made.set(key, p.by);
27859
28572
  if (simple.next === "&&")
27860
28573
  madeInChain.add(key);
@@ -27862,12 +28575,12 @@ function readLine(commands, vars, gitEnv, savedTrusted) {
27862
28575
  if (simple.next === "&&" && simple.words[0]?.text === "rmdir") {
27863
28576
  for (const w of simple.words.slice(1))
27864
28577
  if (w.literal && !w.text.startsWith("-"))
27865
- removedInChain.add(madeKey(folder?.dirs ?? [], w));
28578
+ removedInChain.add(madeKey(folder2?.dirs ?? [], w));
27866
28579
  }
27867
28580
  }
27868
28581
  const nextCmd = commands[i + 1];
27869
28582
  const asJoined = simple.next !== void 0 && (nextCmd === void 0 || scopeKey(nextCmd) !== scopeKey(simple)) ? { ...simple, next: void 0 } : simple;
27870
- const after = folderAfter(folder, asJoined, own);
28583
+ const after = folderAfter(folder2, asJoined, own);
27871
28584
  folders.set(scopeKey(simple), after);
27872
28585
  vars.guard = chainCds === null ? null : [...chainCds];
27873
28586
  vars.update(raw, own);
@@ -28860,7 +29573,7 @@ var Env = class {
28860
29573
  constructor(ctx) {
28861
29574
  this.projectDir = realOrSelf(resolve6(ctx.projectDir));
28862
29575
  this.cwd = realOrSelf(resolve6(this.projectDir, ctx.cwd));
28863
- this.home = realOrSelf(resolve6(ctx.home ?? homedir9()));
29576
+ this.home = realOrSelf(resolve6(ctx.home ?? homedir10()));
28864
29577
  this.timeoutMs = ctx.gitTimeoutMs ?? FACTS_GIT_TIMEOUT_MS;
28865
29578
  this.tempDir = realOrSelf(resolve6(ctx.tempDir ?? tmpdir2()));
28866
29579
  const roots = ctx.tempRoots ?? [this.tempDir, tmpdir2(), ...TEMP_ROOTS];
@@ -28884,7 +29597,7 @@ var Env = class {
28884
29597
  return this.description;
28885
29598
  this.description = null;
28886
29599
  try {
28887
- 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"));
28888
29601
  const checked = checkSystemDescription(raw);
28889
29602
  if (checked.ok)
28890
29603
  this.description = checked.description;
@@ -28948,18 +29661,18 @@ function resolveHome(text, home, base) {
28948
29661
  if (text === "~")
28949
29662
  return home;
28950
29663
  if (text.startsWith("~/"))
28951
- return join14(home, text.slice(2));
29664
+ return join15(home, text.slice(2));
28952
29665
  return resolve6(base, text);
28953
29666
  }
28954
29667
  function realTarget(abs) {
28955
29668
  const parent = dirname6(abs);
28956
29669
  if (parent === abs)
28957
29670
  return abs;
28958
- return join14(realOrSelf(parent), pathBasename(abs));
29671
+ return join15(realOrSelf(parent), pathBasename(abs));
28959
29672
  }
28960
29673
  function wordPath(word, base, env) {
28961
29674
  if (word.temp !== void 0) {
28962
- 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;
28963
29676
  return resolve6(`${root}${word.temp.suffix}`);
28964
29677
  }
28965
29678
  return word.literal ? resolveHome(word.text, env.home, base) : null;
@@ -29105,7 +29818,7 @@ function globPath(abs) {
29105
29818
  const next = [];
29106
29819
  if (!/[*?[]/.test(segment)) {
29107
29820
  for (const c of current)
29108
- next.push(join14(c, segment));
29821
+ next.push(join15(c, segment));
29109
29822
  } else {
29110
29823
  const re = segmentPattern(segment);
29111
29824
  for (const c of current) {
@@ -29119,7 +29832,7 @@ function globPath(abs) {
29119
29832
  if (n.startsWith(".") && !segment.startsWith("."))
29120
29833
  continue;
29121
29834
  if (re.test(n))
29122
- next.push(join14(c, n));
29835
+ next.push(join15(c, n));
29123
29836
  }
29124
29837
  }
29125
29838
  }
@@ -29247,7 +29960,7 @@ function findKeeps(filter, roots, repoRoot) {
29247
29960
  const names = filter.names.map((n) => findPattern(n.pattern, n.fold, false));
29248
29961
  const excluded = filter.excluded.map((p) => findPattern(p, false, true));
29249
29962
  const steps = (rel) => {
29250
- const abs = join14(repoRoot, rel);
29963
+ const abs = join15(repoRoot, rel);
29251
29964
  const out = [];
29252
29965
  for (const r of roots) {
29253
29966
  if (!isInside(abs, r.abs))
@@ -29304,11 +30017,11 @@ function walkRoots(ignoredFolders, specs) {
29304
30017
  const under2 = (child, parent) => parent === "." || child === parent || child.startsWith(`${parent}/`);
29305
30018
  const roots = /* @__PURE__ */ new Set();
29306
30019
  for (const raw of ignoredFolders) {
29307
- const folder = raw.replace(/\/+$/, "");
30020
+ const folder2 = raw.replace(/\/+$/, "");
29308
30021
  for (const spec of specs) {
29309
- if (under2(folder, spec))
29310
- roots.add(folder);
29311
- else if (under2(spec, folder))
30022
+ if (under2(folder2, spec))
30023
+ roots.add(folder2);
30024
+ else if (under2(spec, folder2))
29312
30025
  roots.add(spec);
29313
30026
  }
29314
30027
  }
@@ -29319,9 +30032,9 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
29319
30032
  const out = [];
29320
30033
  const stack = [];
29321
30034
  for (const f of folders) {
29322
- if (isDirectory(join14(repoRoot, f)))
30035
+ if (isDirectory(join15(repoRoot, f)))
29323
30036
  stack.push(f);
29324
- else if (pathExists(join14(repoRoot, f)) && isCredentialPath(f))
30037
+ else if (pathExists(join15(repoRoot, f)) && isCredentialPath(f))
29325
30038
  out.push(f);
29326
30039
  }
29327
30040
  let seen = 0;
@@ -29329,7 +30042,7 @@ function credentialFilesIn(folders, repoRoot, deadline, skip) {
29329
30042
  const rel = stack.pop();
29330
30043
  let entries;
29331
30044
  try {
29332
- entries = readdirSync9(join14(repoRoot, rel), { withFileTypes: true });
30045
+ entries = readdirSync9(join15(repoRoot, rel), { withFileTypes: true });
29333
30046
  } catch {
29334
30047
  continue;
29335
30048
  }
@@ -29487,7 +30200,7 @@ function closeFoldsAll(targets, env) {
29487
30200
  return false;
29488
30201
  let at = root;
29489
30202
  for (const seg of rel.split("/")) {
29490
- at = join14(at, seg);
30203
+ at = join15(at, seg);
29491
30204
  try {
29492
30205
  if (lstatSync3(at).isSymbolicLink())
29493
30206
  return false;
@@ -29720,7 +30433,7 @@ function worktreeFiles(m, where, env) {
29720
30433
  if (path === null)
29721
30434
  return null;
29722
30435
  const abs = realOrSelf(path);
29723
- if (!existsSync7(abs))
30436
+ if (!existsSync8(abs))
29724
30437
  return [];
29725
30438
  const top = env.repoRoot(abs);
29726
30439
  if (top === null)
@@ -30070,7 +30783,7 @@ function publishFacts(m, env) {
30070
30783
  const start = startFolder(m, env);
30071
30784
  if (start !== null && PACKAGE_JSON_PROGRAMS.has(m.program)) {
30072
30785
  try {
30073
- const pkg = JSON.parse(readFileSync21(join14(start, "package.json"), "utf-8"));
30786
+ const pkg = JSON.parse(readFileSync21(join15(start, "package.json"), "utf-8"));
30074
30787
  if (typeof pkg["name"] === "string" && typeof pkg["version"] === "string" && pkg["name"] !== "" && pkg["version"] !== "") {
30075
30788
  name = pkg["name"];
30076
30789
  version2 = pkg["version"];
@@ -30189,17 +30902,17 @@ function recordFileAt(abs, env) {
30189
30902
  const row = recordPatternFor(rel);
30190
30903
  return row !== void 0 && row.shrinkAsked !== false ? { abs, root, rel } : null;
30191
30904
  }
30192
- function recordFilesUnder(folder, env) {
30193
- const root = recordRoot(folder, env);
30905
+ function recordFilesUnder(folder2, env) {
30906
+ const root = recordRoot(folder2, env);
30194
30907
  if (root === null)
30195
30908
  return [];
30196
- const relFolder = toSlash(relative7(root, folder));
30909
+ const relFolder = toSlash(relative7(root, folder2));
30197
30910
  const starts = /* @__PURE__ */ new Set();
30198
30911
  for (const { pattern } of PROJECT_RECORD_PATTERNS) {
30199
30912
  if (relFolder === "" || patternOwns(relFolder, pattern))
30200
- starts.add(join14(root, pattern));
30913
+ starts.add(join15(root, pattern));
30201
30914
  else if (patternOwns(pattern, relFolder))
30202
- starts.add(folder);
30915
+ starts.add(folder2);
30203
30916
  }
30204
30917
  const out = [];
30205
30918
  const stack = [...starts];
@@ -30223,7 +30936,7 @@ function recordFilesUnder(folder, env) {
30223
30936
  continue;
30224
30937
  }
30225
30938
  for (const n of names)
30226
- stack.push(join14(p, n));
30939
+ stack.push(join15(p, n));
30227
30940
  } else if (st.isFile()) {
30228
30941
  const r = recordFileAt(p, env);
30229
30942
  if (r !== null)
@@ -30306,7 +31019,11 @@ var PERSON_HOLD_LINE = "Halfcycle paused a risky step before it ran. Nothing cha
30306
31019
  var HELD_STEP_OPENER = "The step was: ";
30307
31020
  var PRECAUTION_OPENER = "It was stopped as a precaution: ";
30308
31021
  var ALLOW_REPLY = "allow";
30309
- var HOLD_AGENT_INSTRUCTION = `If the step is not needed, carry on without it; there is no need to mention it. If it is needed, ask the person in chat, in one plain sentence, whether to run it, and tell them to reply "${ALLOW_REPLY}" to run it once, or anything else to skip it. Do not try it again until they answer.`;
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);
30310
31027
  var NOTHING_LOST_ON_NO = "Nothing is lost if you say no.";
30311
31028
  var IF_YES = "If you say yes, ";
30312
31029
  var DESCRIBED_TOOLS = /* @__PURE__ */ new Set(["Bash", "Agent"]);
@@ -30342,6 +31059,9 @@ function questionFor(facts, description) {
30342
31059
  const shown2 = staked.slice(0, MAX_LISTED).map((f) => `${whatOf(f)} ${IF_YES}${lostOf(f)}`);
30343
31060
  return `${PAUSED_OPENER.trimEnd().slice(0, -1)}. ${shown2.join(" ")}${moreLine(staked.length - shown2.length)} ${reason} ${NOTHING_LOST_ON_NO}`;
30344
31061
  }
31062
+ function heldAgentText(held, stepNumber) {
31063
+ return `${held.facts} ${holdAgentInstruction(stepNumber)}`;
31064
+ }
30345
31065
  function refusalFor(facts, description) {
30346
31066
  const staked = orderedStaked(facts);
30347
31067
  if (staked.length === 0)
@@ -30352,9 +31072,11 @@ function refusalFor(facts, description) {
30352
31072
  return heldStep(steps, description, shown2.map(whyOf).join("; "));
30353
31073
  }
30354
31074
  function heldStep(steps, description, why) {
31075
+ const facts = `${HELD_STEP_OPENER}${steps}. ${reasonSentence(description)} ${PRECAUTION_OPENER}${why}.`;
30355
31076
  return {
30356
31077
  person: PERSON_HOLD_LINE,
30357
- agent: `${HELD_STEP_OPENER}${steps}. ${reasonSentence(description)} ${PRECAUTION_OPENER}${why}. ${HOLD_AGENT_INSTRUCTION}`,
31078
+ agent: `${facts} ${HOLD_AGENT_INSTRUCTION}`,
31079
+ facts,
30358
31080
  step: steps
30359
31081
  };
30360
31082
  }
@@ -30769,14 +31491,85 @@ function isOneCall(call) {
30769
31491
 
30770
31492
  // dist/risky/allow-once.js
30771
31493
  import { createHash as createHash5 } from "node:crypto";
30772
- import { appendFileSync as appendFileSync4, readFileSync as readFileSync22, renameSync as renameSync11, unlinkSync as unlinkSync9, writeFileSync as writeFileSync13 } from "node:fs";
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";
30773
31496
  var ALLOW_ONCE_MS = 5 * 60 * 1e3;
30774
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}))?$`);
30775
31503
  var STEP_MAX_LENGTH = 1e3;
30776
- function isAllowReply(prompt) {
30777
- 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
+ }
30778
31540
  }
30779
- 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 = {}) {
30780
31573
  const toolName = input["tool_name"];
30781
31574
  const toolInput = input["tool_input"];
30782
31575
  if (typeof toolName !== "string" || toolName === "")
@@ -30787,7 +31580,17 @@ function stepKey(input, staked) {
30787
31580
  if (DESCRIBED_TOOLS.has(toolName))
30788
31581
  delete rest["description"];
30789
31582
  const cwd = typeof input["cwd"] === "string" ? input["cwd"] : "";
30790
- 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;
30791
31594
  }
30792
31595
  function canonical(value) {
30793
31596
  if (Array.isArray(value))
@@ -30798,37 +31601,166 @@ function canonical(value) {
30798
31601
  }
30799
31602
  return JSON.stringify(value) ?? "null";
30800
31603
  }
30801
- 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) {
30802
31691
  const paths = allowOncePaths(sessionId, deps.dir);
30803
31692
  if (paths === null)
30804
31693
  return;
30805
- 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
+ };
30806
31702
  try {
30807
- appendFileSync4(paths.held, `${JSON.stringify(record2)}
31703
+ appendFileSync5(paths.held, `${JSON.stringify(record2)}
30808
31704
  `, { mode: 384 });
30809
31705
  } catch {
30810
31706
  return;
30811
31707
  }
30812
31708
  }
30813
- function armAllowStep(sessionId, deps = {}) {
31709
+ function armAllowStep(sessionId, deps = {}, stepNumber = null) {
31710
+ const none = (outcome) => ({ outcome, step: null, paused: [] });
30814
31711
  try {
30815
31712
  const paths = allowOncePaths(sessionId, deps.dir);
30816
31713
  if (paths === null)
30817
- return { outcome: "nothing-held", step: null };
31714
+ return none(stepNumber === null ? "nothing-held" : "not-paused");
31715
+ const session = sessionId;
30818
31716
  const now = nowOf(deps);
30819
- const held = claimHeld(paths.held, sessionId, now);
30820
- const keys = [...new Set(held.map((r) => r.key))];
30821
- if (keys.length === 0)
30822
- return { outcome: "nothing-held", step: null };
30823
- if (keys.length > 1)
30824
- return { outcome: "several-held", step: null };
30825
- const step = held[held.length - 1].step ?? null;
30826
- const saved = writeRecord(paths.allow, { version: 1, session: sessionId, key: keys[0], at: now });
30827
- return saved ? { outcome: "armed", step } : { outcome: "not-saved", step: null };
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 };
30828
31743
  } catch {
30829
- return { outcome: "not-saved", step: null };
31744
+ return none("not-saved");
30830
31745
  }
30831
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
+ }
30832
31764
  function claimHeld(path, session, now) {
30833
31765
  const claimed = `${path}.${process.pid}.${now}.claim`;
30834
31766
  try {
@@ -30851,6 +31783,16 @@ function claimHeld(path, session, now) {
30851
31783
  }
30852
31784
  return records;
30853
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
+ }
30854
31796
  function isStep(v) {
30855
31797
  return typeof v === "string" && v !== "" && v.length <= STEP_MAX_LENGTH;
30856
31798
  }
@@ -30905,12 +31847,23 @@ function parseRecord(text, session) {
30905
31847
  if (typeof r["at"] !== "number")
30906
31848
  return null;
30907
31849
  const step = r["step"];
30908
- 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
+ };
30909
31859
  }
30910
31860
  function writeRecord(path, record2) {
31861
+ return writeWhole2(path, JSON.stringify(record2));
31862
+ }
31863
+ function writeWhole2(path, body) {
30911
31864
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
30912
31865
  try {
30913
- writeFileSync13(tmp, JSON.stringify(record2), { mode: 384 });
31866
+ writeFileSync13(tmp, body, { mode: 384 });
30914
31867
  renameSync11(tmp, path);
30915
31868
  return true;
30916
31869
  } catch {
@@ -31008,7 +31961,7 @@ function decideAutonomous(facts, mayPublishNow) {
31008
31961
 
31009
31962
  // dist/risky/own-copy.js
31010
31963
  import { readFileSync as readFileSync23 } from "node:fs";
31011
- 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";
31012
31965
  var OWN_COPY_START_TOOL = "Agent";
31013
31966
  var OWN_COPY_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
31014
31967
  var OWN_COPY_ISOLATION = "worktree";
@@ -31069,7 +32022,7 @@ function typeCanChangeFiles(subagentType, deps) {
31069
32022
  return true;
31070
32023
  if (AGENT_FILE_NAME.test(type)) {
31071
32024
  for (const base of [deps.projectDir, deps.home]) {
31072
- const text = readOr(deps, join15(base, ".claude", "agents", `${type}.md`));
32025
+ const text = readOr(deps, join16(base, ".claude", "agents", `${type}.md`));
31073
32026
  if (text === null)
31074
32027
  continue;
31075
32028
  const tools = frontmatterTools(text);
@@ -31175,31 +32128,31 @@ function judgeChange(input, call, deps) {
31175
32128
 
31176
32129
  // dist/risky/save.js
31177
32130
  import { randomBytes } from "node:crypto";
31178
- import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync9, readdirSync as readdirSync10, readFileSync as readFileSync24, readlinkSync, realpathSync as realpathSync8, renameSync as renameSync12, rmSync as rmSync3, statSync as statSync8, symlinkSync, writeFileSync as writeFileSync14 } from "node:fs";
31179
- import { homedir as homedir10 } from "node:os";
31180
- import { dirname as dirname7, isAbsolute as isAbsolute5, join as join16, resolve as resolve7 } from "node:path";
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";
31181
32134
  var SAVE_LIMIT_BYTES = 2e8;
31182
32135
  var SAVE_BUDGET_MS = 5e3;
31183
32136
  function notSaved(reason) {
31184
32137
  return { saved: false, reason };
31185
32138
  }
31186
- function savedFolderOf(engagementId, home = homedir10()) {
32139
+ function savedFolderOf(engagementId, home = homedir11()) {
31187
32140
  if (!isEngagementId(engagementId))
31188
32141
  return null;
31189
- 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);
31190
32143
  }
31191
32144
  function saveFirst(staked, ctx) {
31192
32145
  const now = ctx.now ?? Date.now;
31193
32146
  const deadline = now() + (ctx.budgetMs ?? SAVE_BUDGET_MS);
31194
- const folder = savedFolderOf(ctx.engagementId, ctx.home);
31195
- if (folder === null)
32147
+ const folder2 = savedFolderOf(ctx.engagementId, ctx.home);
32148
+ if (folder2 === null)
31196
32149
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
31197
32150
  const plan = planOf(staked);
31198
32151
  if (!plan.ok)
31199
32152
  return notSaved(plan.reason);
31200
32153
  const g = new SaveGit(plan.root, deadline, now);
31201
32154
  const top = g.run(["rev-parse", "--show-toplevel"]);
31202
- if (!top.ok || realOrSelf2(top.value.trim()) !== plan.root)
32155
+ if (!top.ok || realOrSelf3(top.value.trim()) !== plan.root)
31203
32156
  return notSaved(AUTONOMY_SAVE_NOT_COVERED_REASON);
31204
32157
  const head = g.run(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
31205
32158
  const headCommit2 = head.ok ? head.value.trim() : "";
@@ -31214,23 +32167,23 @@ function saveFirst(staked, ctx) {
31214
32167
  return notSaved(found2.reason);
31215
32168
  let saveDir = null;
31216
32169
  try {
31217
- const id = freshId(folder, ctx.newId);
32170
+ const id = freshId(folder2, ctx.newId);
31218
32171
  if (id === null)
31219
32172
  return notSaved(AUTONOMY_SAVE_FAILED_REASON);
31220
- saveDir = join16(folder, id);
32173
+ saveDir = join17(folder2, id);
31221
32174
  const repository = repositoryOf(g);
31222
32175
  if (repository === null)
31223
32176
  return failed(saveDir);
31224
- writeFileSync14(join16(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
32177
+ writeFileSync14(join17(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
31225
32178
  `, { mode: 384 });
31226
- const privateIndex = join16(saveDir, PRIVATE_INDEX_NAME);
32179
+ const privateIndex = join17(saveDir, PRIVATE_INDEX_NAME);
31227
32180
  const commit = saveCommit(id, headCommit2, found2.tracked, plan.lines, privateIndex, g, now());
31228
32181
  rmSync3(privateIndex, { force: true });
31229
32182
  if (commit === null)
31230
32183
  return failed(saveDir);
31231
- const copiesDir = join16(saveDir, SAVED_COPIES_DIR_NAME);
32184
+ const copiesDir = join17(saveDir, SAVED_COPIES_DIR_NAME);
31232
32185
  for (const rel of found2.copies) {
31233
- if (now() > deadline || !copyOne(join16(plan.root, rel), join16(copiesDir, rel)))
32186
+ if (now() > deadline || !copyOne(join17(plan.root, rel), join17(copiesDir, rel)))
31234
32187
  return failed(saveDir);
31235
32188
  }
31236
32189
  const description = savedSchema.safeParse({
@@ -31246,7 +32199,7 @@ function saveFirst(staked, ctx) {
31246
32199
  });
31247
32200
  if (!description.success)
31248
32201
  return failed(saveDir);
31249
- const file2 = join16(saveDir, SAVED_FILE_NAME);
32202
+ const file2 = join17(saveDir, SAVED_FILE_NAME);
31250
32203
  writeFileSync14(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
31251
32204
  `, { mode: 384 });
31252
32205
  renameSync12(`${file2}.part`, file2);
@@ -31277,7 +32230,7 @@ function planOf(staked) {
31277
32230
  const t = f.save;
31278
32231
  if (t === void 0)
31279
32232
  return { ok: false, reason: f.cannotTell === void 0 ? AUTONOMY_SAVE_NOT_COVERED_REASON : AUTONOMY_COULD_NOT_TELL_REASON };
31280
- const r = realOrSelf2(resolve7(t.repoRoot));
32233
+ const r = realOrSelf3(resolve8(t.repoRoot));
31281
32234
  if (root === null)
31282
32235
  root = r;
31283
32236
  else if (root !== r)
@@ -31296,7 +32249,7 @@ function planOf(staked) {
31296
32249
  function isInsideRel(p) {
31297
32250
  if (p === ".")
31298
32251
  return true;
31299
- if (p === "" || isAbsolute5(p) || p.includes("\\"))
32252
+ if (p === "" || isAbsolute6(p) || p.includes("\\"))
31300
32253
  return false;
31301
32254
  return p.split("/").every((s) => s !== "" && s !== "." && s !== "..");
31302
32255
  }
@@ -31326,7 +32279,7 @@ function filesOf(plan, g, deadline) {
31326
32279
  trackedAll.add(path);
31327
32280
  if (!reached(path, plan.specs))
31328
32281
  continue;
31329
- const st = lstatOrNull(join16(plan.root, path));
32282
+ const st = lstatOrNull(join17(plan.root, path));
31330
32283
  if (st !== null && (st.isFile() || st.isSymbolicLink()))
31331
32284
  tracked.add(path);
31332
32285
  }
@@ -31338,7 +32291,7 @@ function filesOf(plan, g, deadline) {
31338
32291
  const rel = stack.pop();
31339
32292
  if (rel === ".git")
31340
32293
  continue;
31341
- const abs = rel === "." ? plan.root : join16(plan.root, rel);
32294
+ const abs = rel === "." ? plan.root : join17(plan.root, rel);
31342
32295
  const st = lstatOrNull(abs);
31343
32296
  if (st === null)
31344
32297
  continue;
@@ -31376,13 +32329,13 @@ function copyOne(src, dst) {
31376
32329
  }
31377
32330
  copyFileSync(src, dst, fsConstants.COPYFILE_EXCL | fsConstants.COPYFILE_FICLONE);
31378
32331
  chmodSync2(dst, st.mode & 4095);
31379
- return statSync8(dst).size === st.size;
32332
+ return statSync9(dst).size === st.size;
31380
32333
  }
31381
32334
  function saveCommit(id, head, tracked, lines2, privateIndex, g, at) {
31382
32335
  const indexPath = g.run(["rev-parse", "--git-path", "index"]);
31383
32336
  if (!indexPath.ok)
31384
32337
  return null;
31385
- const projectIndex = resolve7(g.root, indexPath.value.trim());
32338
+ const projectIndex = resolve8(g.root, indexPath.value.trim());
31386
32339
  const env = commitEnv(privateIndex, at);
31387
32340
  if (lstatOrNull(projectIndex) !== null)
31388
32341
  copyFileSync(projectIndex, privateIndex);
@@ -31444,14 +32397,14 @@ var SaveGit = class {
31444
32397
  });
31445
32398
  }
31446
32399
  };
31447
- function freshId(folder, newId) {
31448
- mkdirSync9(folder, { recursive: true, mode: 448 });
32400
+ function freshId(folder2, newId) {
32401
+ mkdirSync9(folder2, { recursive: true, mode: 448 });
31449
32402
  for (let i = 0; i < 5; i++) {
31450
32403
  const id = newId?.() ?? defaultId();
31451
32404
  if (!savedIdSchema.safeParse(id).success)
31452
32405
  return null;
31453
32406
  try {
31454
- mkdirSync9(join16(folder, id), { mode: 448 });
32407
+ mkdirSync9(join17(folder2, id), { mode: 448 });
31455
32408
  return id;
31456
32409
  } catch (e) {
31457
32410
  if (e.code !== "EEXIST")
@@ -31472,19 +32425,19 @@ function repositoryOf(g) {
31472
32425
  if (!r.ok)
31473
32426
  return null;
31474
32427
  const dir = r.value.trim();
31475
- return dir === "" ? null : realOrSelf2(resolve7(g.root, dir));
32428
+ return dir === "" ? null : realOrSelf3(resolve8(g.root, dir));
31476
32429
  }
31477
32430
  function markedRepository(dir) {
31478
32431
  try {
31479
- 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();
31480
32433
  return text === "" ? null : text;
31481
32434
  } catch {
31482
32435
  return null;
31483
32436
  }
31484
32437
  }
31485
32438
  function pruneSaves(root, engagementId, ctx = {}) {
31486
- const folder = savedFolderOf(engagementId, ctx.home);
31487
- if (folder === null)
32439
+ const folder2 = savedFolderOf(engagementId, ctx.home);
32440
+ if (folder2 === null)
31488
32441
  return;
31489
32442
  const now = ctx.now ?? Date.now;
31490
32443
  const g = new SaveGit(root, ctx.deadline ?? now() + SAVE_BUDGET_MS, now);
@@ -31501,7 +32454,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
31501
32454
  const repository = repositoryOf(g);
31502
32455
  let names = [];
31503
32456
  try {
31504
- names = readdirSync10(folder);
32457
+ names = readdirSync10(folder2);
31505
32458
  } catch {
31506
32459
  names = [];
31507
32460
  }
@@ -31510,7 +32463,7 @@ function pruneSaves(root, engagementId, ctx = {}) {
31510
32463
  if (!savedIdSchema.safeParse(id).success)
31511
32464
  continue;
31512
32465
  here.add(id);
31513
- const dir = join16(folder, id);
32466
+ const dir = join17(folder2, id);
31514
32467
  const st = lstatOrNull(dir);
31515
32468
  const answer = savedPruneAnswer({
31516
32469
  refMadeAt: refs.get(id) ?? null,
@@ -31539,9 +32492,9 @@ function lstatOrNull(p) {
31539
32492
  return null;
31540
32493
  }
31541
32494
  }
31542
- function realOrSelf2(p) {
32495
+ function realOrSelf3(p) {
31543
32496
  try {
31544
- return realpathSync8(p);
32497
+ return realpathSync9(p);
31545
32498
  } catch {
31546
32499
  return p;
31547
32500
  }
@@ -31550,12 +32503,12 @@ function realOrSelf2(p) {
31550
32503
  // dist/autonomy-state.js
31551
32504
  import { spawnSync as spawnSync2 } from "node:child_process";
31552
32505
  import { chmodSync as chmodSync3, mkdirSync as mkdirSync10, readFileSync as readFileSync25, renameSync as renameSync13, rmSync as rmSync4, writeFileSync as writeFileSync15 } from "node:fs";
31553
- import { homedir as homedir11, platform as platform2 } from "node:os";
31554
- import { dirname as dirname8, join as join17 } from "node:path";
32506
+ import { homedir as homedir12, platform as platform2 } from "node:os";
32507
+ import { dirname as dirname8, join as join18 } from "node:path";
31555
32508
  var AUTONOMY_STATE_FILENAME = "autonomy.json";
31556
32509
  var AUTONOMY_STATE_DEADLINE_MS = 1500;
31557
32510
  function autonomyStateFilePath(engagementId, home) {
31558
- 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);
31559
32512
  }
31560
32513
  function readCachedAutonomy(engagementId, nowMs, home) {
31561
32514
  if (!isEngagementId(engagementId))
@@ -31583,6 +32536,7 @@ function confirmMayPublishNow(opts) {
31583
32536
  timeout: opts.timeoutMs,
31584
32537
  windowsHide: true
31585
32538
  });
32539
+ recordChildRequest(url2, ["Authorization"], r.status === 0);
31586
32540
  if (r.status !== 0 || typeof r.stdout !== "string")
31587
32541
  return false;
31588
32542
  const parsed = autonomyStateSchema.safeParse(JSON.parse(r.stdout));
@@ -31607,7 +32561,7 @@ function deleteAutonomyStateFile(engagementId, home) {
31607
32561
  }
31608
32562
  function writeAutonomyStateFile(engagementId, file2, home) {
31609
32563
  const target = autonomyStateFilePath(engagementId, home);
31610
- const temp = join17(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
32564
+ const temp = join18(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
31611
32565
  try {
31612
32566
  mkdirSync10(dirname8(target), { recursive: true, mode: 448 });
31613
32567
  writeFileSync15(temp, `${JSON.stringify(file2)}
@@ -31669,7 +32623,7 @@ async function cacheAutonomySetting(opts) {
31669
32623
  }
31670
32624
 
31671
32625
  // dist/risky/patch-calls.js
31672
- import { isAbsolute as isAbsolute6, resolve as resolve8 } from "node:path";
32626
+ import { isAbsolute as isAbsolute7, resolve as resolve9 } from "node:path";
31673
32627
  var SHELL_TOOL = "Bash";
31674
32628
  function toolCallsOf(input, profile) {
31675
32629
  const toolName = input["tool_name"];
@@ -31692,7 +32646,7 @@ function toolCallsOf(input, profile) {
31692
32646
  const ops = patchOperations(command);
31693
32647
  if (ops === null || ops.length === 0)
31694
32648
  return null;
31695
- 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;
31696
32650
  const calls = [];
31697
32651
  for (const op of ops) {
31698
32652
  const made = callsFor(op, cwd);
@@ -31730,9 +32684,9 @@ function callsFor(op, cwd) {
31730
32684
  }
31731
32685
  }
31732
32686
  function placed(path, cwd) {
31733
- if (isAbsolute6(path))
31734
- return resolve8(path);
31735
- return cwd === null ? null : resolve8(cwd, path);
32687
+ if (isAbsolute7(path))
32688
+ return resolve9(path);
32689
+ return cwd === null ? null : resolve9(cwd, path);
31736
32690
  }
31737
32691
  function shellWord(text) {
31738
32692
  return `'${text.split("'").join("'\\''")}'`;
@@ -31740,7 +32694,7 @@ function shellWord(text) {
31740
32694
 
31741
32695
  // dist/hooks/pre-tool-use.js
31742
32696
  import { performance } from "node:perf_hooks";
31743
- import { homedir as homedir12 } from "node:os";
32697
+ import { homedir as homedir13 } from "node:os";
31744
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.";
31745
32699
  var PRE_TOOL_USE_LIMIT_MS = (HALFCYCLE_HOOK_WIRING.find((e) => e.event === "PreToolUse")?.timeout ?? 10) * 1e3;
31746
32700
  var AUTONOMY_REPLY_MARGIN_MS = 1e3;
@@ -31811,6 +32765,11 @@ function runPreToolUse(deps) {
31811
32765
  if (held !== null)
31812
32766
  return `${preToolUseReplyText("refuse", held)}
31813
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
+ });
31814
32773
  }
31815
32774
  return own.kind === "unknown" ? ownCopyNotice(input, deps) : "";
31816
32775
  }
@@ -31819,16 +32778,19 @@ function runPreToolUse(deps) {
31819
32778
  return `${preToolUseReplyText("ask", answer.question)}
31820
32779
  `;
31821
32780
  }
32781
+ let agentText = answer.refusal.agent;
31822
32782
  if (allowableByTyping(call, answer.staked)) {
31823
- const key = stepKey(input, answer.staked);
32783
+ const key = stepKey(input, answer.staked, deps.gitTimeoutMs === void 0 ? {} : { gitTimeoutMs: deps.gitTimeoutMs });
31824
32784
  if (key !== null) {
31825
32785
  const allowOnce = { ...deps.liveness?.dir === void 0 ? {} : { dir: deps.liveness.dir }, now: () => at.getTime() };
31826
32786
  if (spendAllow(input["session_id"], key, allowOnce))
31827
32787
  return "";
31828
- 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);
31829
32791
  }
31830
32792
  }
31831
- return `${preToolUseReplyText("refuse", answer.refusal.person, answer.refusal.agent)}
32793
+ return `${preToolUseReplyText("refuse", answer.refusal.person, agentText)}
31832
32794
  `;
31833
32795
  } catch {
31834
32796
  return "";
@@ -31956,7 +32918,7 @@ function ownCopyOf(input, call, deps) {
31956
32918
  const walk2 = deps.walk ?? deps.liveness?.walk;
31957
32919
  return judgeOwnCopy(input, call, {
31958
32920
  projectDir: projectDirOf(deps),
31959
- home: deps.home ?? homedir12(),
32921
+ home: deps.home ?? homedir13(),
31960
32922
  ...walk2 === void 0 ? {} : { walk: walk2 },
31961
32923
  ...deps.liveness === void 0 ? {} : { liveness: deps.liveness }
31962
32924
  });
@@ -31965,8 +32927,8 @@ function ownCopyOf(input, call, deps) {
31965
32927
  }
31966
32928
  }
31967
32929
  function ownCopyNotice(input, deps) {
31968
- const folder = stringOf(input, "cwd") ?? projectDirOf(deps);
31969
- return firstNoticeThisSession("outside-own-copy-unknown", folder, stringOf(input, "session_id"), deps.liveness?.dir) ? `${preToolUseNoticeText(OWN_COPY_UNKNOWN_NOTICE)}
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)}
31970
32932
  ` : "";
31971
32933
  }
31972
32934
  function parseObject2(text) {
@@ -31983,14 +32945,14 @@ function stringOf(input, key) {
31983
32945
  }
31984
32946
  function unreadable(deps, input) {
31985
32947
  const session = (input === null ? void 0 : stringOf(input, "session_id")) ?? /"session_id"\s*:\s*"([^"\\]+)"/.exec(deps.input)?.[1];
31986
- const folder = (input === null ? void 0 : stringOf(input, "cwd")) ?? projectDirOf(deps);
31987
- return firstNoticeThisSession("pre-tool-use-unreadable", folder, session) ? `${preToolUseNoticeText(UNREADABLE_INPUT_NOTICE)}
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)}
31988
32950
  ` : "";
31989
32951
  }
31990
32952
 
31991
32953
  // dist/hooks/session-diff.js
31992
32954
  import { unlinkSync as unlinkSync10 } from "node:fs";
31993
- import { join as join18 } from "node:path";
32955
+ import { join as join19 } from "node:path";
31994
32956
  var STOP_HOOK_EVENT = {
31995
32957
  stop: "Stop",
31996
32958
  "subagent-stop": "SubagentStop"
@@ -32042,7 +33004,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32042
33004
  }
32043
33005
  const repoRoot = repoRootResult.value;
32044
33006
  const sessionId = input.session_id;
32045
- const folder = realPathOf(repoRoot);
33007
+ const folder2 = realPathOf(repoRoot);
32046
33008
  orphanSweep(repoRoot, (ref) => refExists(ref, repoRoot), sessionId, Date.now(), whichCopy(process.cwd())?.common ?? null);
32047
33009
  const base = resolveDiffBase2(repoRoot, sessionId);
32048
33010
  const diffBase = base.kind;
@@ -32055,12 +33017,12 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32055
33017
  const candidates = ownEditedFiles(repoRoot, sessionId).filter((relPath) => !tracked.has(relPath));
32056
33018
  const untracked = getUntrackedFiles(repoRoot, candidates);
32057
33019
  const created = untracked.ok ? untracked.value.filter((relPath) => !tracked.has(relPath)) : [];
32058
- const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join18(repoRoot, relPath), repoRoot);
33020
+ const getDiff = (relPath) => tracked.has(relPath) ? collected.getDiff(relPath) : getSingleFileDiff(join19(repoRoot, relPath), repoRoot);
32059
33021
  const { evaluated } = partitionInstallerOutput(collected.files);
32060
33022
  const { evaluated: createdEvaluated } = partitionInstallerOutput(created);
32061
33023
  const changedFiles2 = [...ownChangedFiles(repoRoot, evaluated, sessionId), ...createdEvaluated];
32062
33024
  if (changedFiles2.length === 0) {
32063
- queueTurnEnd(keepBooks(sessionId, folder, { kind: "no-own-changes" }, kind === "stop"), reply);
33025
+ queueTurnEnd(keepBooks(sessionId, folder2, { kind: "no-own-changes" }, kind === "stop"), reply);
32064
33026
  return;
32065
33027
  }
32066
33028
  const fileDiffEntries = changedFiles2.map((relPath) => ({
@@ -32071,7 +33033,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32071
33033
  const digest = digestChangeState(baseKey, fileDiffEntries);
32072
33034
  const prior = readDedupe(repoRoot, sessionId);
32073
33035
  if (prior && prior.base === baseKey && prior.digest === digest) {
32074
- queueTurnEnd(keepBooks(sessionId, folder, { kind: "unchanged" }, kind === "stop"), reply);
33036
+ queueTurnEnd(keepBooks(sessionId, folder2, { kind: "unchanged" }, kind === "stop"), reply);
32075
33037
  return;
32076
33038
  }
32077
33039
  const context = loadChangeSetContext(repoRoot);
@@ -32151,12 +33113,12 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
32151
33113
  warn: (msg) => process.stderr.write(msg + "\n")
32152
33114
  });
32153
33115
  writeDedupe(repoRoot, sessionId, { base: baseKey, digest });
32154
- 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);
32155
33117
  }
32156
- function keepBooks(sessionId, folder, outcome, topLevel) {
33118
+ function keepBooks(sessionId, folder2, outcome, topLevel) {
32157
33119
  const books = () => {
32158
33120
  const before = readFindings(sessionId);
32159
- 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());
32160
33122
  if (before !== null && JSON.stringify(before) !== JSON.stringify(file2)) {
32161
33123
  if (file2.findings.length > 0)
32162
33124
  writeFindings(file2);
@@ -32181,7 +33143,7 @@ function queueTurnEnd(words, reply) {
32181
33143
  if (words !== null)
32182
33144
  reply.notice(words);
32183
33145
  }
32184
- function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder) {
33146
+ function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder2) {
32185
33147
  const notRun = notRunMessage((envelope.notRun ?? []).map((n) => n.reason));
32186
33148
  const outcome = {
32187
33149
  kind: "evaluated",
@@ -32190,7 +33152,7 @@ function actOnEnvelope3(envelope, kind, neverBlock, reply, sessionId, folder) {
32190
33152
  };
32191
33153
  const topLevel = kind === "stop";
32192
33154
  if (envelope.blocking && !neverBlock) {
32193
- keepBooks(sessionId, folder, outcome, false);
33155
+ keepBooks(sessionId, folder2, outcome, false);
32194
33156
  const reason = blockReason(envelope.guardsFired.map((g) => g.explanation));
32195
33157
  reply.block(notRun ? `${reason}
32196
33158
  ${notRun}` : reason, stoppedMessage(blockingExplanations(envelope.guardsFired)));
@@ -32198,7 +33160,7 @@ ${notRun}` : reason, stoppedMessage(blockingExplanations(envelope.guardsFired)))
32198
33160
  if (envelope.blocking && neverBlock) {
32199
33161
  reply.notice(couldNotFixMessage(blockingExplanations(envelope.guardsFired)));
32200
33162
  }
32201
- queueTurnEnd(keepBooks(sessionId, folder, outcome, topLevel), reply);
33163
+ queueTurnEnd(keepBooks(sessionId, folder2, outcome, topLevel), reply);
32202
33164
  if (notRun)
32203
33165
  reply.notice(notRun);
32204
33166
  }
@@ -32219,9 +33181,9 @@ async function readHookStdin() {
32219
33181
  }
32220
33182
  }
32221
33183
  function readStdin2() {
32222
- return new Promise((resolve11) => {
33184
+ return new Promise((resolve12) => {
32223
33185
  if (process.stdin.isTTY) {
32224
- resolve11("");
33186
+ resolve12("");
32225
33187
  return;
32226
33188
  }
32227
33189
  const chunks = [];
@@ -32231,7 +33193,7 @@ function readStdin2() {
32231
33193
  return;
32232
33194
  settled2 = true;
32233
33195
  clearTimeout(timer);
32234
- resolve11(Buffer.concat(chunks).toString("utf-8"));
33196
+ resolve12(Buffer.concat(chunks).toString("utf-8"));
32235
33197
  };
32236
33198
  const timer = setTimeout(done, HOOK_STDIN_GRACE_MS);
32237
33199
  process.stdin.on("data", (chunk) => chunks.push(chunk));
@@ -32258,19 +33220,19 @@ async function runStop(observeDeps = {}, timeDeps = {}, paceDeps = {}, profile =
32258
33220
  recordTimeEvent("turn-end", input);
32259
33221
  await runSessionDiff(input, "stop", readSeatConfiguration, async (reply) => {
32260
33222
  await observeChanges(input.session_id, reply, observeDeps);
32261
- await sendTimeRecord(timeDeps);
33223
+ await sendTimeRecord(timeDeps, { agent: profile.id });
32262
33224
  await paceAtTurnEnd(input, reply, { configFolder: profile.configFolder, ...paceDeps });
32263
33225
  }, profile);
32264
33226
  }
32265
33227
 
32266
33228
  // dist/relayed-choices.js
32267
33229
  import { mkdirSync as mkdirSync11, readFileSync as readFileSync26, renameSync as renameSync14, rmSync as rmSync5, writeFileSync as writeFileSync16 } from "node:fs";
32268
- import { homedir as homedir13 } from "node:os";
32269
- import { dirname as dirname9, join as join19 } from "node:path";
33230
+ import { homedir as homedir14 } from "node:os";
33231
+ import { dirname as dirname9, join as join20 } from "node:path";
32270
33232
  function relayedChoicesFilePath(engagementId, home) {
32271
33233
  if (!isEngagementId(engagementId))
32272
33234
  return null;
32273
- 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);
32274
33236
  }
32275
33237
  function readRelayedChoices(engagementId, phase, home) {
32276
33238
  const path = relayedChoicesFilePath(engagementId, home);
@@ -32306,7 +33268,7 @@ function recordRelayedChoice(opts) {
32306
33268
  });
32307
33269
  if (!file2.success)
32308
33270
  return false;
32309
- const temp = join19(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
33271
+ const temp = join20(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
32310
33272
  try {
32311
33273
  mkdirSync11(dirname9(target), { recursive: true, mode: 448 });
32312
33274
  writeFileSync16(temp, `${JSON.stringify(file2.data)}
@@ -32535,19 +33497,19 @@ function matchReviewPhraseShape(shape, reviews) {
32535
33497
 
32536
33498
  // dist/hooks/shown-record.js
32537
33499
  import { chmodSync as chmodSync4, readFileSync as readFileSync27, renameSync as renameSync15, unlinkSync as unlinkSync11, writeFileSync as writeFileSync17 } from "node:fs";
32538
- import { homedir as homedir14, platform as platform3 } from "node:os";
32539
- import { join as join20 } from "node:path";
33500
+ import { homedir as homedir15, platform as platform3 } from "node:os";
33501
+ import { join as join21 } from "node:path";
32540
33502
  function recordDir(engagementId, home) {
32541
33503
  if (!isEngagementId(engagementId))
32542
33504
  return null;
32543
- return join20(home ?? homedir14(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId);
33505
+ return join21(home ?? homedir15(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId);
32544
33506
  }
32545
33507
  function readShownRecord(engagementId, home) {
32546
33508
  const dir = recordDir(engagementId, home);
32547
33509
  if (dir === null)
32548
33510
  return { entries: [], reviews: [] };
32549
33511
  try {
32550
- return parseShownRecord(readFileSync27(join20(dir, SHOWN_PLANS_FILENAME), "utf-8"));
33512
+ return parseShownRecord(readFileSync27(join21(dir, SHOWN_PLANS_FILENAME), "utf-8"));
32551
33513
  } catch {
32552
33514
  return { entries: [], reviews: [] };
32553
33515
  }
@@ -32568,7 +33530,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
32568
33530
  const dir = recordDir(engagementId, home);
32569
33531
  if (dir === null)
32570
33532
  return false;
32571
- const path = join20(dir, SHOWN_PLANS_FILENAME);
33533
+ const path = join21(dir, SHOWN_PLANS_FILENAME);
32572
33534
  const current = parseShownRecord(readFileSync27(path, "utf-8"));
32573
33535
  const at = current.reviews.findIndex((r) => r.phase === listed.phase && r.shownAt === listed.shownAt && r.reviewVersion === listed.reviewVersion);
32574
33536
  if (at === -1)
@@ -32576,7 +33538,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
32576
33538
  const acts = new Map(decided.map((d) => [d.part, d.act]));
32577
33539
  const reviews = current.reviews.map((review, i) => i === at ? { ...review, reviewVersion, parts: review.parts.map((p) => movedPart(p, acts.get(p.part))) } : review);
32578
33540
  const body = serialiseShownRecord({ entries: current.entries, reviews });
32579
- temp = join20(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
33541
+ temp = join21(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
32580
33542
  writeFileSync17(temp, body, { mode: 384 });
32581
33543
  if (platform3() !== "win32")
32582
33544
  chmodSync4(temp, 384);
@@ -32960,8 +33922,16 @@ function allowArmedLine(step = null) {
32960
33922
  function allowNothingHeldLine() {
32961
33923
  return line(`${NOTHING_ALLOWED}: no step is paused in this session.`);
32962
33924
  }
32963
- function allowSeveralHeldLine() {
32964
- return `${NOTHING_ALLOWED}: more than one step was paused, so Halfcycle could not tell which one you meant. Nothing ran. When your agent tries the step again, type ${ALLOW_REPLY} once more. ${SHOW_AS_WRITTEN}`;
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.`);
32965
33935
  }
32966
33936
  function allowNotAttendedLine() {
32967
33937
  return line(`${NOTHING_ALLOWED}: a paused step can be allowed by typing only when a person types it into an open session.`);
@@ -33047,8 +34017,8 @@ function waitLineForAnswer(context, answer) {
33047
34017
  }
33048
34018
  }
33049
34019
  var PACE_NOTHING_CHANGED_OPENING = "Nothing changed";
33050
- function paceAnswerLine(text, agent) {
33051
- return agent === "claude-code" ? line(text) : codexLine(text);
34020
+ function paceAnswerLine(text, _agent) {
34021
+ return codexLine(text);
33052
34022
  }
33053
34023
  function paceNumberDoneText(n, more) {
33054
34024
  const head = `Done. This build now runs up to ${paceAgentsOf(n)} at once.`;
@@ -33156,8 +34126,9 @@ async function decideLine(opts) {
33156
34126
  const review = { recognised: false };
33157
34127
  try {
33158
34128
  const prompt = promptOf(opts.input);
33159
- if (isAllowReply(prompt))
33160
- return `${decideAllow(opts)}
34129
+ const allowReply = allowReplyOf(prompt);
34130
+ if (allowReply !== null)
34131
+ return `${decideAllow(allowReply.number, opts)}
33161
34132
  `;
33162
34133
  const autonomyShape = autonomyPhraseShape(prompt);
33163
34134
  if (autonomyShape !== null) {
@@ -33210,21 +34181,23 @@ async function decideLine(opts) {
33210
34181
  `;
33211
34182
  }
33212
34183
  }
33213
- function decideAllow(opts) {
34184
+ function decideAllow(stepNumber, opts) {
33214
34185
  const env = opts.env ?? process.env;
33215
34186
  if ((opts.agent ?? "claude-code") === "claude-code" && !readSessionAttended(env))
33216
34187
  return allowNotAttendedLine();
33217
- const { outcome, step } = armAllowStep(sessionIdOf(opts.input), {
34188
+ const { outcome, step, paused } = armAllowStep(sessionIdOf(opts.input), {
33218
34189
  ...opts.tempDir === void 0 ? {} : { dir: opts.tempDir },
33219
34190
  ...opts.now === void 0 ? {} : { now: opts.now }
33220
- });
34191
+ }, stepNumber);
33221
34192
  switch (outcome) {
33222
34193
  case "armed":
33223
34194
  return allowArmedLine(step);
33224
34195
  case "nothing-held":
33225
34196
  return allowNothingHeldLine();
34197
+ case "not-paused":
34198
+ return allowNotPausedLine(stepNumber ?? 0);
33226
34199
  case "several-held":
33227
- return allowSeveralHeldLine();
34200
+ return allowSeveralHeldLine(paused);
33228
34201
  case "not-saved":
33229
34202
  return allowNotSavedLine();
33230
34203
  default: {
@@ -33751,14 +34724,14 @@ function runUserPromptFindings(opts) {
33751
34724
  }
33752
34725
 
33753
34726
  // dist/resolve-engagement.js
33754
- import { existsSync as existsSync8, readFileSync as readFileSync28, realpathSync as realpathSync9 } from "node:fs";
33755
- import { join as join21, sep as sep6 } from "node:path";
33756
- import { homedir as homedir15 } from "node:os";
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";
33757
34730
  function defaultRegistryPath() {
33758
- return join21(homedir15(), ".halfcycle", "registry.json");
34731
+ return join22(homedir16(), ".halfcycle", "registry.json");
33759
34732
  }
33760
34733
  function readRegistry(registryPath = defaultRegistryPath()) {
33761
- if (!existsSync8(registryPath))
34734
+ if (!existsSync9(registryPath))
33762
34735
  return {};
33763
34736
  try {
33764
34737
  const parsed = JSON.parse(readFileSync28(registryPath, "utf-8"));
@@ -33776,7 +34749,7 @@ function readRegistry(registryPath = defaultRegistryPath()) {
33776
34749
  }
33777
34750
  function canonicalise(p) {
33778
34751
  try {
33779
- return realpathSync9(p);
34752
+ return realpathSync10(p);
33780
34753
  } catch {
33781
34754
  return p;
33782
34755
  }
@@ -33797,7 +34770,7 @@ function resolveEngagement(cwd, registry2) {
33797
34770
  function isSelfOrAncestor(ancestor, target) {
33798
34771
  if (ancestor === target)
33799
34772
  return true;
33800
- const withSep = ancestor.endsWith(sep6) ? ancestor : ancestor + sep6;
34773
+ const withSep = ancestor.endsWith(sep7) ? ancestor : ancestor + sep7;
33801
34774
  return target.startsWith(withSep);
33802
34775
  }
33803
34776
  function runResolveEngagement(cwd, registryPath) {
@@ -33810,12 +34783,12 @@ function runResolveEngagement(cwd, registryPath) {
33810
34783
 
33811
34784
  // dist/refresh-credential.js
33812
34785
  import { readFileSync as readFileSync30 } from "node:fs";
33813
- import { resolve as resolve10 } from "node:path";
34786
+ import { resolve as resolve11 } from "node:path";
33814
34787
 
33815
34788
  // dist/mcp-headers.js
33816
34789
  import { readFileSync as readFileSync29 } from "node:fs";
33817
- import { homedir as homedir16 } from "node:os";
33818
- import { dirname as dirname10, resolve as resolve9 } from "node:path";
34790
+ import { homedir as homedir17 } from "node:os";
34791
+ import { dirname as dirname10, resolve as resolve10 } from "node:path";
33819
34792
  var URL_FLAG = "--url";
33820
34793
  function urlFromArguments(rest) {
33821
34794
  const at = [];
@@ -33842,12 +34815,12 @@ function headerLine(bearer, clientVersion) {
33842
34815
  }
33843
34816
  function projectRootFromBinary(binaryPath) {
33844
34817
  const depth = VENDORED_BIN_REL.split("/").length - 1;
33845
- return resolve9(dirname10(binaryPath), ...Array(depth).fill(".."));
34818
+ return resolve10(dirname10(binaryPath), ...Array(depth).fill(".."));
33846
34819
  }
33847
34820
  function readPinnedVersion(root) {
33848
34821
  let raw;
33849
34822
  try {
33850
- raw = readFileSync29(resolve9(root, BUNDLE_PIN_REL), "utf-8");
34823
+ raw = readFileSync29(resolve10(root, BUNDLE_PIN_REL), "utf-8");
33851
34824
  } catch {
33852
34825
  return void 0;
33853
34826
  }
@@ -33889,7 +34862,7 @@ function sentences(lines2) {
33889
34862
  }
33890
34863
  function badIdSentence(root) {
33891
34864
  return sentences([
33892
- `${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,`,
33893
34866
  'so no credential was read. Delete that file and run "npx halfcycle" again: this repository',
33894
34867
  "is then set up as a new Halfcycle project."
33895
34868
  ]);
@@ -33903,7 +34876,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
33903
34876
  ]);
33904
34877
  case "no-id":
33905
34878
  return sentences([
33906
- `${resolve9(root, BUNDLE_PIN_REL)} names no engagement id.`,
34879
+ `${resolve10(root, BUNDLE_PIN_REL)} names no engagement id.`,
33907
34880
  'run "npx halfcycle" here to rewrite it.'
33908
34881
  ]);
33909
34882
  case "bad-id":
@@ -33920,7 +34893,7 @@ function storeProblemSentence(problem, root, home, engagementId) {
33920
34893
  case "no-credential":
33921
34894
  return sentences([
33922
34895
  `this machine holds no credential for engagement ${engagementId ?? ""}.`,
33923
- `run "npx halfcycle" in this repository \u2014 it will sign you in through your browser if needed, and write the credential to ${resolve9(home, HALFCYCLE_DIR_NAME)}, outside this repository.`
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.`
33924
34897
  ]);
33925
34898
  }
33926
34899
  }
@@ -33943,7 +34916,7 @@ async function refreshWithinDeadline(source, opts, writeErr) {
33943
34916
  }
33944
34917
  async function chooseBearer(opts) {
33945
34918
  const writeErr = opts.writeErr ?? defaultWriteErr;
33946
- const home = opts.home ?? homedir16();
34919
+ const home = opts.home ?? homedir17();
33947
34920
  const root = projectRootFromBinary(opts.binaryPath);
33948
34921
  const environmentToken = opts.environmentTokenFirst ? readEnvironmentToken() : void 0;
33949
34922
  const store = resolveStore(root, home);
@@ -34086,8 +35059,8 @@ async function refreshCredentialToken(opts) {
34086
35059
  source
34087
35060
  });
34088
35061
  if (!refreshed.ok) {
34089
- const write = opts?.write ?? ((text) => process.stdout.write(text));
34090
- write(credentialRenewalFailedMessage(refreshed.remedy));
35062
+ const write2 = opts?.write ?? ((text) => process.stdout.write(text));
35063
+ write2(credentialRenewalFailedMessage(refreshed.remedy));
34091
35064
  return { token: void 0 };
34092
35065
  }
34093
35066
  return { token: refreshed.token };
@@ -34109,7 +35082,7 @@ var SESSION_START_PROBE_ATTEMPTS = 2;
34109
35082
  function registeredMcpUrl(projectRoot) {
34110
35083
  let parsed;
34111
35084
  try {
34112
- parsed = JSON.parse(readFileSync30(resolve10(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
35085
+ parsed = JSON.parse(readFileSync30(resolve11(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
34113
35086
  } catch {
34114
35087
  return void 0;
34115
35088
  }
@@ -34358,7 +35331,7 @@ async function runRefreshCredential(opts) {
34358
35331
  import { fileURLToPath } from "node:url";
34359
35332
 
34360
35333
  // dist/capture.js
34361
- 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";
34362
35335
  import { dirname as dirname11 } from "node:path";
34363
35336
  function runCapture(provider, endpoint, opts = {}) {
34364
35337
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -34440,8 +35413,8 @@ function relativeFixturePath(repoRoot, fixture) {
34440
35413
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
34441
35414
  import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
34442
35415
  import { createWriteStream, mkdirSync as mkdirSync13, readdirSync as readdirSync11, readFileSync as readFileSync32, renameSync as renameSync16, unlinkSync as unlinkSync12, writeFileSync as writeFileSync19 } from "node:fs";
34443
- import { homedir as homedir17 } from "node:os";
34444
- import { dirname as dirname12, join as join22 } from "node:path";
35416
+ import { homedir as homedir18 } from "node:os";
35417
+ import { dirname as dirname12, join as join23 } from "node:path";
34445
35418
 
34446
35419
  // dist/test-counts.js
34447
35420
  function sum(values) {
@@ -34550,7 +35523,7 @@ function isProofTestPath(path) {
34550
35523
  function testRunsDir(engagementId, home) {
34551
35524
  if (!isEngagementId(engagementId))
34552
35525
  return null;
34553
- 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);
34554
35527
  }
34555
35528
  function commandIdOf(command) {
34556
35529
  return createHash6("sha256").update(command, "utf8").digest("hex").slice(0, 16);
@@ -34646,7 +35619,7 @@ function subjectKey(subject) {
34646
35619
  }
34647
35620
  function keptCheckPath(dir, subject, label) {
34648
35621
  const key = createHash6("sha256").update(JSON.stringify([...subjectKey(subject), label]), "utf8").digest("hex");
34649
- return join22(dir, COMMANDS_DIR_NAME, `${key.slice(0, 32)}.json`);
35622
+ return join23(dir, COMMANDS_DIR_NAME, `${key.slice(0, 32)}.json`);
34650
35623
  }
34651
35624
  function readKeptCheck(dir, subject, label) {
34652
35625
  try {
@@ -34661,18 +35634,18 @@ function readKeptCheck(dir, subject, label) {
34661
35634
  }
34662
35635
  }
34663
35636
  function queuePath(dir, runId) {
34664
- return join22(dir, QUEUE_DIR_NAME, `${runId}.json`);
35637
+ return join23(dir, QUEUE_DIR_NAME, `${runId}.json`);
34665
35638
  }
34666
35639
  function readQueue(dir) {
34667
35640
  let names;
34668
35641
  try {
34669
- 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"));
34670
35643
  } catch {
34671
35644
  return [];
34672
35645
  }
34673
35646
  const reports = [];
34674
35647
  for (const name of names) {
34675
- const path = join22(dir, QUEUE_DIR_NAME, name);
35648
+ const path = join23(dir, QUEUE_DIR_NAME, name);
34676
35649
  let parsed = null;
34677
35650
  try {
34678
35651
  parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync32(path, "utf-8")));
@@ -34745,17 +35718,17 @@ function killTree(child, platform4, signal) {
34745
35718
  }
34746
35719
  }
34747
35720
  }
34748
- function runCommand(command, repoRoot, logPath, env, deps) {
35721
+ function runCommand(command, repoRoot, logPath2, env, deps) {
34749
35722
  const platform4 = deps.platform ?? process.platform;
34750
35723
  const out = deps.stdout ?? process.stdout;
34751
35724
  const err = deps.stderr ?? process.stderr;
34752
35725
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
34753
35726
  const childEnv = { ...env };
34754
35727
  delete childEnv[SERVICE_TOKEN_VAR];
34755
- mkdirSync13(dirname12(logPath), { recursive: true, mode: 448 });
34756
- const log = createWriteStream(logPath, { mode: 384 });
35728
+ mkdirSync13(dirname12(logPath2), { recursive: true, mode: 448 });
35729
+ const log = createWriteStream(logPath2, { mode: 384 });
34757
35730
  const began = process.hrtime.bigint();
34758
- return new Promise((resolve11) => {
35731
+ return new Promise((resolve12) => {
34759
35732
  let timedOut = false;
34760
35733
  let started = true;
34761
35734
  let settled2 = false;
@@ -34790,7 +35763,7 @@ function runCommand(command, repoRoot, logPath, env, deps) {
34790
35763
  process.off("SIGINT", onSignal);
34791
35764
  process.off("SIGTERM", onSignal);
34792
35765
  const durationMs = Number((process.hrtime.bigint() - began) / 1000000n);
34793
- log.end(() => resolve11({ exitCode: timedOut || !started ? null : code, timedOut, started, durationMs }));
35766
+ log.end(() => resolve12({ exitCode: timedOut || !started ? null : code, timedOut, started, durationMs }));
34794
35767
  };
34795
35768
  child.on("error", (e) => {
34796
35769
  started = false;
@@ -34912,13 +35885,13 @@ ${PROVE_USAGE}`);
34912
35885
  if (request.when === "before" && changedOutsideTests > 0)
34913
35886
  say(lateBeforeLine(changedOutsideTests));
34914
35887
  const runId = randomUUID3();
34915
- const logPath = join22(dir, `${runId}.log`);
35888
+ const logPath2 = join23(dir, `${runId}.log`);
34916
35889
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
34917
35890
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
34918
- const end = await runCommand(command, repoRoot, logPath, env, deps);
35891
+ const end = await runCommand(command, repoRoot, logPath2, env, deps);
34919
35892
  let output = "";
34920
35893
  try {
34921
- output = readFileSync32(logPath, "utf-8");
35894
+ output = readFileSync32(logPath2, "utf-8");
34922
35895
  } catch {
34923
35896
  output = "";
34924
35897
  }
@@ -34962,7 +35935,7 @@ ${PROVE_USAGE}`);
34962
35935
  sendLine = mine.outcome === "recorded" ? RECORDED_LINE : mine.outcome === "kept" ? NOT_SENT_YET_LINE : refusedLine(mine.refusal);
34963
35936
  }
34964
35937
  say(`${line2} ${sendLine}`);
34965
- say(`Its output is kept on this computer at ${logPath}`);
35938
+ say(`Its output is kept on this computer at ${logPath2}`);
34966
35939
  return 0;
34967
35940
  }
34968
35941
 
@@ -35030,14 +36003,14 @@ function personCommandEnv(env) {
35030
36003
  }
35031
36004
  return out;
35032
36005
  }
35033
- var runOwnCommandReal = (command, input, env) => new Promise((resolve11) => {
36006
+ var runOwnCommandReal = (command, input, env) => new Promise((resolve12) => {
35034
36007
  let out = "";
35035
36008
  let settled2 = false;
35036
36009
  const done = (v) => {
35037
36010
  if (settled2)
35038
36011
  return;
35039
36012
  settled2 = true;
35040
- resolve11(v);
36013
+ resolve12(v);
35041
36014
  };
35042
36015
  try {
35043
36016
  const child = spawn3(command, {
@@ -35185,7 +36158,7 @@ async function main() {
35185
36158
  if (cmd === "time") {
35186
36159
  let line2;
35187
36160
  try {
35188
- line2 = JSON.stringify(await runTimeVerb());
36161
+ line2 = JSON.stringify(await runTimeVerb({}, { final: args.slice(1).includes("--final") }));
35189
36162
  } catch {
35190
36163
  line2 = JSON.stringify({ outcome: "not-sent", reason: "unreachable" });
35191
36164
  }
@@ -35351,7 +36324,7 @@ function unknownCommand(name = args.join(" ")) {
35351
36324
  halfcycle-runner mcp-headers [--url <address>]
35352
36325
  halfcycle-runner observe
35353
36326
  halfcycle-runner observe --change <sortingId>
35354
- halfcycle-runner time
36327
+ halfcycle-runner time [--final]
35355
36328
  halfcycle-runner prove <phase> <task> --label <label> --before|--after [-- <command>]
35356
36329
  halfcycle-runner pace [--start <phase> [--agents <n>]]
35357
36330
  halfcycle-runner pace --scan [--config-folder <dir>]
@@ -35362,10 +36335,10 @@ function unknownCommand(name = args.join(" ")) {
35362
36335
  process.exit(1);
35363
36336
  }
35364
36337
  function readStdin3() {
35365
- return new Promise((resolve11, reject) => {
36338
+ return new Promise((resolve12, reject) => {
35366
36339
  const chunks = [];
35367
36340
  process.stdin.on("data", (chunk) => chunks.push(chunk));
35368
- process.stdin.on("end", () => resolve11(Buffer.concat(chunks).toString("utf-8")));
36341
+ process.stdin.on("end", () => resolve12(Buffer.concat(chunks).toString("utf-8")));
35369
36342
  process.stdin.on("error", reject);
35370
36343
  });
35371
36344
  }