@brainervirus/workit-pi 8.9.0 → 8.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workit.js CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
5
5
  // packages/workit-core/src/core.ts
6
6
  import { spawnSync as spawnSync14 } from "node:child_process";
7
7
  import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
8
- import path19 from "node:path";
8
+ import path20 from "node:path";
9
9
 
10
10
  // packages/workit-core/src/core/task-contract.ts
11
11
  import { createHash, randomUUID } from "node:crypto";
@@ -8991,9 +8991,9 @@ function parseAdvertisedOperation(family, input, host) {
8991
8991
  });
8992
8992
  }
8993
8993
  // packages/workit-core/src/core/task-store.ts
8994
- import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
8995
- import * as fs7 from "node:fs";
8996
- import path6 from "node:path";
8994
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
8995
+ import * as fs8 from "node:fs";
8996
+ import path7 from "node:path";
8997
8997
 
8998
8998
  // node_modules/@openclaw/fs-safe/dist/errors.js
8999
8999
  var OPERATIONAL_CODES = new Set([
@@ -9597,63 +9597,15 @@ function acquireFileLockSync(targetPath, options) {
9597
9597
  }
9598
9598
  }
9599
9599
  // packages/workit-core/src/host-session.ts
9600
- var HOST_SESSION_VARS = [
9601
- ["OPENCODE_SESSION_ID", () => "opencode"],
9602
- ["PI_SESSION_ID", () => "pi"],
9603
- [
9604
- "CODEX_THREAD_ID",
9605
- (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
9606
- ]
9607
- ];
9608
- var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
9609
- function hostSessionFromEnv(env) {
9610
- const value = (key) => env[key]?.trim() || null;
9611
- let inner = null;
9612
- for (const [key, host] of HOST_SESSION_VARS) {
9613
- const session = value(key);
9614
- if (session) {
9615
- inner = { host: host(env), session };
9616
- break;
9617
- }
9618
- }
9619
- const declared = value("WORKIT_HOST");
9620
- if (env.WORKIT_SESSION_ID !== undefined) {
9621
- const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
9622
- if (!inherited)
9623
- return { host: declared, session: value("WORKIT_SESSION_ID") };
9624
- }
9625
- return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
9626
- }
9627
-
9628
- // packages/workit-core/src/core/package-root.ts
9629
- import { existsSync } from "node:fs";
9630
- import path3 from "node:path";
9631
- import { fileURLToPath } from "node:url";
9632
- var packageRoot = () => {
9633
- let dir = path3.dirname(fileURLToPath(import.meta.url));
9634
- while (true) {
9635
- const parent = path3.dirname(dir);
9636
- if (existsSync(path3.join(dir, "package.json")) || parent === dir)
9637
- return dir;
9638
- dir = parent;
9639
- }
9640
- };
9641
- var assetRoot = () => {
9642
- const root = packageRoot();
9643
- const assets = path3.join(root, "assets");
9644
- return existsSync(assets) ? assets : root;
9645
- };
9646
-
9647
- // packages/workit-core/src/core/store-lock.ts
9648
- import { spawnSync as spawnSync2 } from "node:child_process";
9600
+ import { createHash as createHash3 } from "node:crypto";
9649
9601
  import fs5 from "node:fs";
9650
- import { hostname } from "node:os";
9602
+ import path4 from "node:path";
9651
9603
 
9652
9604
  // packages/workit-core/src/store/paths.ts
9653
9605
  import { spawnSync } from "node:child_process";
9654
9606
  import { createHash as createHash2 } from "node:crypto";
9655
9607
  import fs4 from "node:fs";
9656
- import path4 from "node:path";
9608
+ import path3 from "node:path";
9657
9609
  var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
9658
9610
  var gitRun = (cwd, args) => {
9659
9611
  const run = spawnSync("git", args, {
@@ -9695,14 +9647,14 @@ var DISCOVERY_ENV = [
9695
9647
  ];
9696
9648
  var isGitDir = (dir) => {
9697
9649
  try {
9698
- return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
9650
+ return fs4.statSync(path3.join(dir, "HEAD")).isFile() && fs4.statSync(path3.join(dir, "objects")).isDirectory() && fs4.statSync(path3.join(dir, "refs")).isDirectory();
9699
9651
  } catch {
9700
9652
  return false;
9701
9653
  }
9702
9654
  };
9703
9655
  var reftableRefs = (common) => {
9704
9656
  try {
9705
- return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
9657
+ return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path3.join(common, "config"), "utf8"));
9706
9658
  } catch {
9707
9659
  return false;
9708
9660
  }
@@ -9722,7 +9674,7 @@ function fastGit(root) {
9722
9674
  if (device !== null && stat.dev !== device)
9723
9675
  return { kind: "none" };
9724
9676
  device = stat.dev;
9725
- const dotGit = path4.join(dir, ".git");
9677
+ const dotGit = path3.join(dir, ".git");
9726
9678
  let entry = null;
9727
9679
  try {
9728
9680
  entry = fs4.statSync(dotGit);
@@ -9743,19 +9695,19 @@ function fastGit(root) {
9743
9695
  const match = /^gitdir: (.+)$/mu.exec(text);
9744
9696
  if (!match)
9745
9697
  return null;
9746
- gitDir = path4.resolve(dir, match[1].trim());
9698
+ gitDir = path3.resolve(dir, match[1].trim());
9747
9699
  } else
9748
9700
  return null;
9749
- if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
9701
+ if (!fs4.existsSync(path3.join(gitDir, "HEAD")))
9750
9702
  return null;
9751
9703
  } else if (isGitDir(dir))
9752
9704
  gitDir = dir;
9753
9705
  if (gitDir) {
9754
9706
  let common = gitDir;
9755
9707
  try {
9756
- common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
9708
+ common = path3.resolve(gitDir, fs4.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
9757
9709
  } catch {}
9758
- if (!fs4.existsSync(path4.join(common, "objects")))
9710
+ if (!fs4.existsSync(path3.join(common, "objects")))
9759
9711
  return null;
9760
9712
  return {
9761
9713
  kind: "repo",
@@ -9765,26 +9717,26 @@ function fastGit(root) {
9765
9717
  reftable: reftableRefs(common)
9766
9718
  };
9767
9719
  }
9768
- const parent = path4.dirname(dir);
9720
+ const parent = path3.dirname(dir);
9769
9721
  if (parent === dir)
9770
9722
  return { kind: "none" };
9771
9723
  dir = parent;
9772
9724
  }
9773
9725
  }
9774
9726
  function resolveStore(root) {
9775
- root = path4.resolve(root);
9727
+ root = path3.resolve(root);
9776
9728
  let directory = false;
9777
9729
  try {
9778
9730
  directory = fs4.statSync(root).isDirectory();
9779
9731
  } catch {}
9780
9732
  if (!directory)
9781
- return { dir: path4.join(root, ".workit"), shared: false, top: null };
9733
+ return { dir: path3.join(root, ".workit"), shared: false, top: null };
9782
9734
  const fast = fastGit(root);
9783
9735
  if (fast?.kind === "none")
9784
- return { dir: path4.join(root, ".workit"), shared: false, top: null };
9736
+ return { dir: path3.join(root, ".workit"), shared: false, top: null };
9785
9737
  if (fast?.kind === "repo")
9786
9738
  return {
9787
- dir: path4.join(fast.common, "workit"),
9739
+ dir: path3.join(fast.common, "workit"),
9788
9740
  shared: true,
9789
9741
  top: fast.top,
9790
9742
  git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
@@ -9794,20 +9746,20 @@ function resolveStore(root) {
9794
9746
  if (run.ok && common) {
9795
9747
  const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
9796
9748
  return {
9797
- dir: path4.join(real(path4.resolve(root, common)), "workit"),
9749
+ dir: path3.join(real(path3.resolve(root, common)), "workit"),
9798
9750
  shared: true,
9799
9751
  top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
9800
9752
  };
9801
9753
  }
9802
9754
  if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
9803
- return { dir: path4.join(root, ".workit"), shared: false, top: null };
9755
+ return { dir: path3.join(root, ".workit"), shared: false, top: null };
9804
9756
  return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
9805
9757
  `)[0] || "no output"}); fix the repository (git status should work here)`);
9806
9758
  }
9807
9759
  var rebasingBranch = (gitDir) => {
9808
9760
  for (const state of ["rebase-merge", "rebase-apply"]) {
9809
9761
  try {
9810
- const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
9762
+ const ref = fs4.readFileSync(path3.join(gitDir, state, "head-name"), "utf8").trim();
9811
9763
  const match = /^refs\/heads\/(.+)$/u.exec(ref);
9812
9764
  if (match)
9813
9765
  return match[1];
@@ -9822,7 +9774,7 @@ function resolveTaskKey(root, location) {
9822
9774
  if (location.git && !location.git.reftable) {
9823
9775
  let head = "";
9824
9776
  try {
9825
- head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
9777
+ head = fs4.readFileSync(path3.join(location.git.gitDir, "HEAD"), "utf8").trim();
9826
9778
  } catch {}
9827
9779
  const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
9828
9780
  if (ref && ref[1] !== ".invalid")
@@ -9853,7 +9805,7 @@ function resolveTaskKey(root, location) {
9853
9805
  return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
9854
9806
  }
9855
9807
  function checkoutRootOf(root, location) {
9856
- const resolved = path4.resolve(root);
9808
+ const resolved = path3.resolve(root);
9857
9809
  let canonical = resolved;
9858
9810
  try {
9859
9811
  canonical = fs4.realpathSync(resolved);
@@ -9884,13 +9836,13 @@ function branchRenames(location) {
9884
9836
  }
9885
9837
  for (const entry of entries)
9886
9838
  if (entry.isDirectory())
9887
- walk(path4.join(dir, entry.name));
9839
+ walk(path3.join(dir, entry.name));
9888
9840
  else
9889
- files.push(path4.join(dir, entry.name));
9841
+ files.push(path3.join(dir, entry.name));
9890
9842
  };
9891
- walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
9843
+ walk(path3.join(path3.dirname(location.dir), "logs", "refs", "heads"));
9892
9844
  if (location.git)
9893
- files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
9845
+ files.push(path3.join(location.git.gitDir, "logs", "HEAD"));
9894
9846
  const seen = new Set;
9895
9847
  const renames = [];
9896
9848
  for (const file of files) {
@@ -9925,11 +9877,96 @@ var checkoutSlug = (root) => {
9925
9877
  let canonical = real(root);
9926
9878
  if (process.platform === "win32")
9927
9879
  canonical = canonical.toLowerCase();
9928
- const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
9880
+ const base = path3.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
9929
9881
  return `${base}-${shortHash(canonical)}`;
9930
9882
  };
9931
9883
 
9884
+ // packages/workit-core/src/host-session.ts
9885
+ var HOST_SESSION_VARS = [
9886
+ ["OPENCODE_SESSION_ID", () => "opencode"],
9887
+ ["PI_SESSION_ID", () => "pi"],
9888
+ [
9889
+ "CODEX_THREAD_ID",
9890
+ (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
9891
+ ]
9892
+ ];
9893
+ var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
9894
+ function hostSessionFromEnv(env) {
9895
+ const value = (key) => env[key]?.trim() || null;
9896
+ let inner = null;
9897
+ for (const [key, host] of HOST_SESSION_VARS) {
9898
+ const session = value(key);
9899
+ if (session) {
9900
+ inner = { host: host(env), session };
9901
+ break;
9902
+ }
9903
+ }
9904
+ const declared = value("WORKIT_HOST");
9905
+ if (env.WORKIT_SESSION_ID !== undefined) {
9906
+ const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
9907
+ if (!inherited)
9908
+ return { host: declared, session: value("WORKIT_SESSION_ID") };
9909
+ }
9910
+ return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
9911
+ }
9912
+ var ISSUED_PER_LEAD = 64;
9913
+ var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/1";
9914
+ var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
9915
+ var issuedRoot = (cwd) => {
9916
+ const location = resolveStore(cwd);
9917
+ return location instanceof Error ? null : path4.join(location.dir, "hooks", "issued");
9918
+ };
9919
+ var issuedDir = (cwd, lead) => {
9920
+ const root = issuedRoot(cwd);
9921
+ return root === null ? null : path4.join(root, hash(lead));
9922
+ };
9923
+ function recordIssuedSession(cwd, lead, issued) {
9924
+ try {
9925
+ const dir = issuedDir(cwd, lead);
9926
+ if (!dir)
9927
+ return;
9928
+ fs5.mkdirSync(dir, { recursive: true });
9929
+ const own = hash(issued);
9930
+ fs5.writeFileSync(path4.join(dir, own), ISSUED_SESSIONS_MARKER);
9931
+ const entries = fs5.readdirSync(dir);
9932
+ if (entries.length <= ISSUED_PER_LEAD)
9933
+ return;
9934
+ const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs5.statSync(path4.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
9935
+ for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
9936
+ fs5.rmSync(path4.join(dir, name), { force: true });
9937
+ } catch {}
9938
+ }
9939
+ var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
9940
+ var SESSION_UNSAFE = /[^A-Za-z0-9_.:@/+-]/g;
9941
+ function childSessionId(lead, child) {
9942
+ const leaf = child.replace(SESSION_UNSAFE, "-").slice(-120);
9943
+ const prefix = lead.replace(SESSION_UNSAFE, "-").slice(0, 127 - leaf.length);
9944
+ return prefix ? `${prefix}:${leaf}` : leaf;
9945
+ }
9946
+
9947
+ // packages/workit-core/src/core/package-root.ts
9948
+ import { existsSync } from "node:fs";
9949
+ import path5 from "node:path";
9950
+ import { fileURLToPath } from "node:url";
9951
+ var packageRoot = () => {
9952
+ let dir = path5.dirname(fileURLToPath(import.meta.url));
9953
+ while (true) {
9954
+ const parent = path5.dirname(dir);
9955
+ if (existsSync(path5.join(dir, "package.json")) || parent === dir)
9956
+ return dir;
9957
+ dir = parent;
9958
+ }
9959
+ };
9960
+ var assetRoot = () => {
9961
+ const root = packageRoot();
9962
+ const assets = path5.join(root, "assets");
9963
+ return existsSync(assets) ? assets : root;
9964
+ };
9965
+
9932
9966
  // packages/workit-core/src/core/store-lock.ts
9967
+ import { spawnSync as spawnSync2 } from "node:child_process";
9968
+ import fs6 from "node:fs";
9969
+ import { hostname } from "node:os";
9933
9970
  var metadataLockSchema = object({
9934
9971
  pid: number2().int().nonnegative().safe(),
9935
9972
  processStart: string2().nullable(),
@@ -9970,7 +10007,7 @@ var parseProcStatStart = (stat) => {
9970
10007
  var processStartOf = (pid) => {
9971
10008
  if (process.platform === "linux") {
9972
10009
  try {
9973
- return parseProcStatStart(fs5.readFileSync(`/proc/${pid}/stat`, "utf8"));
10010
+ return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
9974
10011
  } catch {
9975
10012
  return null;
9976
10013
  }
@@ -9991,7 +10028,7 @@ var processStartOf = (pid) => {
9991
10028
  };
9992
10029
  var readTrimmed = (file) => {
9993
10030
  try {
9994
- return fs5.readFileSync(file, "utf8").trim() || null;
10031
+ return fs6.readFileSync(file, "utf8").trim() || null;
9995
10032
  } catch {
9996
10033
  return null;
9997
10034
  }
@@ -10002,7 +10039,7 @@ var localLockHost = () => {
10002
10039
  return cachedLockHost;
10003
10040
  let pidns = null;
10004
10041
  try {
10005
- pidns = /\[(\d+)\]/.exec(fs5.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10042
+ pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10006
10043
  } catch {}
10007
10044
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
10008
10045
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -10048,7 +10085,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
10048
10085
  };
10049
10086
  var ageOf = (file, nowMs) => {
10050
10087
  try {
10051
- return nowMs - fs5.lstatSync(file).mtimeMs;
10088
+ return nowMs - fs6.lstatSync(file).mtimeMs;
10052
10089
  } catch {
10053
10090
  return null;
10054
10091
  }
@@ -10059,7 +10096,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10059
10096
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
10060
10097
  return false;
10061
10098
  try {
10062
- fs5.rmdirSync(guard);
10099
+ fs6.rmdirSync(guard);
10063
10100
  return true;
10064
10101
  } catch {
10065
10102
  return false;
@@ -10068,8 +10105,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10068
10105
 
10069
10106
  // packages/workit-core/src/store/event-log.ts
10070
10107
  import { randomUUID as randomUUID2 } from "node:crypto";
10071
- import fs6 from "node:fs";
10072
- import path5 from "node:path";
10108
+ import fs7 from "node:fs";
10109
+ import path6 from "node:path";
10073
10110
  var EVENT_VERSION = 1;
10074
10111
 
10075
10112
  class LogDamage extends Error {
@@ -10098,7 +10135,7 @@ var parseEvent = (line, file, offset) => {
10098
10135
  function readLog(file, from = 0, expectSeq = null) {
10099
10136
  let fd;
10100
10137
  try {
10101
- fd = fs6.openSync(file, "r");
10138
+ fd = fs7.openSync(file, "r");
10102
10139
  } catch (error) {
10103
10140
  if (error.code === "ENOENT")
10104
10141
  return null;
@@ -10107,13 +10144,13 @@ function readLog(file, from = 0, expectSeq = null) {
10107
10144
  let bytes;
10108
10145
  let size;
10109
10146
  try {
10110
- size = fs6.fstatSync(fd).size;
10147
+ size = fs7.fstatSync(fd).size;
10111
10148
  if (from > size)
10112
10149
  throw new LogDamage(`${file}: shorter than its snapshot`);
10113
10150
  bytes = Buffer.alloc(size - from);
10114
10151
  let read = 0;
10115
10152
  while (read < bytes.length) {
10116
- const got = fs6.readSync(fd, bytes, read, bytes.length - read, from + read);
10153
+ const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
10117
10154
  if (got === 0)
10118
10155
  break;
10119
10156
  read += got;
@@ -10121,7 +10158,7 @@ function readLog(file, from = 0, expectSeq = null) {
10121
10158
  bytes = bytes.subarray(0, read);
10122
10159
  size = from + read;
10123
10160
  } finally {
10124
- fs6.closeSync(fd);
10161
+ fs7.closeSync(fd);
10125
10162
  }
10126
10163
  const events = [];
10127
10164
  let start = 0;
@@ -10146,11 +10183,11 @@ function readLog(file, from = 0, expectSeq = null) {
10146
10183
  }
10147
10184
  var fsyncDirectory = (dir) => {
10148
10185
  try {
10149
- const fd = fs6.openSync(dir, "r");
10186
+ const fd = fs7.openSync(dir, "r");
10150
10187
  try {
10151
- fs6.fsyncSync(fd);
10188
+ fs7.fsyncSync(fd);
10152
10189
  } finally {
10153
- fs6.closeSync(fd);
10190
+ fs7.closeSync(fd);
10154
10191
  }
10155
10192
  } catch (error) {
10156
10193
  if (process.platform !== "win32")
@@ -10160,46 +10197,46 @@ var fsyncDirectory = (dir) => {
10160
10197
  function appendEvent(file, event, validEnd) {
10161
10198
  const line = Buffer.from(`${JSON.stringify(event)}
10162
10199
  `, "utf8");
10163
- const created = !fs6.existsSync(file);
10164
- if (!created && fs6.statSync(file).size !== validEnd) {
10165
- const cut = fs6.openSync(file, "r+");
10200
+ const created = !fs7.existsSync(file);
10201
+ if (!created && fs7.statSync(file).size !== validEnd) {
10202
+ const cut = fs7.openSync(file, "r+");
10166
10203
  try {
10167
- fs6.ftruncateSync(cut, validEnd);
10168
- fs6.fsyncSync(cut);
10204
+ fs7.ftruncateSync(cut, validEnd);
10205
+ fs7.fsyncSync(cut);
10169
10206
  } finally {
10170
- fs6.closeSync(cut);
10207
+ fs7.closeSync(cut);
10171
10208
  }
10172
10209
  }
10173
- const fd = fs6.openSync(file, "a", 384);
10210
+ const fd = fs7.openSync(file, "a", 384);
10174
10211
  try {
10175
10212
  let written = 0;
10176
10213
  while (written < line.length)
10177
- written += fs6.writeSync(fd, line, written, line.length - written);
10178
- fs6.fsyncSync(fd);
10214
+ written += fs7.writeSync(fd, line, written, line.length - written);
10215
+ fs7.fsyncSync(fd);
10179
10216
  } finally {
10180
- fs6.closeSync(fd);
10217
+ fs7.closeSync(fd);
10181
10218
  }
10182
10219
  if (created)
10183
- fsyncDirectory(path5.dirname(file));
10220
+ fsyncDirectory(path6.dirname(file));
10184
10221
  return validEnd;
10185
10222
  }
10186
10223
  function replaceFile(file, bytes, durable) {
10187
10224
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
10188
10225
  try {
10189
- const fd = fs6.openSync(temporary, "wx", 384);
10226
+ const fd = fs7.openSync(temporary, "wx", 384);
10190
10227
  try {
10191
- fs6.writeSync(fd, bytes);
10228
+ fs7.writeSync(fd, bytes);
10192
10229
  if (durable)
10193
- fs6.fsyncSync(fd);
10230
+ fs7.fsyncSync(fd);
10194
10231
  } finally {
10195
- fs6.closeSync(fd);
10232
+ fs7.closeSync(fd);
10196
10233
  }
10197
10234
  renameWithRetry(temporary, file);
10198
10235
  if (durable)
10199
- fsyncDirectory(path5.dirname(file));
10236
+ fsyncDirectory(path6.dirname(file));
10200
10237
  } catch (error) {
10201
10238
  try {
10202
- fs6.unlinkSync(temporary);
10239
+ fs7.unlinkSync(temporary);
10203
10240
  } catch {}
10204
10241
  throw error;
10205
10242
  }
@@ -10224,7 +10261,7 @@ var retryTransient = (run) => {
10224
10261
  }
10225
10262
  }
10226
10263
  };
10227
- var renameWithRetry = (from, to) => retryTransient(() => fs6.renameSync(from, to));
10264
+ var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
10228
10265
 
10229
10266
  // packages/workit-core/src/store/patch.ts
10230
10267
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -10337,7 +10374,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
10337
10374
  });
10338
10375
 
10339
10376
  // packages/workit-core/src/store/reduce.ts
10340
- import { createHash as createHash3 } from "node:crypto";
10377
+ import { createHash as createHash4 } from "node:crypto";
10341
10378
  var OPENING_TYPES = new Set([
10342
10379
  "task.opened",
10343
10380
  "task.imported",
@@ -10366,7 +10403,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
10366
10403
  var isKey = (value) => isObject4(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
10367
10404
  var BLOB_REF = "$blob";
10368
10405
  var blobRef = (value) => isObject4(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
10369
- var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
10406
+ var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
10370
10407
  var toBlob = (value, blobs) => {
10371
10408
  if (!isObject4(value))
10372
10409
  return value;
@@ -10521,7 +10558,7 @@ var indexEntry = (task, key, legacy, file) => {
10521
10558
  };
10522
10559
  var fileSignature = (file) => {
10523
10560
  try {
10524
- const stat = fs7.statSync(file, { bigint: true });
10561
+ const stat = fs8.statSync(file, { bigint: true });
10525
10562
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
10526
10563
  } catch {
10527
10564
  return null;
@@ -10539,7 +10576,7 @@ var cachedRuntimeVersion = null;
10539
10576
  var runtimeVersion = () => {
10540
10577
  if (cachedRuntimeVersion === null) {
10541
10578
  try {
10542
- const pkg = JSON.parse(fs7.readFileSync(path6.join(packageRoot(), "package.json"), "utf8"));
10579
+ const pkg = JSON.parse(fs8.readFileSync(path7.join(packageRoot(), "package.json"), "utf8"));
10543
10580
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
10544
10581
  } catch {
10545
10582
  cachedRuntimeVersion = "0.0.0";
@@ -10563,20 +10600,52 @@ var isNewerVersion = (candidate, current) => {
10563
10600
  return false;
10564
10601
  };
10565
10602
  var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
10603
+ var readText = (file) => {
10604
+ try {
10605
+ return fs8.readFileSync(file, "utf8");
10606
+ } catch {
10607
+ return "";
10608
+ }
10609
+ };
10610
+ var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
10611
+ var branchStart = (location, branch, aliases) => {
10612
+ const first = readText(path7.join(path7.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10613
+ `, 1)[0];
10614
+ const created = REFLOG_LINE.exec(first);
10615
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
10616
+ if (!created || !start)
10617
+ return null;
10618
+ if (start !== "HEAD")
10619
+ return start;
10620
+ if (!location.git)
10621
+ return null;
10622
+ for (const line of readText(path7.join(location.git.gitDir, "logs", "HEAD")).split(`
10623
+ `)) {
10624
+ const entry = REFLOG_LINE.exec(line);
10625
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
10626
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
10627
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
10628
+ return moved[1];
10629
+ }
10630
+ return null;
10631
+ };
10632
+ var refExists = (common, ref) => fs8.existsSync(path7.join(common, ref)) || readText(path7.join(common, "packed-refs")).includes(` ${ref}
10633
+ `);
10634
+ var origins = new Map;
10566
10635
  var sameDirectoryIdentity = (left, right) => {
10567
- if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
10636
+ if (!path7.isAbsolute(left) || !path7.isAbsolute(right))
10568
10637
  return false;
10569
- const normalizedLeft = path6.resolve(left);
10570
- const normalizedRight = path6.resolve(right);
10638
+ const normalizedLeft = path7.resolve(left);
10639
+ const normalizedRight = path7.resolve(right);
10571
10640
  try {
10572
- const a = fs7.statSync(normalizedLeft, { bigint: true });
10573
- const b = fs7.statSync(normalizedRight, { bigint: true });
10641
+ const a = fs8.statSync(normalizedLeft, { bigint: true });
10642
+ const b = fs8.statSync(normalizedRight, { bigint: true });
10574
10643
  if (!a.isDirectory() || !b.isDirectory())
10575
10644
  return false;
10576
10645
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
10577
10646
  return true;
10578
- const canonicalLeft = fs7.realpathSync(normalizedLeft);
10579
- const canonicalRight = fs7.realpathSync(normalizedRight);
10647
+ const canonicalLeft = fs8.realpathSync(normalizedLeft);
10648
+ const canonicalRight = fs8.realpathSync(normalizedRight);
10580
10649
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
10581
10650
  return true;
10582
10651
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -10613,8 +10682,8 @@ class TaskStore {
10613
10682
  migrationChecked = false;
10614
10683
  compactDue = new Set;
10615
10684
  constructor(root, options = {}) {
10616
- const resolved = path6.resolve(root);
10617
- this.origin = fs7.existsSync(resolved) ? fs7.realpathSync(resolved) : resolved;
10685
+ const resolved = path7.resolve(root);
10686
+ this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
10618
10687
  this.located = resolveStore(this.origin);
10619
10688
  this.root = checkoutRootOf(this.origin, this.located);
10620
10689
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -10622,7 +10691,7 @@ class TaskStore {
10622
10691
  this.migrateOnRead = options.migrateOnRead;
10623
10692
  }
10624
10693
  location() {
10625
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs7.existsSync(path6.join(this.root, ".git"))) {
10694
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path7.join(this.root, ".git"))) {
10626
10695
  this.located = undefined;
10627
10696
  this.migrationChecked = false;
10628
10697
  }
@@ -10686,7 +10755,7 @@ class TaskStore {
10686
10755
  const tasksDir = this.paths().tasks;
10687
10756
  let names;
10688
10757
  try {
10689
- names = fs7.existsSync(tasksDir) ? fs7.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10758
+ names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10690
10759
  } catch (error) {
10691
10760
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
10692
10761
  path: tasksDir
@@ -10727,7 +10796,7 @@ class TaskStore {
10727
10796
  const file = this.paths().workspace;
10728
10797
  let bytes;
10729
10798
  try {
10730
- bytes = retryTransient(() => fs7.readFileSync(file, "utf8"));
10799
+ bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
10731
10800
  } catch (error) {
10732
10801
  if (error.code === "ENOENT")
10733
10802
  return success(null, null, null);
@@ -10778,7 +10847,7 @@ class TaskStore {
10778
10847
  const moved = this.bindLocked(again.data.id, key, input.now);
10779
10848
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
10780
10849
  }
10781
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path6.basename(this.root) || this.root;
10850
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path7.basename(this.root) || this.root;
10782
10851
  const intent = {
10783
10852
  objective: input.objective?.trim() || `Work on ${label}`,
10784
10853
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -10815,6 +10884,66 @@ class TaskStore {
10815
10884
  return bound;
10816
10885
  return success(null, null, { key: key.data, bound: bound.data });
10817
10886
  }
10887
+ taskForWorker(workerId) {
10888
+ if (typeof workerId !== "string" || !validId(workerId))
10889
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
10890
+ const listed = this.listTaskIndex();
10891
+ if (!listed.ok)
10892
+ return listed;
10893
+ for (const entry of listed.data) {
10894
+ if (entry.status === "closed")
10895
+ continue;
10896
+ const loaded = this.load(entry.id);
10897
+ if (!loaded.ok)
10898
+ return loaded;
10899
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
10900
+ return success(null, null, entry.id);
10901
+ }
10902
+ return success(null, null, null);
10903
+ }
10904
+ originTaskEntry() {
10905
+ const key = this.currentKey();
10906
+ const location = this.location();
10907
+ const branch = key.ok ? key.data.branch : null;
10908
+ if (!branch || !location.ok)
10909
+ return success(null, null, null);
10910
+ const common = path7.dirname(location.data.dir);
10911
+ const heads = path7.join(common, "logs", "refs", "heads");
10912
+ const headLog = location.data.git ? path7.join(location.data.git.gitDir, "logs", "HEAD") : "";
10913
+ const signatures = [
10914
+ path7.join(heads, branch),
10915
+ headLog,
10916
+ heads,
10917
+ path7.join(common, "packed-refs")
10918
+ ].map((file) => fileSignature(file) ?? "-");
10919
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
10920
+ let resolved = origins.get(slot);
10921
+ if (resolved?.signature !== signatures.join("|")) {
10922
+ const renames = branchRenames(location.data);
10923
+ const aliases = [branch];
10924
+ for (const rename of renames.toReversed())
10925
+ if (rename.to === aliases.at(-1))
10926
+ aliases.push(rename.from);
10927
+ const start = branchStart(location.data, branch, aliases);
10928
+ const slash = start?.indexOf("/") ?? -1;
10929
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path7.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
10930
+ const name = start && (remote ? start.slice(slash + 1) : start);
10931
+ resolved = {
10932
+ signature: signatures.join("|"),
10933
+ name: name && !aliases.includes(name) ? name : null,
10934
+ renames
10935
+ };
10936
+ origins.set(slot, resolved);
10937
+ }
10938
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
10939
+ if (entry && !entry.ok)
10940
+ return entry;
10941
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
10942
+ }
10943
+ readStoreTask(taskId) {
10944
+ const loaded = this.load(taskId);
10945
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
10946
+ }
10818
10947
  create(input, provenance, expectedWorkspaceRevision) {
10819
10948
  const value = "intent" in input ? input : {
10820
10949
  intent: input,
@@ -11035,13 +11164,13 @@ class TaskStore {
11035
11164
  if (!ready.ok)
11036
11165
  return ready;
11037
11166
  const dirs = this.paths();
11038
- const recovery = path6.join(this.root, ".workit", "recovery");
11039
- if (fs7.existsSync(recovery)) {
11167
+ const recovery = path7.join(this.root, ".workit", "recovery");
11168
+ if (fs8.existsSync(recovery)) {
11040
11169
  let files = 0;
11041
11170
  let bytes = 0;
11042
11171
  try {
11043
- for (const name of fs7.readdirSync(recovery)) {
11044
- const stat = fs7.lstatSync(path6.join(recovery, name));
11172
+ for (const name of fs8.readdirSync(recovery)) {
11173
+ const stat = fs8.lstatSync(path7.join(recovery, name));
11045
11174
  if (!stat.isFile())
11046
11175
  continue;
11047
11176
  files += 1;
@@ -11050,17 +11179,17 @@ class TaskStore {
11050
11179
  } catch {}
11051
11180
  const remove = options.pruneRecovery === true && !dryRun;
11052
11181
  if (remove)
11053
- fs7.rmSync(recovery, { recursive: true, force: true });
11182
+ fs8.rmSync(recovery, { recursive: true, force: true });
11054
11183
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
11055
11184
  }
11056
- if (!fs7.existsSync(dirs.tasks))
11185
+ if (!fs8.existsSync(dirs.tasks))
11057
11186
  return success(null, null, report);
11058
- const names = fs7.readdirSync(dirs.tasks).filter((name) => validId(name));
11187
+ const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
11059
11188
  for (const name of names) {
11060
11189
  const file = this.eventsPath(name);
11061
11190
  let size = 0;
11062
11191
  try {
11063
- size = fs7.statSync(file).size;
11192
+ size = fs8.statSync(file).size;
11064
11193
  } catch {
11065
11194
  continue;
11066
11195
  }
@@ -11088,16 +11217,16 @@ class TaskStore {
11088
11217
  const references = new Set;
11089
11218
  for (const name of names)
11090
11219
  try {
11091
- for (const match of fs7.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11220
+ for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11092
11221
  references.add(match[1]);
11093
11222
  } catch {}
11094
- if (fs7.existsSync(dirs.blobs))
11095
- for (const name of fs7.readdirSync(dirs.blobs)) {
11223
+ if (fs8.existsSync(dirs.blobs))
11224
+ for (const name of fs8.readdirSync(dirs.blobs)) {
11096
11225
  const digest = name.replace(/\.json$/, "");
11097
- const file = path6.join(dirs.blobs, name);
11226
+ const file = path7.join(dirs.blobs, name);
11098
11227
  let stat;
11099
11228
  try {
11100
- stat = fs7.lstatSync(file);
11229
+ stat = fs8.lstatSync(file);
11101
11230
  } catch {
11102
11231
  continue;
11103
11232
  }
@@ -11108,11 +11237,11 @@ class TaskStore {
11108
11237
  report.blobs.removed += 1;
11109
11238
  report.blobs.removedBytes += stat.size;
11110
11239
  if (!dryRun)
11111
- fs7.rmSync(file, { force: true });
11240
+ fs8.rmSync(file, { force: true });
11112
11241
  }
11113
11242
  return success(null, null, null);
11114
11243
  };
11115
- const swept = dryRun ? sweep() : this.withLock(path6.join(dirs.dir, "gc.lock"), sweep);
11244
+ const swept = dryRun ? sweep() : this.withLock(path7.join(dirs.dir, "gc.lock"), sweep);
11116
11245
  if (!swept.ok)
11117
11246
  return swept;
11118
11247
  const temporaryDirs = [
@@ -11120,17 +11249,17 @@ class TaskStore {
11120
11249
  dirs.tasks,
11121
11250
  dirs.blobs,
11122
11251
  dirs.checkout,
11123
- ...names.map((name) => path6.join(dirs.tasks, name))
11252
+ ...names.map((name) => path7.join(dirs.tasks, name))
11124
11253
  ];
11125
11254
  for (const directory of temporaryDirs) {
11126
- if (!fs7.existsSync(directory))
11255
+ if (!fs8.existsSync(directory))
11127
11256
  continue;
11128
- for (const name of fs7.readdirSync(directory)) {
11257
+ for (const name of fs8.readdirSync(directory)) {
11129
11258
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
11130
11259
  continue;
11131
- const file = path6.join(directory, name);
11260
+ const file = path7.join(directory, name);
11132
11261
  try {
11133
- const stat = fs7.lstatSync(file);
11262
+ const stat = fs8.lstatSync(file);
11134
11263
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
11135
11264
  continue;
11136
11265
  } catch {
@@ -11138,37 +11267,37 @@ class TaskStore {
11138
11267
  }
11139
11268
  report.temporary.removed += 1;
11140
11269
  if (!dryRun)
11141
- fs7.rmSync(file, { force: true });
11270
+ fs8.rmSync(file, { force: true });
11142
11271
  }
11143
11272
  }
11144
11273
  return success(null, null, report);
11145
11274
  }
11146
11275
  paths() {
11147
11276
  const location = this.location();
11148
- const dir = location.ok ? location.data.dir : path6.join(this.root, ".workit");
11149
- const checkout = path6.join(dir, "checkouts", checkoutSlug(this.root));
11277
+ const dir = location.ok ? location.data.dir : path7.join(this.root, ".workit");
11278
+ const checkout = path7.join(dir, "checkouts", checkoutSlug(this.root));
11150
11279
  return {
11151
11280
  dir,
11152
11281
  shared: location.ok ? location.data.shared : false,
11153
- marker: path6.join(dir, "store.json"),
11282
+ marker: path7.join(dir, "store.json"),
11154
11283
  checkout,
11155
- workspace: path6.join(checkout, "workspace.json"),
11156
- checkoutLock: path6.join(checkout, "metadata.lock"),
11157
- tasks: path6.join(dir, "tasks"),
11158
- index: path6.join(dir, "task-index.json"),
11159
- blobs: path6.join(dir, "blobs", "candidates"),
11160
- legacy: path6.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11161
- v2: path6.join(this.root, ".workit")
11284
+ workspace: path7.join(checkout, "workspace.json"),
11285
+ checkoutLock: path7.join(checkout, "metadata.lock"),
11286
+ tasks: path7.join(dir, "tasks"),
11287
+ index: path7.join(dir, "task-index.json"),
11288
+ blobs: path7.join(dir, "blobs", "candidates"),
11289
+ legacy: path7.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11290
+ v2: path7.join(this.root, ".workit")
11162
11291
  };
11163
11292
  }
11164
11293
  eventsPath(taskId) {
11165
- return path6.join(this.paths().tasks, taskId, "events.jsonl");
11294
+ return path7.join(this.paths().tasks, taskId, "events.jsonl");
11166
11295
  }
11167
11296
  snapshotPath(taskId) {
11168
- return path6.join(this.paths().tasks, taskId, "snapshot.json");
11297
+ return path7.join(this.paths().tasks, taskId, "snapshot.json");
11169
11298
  }
11170
11299
  taskLockPath(taskId) {
11171
- return path6.join(this.paths().tasks, taskId, "lock");
11300
+ return path7.join(this.paths().tasks, taskId, "lock");
11172
11301
  }
11173
11302
  ready(write = false) {
11174
11303
  const location = this.location();
@@ -11178,7 +11307,7 @@ class TaskStore {
11178
11307
  return success(null, null, null);
11179
11308
  const dirs = this.paths();
11180
11309
  try {
11181
- const marker = JSON.parse(fs7.readFileSync(dirs.marker, "utf8"));
11310
+ const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
11182
11311
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
11183
11312
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
11184
11313
  } catch {}
@@ -11207,21 +11336,21 @@ class TaskStore {
11207
11336
  return {
11208
11337
  read: (digest) => {
11209
11338
  try {
11210
- return JSON.parse(fs7.readFileSync(path6.join(dir, `${digest}.json`), "utf8"));
11339
+ return JSON.parse(fs8.readFileSync(path7.join(dir, `${digest}.json`), "utf8"));
11211
11340
  } catch (error) {
11212
11341
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
11213
11342
  }
11214
11343
  },
11215
11344
  write: (digest, value) => {
11216
- const file = path6.join(dir, `${digest}.json`);
11217
- if (fs7.existsSync(file)) {
11345
+ const file = path7.join(dir, `${digest}.json`);
11346
+ if (fs8.existsSync(file)) {
11218
11347
  const stamp = new Date;
11219
11348
  try {
11220
- fs7.utimesSync(file, stamp, stamp);
11349
+ fs8.utimesSync(file, stamp, stamp);
11221
11350
  return;
11222
11351
  } catch {}
11223
11352
  }
11224
- fs7.mkdirSync(dir, { recursive: true });
11353
+ fs8.mkdirSync(dir, { recursive: true });
11225
11354
  replaceFile(file, JSON.stringify(value), true);
11226
11355
  }
11227
11356
  };
@@ -11303,7 +11432,7 @@ class TaskStore {
11303
11432
  }
11304
11433
  readSnapshot(taskId) {
11305
11434
  try {
11306
- const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
11435
+ const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
11307
11436
  if (!isObject5(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject5(value.record))
11308
11437
  return null;
11309
11438
  return value;
@@ -11405,8 +11534,8 @@ class TaskStore {
11405
11534
  }
11406
11535
  }
11407
11536
  openTask(record, type, key, legacy = null) {
11408
- const dir = path6.join(this.paths().tasks, record.id);
11409
- fs7.mkdirSync(dir, { recursive: true });
11537
+ const dir = path7.join(this.paths().tasks, record.id);
11538
+ fs8.mkdirSync(dir, { recursive: true });
11410
11539
  const blobs = this.blobStore();
11411
11540
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
11412
11541
  const event = this.event(record.id, 1, type, {
@@ -11458,11 +11587,11 @@ class TaskStore {
11458
11587
  } catch {}
11459
11588
  if (!latest || latest.events[0]?.id !== last.id)
11460
11589
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
11461
- const appended = fs7.readFileSync(file).subarray(log.end, latest.end);
11590
+ const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
11462
11591
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
11463
11592
  replaceFile(file, bytes.toString("utf8"), true);
11464
11593
  try {
11465
- fs7.rmSync(this.snapshotPath(taskId), { force: true });
11594
+ fs8.rmSync(this.snapshotPath(taskId), { force: true });
11466
11595
  } catch {}
11467
11596
  const reloaded = this.load(taskId);
11468
11597
  if (reloaded.ok && reloaded.data)
@@ -11475,7 +11604,7 @@ class TaskStore {
11475
11604
  }
11476
11605
  countEvents(file) {
11477
11606
  try {
11478
- const bytes = fs7.readFileSync(file);
11607
+ const bytes = fs8.readFileSync(file);
11479
11608
  let count = 0;
11480
11609
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
11481
11610
  count += 1;
@@ -11484,12 +11613,12 @@ class TaskStore {
11484
11613
  return 0;
11485
11614
  }
11486
11615
  }
11487
- openTasksForKey(key) {
11616
+ openTasksForKey(key, known) {
11488
11617
  const all = this.listStoreIndex();
11489
11618
  if (!all.ok)
11490
11619
  return all;
11491
- const location = this.location();
11492
- const renames = location.ok ? branchRenames(location.data) : [];
11620
+ const location = known ? null : this.location();
11621
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
11493
11622
  return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
11494
11623
  }
11495
11624
  openTaskForKey(key) {
@@ -11497,8 +11626,8 @@ class TaskStore {
11497
11626
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
11498
11627
  }
11499
11628
  withKeyLock(key, operation) {
11500
- const digest = createHash4("sha256").update(key).digest("hex").slice(0, 32);
11501
- return this.withLock(path6.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11629
+ const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
11630
+ return this.withLock(path7.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11502
11631
  }
11503
11632
  bindLocked(taskId, key, timestamp) {
11504
11633
  const current = this.readWorkspace();
@@ -11639,7 +11768,7 @@ class TaskStore {
11639
11768
  writeWorkspace(record) {
11640
11769
  const file = this.paths().workspace;
11641
11770
  try {
11642
- fs7.mkdirSync(path6.dirname(file), { recursive: true });
11771
+ fs8.mkdirSync(path7.dirname(file), { recursive: true });
11643
11772
  replaceFile(file, `${canonicalJson(record)}
11644
11773
  `, true);
11645
11774
  return success(null, null, null);
@@ -11649,7 +11778,7 @@ class TaskStore {
11649
11778
  }
11650
11779
  readIndex() {
11651
11780
  try {
11652
- const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
11781
+ const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
11653
11782
  if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
11654
11783
  return {};
11655
11784
  const tasks = {};
@@ -11663,7 +11792,7 @@ class TaskStore {
11663
11792
  }
11664
11793
  writeIndex(entries) {
11665
11794
  try {
11666
- fs7.mkdirSync(this.paths().tasks, { recursive: true });
11795
+ fs8.mkdirSync(this.paths().tasks, { recursive: true });
11667
11796
  replaceFile(this.paths().index, JSON.stringify({
11668
11797
  version: INDEX_VERSION,
11669
11798
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -11717,23 +11846,23 @@ class TaskStore {
11717
11846
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
11718
11847
  }
11719
11848
  v2Dirs() {
11720
- return [...new Set([this.root, this.origin])].map((dir) => path6.join(dir, ".workit"));
11849
+ return [...new Set([this.root, this.origin])].map((dir) => path7.join(dir, ".workit"));
11721
11850
  }
11722
11851
  pendingMigrations() {
11723
11852
  return this.v2Dirs().filter((dir) => {
11724
- const raw = this.readV2Workspace(path6.join(dir, "workspace.json"));
11853
+ const raw = this.readV2Workspace(path7.join(dir, "workspace.json"));
11725
11854
  return raw !== null && raw !== TOMBSTONE;
11726
11855
  });
11727
11856
  }
11728
11857
  migrateV2(v2) {
11729
11858
  const dirs = this.paths();
11730
- const v2Workspace = path6.join(v2, "workspace.json");
11731
- const v2Tasks = path6.join(v2, "tasks");
11732
- const legacy = path6.join(dirs.dir, "legacy", checkoutSlug(path6.dirname(v2)), "v2");
11859
+ const v2Workspace = path7.join(v2, "workspace.json");
11860
+ const v2Tasks = path7.join(v2, "tasks");
11861
+ const legacy = path7.join(dirs.dir, "legacy", checkoutSlug(path7.dirname(v2)), "v2");
11733
11862
  const raw = this.readV2Workspace(v2Workspace);
11734
11863
  if (raw === null || raw === TOMBSTONE)
11735
11864
  return success(null, null, null);
11736
- return this.withLock(dirs.checkoutLock, () => this.withLock(path6.join(v2, "metadata.lock"), () => {
11865
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path7.join(v2, "metadata.lock"), () => {
11737
11866
  const workspace = this.readV2Workspace(v2Workspace);
11738
11867
  if (workspace === null || workspace === TOMBSTONE)
11739
11868
  return success(null, null, null);
@@ -11742,7 +11871,7 @@ class TaskStore {
11742
11871
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
11743
11872
  let files = [];
11744
11873
  try {
11745
- files = fs7.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path6.join(v2Tasks, name)));
11874
+ files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path7.join(v2Tasks, name)));
11746
11875
  } catch {}
11747
11876
  const current = this.readWorkspace();
11748
11877
  if (!current.ok)
@@ -11760,22 +11889,22 @@ class TaskStore {
11760
11889
  let skipped = 0;
11761
11890
  const done = new Map;
11762
11891
  try {
11763
- fs7.mkdirSync(path6.join(legacy, "tasks"), { recursive: true });
11892
+ fs8.mkdirSync(path7.join(legacy, "tasks"), { recursive: true });
11764
11893
  for (const name of ["workspace.json", "index.json"])
11765
- if (fs7.existsSync(path6.join(v2, name)))
11766
- fs7.copyFileSync(path6.join(v2, name), path6.join(legacy, name));
11894
+ if (fs8.existsSync(path7.join(v2, name)))
11895
+ fs8.copyFileSync(path7.join(v2, name), path7.join(legacy, name));
11767
11896
  for (const name of files) {
11768
11897
  const id = name.slice(0, -5);
11769
- const file = path6.join(v2Tasks, name);
11770
- const bytes = fs7.readFileSync(file, "utf8");
11898
+ const file = path7.join(v2Tasks, name);
11899
+ const bytes = fs8.readFileSync(file, "utf8");
11771
11900
  const digest = sha256(bytes);
11772
- fs7.writeFileSync(path6.join(legacy, "tasks", name), bytes);
11901
+ fs8.writeFileSync(path7.join(legacy, "tasks", name), bytes);
11773
11902
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
11774
11903
  if (!parsed.ok || parsed.data.id !== id)
11775
11904
  return failure2("recovery_required", `cannot migrate 2.x task ${id}: ${parsed.ok ? "file name and record ID differ" : parsed.error}; move ${file} aside to continue`, { path: file });
11776
11905
  const record = { ...parsed.data, workspaceId: target.id };
11777
- const origin = { path: path6.join(legacy, "tasks", name), digest };
11778
- if (!fs7.existsSync(this.eventsPath(id))) {
11906
+ const origin = { path: path7.join(legacy, "tasks", name), digest };
11907
+ if (!fs8.existsSync(this.eventsPath(id))) {
11779
11908
  this.openTask(record, "migrated.from_v2", null, origin);
11780
11909
  migrated += 1;
11781
11910
  } else {
@@ -11810,8 +11939,8 @@ class TaskStore {
11810
11939
  try {
11811
11940
  let complete = true;
11812
11941
  for (const [name, digest] of done) {
11813
- const file = path6.join(v2Tasks, name);
11814
- if (sha256(fs7.readFileSync(file, "utf8")) !== digest) {
11942
+ const file = path7.join(v2Tasks, name);
11943
+ if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
11815
11944
  complete = false;
11816
11945
  continue;
11817
11946
  }
@@ -11821,7 +11950,7 @@ class TaskStore {
11821
11950
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
11822
11951
  path: v2
11823
11952
  });
11824
- fs7.rmSync(path6.join(v2, "index.json"), { force: true });
11953
+ fs8.rmSync(path7.join(v2, "index.json"), { force: true });
11825
11954
  this.writeTombstone(v2, target.id);
11826
11955
  } catch (error) {
11827
11956
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -11841,45 +11970,45 @@ class TaskStore {
11841
11970
  }
11842
11971
  adoptLocalStore() {
11843
11972
  const dirs = this.paths();
11844
- const marker = path6.join(dirs.v2, "store.json");
11845
- if (!dirs.shared || !fs7.existsSync(marker))
11973
+ const marker = path7.join(dirs.v2, "store.json");
11974
+ if (!dirs.shared || !fs8.existsSync(marker))
11846
11975
  return success(null, null, null);
11847
11976
  return this.withLock(dirs.checkoutLock, () => {
11848
- if (!fs7.existsSync(marker))
11977
+ if (!fs8.existsSync(marker))
11849
11978
  return success(null, null, null);
11850
11979
  let tasks = 0;
11851
11980
  try {
11852
11981
  const merge = (from, to) => {
11853
- if (!fs7.existsSync(to)) {
11854
- fs7.mkdirSync(path6.dirname(to), { recursive: true });
11982
+ if (!fs8.existsSync(to)) {
11983
+ fs8.mkdirSync(path7.dirname(to), { recursive: true });
11855
11984
  try {
11856
- fs7.renameSync(from, to);
11985
+ fs8.renameSync(from, to);
11857
11986
  } catch {
11858
- fs7.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
11859
- fs7.rmSync(from, { recursive: true, force: true });
11987
+ fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
11988
+ fs8.rmSync(from, { recursive: true, force: true });
11860
11989
  }
11861
11990
  return;
11862
11991
  }
11863
- if (fs7.lstatSync(from).isDirectory() && fs7.lstatSync(to).isDirectory())
11864
- for (const name of fs7.readdirSync(from))
11865
- merge(path6.join(from, name), path6.join(to, name));
11992
+ if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
11993
+ for (const name of fs8.readdirSync(from))
11994
+ merge(path7.join(from, name), path7.join(to, name));
11866
11995
  };
11867
11996
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
11868
- const from = path6.join(dirs.v2, sub);
11869
- if (!fs7.existsSync(from))
11997
+ const from = path7.join(dirs.v2, sub);
11998
+ if (!fs8.existsSync(from))
11870
11999
  continue;
11871
12000
  if (sub === "tasks") {
11872
- for (const name of fs7.readdirSync(from))
11873
- if (validId(name) && !fs7.existsSync(path6.join(dirs.dir, sub, name)))
12001
+ for (const name of fs8.readdirSync(from))
12002
+ if (validId(name) && !fs8.existsSync(path7.join(dirs.dir, sub, name)))
11874
12003
  tasks += 1;
11875
12004
  }
11876
- merge(from, path6.join(dirs.dir, sub));
12005
+ merge(from, path7.join(dirs.dir, sub));
11877
12006
  }
11878
- const ledger = path6.join(dirs.v2, "ledger");
11879
- if (fs7.existsSync(ledger) && !fs7.existsSync(path6.join(dirs.dir, "ledger")))
11880
- merge(ledger, path6.join(dirs.dir, "ledger"));
11881
- fs7.rmSync(path6.join(dirs.v2, "task-index.json"), { force: true });
11882
- fs7.renameSync(marker, path6.join(dirs.v2, "store.moved.json"));
12007
+ const ledger = path7.join(dirs.v2, "ledger");
12008
+ if (fs8.existsSync(ledger) && !fs8.existsSync(path7.join(dirs.dir, "ledger")))
12009
+ merge(ledger, path7.join(dirs.dir, "ledger"));
12010
+ fs8.rmSync(path7.join(dirs.v2, "task-index.json"), { force: true });
12011
+ fs8.renameSync(marker, path7.join(dirs.v2, "store.moved.json"));
11883
12012
  } catch (error) {
11884
12013
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
11885
12014
  path: dirs.v2
@@ -11899,7 +12028,7 @@ class TaskStore {
11899
12028
  readV2Workspace(file) {
11900
12029
  let bytes;
11901
12030
  try {
11902
- bytes = fs7.readFileSync(file, "utf8");
12031
+ bytes = fs8.readFileSync(file, "utf8");
11903
12032
  } catch {
11904
12033
  return null;
11905
12034
  }
@@ -11917,7 +12046,7 @@ class TaskStore {
11917
12046
  schemaVersion: SCHEMA_VERSION,
11918
12047
  id: workspaceId ?? newId(),
11919
12048
  revision: newRevision(),
11920
- root: path6.dirname(v2),
12049
+ root: path7.dirname(v2),
11921
12050
  runtime: { createdWith: version, updatedWith: version },
11922
12051
  writer: null,
11923
12052
  store: {
@@ -11928,12 +12057,12 @@ class TaskStore {
11928
12057
  },
11929
12058
  critical: ["store"]
11930
12059
  };
11931
- fs7.mkdirSync(v2, { recursive: true });
11932
- const ignore = path6.join(v2, ".gitignore");
11933
- if (!fs7.existsSync(ignore))
11934
- fs7.writeFileSync(ignore, `*
12060
+ fs8.mkdirSync(v2, { recursive: true });
12061
+ const ignore = path7.join(v2, ".gitignore");
12062
+ if (!fs8.existsSync(ignore))
12063
+ fs8.writeFileSync(ignore, `*
11935
12064
  `);
11936
- replaceFile(path6.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
12065
+ replaceFile(path7.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
11937
12066
  `, true);
11938
12067
  }
11939
12068
  stubFor(taskId) {
@@ -11949,7 +12078,7 @@ class TaskStore {
11949
12078
  }
11950
12079
  isStub(file) {
11951
12080
  try {
11952
- const value = JSON.parse(fs7.readFileSync(file, "utf8"));
12081
+ const value = JSON.parse(fs8.readFileSync(file, "utf8"));
11953
12082
  return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
11954
12083
  } catch {
11955
12084
  return false;
@@ -11957,32 +12086,32 @@ class TaskStore {
11957
12086
  }
11958
12087
  writeTombstoneIfMissing() {
11959
12088
  try {
11960
- const v2 = path6.join(this.root, ".workit");
11961
- if (fs7.existsSync(path6.join(v2, "workspace.json")))
12089
+ const v2 = path7.join(this.root, ".workit");
12090
+ if (fs8.existsSync(path7.join(v2, "workspace.json")))
11962
12091
  return;
11963
12092
  let id = null;
11964
12093
  try {
11965
- id = JSON.parse(fs7.readFileSync(this.paths().workspace, "utf8")).id ?? null;
12094
+ id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
11966
12095
  } catch {}
11967
12096
  this.writeTombstone(v2, id);
11968
12097
  } catch {}
11969
12098
  }
11970
12099
  initializeStorage() {
11971
12100
  const dirs = this.paths();
11972
- retryTransient(() => fs7.mkdirSync(dirs.checkout, { recursive: true }));
11973
- retryTransient(() => fs7.mkdirSync(dirs.tasks, { recursive: true }));
11974
- if (!fs7.existsSync(dirs.marker))
12101
+ retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
12102
+ retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
12103
+ if (!fs8.existsSync(dirs.marker))
11975
12104
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
11976
12105
  `, false);
11977
12106
  if (!dirs.shared) {
11978
- const ignore = path6.join(dirs.dir, ".gitignore");
12107
+ const ignore = path7.join(dirs.dir, ".gitignore");
11979
12108
  let current = null;
11980
12109
  try {
11981
- current = fs7.readFileSync(ignore, "utf8");
12110
+ current = fs8.readFileSync(ignore, "utf8");
11982
12111
  } catch {}
11983
12112
  if (current !== `*
11984
12113
  `)
11985
- retryTransient(() => fs7.writeFileSync(ignore, `*
12114
+ retryTransient(() => fs8.writeFileSync(ignore, `*
11986
12115
  `));
11987
12116
  }
11988
12117
  this.writeTombstoneIfMissing();
@@ -11998,7 +12127,7 @@ class TaskStore {
11998
12127
  shouldReclaim: ({ payload, nowMs }) => {
11999
12128
  let ageMs = null;
12000
12129
  try {
12001
- ageMs = nowMs - fs7.lstatSync(lockPath).mtimeMs;
12130
+ ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
12002
12131
  } catch {}
12003
12132
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
12004
12133
  },
@@ -12035,7 +12164,7 @@ class TaskStore {
12035
12164
  withLock(lockPath, operation) {
12036
12165
  try {
12037
12166
  this.initializeStorage();
12038
- fs7.mkdirSync(path6.dirname(lockPath), { recursive: true });
12167
+ fs8.mkdirSync(path7.dirname(lockPath), { recursive: true });
12039
12168
  } catch (error) {
12040
12169
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
12041
12170
  path: this.paths().dir
@@ -12075,18 +12204,18 @@ class TaskStore {
12075
12204
  lockDirWritable(lockPath) {
12076
12205
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
12077
12206
  try {
12078
- fs7.closeSync(fs7.openSync(probe, "wx", 384));
12207
+ fs8.closeSync(fs8.openSync(probe, "wx", 384));
12079
12208
  } catch {
12080
12209
  return false;
12081
12210
  }
12082
12211
  try {
12083
- fs7.rmSync(probe, { force: true });
12212
+ fs8.rmSync(probe, { force: true });
12084
12213
  } catch {}
12085
12214
  return true;
12086
12215
  }
12087
12216
  lockHolderPresent(lockPath) {
12088
12217
  try {
12089
- fs7.lstatSync(lockPath);
12218
+ fs8.lstatSync(lockPath);
12090
12219
  return true;
12091
12220
  } catch (error) {
12092
12221
  return error?.code !== "ENOENT";
@@ -12098,12 +12227,12 @@ class TaskStore {
12098
12227
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
12099
12228
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
12100
12229
  path: lockPath,
12101
- guidance: `Check permissions and the read-only attribute on ${path6.dirname(lockPath)}.`
12230
+ guidance: `Check permissions and the read-only attribute on ${path7.dirname(lockPath)}.`
12102
12231
  });
12103
12232
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
12104
12233
  let lock = null;
12105
12234
  try {
12106
- lock = parseMetadataLockOrNull(fs7.readFileSync(lockPath, "utf8"));
12235
+ lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
12107
12236
  } catch {}
12108
12237
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
12109
12238
  outcome: "not_started",
@@ -12121,15 +12250,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
12121
12250
  var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
12122
12251
 
12123
12252
  // packages/workit-core/src/core/task-evaluation.ts
12124
- import { createHash as createHash9 } from "node:crypto";
12125
- import * as fs12 from "node:fs";
12253
+ import { createHash as createHash10 } from "node:crypto";
12254
+ import * as fs13 from "node:fs";
12126
12255
  import { spawnSync as spawnSync10 } from "node:child_process";
12127
- import path14 from "node:path";
12256
+ import path15 from "node:path";
12128
12257
 
12129
12258
  // packages/workit-core/src/check-config.ts
12130
12259
  import { spawnSync as spawnSync3 } from "node:child_process";
12131
- import fs8 from "node:fs";
12132
- import path7 from "node:path";
12260
+ import fs9 from "node:fs";
12261
+ import path8 from "node:path";
12133
12262
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12134
12263
  var CHECKS_FILE = "workit.checks.json";
12135
12264
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -12182,10 +12311,10 @@ var git = (cwd, args) => {
12182
12311
  return value || null;
12183
12312
  };
12184
12313
  function checkRoot(cwd) {
12185
- return path7.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12314
+ return path8.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12186
12315
  }
12187
12316
  var packageManager = (root) => {
12188
- const has = (file) => fs8.existsSync(path7.join(root, file));
12317
+ const has = (file) => fs9.existsSync(path8.join(root, file));
12189
12318
  if (has("bun.lock") || has("bun.lockb"))
12190
12319
  return "bun";
12191
12320
  if (has("pnpm-lock.yaml"))
@@ -12205,7 +12334,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
12205
12334
  function detectedChecks(root) {
12206
12335
  const read = (file) => {
12207
12336
  try {
12208
- return fs8.readFileSync(path7.join(root, file), "utf8");
12337
+ return fs9.readFileSync(path8.join(root, file), "utf8");
12209
12338
  } catch {
12210
12339
  return null;
12211
12340
  }
@@ -12244,7 +12373,7 @@ function detectedChecks(root) {
12244
12373
  var GATES = ["testing", "verification"];
12245
12374
  function loadCheckConfig(cwd) {
12246
12375
  const root = checkRoot(cwd);
12247
- const file = path7.join(root, CHECKS_FILE);
12376
+ const file = path8.join(root, CHECKS_FILE);
12248
12377
  const none = (error = null) => ({
12249
12378
  source: error ? CHECKS_FILE : "none",
12250
12379
  root,
@@ -12254,7 +12383,7 @@ function loadCheckConfig(cwd) {
12254
12383
  });
12255
12384
  let text = null;
12256
12385
  try {
12257
- text = fs8.readFileSync(file, "utf8");
12386
+ text = fs9.readFileSync(file, "utf8");
12258
12387
  } catch (error) {
12259
12388
  if (error.code !== "ENOENT")
12260
12389
  return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
@@ -12317,10 +12446,10 @@ function workitAvailable(env = process.env) {
12317
12446
  return workitOnPath;
12318
12447
  const windows = process.platform === "win32";
12319
12448
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
12320
- const dirs = (env.PATH ?? env.Path ?? "").split(path7.delimiter).filter(Boolean);
12449
+ const dirs = (env.PATH ?? env.Path ?? "").split(path8.delimiter).filter(Boolean);
12321
12450
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
12322
12451
  try {
12323
- return fs8.statSync(path7.join(dir, `workit${ext}`)).isFile();
12452
+ return fs9.statSync(path8.join(dir, `workit${ext}`)).isFile();
12324
12453
  } catch {
12325
12454
  return false;
12326
12455
  }
@@ -12333,10 +12462,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
12333
12462
 
12334
12463
  // packages/workit-core/src/git/rev.ts
12335
12464
  import { spawnSync as spawnSync4 } from "node:child_process";
12336
- import { createHash as createHash5 } from "node:crypto";
12337
- import fs9 from "node:fs";
12465
+ import { createHash as createHash6 } from "node:crypto";
12466
+ import fs10 from "node:fs";
12338
12467
  import os from "node:os";
12339
- import path8 from "node:path";
12468
+ import path9 from "node:path";
12340
12469
  var MAX_BUFFER = 256 * 1024 * 1024;
12341
12470
  var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
12342
12471
  var git2 = (cwd, args, options = {}) => {
@@ -12371,10 +12500,10 @@ function headSha(cwd) {
12371
12500
  var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
12372
12501
  var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
12373
12502
  var seedIndex = (from, to) => {
12374
- fs9.copyFileSync(from, to);
12375
- const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
12503
+ fs10.copyFileSync(from, to);
12504
+ const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
12376
12505
  const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
12377
- fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
12506
+ fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
12378
12507
  };
12379
12508
  function worktreeTree(cwd, options = {}) {
12380
12509
  const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
@@ -12383,10 +12512,10 @@ function worktreeTree(cwd, options = {}) {
12383
12512
  return null;
12384
12513
  const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
12385
12514
  const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
12386
- const scratch = fs9.mkdtempSync(path8.join(os.tmpdir(), "workit-tree-"));
12515
+ const scratch = fs10.mkdtempSync(path9.join(os.tmpdir(), "workit-tree-"));
12387
12516
  try {
12388
- const tempIndex = path8.join(scratch, "index");
12389
- if (fs9.existsSync(indexPath))
12517
+ const tempIndex = path9.join(scratch, "index");
12518
+ if (fs10.existsSync(indexPath))
12390
12519
  seedIndex(indexPath, tempIndex);
12391
12520
  const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
12392
12521
  const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
@@ -12398,7 +12527,7 @@ function worktreeTree(cwd, options = {}) {
12398
12527
  const skipped = [];
12399
12528
  for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
12400
12529
  try {
12401
- const stat = fs9.statSync(path8.join(top, file));
12530
+ const stat = fs10.statSync(path9.join(top, file));
12402
12531
  if (stat.isFile() && stat.size > cap)
12403
12532
  skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
12404
12533
  } catch {}
@@ -12415,10 +12544,10 @@ function worktreeTree(cwd, options = {}) {
12415
12544
  if (!tree)
12416
12545
  return null;
12417
12546
  const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
12418
- const key = skipped.length ? `sha256:${createHash5("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
12547
+ const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
12419
12548
  return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
12420
12549
  } finally {
12421
- fs9.rmSync(scratch, { recursive: true, force: true });
12550
+ fs10.rmSync(scratch, { recursive: true, force: true });
12422
12551
  }
12423
12552
  }
12424
12553
  function worktreeSignal(cwd, options = {}) {
@@ -12438,16 +12567,16 @@ function worktreeSignal(cwd, options = {}) {
12438
12567
  });
12439
12568
  if (!status.ok)
12440
12569
  return null;
12441
- const hash = createHash5("sha256");
12570
+ const hash = createHash6("sha256");
12442
12571
  hash.update(`${headSha(top) ?? "unborn"}\x00`);
12443
12572
  for (const entry of status.stdout.split("\x00")) {
12444
12573
  if (entry.length < 4)
12445
12574
  continue;
12446
12575
  const file = entry.slice(3);
12447
- const absolute = path8.join(top, file);
12576
+ const absolute = path9.join(top, file);
12448
12577
  let stat = "-";
12449
12578
  try {
12450
- const info = fs9.lstatSync(absolute, { bigint: true });
12579
+ const info = fs10.lstatSync(absolute, { bigint: true });
12451
12580
  stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
12452
12581
  if (info.isDirectory())
12453
12582
  stat += `:${headSha(absolute) ?? "-"}`;
@@ -12514,17 +12643,17 @@ function pushRemoteName(cwd, branch) {
12514
12643
 
12515
12644
  // packages/workit-core/src/core/policy/derive.ts
12516
12645
  import { existsSync as existsSync4 } from "node:fs";
12517
- import path13 from "node:path";
12646
+ import path14 from "node:path";
12518
12647
 
12519
12648
  // packages/workit-core/src/ledger.ts
12520
12649
  import { spawnSync as spawnSync9 } from "node:child_process";
12521
- import { createHash as createHash8, randomBytes as randomBytes2 } from "node:crypto";
12522
- import fs11 from "node:fs";
12523
- import path12 from "node:path";
12650
+ import { createHash as createHash9, randomBytes as randomBytes2 } from "node:crypto";
12651
+ import fs12 from "node:fs";
12652
+ import path13 from "node:path";
12524
12653
 
12525
12654
  // packages/workit-core/src/core/vcs-config.ts
12526
- import fs10 from "node:fs";
12527
- import path11 from "node:path";
12655
+ import fs11 from "node:fs";
12656
+ import path12 from "node:path";
12528
12657
  import { spawnSync as spawnSync8 } from "node:child_process";
12529
12658
 
12530
12659
  // packages/workit-core/src/core/config.ts
@@ -12538,7 +12667,7 @@ import {
12538
12667
  writeFileSync as writeFileSync2
12539
12668
  } from "node:fs";
12540
12669
  import os2 from "node:os";
12541
- import path9 from "node:path";
12670
+ import path10 from "node:path";
12542
12671
 
12543
12672
  // packages/workit-core/src/core/boundary.ts
12544
12673
  var EVENT = {
@@ -12582,7 +12711,7 @@ var mergePreset = (preset, input = {}, current = {
12582
12711
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
12583
12712
  };
12584
12713
  };
12585
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path9.join(process.env.XDG_CONFIG_HOME || path9.join(os2.homedir(), ".config"), "workit");
12714
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path10.join(process.env.XDG_CONFIG_HOME || path10.join(os2.homedir(), ".config"), "workit");
12586
12715
  var migratedDir = null;
12587
12716
  var migrationFailed = false;
12588
12717
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -12592,7 +12721,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12592
12721
  migratedDir = dir;
12593
12722
  return dir;
12594
12723
  }
12595
- const legacy = path9.join(process.env.XDG_CONFIG_HOME || path9.join(os2.homedir(), ".config"), "workflow-toolkit");
12724
+ const legacy = path10.join(process.env.XDG_CONFIG_HOME || path10.join(os2.homedir(), ".config"), "workflow-toolkit");
12596
12725
  if (!existsSync3(legacy)) {
12597
12726
  migratedDir = dir;
12598
12727
  return dir;
@@ -12605,8 +12734,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12605
12734
  mkdirSync2(dir, { recursive: true });
12606
12735
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
12607
12736
  for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
12608
- const src = path9.join(legacy, entry.name);
12609
- const dest = path9.join(dir, entry.name);
12737
+ const src = path10.join(legacy, entry.name);
12738
+ const dest = path10.join(dir, entry.name);
12610
12739
  if (existsSync3(dest))
12611
12740
  continue;
12612
12741
  try {
@@ -12678,7 +12807,7 @@ var parseConfigResult = (raw, file) => {
12678
12807
  return { status: "valid", path: file, config };
12679
12808
  };
12680
12809
  var readConfigTyped = (dir) => {
12681
- const file = path9.join(dir ?? configDir(), "config.json");
12810
+ const file = path10.join(dir ?? configDir(), "config.json");
12682
12811
  return parseConfigResult(readSafe(file), file);
12683
12812
  };
12684
12813
  var readConfig = () => {
@@ -12687,7 +12816,7 @@ var readConfig = () => {
12687
12816
  throw new Error(result.error);
12688
12817
  return result.config ?? DEFAULTS;
12689
12818
  };
12690
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
12819
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
12691
12820
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
12692
12821
  var resolveBranchPolicy = (config, workspace, repoPreset) => {
12693
12822
  const wp = workspace?.branchPolicy ?? {};
@@ -12729,9 +12858,9 @@ var resolveCommitPolicy = (config, workspace) => {
12729
12858
 
12730
12859
  // packages/workit-core/src/core/workspaces.ts
12731
12860
  import { spawnSync as spawnSync6 } from "node:child_process";
12732
- import { createHash as createHash6 } from "node:crypto";
12861
+ import { createHash as createHash7 } from "node:crypto";
12733
12862
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
12734
- import path10 from "node:path";
12863
+ import path11 from "node:path";
12735
12864
 
12736
12865
  // packages/workit-core/src/core/branch-policy.ts
12737
12866
  import { spawnSync as spawnSync5 } from "node:child_process";
@@ -12856,7 +12985,7 @@ var releaseTrackSchema = object({
12856
12985
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
12857
12986
  }
12858
12987
  }
12859
- if (track.versionSource?.kind === "package-json" && (path10.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
12988
+ if (track.versionSource?.kind === "package-json" && (path11.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
12860
12989
  context.addIssue({
12861
12990
  code: "custom",
12862
12991
  path: ["versionSource", "path"],
@@ -12916,7 +13045,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
12916
13045
  names.add(workspace.name);
12917
13046
  }
12918
13047
  });
12919
- var workspacesRevision = (contents) => createHash6("sha256").update(contents ?? "<missing>").digest("hex");
13048
+ var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
12920
13049
  var validateWorkspacesDocument = (parsed, file) => {
12921
13050
  if (!isConfigObject(parsed)) {
12922
13051
  return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
@@ -12954,7 +13083,7 @@ var validateWorkspacesDocument = (parsed, file) => {
12954
13083
  };
12955
13084
  };
12956
13085
  var readWorkspacesResult = (dir = configDir()) => {
12957
- const file = path10.join(dir, "workspaces.json");
13086
+ const file = path11.join(dir, "workspaces.json");
12958
13087
  let raw;
12959
13088
  try {
12960
13089
  raw = readFileSync3(file, "utf8");
@@ -13065,9 +13194,9 @@ var mainCheckoutOf = (cwd) => {
13065
13194
  let main = null;
13066
13195
  const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
13067
13196
  const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
13068
- if (gitDir && common && path10.resolve(gitDir) !== path10.resolve(common)) {
13197
+ if (gitDir && common && path11.resolve(gitDir) !== path11.resolve(common)) {
13069
13198
  const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
13070
- main = listed?.trim() || (path10.basename(common) === ".git" ? path10.dirname(common) : common);
13199
+ main = listed?.trim() || (path11.basename(common) === ".git" ? path11.dirname(common) : common);
13071
13200
  }
13072
13201
  mainCheckouts.set(cwd, main);
13073
13202
  return main;
@@ -13158,7 +13287,7 @@ var runtimeWorkspaceIndexSchema = object({
13158
13287
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
13159
13288
  }).passthrough();
13160
13289
  var matchRuntimeWorkspace = (cwd) => {
13161
- const file = path10.join(configDir(), "workspaces.json");
13290
+ const file = path11.join(configDir(), "workspaces.json");
13162
13291
  let raw;
13163
13292
  try {
13164
13293
  raw = readFileSync3(file, "utf8");
@@ -13233,7 +13362,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
13233
13362
  }
13234
13363
 
13235
13364
  // packages/workit-core/src/core/release-tracks.ts
13236
- import { createHash as createHash7 } from "node:crypto";
13365
+ import { createHash as createHash8 } from "node:crypto";
13237
13366
  import { spawnSync as spawnSync7 } from "node:child_process";
13238
13367
  class ReleaseTrackError extends Error {
13239
13368
  }
@@ -13616,7 +13745,7 @@ function resolveTrackFor(cwd, options) {
13616
13745
  ]) ?? "",
13617
13746
  git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
13618
13747
  ] : [];
13619
- key = createHash7("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13748
+ key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13620
13749
  const hit = memo2.get(key);
13621
13750
  if (hit)
13622
13751
  return hit;
@@ -13631,7 +13760,7 @@ function resolveTrackFor(cwd, options) {
13631
13760
  }
13632
13761
 
13633
13762
  // packages/workit-core/src/core/vcs-config.ts
13634
- var workspacesPath = () => path11.join(configDir(), "workspaces.json");
13763
+ var workspacesPath = () => path12.join(configDir(), "workspaces.json");
13635
13764
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
13636
13765
  var remoteProvider = (cwd) => {
13637
13766
  const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
@@ -13721,7 +13850,7 @@ var readVcsConfig = () => {
13721
13850
  const file = vcsConfigPath();
13722
13851
  let raw;
13723
13852
  try {
13724
- raw = fs10.readFileSync(file, "utf8");
13853
+ raw = fs11.readFileSync(file, "utf8");
13725
13854
  } catch {
13726
13855
  return { status: "missing", path: file, config: {} };
13727
13856
  }
@@ -13806,7 +13935,7 @@ function vcsConfig(mode, cwd, options = {}) {
13806
13935
  const prov = cfg[provider] ?? {};
13807
13936
  const out = {
13808
13937
  ok: true,
13809
- configPath: path11.resolve(cfgPath),
13938
+ configPath: path12.resolve(cfgPath),
13810
13939
  provider,
13811
13940
  defaultTargetBranch: workspaceDefault,
13812
13941
  pr: cfg.pr ?? {},
@@ -14017,7 +14146,7 @@ function diffHash(cwd, base, head) {
14017
14146
  ], GIT_TIMEOUTS.worktree);
14018
14147
  if (!diff.ok || !diff.stdout)
14019
14148
  return null;
14020
- const hash = createHash8("sha256");
14149
+ const hash = createHash9("sha256");
14021
14150
  for (const line of diff.stdout.split(`
14022
14151
  `)) {
14023
14152
  if (line.startsWith("index "))
@@ -14029,14 +14158,14 @@ function diffHash(cwd, base, head) {
14029
14158
  return `sha256:${hash.digest("hex")}`;
14030
14159
  }
14031
14160
  function storeRoot(cwd) {
14032
- const location = resolveStore(path12.resolve(cwd));
14161
+ const location = resolveStore(path13.resolve(cwd));
14033
14162
  if (location instanceof Error)
14034
14163
  return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
14035
14164
  return { ok: true, value: { root: location.dir, shared: location.shared } };
14036
14165
  }
14037
14166
  function ledgerPath(cwd) {
14038
14167
  const root = storeRoot(cwd);
14039
- return root.ok ? { ok: true, value: path12.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14168
+ return root.ok ? { ok: true, value: path13.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14040
14169
  }
14041
14170
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
14042
14171
  var NETWORK_FS_TYPES = new Set([
@@ -14056,7 +14185,7 @@ var onNetworkFs = (dir) => {
14056
14185
  if (process.platform !== "linux")
14057
14186
  return false;
14058
14187
  try {
14059
- return isNetworkFsType(fs11.statfsSync(dir).type);
14188
+ return isNetworkFsType(fs12.statfsSync(dir).type);
14060
14189
  } catch {
14061
14190
  return false;
14062
14191
  }
@@ -14066,15 +14195,15 @@ var LOCK_STALE_MS = 30000;
14066
14195
  var LOCK_OWNER = "owner";
14067
14196
  var lockToken = (lock) => {
14068
14197
  try {
14069
- return fs11.readFileSync(path12.join(lock, LOCK_OWNER), "utf8");
14198
+ return fs12.readFileSync(path13.join(lock, LOCK_OWNER), "utf8");
14070
14199
  } catch {
14071
14200
  return null;
14072
14201
  }
14073
14202
  };
14074
14203
  var lockAgeMs = (lock) => {
14075
- for (const target of [path12.join(lock, LOCK_OWNER), lock]) {
14204
+ for (const target of [path13.join(lock, LOCK_OWNER), lock]) {
14076
14205
  try {
14077
- return Date.now() - fs11.statSync(target).mtimeMs;
14206
+ return Date.now() - fs12.statSync(target).mtimeMs;
14078
14207
  } catch {}
14079
14208
  }
14080
14209
  return null;
@@ -14082,14 +14211,14 @@ var lockAgeMs = (lock) => {
14082
14211
  var ledgerLock = {
14083
14212
  acquire(lock) {
14084
14213
  try {
14085
- fs11.mkdirSync(lock);
14214
+ fs12.mkdirSync(lock);
14086
14215
  } catch (error) {
14087
14216
  if (error.code === "EEXIST")
14088
14217
  return null;
14089
14218
  throw error;
14090
14219
  }
14091
14220
  const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
14092
- fs11.writeFileSync(path12.join(lock, LOCK_OWNER), token);
14221
+ fs12.writeFileSync(path13.join(lock, LOCK_OWNER), token);
14093
14222
  return token;
14094
14223
  },
14095
14224
  release(lock, token) {
@@ -14097,35 +14226,35 @@ var ledgerLock = {
14097
14226
  return false;
14098
14227
  const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
14099
14228
  try {
14100
- fs11.renameSync(lock, tomb);
14229
+ fs12.renameSync(lock, tomb);
14101
14230
  } catch {
14102
14231
  return false;
14103
14232
  }
14104
14233
  if (lockToken(tomb) !== token) {
14105
14234
  try {
14106
- fs11.renameSync(tomb, lock);
14235
+ fs12.renameSync(tomb, lock);
14107
14236
  } catch {}
14108
14237
  return false;
14109
14238
  }
14110
- fs11.rmSync(tomb, { recursive: true, force: true });
14239
+ fs12.rmSync(tomb, { recursive: true, force: true });
14111
14240
  return true;
14112
14241
  },
14113
14242
  token: lockToken,
14114
14243
  reap(lock, observed) {
14115
14244
  const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
14116
14245
  try {
14117
- fs11.renameSync(lock, tomb);
14246
+ fs12.renameSync(lock, tomb);
14118
14247
  } catch {
14119
14248
  return false;
14120
14249
  }
14121
14250
  if (lockToken(tomb) === observed) {
14122
- fs11.rmSync(tomb, { recursive: true, force: true });
14251
+ fs12.rmSync(tomb, { recursive: true, force: true });
14123
14252
  return true;
14124
14253
  }
14125
14254
  try {
14126
- fs11.renameSync(tomb, lock);
14255
+ fs12.renameSync(tomb, lock);
14127
14256
  } catch {
14128
- fs11.rmSync(tomb, { recursive: true, force: true });
14257
+ fs12.rmSync(tomb, { recursive: true, force: true });
14129
14258
  }
14130
14259
  return false;
14131
14260
  }
@@ -14152,14 +14281,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
14152
14281
  ledgerLock.release(lock, token);
14153
14282
  }
14154
14283
  }
14155
- var sha2562 = (text) => createHash8("sha256").update(text).digest("hex");
14284
+ var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
14156
14285
  var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
14157
14286
  ${body}`);
14158
14287
  var TAIL_BYTES = 64 * 1024;
14159
14288
  var lastLine = (fd, size) => {
14160
14289
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
14161
14290
  const buffer = Buffer.alloc(span);
14162
- fs11.readSync(fd, buffer, 0, span, size - span);
14291
+ fs12.readSync(fd, buffer, 0, span, size - span);
14163
14292
  const end = buffer.lastIndexOf(10);
14164
14293
  if (end < 0) {
14165
14294
  if (span === size)
@@ -14190,13 +14319,13 @@ var lastLine = (fd, size) => {
14190
14319
  };
14191
14320
  function appendLine(file, build, options) {
14192
14321
  const write = () => {
14193
- const fd = fs11.openSync(file, "a+");
14322
+ const fd = fs12.openSync(file, "a+");
14194
14323
  try {
14195
- const size = fs11.fstatSync(fd).size;
14324
+ const size = fs12.fstatSync(fd).size;
14196
14325
  let fence = "";
14197
14326
  if (size > 0) {
14198
14327
  const last = Buffer.alloc(1);
14199
- fs11.readSync(fd, last, 0, 1, size - 1);
14328
+ fs12.readSync(fd, last, 0, 1, size - 1);
14200
14329
  if (last[0] !== 10)
14201
14330
  fence = `
14202
14331
  `;
@@ -14205,17 +14334,17 @@ function appendLine(file, build, options) {
14205
14334
  if (!line.ok)
14206
14335
  return line;
14207
14336
  const bytes = Buffer.from(fence + line.value);
14208
- const written = fs11.writeSync(fd, bytes);
14337
+ const written = fs12.writeSync(fd, bytes);
14209
14338
  if (written !== bytes.length)
14210
14339
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
14211
14340
  return { ok: true, value: undefined };
14212
14341
  } finally {
14213
- fs11.closeSync(fd);
14342
+ fs12.closeSync(fd);
14214
14343
  }
14215
14344
  };
14216
14345
  try {
14217
- fs11.mkdirSync(path12.dirname(file), { recursive: true });
14218
- if (!options.forceLock && !onNetworkFs(path12.dirname(file)))
14346
+ fs12.mkdirSync(path13.dirname(file), { recursive: true });
14347
+ if (!options.forceLock && !onNetworkFs(path13.dirname(file)))
14219
14348
  return write();
14220
14349
  const locked = withLedgerLock(file, write);
14221
14350
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -14287,7 +14416,7 @@ function readLedger(cwd, options = {}) {
14287
14416
  return file;
14288
14417
  let text = "";
14289
14418
  try {
14290
- text = fs11.readFileSync(file.value, "utf8");
14419
+ text = fs12.readFileSync(file.value, "utf8");
14291
14420
  } catch (error) {
14292
14421
  if (error.code === "ENOENT")
14293
14422
  return {
@@ -14481,11 +14610,20 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
14481
14610
  sessions.add(session);
14482
14611
  return sessions;
14483
14612
  }
14613
+ var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
14614
+ var authoredBy = (authors, session) => {
14615
+ if (authors.has(session))
14616
+ return true;
14617
+ for (const author of authors)
14618
+ if (derivesFrom(author, session) || derivesFrom(session, author))
14619
+ return true;
14620
+ return false;
14621
+ };
14484
14622
  var independenceReasons = (row, authors) => {
14485
14623
  const reasons = [];
14486
14624
  if (!row.actor.session)
14487
14625
  reasons.push("no_session");
14488
- else if (authors.has(row.actor.session))
14626
+ else if (authoredBy(authors, row.actor.session))
14489
14627
  reasons.push("author_session");
14490
14628
  if (row.self === true && !reasons.includes("no_session"))
14491
14629
  reasons.push("self");
@@ -14769,7 +14907,7 @@ var productDecision = (task, ledger) => {
14769
14907
  };
14770
14908
  };
14771
14909
  var planRecorded = (task, root) => {
14772
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path13.resolve(root, ref.path)));
14910
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path14.resolve(root, ref.path)));
14773
14911
  if (cited)
14774
14912
  return {
14775
14913
  met: true,
@@ -14828,15 +14966,15 @@ function writeBlockers(task, root) {
14828
14966
  }
14829
14967
 
14830
14968
  // packages/workit-core/src/core/task-evaluation.ts
14831
- var digestBytes = (value) => createHash9("sha256").update(value).digest("hex");
14832
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path14.sep}`);
14833
- var relative = (root, candidate) => path14.relative(root, candidate).split(path14.sep).join("/") || ".";
14969
+ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
14970
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path15.sep}`);
14971
+ var relative = (root, candidate) => path15.relative(root, candidate).split(path15.sep).join("/") || ".";
14834
14972
  var canonicalPath = (value) => {
14835
14973
  if (typeof value !== "string" || !value)
14836
14974
  return null;
14837
14975
  if (value.includes("\\") || value.split("/").includes(".."))
14838
14976
  return null;
14839
- const normalized = path14.posix.normalize(value);
14977
+ const normalized = path15.posix.normalize(value);
14840
14978
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14841
14979
  return null;
14842
14980
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14884,21 +15022,21 @@ var gitPaths = (root) => {
14884
15022
  git: !/not a git repository/i.test(stderr)
14885
15023
  };
14886
15024
  }
14887
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")) : [];
15025
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")) : [];
14888
15026
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14889
15027
  if (stagedDeleted.status !== 0) {
14890
15028
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14891
15029
  if (!/not a git repository/i.test(stderr))
14892
15030
  return { paths, uncertain: true, git: true };
14893
15031
  } else if (stagedDeleted.stdout) {
14894
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")));
15032
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")));
14895
15033
  }
14896
15034
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14897
15035
  };
14898
15036
  var walk = (root, directory, output) => {
14899
15037
  let names;
14900
15038
  try {
14901
- names = fs12.readdirSync(directory);
15039
+ names = fs13.readdirSync(directory);
14902
15040
  } catch {
14903
15041
  return true;
14904
15042
  }
@@ -14906,10 +15044,10 @@ var walk = (root, directory, output) => {
14906
15044
  for (const name of names) {
14907
15045
  if (name === ".git" || name === ".workit")
14908
15046
  continue;
14909
- const target = path14.join(directory, name);
15047
+ const target = path15.join(directory, name);
14910
15048
  let stat;
14911
15049
  try {
14912
- stat = fs12.lstatSync(target);
15050
+ stat = fs13.lstatSync(target);
14913
15051
  } catch {
14914
15052
  uncertain = true;
14915
15053
  continue;
@@ -14924,14 +15062,14 @@ var walk = (root, directory, output) => {
14924
15062
  var scopeRoots = (root, scope) => {
14925
15063
  const roots = [];
14926
15064
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14927
- const target = path14.resolve(root, item);
15065
+ const target = path15.resolve(root, item);
14928
15066
  if (!inside(root, target))
14929
15067
  return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
14930
15068
  let ancestor = target;
14931
- while (!fs12.existsSync(ancestor) && ancestor !== root)
14932
- ancestor = path14.dirname(ancestor);
15069
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
15070
+ ancestor = path15.dirname(ancestor);
14933
15071
  try {
14934
- if (!inside(root, fs12.realpathSync(ancestor)))
15072
+ if (!inside(root, fs13.realpathSync(ancestor)))
14935
15073
  return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
14936
15074
  } catch {
14937
15075
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -14943,7 +15081,7 @@ var scopeRoots = (root, scope) => {
14943
15081
  function captureCandidate(root, scope, environment = []) {
14944
15082
  let checkout;
14945
15083
  try {
14946
- checkout = fs12.realpathSync(root);
15084
+ checkout = fs13.realpathSync(root);
14947
15085
  } catch {
14948
15086
  return failure2("invalid_input", "candidate root does not exist");
14949
15087
  }
@@ -14961,11 +15099,11 @@ function captureCandidate(root, scope, environment = []) {
14961
15099
  if (item === ".git" || item === ".workit")
14962
15100
  continue;
14963
15101
  names.add(item);
14964
- if (!fs12.existsSync(target))
15102
+ if (!fs13.existsSync(target))
14965
15103
  continue;
14966
15104
  let stat;
14967
15105
  try {
14968
- stat = fs12.lstatSync(target);
15106
+ stat = fs13.lstatSync(target);
14969
15107
  } catch {
14970
15108
  uncertain = true;
14971
15109
  continue;
@@ -14981,17 +15119,17 @@ function captureCandidate(root, scope, environment = []) {
14981
15119
  for (const item of [...names].toSorted()) {
14982
15120
  if (!scopeMatches(item, normalizedScope))
14983
15121
  continue;
14984
- const target = path14.join(checkout, item);
15122
+ const target = path15.join(checkout, item);
14985
15123
  try {
14986
- const stat = fs12.lstatSync(target);
15124
+ const stat = fs13.lstatSync(target);
14987
15125
  if (stat.isSymbolicLink()) {
14988
- const link = fs12.readlinkSync(target);
15126
+ const link = fs13.readlinkSync(target);
14989
15127
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
14990
15128
  } else if (stat.isFile()) {
14991
15129
  files.push({
14992
15130
  path: item,
14993
15131
  kind: "file",
14994
- digest: digestBytes(fs12.readFileSync(target)),
15132
+ digest: digestBytes(fs13.readFileSync(target)),
14995
15133
  executable: (stat.mode & 73) !== 0
14996
15134
  });
14997
15135
  } else if (!stat.isDirectory()) {
@@ -15156,7 +15294,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
15156
15294
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15157
15295
  let root;
15158
15296
  try {
15159
- root = fs12.realpathSync(checkoutRoot);
15297
+ root = fs13.realpathSync(checkoutRoot);
15160
15298
  } catch {
15161
15299
  return failure2("permission_denied", "checkout root is unavailable");
15162
15300
  }
@@ -15165,23 +15303,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15165
15303
  continue;
15166
15304
  if (!reference.digest)
15167
15305
  return failure2("invalid_input", "document references require a byte digest");
15168
- const target = path14.resolve(root, reference.path);
15169
- if (target !== root && !target.startsWith(`${root}${path14.sep}`))
15306
+ const target = path15.resolve(root, reference.path);
15307
+ if (target !== root && !target.startsWith(`${root}${path15.sep}`))
15170
15308
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15171
15309
  fields: [
15172
15310
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
15173
15311
  ]
15174
15312
  });
15175
15313
  try {
15176
- const below = path14.relative(root, target);
15314
+ const below = path15.relative(root, target);
15177
15315
  let current = root;
15178
- for (const segment of below.split(path14.sep)) {
15316
+ for (const segment of below.split(path15.sep)) {
15179
15317
  if (!segment)
15180
15318
  continue;
15181
- current = path14.join(current, segment);
15182
- const stat = fs12.lstatSync(current);
15183
- const real = fs12.realpathSync(current);
15184
- if (real !== root && !real.startsWith(`${root}${path14.sep}`))
15319
+ current = path15.join(current, segment);
15320
+ const stat = fs13.lstatSync(current);
15321
+ const real = fs13.realpathSync(current);
15322
+ if (real !== root && !real.startsWith(`${root}${path15.sep}`))
15185
15323
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15186
15324
  fields: [
15187
15325
  {
@@ -15197,7 +15335,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15197
15335
  if (current !== target && !stat.isDirectory())
15198
15336
  return failure2("invalid_input", "document reference ancestor is not a directory");
15199
15337
  }
15200
- if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
15338
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
15201
15339
  return failure2("permission_denied", "decision document bytes have changed");
15202
15340
  } catch {
15203
15341
  return failure2("invalid_input", "decision document is unavailable");
@@ -15566,7 +15704,8 @@ Autonomy contract:
15566
15704
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
15567
15705
  - Author is not verifier: your own verdict is \`--self\`, shown as
15568
15706
  self-reviewed, never verified. High risk, \`verification: independent\` and
15569
- merge need a verifier with its own session (\`WORKIT_SESSION_ID=<lead>-v<n>\`).
15707
+ merge need a verifier with its own host-derived session (a subagent's
15708
+ \`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
15570
15709
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
15571
15710
  is not evidence of a requested remote push. Before reporting delivery,
15572
15711
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -15659,38 +15798,45 @@ var compactReference = (ref) => {
15659
15798
  };
15660
15799
  return { kind: ref.kind };
15661
15800
  };
15662
- function compactTaskContext(view) {
15663
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
15664
- const leftAt = compactTimestamp(left.recordedAt);
15665
- const rightAt = compactTimestamp(right.recordedAt);
15666
- if (leftAt !== rightAt)
15667
- return leftAt > rightAt ? -1 : 1;
15668
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15669
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
15670
- id: entry.id,
15671
- purpose: entry.data.purpose,
15672
- status: entry.data.response,
15673
- digest: entry.data.digest,
15674
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
15675
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
15676
- }));
15677
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15801
+ var newestDecisionFirst = (left, right) => {
15802
+ const leftAt = compactTimestamp(left.recordedAt);
15803
+ const rightAt = compactTimestamp(right.recordedAt);
15804
+ if (leftAt !== rightAt)
15805
+ return leftAt > rightAt ? -1 : 1;
15806
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
15807
+ };
15808
+ var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
15809
+ id: entry.id,
15810
+ purpose: entry.data.purpose,
15811
+ status: entry.data.response,
15812
+ digest: entry.data.digest,
15813
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
15814
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
15815
+ origin: source.origin ?? undefined
15816
+ })) : []).slice(0, COMPACT_MAX_ITEMS);
15817
+ function compactTaskContext(view, inherited = null) {
15818
+ const decisions = compactDecisions([
15819
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
15820
+ inherited
15821
+ ]);
15822
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
15678
15823
  id: method.id,
15679
15824
  assurance: method.assurance,
15680
15825
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
15681
15826
  })) : [];
15827
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
15682
15828
  const gaps = Array.from(new Set([
15683
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
15684
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15685
- ...view.task.progress.blockers.map((entry) => entry.reason)
15829
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
15830
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
15831
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
15686
15832
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
15687
15833
  const context = {
15688
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
15689
- status: view.task.status,
15834
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
15835
+ status: view?.task.status ?? null,
15690
15836
  decisions,
15691
15837
  methods,
15692
15838
  gaps,
15693
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
15839
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
15694
15840
  };
15695
15841
  let encoded = JSON.stringify(context);
15696
15842
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -15716,7 +15862,7 @@ var exportDigest = (bundle) => sha256({
15716
15862
  task: bundle.task
15717
15863
  });
15718
15864
  // packages/workit-core/src/core/branch.ts
15719
- import { createHash as createHash10 } from "node:crypto";
15865
+ import { createHash as createHash11 } from "node:crypto";
15720
15866
 
15721
15867
  // packages/workit-core/src/core/git.ts
15722
15868
  import { execFileSync } from "node:child_process";
@@ -15854,7 +16000,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
15854
16000
  const source = resolved.provenance.branchPolicy;
15855
16001
  const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
15856
16002
  const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
15857
- const fingerprint = createHash10("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
16003
+ const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
15858
16004
  return {
15859
16005
  branchPolicy,
15860
16006
  namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
@@ -16110,7 +16256,7 @@ var judgeInputSummary = (raw) => {
16110
16256
 
16111
16257
  // packages/workit-core/src/autonomy.ts
16112
16258
  import os3 from "node:os";
16113
- import path15 from "node:path";
16259
+ import path16 from "node:path";
16114
16260
  var accountHome = () => {
16115
16261
  try {
16116
16262
  return os3.userInfo().homedir;
@@ -16119,10 +16265,10 @@ var accountHome = () => {
16119
16265
  }
16120
16266
  };
16121
16267
  var grantsHome = { resolve: accountHome };
16122
- var grantsDir = () => path15.join(grantsHome.resolve(), ".config", "workit");
16268
+ var grantsDir = () => path16.join(grantsHome.resolve(), ".config", "workit");
16123
16269
  var grantsOverride = () => {
16124
- const configured = path15.resolve(resolveConfigDir());
16125
- return configured === path15.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path15.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
16270
+ const configured = path16.resolve(resolveConfigDir());
16271
+ return configured === path16.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path16.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
16126
16272
  };
16127
16273
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
16128
16274
  var DEFAULT_GRANTS = Object.freeze({
@@ -16408,6 +16554,13 @@ var TASK_SCOPED_ACTIONS = new Set([
16408
16554
  "worker.cancel",
16409
16555
  "state.export"
16410
16556
  ]);
16557
+ var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
16558
+ var workerCommand = (taskId, request) => {
16559
+ const { action, workerId, reason, report } = request;
16560
+ const rest = JSON.stringify({ reason, report });
16561
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
16562
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
16563
+ };
16411
16564
  var CREATING_ACTIONS = new Set([
16412
16565
  "task.revise",
16413
16566
  "task.progress",
@@ -16658,6 +16811,17 @@ class WorkitCore {
16658
16811
  const root = this.contextRootError();
16659
16812
  if (!root.ok)
16660
16813
  return root;
16814
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
16815
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
16816
+ if (workerId !== null) {
16817
+ const owner = this.store.taskForWorker(workerId);
16818
+ if (!owner.ok)
16819
+ return owner;
16820
+ if (owner.data)
16821
+ return success(null, null, { ...request, taskId: owner.data });
16822
+ if (addressed)
16823
+ return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
16824
+ }
16661
16825
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
16662
16826
  const found = this.store.implicitTask({
16663
16827
  provenance: provenance(this.context),
@@ -17364,7 +17528,9 @@ class WorkitCore {
17364
17528
  if (!resolved.ok)
17365
17529
  return resolved;
17366
17530
  const value = resolved.data;
17367
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
17531
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
17532
+ const { action, taskId } = value ?? {};
17533
+ return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
17368
17534
  }
17369
17535
  workerOnce(request) {
17370
17536
  const root = this.contextRootError();
@@ -17569,10 +17735,18 @@ class WorkitCore {
17569
17735
  requirements
17570
17736
  });
17571
17737
  }
17572
- compactContext(taskId, freshness) {
17738
+ compactContext(taskId, freshness, origin = null) {
17573
17739
  const root = this.contextRootError();
17574
17740
  if (!root.ok)
17575
17741
  return root;
17742
+ const base = origin && this.store.readStoreTask(origin.taskId);
17743
+ const inherited = base?.ok && base.data ? {
17744
+ origin: origin.origin,
17745
+ taskId: base.data.id,
17746
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
17747
+ } : null;
17748
+ if (taskId === null)
17749
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
17576
17750
  const task = this.store.readTask(taskId);
17577
17751
  if (!task.ok)
17578
17752
  return task;
@@ -17582,7 +17756,7 @@ class WorkitCore {
17582
17756
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
17583
17757
  if (!view.ok)
17584
17758
  return view;
17585
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
17759
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
17586
17760
  }
17587
17761
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
17588
17762
  const workspace = this.store.readWorkspace();
@@ -17866,7 +18040,7 @@ class WorkitCore {
17866
18040
  // packages/workit-core/src/core/repo-context.ts
17867
18041
  import { spawnSync as spawnSync11 } from "node:child_process";
17868
18042
  import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
17869
- import path16 from "node:path";
18043
+ import path17 from "node:path";
17870
18044
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
17871
18045
  try {
17872
18046
  gitRevisionParts(value);
@@ -18000,7 +18174,7 @@ var findPrTemplate = (cwd) => {
18000
18174
  ]) {
18001
18175
  const parts = rel.split("/");
18002
18176
  const base = parts[parts.length - 1];
18003
- const dir = parts.length > 1 ? path16.join(cwd, ...parts.slice(0, -1)) : cwd;
18177
+ const dir = parts.length > 1 ? path17.join(cwd, ...parts.slice(0, -1)) : cwd;
18004
18178
  const actual = probeCaseInsensitive(dir, base);
18005
18179
  if (actual)
18006
18180
  return [...parts.slice(0, -1), actual].join("/");
@@ -18031,7 +18205,7 @@ var diffStatForRange = (cwd, range) => {
18031
18205
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
18032
18206
  };
18033
18207
  var packageScripts = (cwd, keys) => {
18034
- const pkgPath = path16.join(cwd, "package.json");
18208
+ const pkgPath = path17.join(cwd, "package.json");
18035
18209
  if (!existsSync6(pkgPath))
18036
18210
  return [];
18037
18211
  try {
@@ -18057,9 +18231,9 @@ var documentationFiles = (cwd) => {
18057
18231
  if (entry.isDirectory()) {
18058
18232
  if (excluded.has(entry.name))
18059
18233
  continue;
18060
- walk(path16.join(dir, entry.name), depth + 1);
18234
+ walk(path17.join(dir, entry.name), depth + 1);
18061
18235
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
18062
- matches.push("./" + path16.relative(cwd, path16.join(dir, entry.name)).replaceAll("\\", "/"));
18236
+ matches.push("./" + path17.relative(cwd, path17.join(dir, entry.name)).replaceAll("\\", "/"));
18063
18237
  }
18064
18238
  }
18065
18239
  };
@@ -18075,7 +18249,7 @@ var readTrimmed2 = (file, maxLines) => {
18075
18249
  `);
18076
18250
  };
18077
18251
  function prReadyContext(root, range) {
18078
- const cwd = path16.resolve(repoRoot(root));
18252
+ const cwd = path17.resolve(repoRoot(root));
18079
18253
  if (range !== undefined && !isResolvableContextRange(cwd, range))
18080
18254
  return invalidRange(cwd);
18081
18255
  let stdout = "";
@@ -18133,7 +18307,7 @@ ${status.stderr}` : "");
18133
18307
  stdout += `template_path: ${template}
18134
18308
 
18135
18309
  `;
18136
- stdout += readTrimmed2(path16.join(cwd, template), 220);
18310
+ stdout += readTrimmed2(path17.join(cwd, template), 220);
18137
18311
  } else {
18138
18312
  stdout += `template_path: none
18139
18313
 
@@ -18143,7 +18317,7 @@ ${status.stderr}` : "");
18143
18317
  stdout += `
18144
18318
  `;
18145
18319
  stdout += printSection("Recent Validation Signals");
18146
- if (existsSync6(path16.join(cwd, "package.json"))) {
18320
+ if (existsSync6(path17.join(cwd, "package.json"))) {
18147
18321
  stdout += `package.json detected. Common scripts:
18148
18322
  `;
18149
18323
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -18152,7 +18326,7 @@ ${status.stderr}` : "");
18152
18326
  `) + `
18153
18327
  `;
18154
18328
  }
18155
- if (existsSync6(path16.join(cwd, "Cargo.toml")) || existsSync6(path16.join(cwd, "src-tauri/Cargo.toml"))) {
18329
+ if (existsSync6(path17.join(cwd, "Cargo.toml")) || existsSync6(path17.join(cwd, "src-tauri/Cargo.toml"))) {
18156
18330
  stdout += `Rust project detected.
18157
18331
  `;
18158
18332
  }
@@ -18185,7 +18359,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
18185
18359
  - Apply with the host-native editor, preserving the repository changelog conventions.
18186
18360
  - If Unreleased already has duplicate category headings, normalize_only first.`;
18187
18361
  function changelogContext(root, range) {
18188
- const cwd = path16.resolve(repoRoot(root));
18362
+ const cwd = path17.resolve(repoRoot(root));
18189
18363
  if (range !== undefined && !isResolvableContextRange(cwd, range))
18190
18364
  return invalidRange(cwd);
18191
18365
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -18203,7 +18377,7 @@ function changelogContext(root, range) {
18203
18377
  stdout += CHANGELOG_RULES + `
18204
18378
  `;
18205
18379
  stdout += printSection("Existing CHANGELOG.md");
18206
- const changelogPath = path16.join(cwd, "CHANGELOG.md");
18380
+ const changelogPath = path17.join(cwd, "CHANGELOG.md");
18207
18381
  if (existsSync6(changelogPath)) {
18208
18382
  stdout += readTrimmed2(changelogPath, 260) + `
18209
18383
  `;
@@ -18223,7 +18397,7 @@ function changelogContext(root, range) {
18223
18397
  return { stdout, stderr: "", exitCode: 0, cwd };
18224
18398
  }
18225
18399
  function docsRefreshContext(root, range) {
18226
- const cwd = path16.resolve(repoRoot(root));
18400
+ const cwd = path17.resolve(repoRoot(root));
18227
18401
  if (range !== undefined && !isResolvableContextRange(cwd, range))
18228
18402
  return invalidRange(cwd);
18229
18403
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -18245,7 +18419,7 @@ function docsRefreshContext(root, range) {
18245
18419
  `) + `
18246
18420
  `;
18247
18421
  stdout += printSection("README Preview");
18248
- const readme = path16.join(cwd, "README.md");
18422
+ const readme = path17.join(cwd, "README.md");
18249
18423
  if (existsSync6(readme)) {
18250
18424
  stdout += readTrimmed2(readme, 220) + `
18251
18425
  `;
@@ -18254,7 +18428,7 @@ function docsRefreshContext(root, range) {
18254
18428
  `;
18255
18429
  }
18256
18430
  stdout += printSection("Package Scripts");
18257
- const pkgPath = path16.join(cwd, "package.json");
18431
+ const pkgPath = path17.join(cwd, "package.json");
18258
18432
  if (existsSync6(pkgPath)) {
18259
18433
  try {
18260
18434
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
@@ -18268,7 +18442,7 @@ function docsRefreshContext(root, range) {
18268
18442
  return { stdout, stderr: "", exitCode: 0, cwd };
18269
18443
  }
18270
18444
  function releaseNotesContext(root, rangeOrTag) {
18271
- const cwd = path16.resolve(repoRoot(root));
18445
+ const cwd = path17.resolve(repoRoot(root));
18272
18446
  if (!rangeOrTag) {
18273
18447
  return { stdout: "", stderr: `ERROR: release tag or range required
18274
18448
  `, exitCode: 1, cwd };
@@ -18306,7 +18480,7 @@ function releaseNotesContext(root, rangeOrTag) {
18306
18480
  `;
18307
18481
  stdout += printSection("Existing Release Files");
18308
18482
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
18309
- if (existsSync6(path16.join(cwd, rel)))
18483
+ if (existsSync6(path17.join(cwd, rel)))
18310
18484
  stdout += rel + `
18311
18485
  `;
18312
18486
  }
@@ -18337,8 +18511,8 @@ function releaseNotesContext(root, rangeOrTag) {
18337
18511
  import { spawnSync as spawnSync13 } from "node:child_process";
18338
18512
 
18339
18513
  // packages/workit-core/src/core/pr-create.ts
18340
- import fs13 from "node:fs";
18341
- import path17 from "node:path";
18514
+ import fs14 from "node:fs";
18515
+ import path18 from "node:path";
18342
18516
  import { spawnSync as spawnSync12 } from "node:child_process";
18343
18517
  // packages/workit-core/src/forge/pr-body.ts
18344
18518
  function parseGhIssue(value) {
@@ -18349,13 +18523,13 @@ function parseGhIssue(value) {
18349
18523
  // packages/workit-core/src/core/pr-create.ts
18350
18524
  function whichOnPath(tool) {
18351
18525
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
18352
- for (const dir of (process.env.PATH ?? "").split(path17.delimiter)) {
18526
+ for (const dir of (process.env.PATH ?? "").split(path18.delimiter)) {
18353
18527
  if (!dir)
18354
18528
  continue;
18355
18529
  for (const name of names) {
18356
- const candidate = path17.join(dir, name);
18530
+ const candidate = path18.join(dir, name);
18357
18531
  try {
18358
- fs13.accessSync(candidate, fs13.constants.X_OK);
18532
+ fs14.accessSync(candidate, fs14.constants.X_OK);
18359
18533
  return candidate;
18360
18534
  } catch {}
18361
18535
  }
@@ -18473,8 +18647,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
18473
18647
  };
18474
18648
 
18475
18649
  // packages/workit-core/src/core/youtrack.ts
18476
- import fs14 from "node:fs";
18477
- import path18 from "node:path";
18650
+ import fs15 from "node:fs";
18651
+ import path19 from "node:path";
18478
18652
 
18479
18653
  // packages/workit-core/src/core/templates.ts
18480
18654
  var repoRoot2 = assetRoot();
@@ -18487,22 +18661,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
18487
18661
  // packages/workit-core/src/core/youtrack.ts
18488
18662
  var ISSUE_RE = /^[A-Z]+-\d+$/;
18489
18663
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
18490
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
18491
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
18664
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path19.join(configDir(), "youtrack.json");
18665
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path19.join(resolveConfigDir(), "youtrack.json");
18492
18666
  var youTrackTokenModeOk = (p) => {
18493
18667
  if (process.platform === "win32")
18494
18668
  return true;
18495
- const mode = fs14.statSync(p).mode & 511;
18669
+ const mode = fs15.statSync(p).mode & 511;
18496
18670
  return mode === 384;
18497
18671
  };
18498
18672
  function readYouTrackConfig(required) {
18499
18673
  const cfgPath = youTrackConfigPath();
18500
- if (!fs14.existsSync(cfgPath)) {
18674
+ if (!fs15.existsSync(cfgPath)) {
18501
18675
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
18502
18676
  }
18503
18677
  let parsed;
18504
18678
  try {
18505
- parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
18679
+ parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
18506
18680
  } catch {
18507
18681
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
18508
18682
  }
@@ -18513,11 +18687,11 @@ function readYouTrackConfig(required) {
18513
18687
  }
18514
18688
  var readYouTrackContextConfig = () => {
18515
18689
  const cfgPath = youTrackReadOnlyConfigPath();
18516
- if (!fs14.existsSync(cfgPath))
18690
+ if (!fs15.existsSync(cfgPath))
18517
18691
  return { data: {} };
18518
18692
  let parsed;
18519
18693
  try {
18520
- parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
18694
+ parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
18521
18695
  } catch {
18522
18696
  return { error: "YouTrack configuration is unavailable" };
18523
18697
  }
@@ -18556,12 +18730,12 @@ var youTrackToken = () => {
18556
18730
  if ("error" in loaded)
18557
18731
  return loaded;
18558
18732
  const tokenFile = String(loaded.config.tokenFile ?? "");
18559
- const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
18560
- if (!tokenPath || !fs14.existsSync(tokenPath))
18733
+ const tokenPath = tokenFile ? path19.isAbsolute(tokenFile) ? path19.resolve(tokenFile) : path19.resolve(process.cwd(), tokenFile) : "";
18734
+ if (!tokenPath || !fs15.existsSync(tokenPath))
18561
18735
  return { error: "missing youtrack.token" };
18562
18736
  if (!youTrackTokenModeOk(tokenPath))
18563
18737
  return { error: "youtrack.token mode must be 0600" };
18564
- const token = fs14.readFileSync(tokenPath, "utf8").trim();
18738
+ const token = fs15.readFileSync(tokenPath, "utf8").trim();
18565
18739
  if (!token)
18566
18740
  return { error: "empty token file" };
18567
18741
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -18626,9 +18800,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
18626
18800
  } catch {
18627
18801
  continue;
18628
18802
  }
18629
- if (!fs14.existsSync(full))
18803
+ if (!fs15.existsSync(full))
18630
18804
  continue;
18631
- const text = fs14.readFileSync(full, "utf8");
18805
+ const text = fs15.readFileSync(full, "utf8");
18632
18806
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
18633
18807
  if (m)
18634
18808
  return m[1];
@@ -18913,15 +19087,15 @@ function resolveGitRevision(root, value) {
18913
19087
  }
18914
19088
  function resolveInside(root, candidate) {
18915
19089
  const base = realpathSync5(root);
18916
- const target = path19.resolve(base, candidate);
18917
- if (target !== base && !target.startsWith(base + path19.sep)) {
19090
+ const target = path20.resolve(base, candidate);
19091
+ if (target !== base && !target.startsWith(base + path20.sep)) {
18918
19092
  throw new Error("path must stay inside repository root");
18919
19093
  }
18920
19094
  let ancestor = target;
18921
19095
  while (!existsSync7(ancestor))
18922
- ancestor = path19.dirname(ancestor);
19096
+ ancestor = path20.dirname(ancestor);
18923
19097
  const canonicalAncestor = realpathSync5(ancestor);
18924
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path19.sep)) {
19098
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path20.sep)) {
18925
19099
  throw new Error("path must stay inside repository root");
18926
19100
  }
18927
19101
  return target;
@@ -18965,9 +19139,9 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
18965
19139
  };
18966
19140
  });
18967
19141
  // packages/workit-core/src/hooks/context.ts
18968
- import { createHash as createHash11 } from "node:crypto";
18969
- import fs15 from "node:fs";
18970
- import path20 from "node:path";
19142
+ import { createHash as createHash12 } from "node:crypto";
19143
+ import fs16 from "node:fs";
19144
+ import path21 from "node:path";
18971
19145
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
18972
19146
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
18973
19147
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -18980,8 +19154,9 @@ var currentTaskEntry = (entries, session, selection) => {
18980
19154
  const active = entries.filter((entry) => entry.status === "active");
18981
19155
  return active.length === 1 ? active[0] : null;
18982
19156
  };
19157
+ var otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle));
18983
19158
  var implicitTaskEntry = (store, entries, session) => {
18984
- const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle)));
19159
+ const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
18985
19160
  if (open.length === 0)
18986
19161
  return null;
18987
19162
  const key = store.currentKey();
@@ -19022,18 +19197,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
19022
19197
  if (!listed.ok)
19023
19198
  return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
19024
19199
  const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
19025
- return entry ? entryCompactContext(store, entry, session, context) : null;
19200
+ return entryCompactContext(store, entry, session, context);
19026
19201
  }
19027
19202
  function entryCompactContext(store, entry, session, context) {
19203
+ const found = store.originTaskEntry();
19204
+ const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
19205
+ if (!entry && !origin)
19206
+ return null;
19028
19207
  const workspace = store.readWorkspace();
19029
- if (!workspace.ok || !workspace.data)
19208
+ if (!workspace.ok || entry && !workspace.data)
19030
19209
  return null;
19031
19210
  const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
19032
19211
  const key = canonicalJson({
19033
- task: entry.id,
19034
- revision: entry.revision,
19035
- file: entry.file,
19036
- workspace: workspace.data.revision,
19212
+ task: entry?.id ?? null,
19213
+ revision: entry?.revision ?? null,
19214
+ file: entry?.file ?? null,
19215
+ origin: origin && origin.entry.file,
19216
+ workspace: workspace.data?.revision ?? null,
19037
19217
  caller: context.caller,
19038
19218
  workerId: context.workerId ?? null,
19039
19219
  capabilities: context.capabilities
@@ -19041,15 +19221,15 @@ function entryCompactContext(store, entry, session, context) {
19041
19221
  const hit = cache.get(slot);
19042
19222
  if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
19043
19223
  return hit.value;
19044
- const task = store.readTask(entry.id);
19045
- if (!task.ok)
19224
+ const task = entry ? store.readTask(entry.id) : null;
19225
+ if (task && !task.ok)
19046
19226
  return null;
19047
19227
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
19048
- const observed = task.data.evidence.some(isObservedCheck);
19049
- const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
19228
+ const observed = task?.data.evidence.some(isObservedCheck) ?? false;
19229
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path21.resolve(store.root, ref.path)] : []));
19050
19230
  const observedFiles = filesKey(files);
19051
- const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
19052
- if (!compact.ok)
19231
+ const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
19232
+ if (!compact.ok || !compact.data)
19053
19233
  return null;
19054
19234
  cache.delete(slot);
19055
19235
  cache.set(slot, {
@@ -19088,7 +19268,7 @@ ${options.addendum}` : ""}
19088
19268
  if (!listed.ok)
19089
19269
  throw new Error(listed.error);
19090
19270
  const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
19091
- const text = entry ? entryCompactContext(store, entry, session, hookOperationContext(input, descriptor)) : null;
19271
+ const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
19092
19272
  if (text)
19093
19273
  compact = `
19094
19274
  <workit-task-context>${text}</workit-task-context>`;
@@ -19115,7 +19295,7 @@ var turnContextText = (input, descriptor) => {
19115
19295
  }
19116
19296
  };
19117
19297
  var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
19118
- var digestOf = (text) => createHash11("sha256").update(text).digest("hex");
19298
+ var digestOf = (text) => createHash12("sha256").update(text).digest("hex");
19119
19299
  var turnMarker = (input) => {
19120
19300
  if (!input.session.id)
19121
19301
  return null;
@@ -19124,32 +19304,32 @@ var turnMarker = (input) => {
19124
19304
  if (location instanceof Error)
19125
19305
  return null;
19126
19306
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
19127
- return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
19307
+ return path21.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
19128
19308
  } catch {
19129
19309
  return null;
19130
19310
  }
19131
19311
  };
19132
19312
  var writeTurnDigest = (file, digest) => {
19133
19313
  try {
19134
- fs15.mkdirSync(path20.dirname(file), { recursive: true });
19135
- fs15.writeFileSync(file, `${JSON.stringify({ digest })}
19314
+ fs16.mkdirSync(path21.dirname(file), { recursive: true });
19315
+ fs16.writeFileSync(file, `${JSON.stringify({ digest })}
19136
19316
  `);
19137
19317
  } catch {}
19138
19318
  };
19139
19319
  var clearTurnDigest = (file) => {
19140
19320
  try {
19141
- fs15.rmSync(file, { force: true });
19321
+ fs16.rmSync(file, { force: true });
19142
19322
  } catch {}
19143
19323
  };
19144
19324
  var pruneTurnDigests = (dir, now) => {
19145
19325
  try {
19146
- for (const name of fs15.readdirSync(dir)) {
19326
+ for (const name of fs16.readdirSync(dir)) {
19147
19327
  if (!name.startsWith("turn-"))
19148
19328
  continue;
19149
- const file = path20.join(dir, name);
19329
+ const file = path21.join(dir, name);
19150
19330
  try {
19151
- if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
19152
- fs15.rmSync(file, { force: true });
19331
+ if (now - fs16.statSync(file).mtimeMs > TURN_TTL_MS)
19332
+ fs16.rmSync(file, { force: true });
19153
19333
  } catch {}
19154
19334
  }
19155
19335
  } catch {}
@@ -19165,7 +19345,7 @@ var changedTurnContext = (input, text) => {
19165
19345
  return text;
19166
19346
  const digest = digestOf(text);
19167
19347
  try {
19168
- const previous = JSON.parse(fs15.readFileSync(file, "utf8"));
19348
+ const previous = JSON.parse(fs16.readFileSync(file, "utf8"));
19169
19349
  if (previous.digest === digest)
19170
19350
  return null;
19171
19351
  } catch {}
@@ -19176,7 +19356,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
19176
19356
  const file = turnMarker(input);
19177
19357
  if (!file)
19178
19358
  return;
19179
- pruneTurnDigests(path20.dirname(file), Date.now());
19359
+ pruneTurnDigests(path21.dirname(file), Date.now());
19180
19360
  if (text)
19181
19361
  writeTurnDigest(file, digestOf(text));
19182
19362
  else
@@ -19189,7 +19369,7 @@ var shellPolicy = (cwd, command) => {
19189
19369
  };
19190
19370
  // packages/workit-core/src/hooks/write-gate.ts
19191
19371
  import { realpathSync as realpathSync6 } from "node:fs";
19192
- import path21 from "node:path";
19372
+ import path22 from "node:path";
19193
19373
 
19194
19374
  // packages/workit-core/src/hooks/shell-words.ts
19195
19375
  var closingQuote = (command, start, dialect) => {
@@ -19319,22 +19499,22 @@ var NONE2 = { kind: "none" };
19319
19499
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
19320
19500
  var canonical2 = (file) => {
19321
19501
  let rest = "";
19322
- for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
19502
+ for (let dir = path22.resolve(file);; dir = path22.dirname(dir)) {
19323
19503
  try {
19324
- return path21.join(realpathSync6(dir), rest);
19504
+ return path22.join(realpathSync6(dir), rest);
19325
19505
  } catch {
19326
- if (path21.dirname(dir) === dir)
19327
- return path21.resolve(file);
19328
- rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
19506
+ if (path22.dirname(dir) === dir)
19507
+ return path22.resolve(file);
19508
+ rest = rest ? path22.join(path22.basename(dir), rest) : path22.basename(dir);
19329
19509
  }
19330
19510
  }
19331
19511
  };
19332
19512
  var exempt = (cwd, root, file, plans) => {
19333
19513
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
19334
- const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
19335
- if (relative.startsWith("..") || path21.isAbsolute(relative))
19514
+ const relative = path22.relative(fold(canonical2(root)), fold(canonical2(path22.resolve(cwd, file))));
19515
+ if (relative.startsWith("..") || path22.isAbsolute(relative))
19336
19516
  return true;
19337
- const posix = relative.split(path21.sep).join("/");
19517
+ const posix = relative.split(path22.sep).join("/");
19338
19518
  const segments = posix.split("/");
19339
19519
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
19340
19520
  };
@@ -19432,7 +19612,7 @@ var shellWrites = (command) => {
19432
19612
  return { writes: true, targets: unknown ? null : targets };
19433
19613
  };
19434
19614
  var cache2 = new Map;
19435
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00");
19615
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path22.resolve(root, file)) ?? "-").join("\x00");
19436
19616
  var gateFor = (cwd) => {
19437
19617
  const store = new TaskStore(cwd);
19438
19618
  const listed = store.listTaskIndex();
@@ -19492,15 +19672,15 @@ import { spawnSync as spawnSync16 } from "node:child_process";
19492
19672
 
19493
19673
  // packages/workit-core/src/hooks/raw-git.ts
19494
19674
  import { spawnSync as spawnSync15 } from "node:child_process";
19495
- import { createHash as createHash13 } from "node:crypto";
19496
- import fs17 from "node:fs";
19675
+ import { createHash as createHash14 } from "node:crypto";
19676
+ import fs18 from "node:fs";
19497
19677
  import os4 from "node:os";
19498
- import path23 from "node:path";
19678
+ import path24 from "node:path";
19499
19679
 
19500
19680
  // packages/workit-core/src/hooks/session-marker.ts
19501
- import { createHash as createHash12 } from "node:crypto";
19502
- import fs16 from "node:fs";
19503
- import path22 from "node:path";
19681
+ import { createHash as createHash13 } from "node:crypto";
19682
+ import fs17 from "node:fs";
19683
+ import path23 from "node:path";
19504
19684
  var sessionMarkerFile = (input, session, store) => {
19505
19685
  let dir = store;
19506
19686
  if (dir === undefined) {
@@ -19509,13 +19689,13 @@ var sessionMarkerFile = (input, session, store) => {
19509
19689
  return null;
19510
19690
  dir = location.dir;
19511
19691
  }
19512
- const name = createHash12("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
19513
- return path22.join(dir, "hooks", `skills-${name}.json`);
19692
+ const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
19693
+ return path23.join(dir, "hooks", `skills-${name}.json`);
19514
19694
  };
19515
19695
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
19516
19696
  var readSessionMarker = (file) => {
19517
19697
  try {
19518
- const value = JSON.parse(fs16.readFileSync(file, "utf8"));
19698
+ const value = JSON.parse(fs17.readFileSync(file, "utf8"));
19519
19699
  return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
19520
19700
  } catch {
19521
19701
  return { nudged: [], loaded: [], raw: [] };
@@ -19523,8 +19703,8 @@ var readSessionMarker = (file) => {
19523
19703
  };
19524
19704
  var writeSessionMarker = (file, marker) => {
19525
19705
  try {
19526
- fs16.mkdirSync(path22.dirname(file), { recursive: true });
19527
- fs16.writeFileSync(file, JSON.stringify(marker));
19706
+ fs17.mkdirSync(path23.dirname(file), { recursive: true });
19707
+ fs17.writeFileSync(file, JSON.stringify(marker));
19528
19708
  } catch {}
19529
19709
  };
19530
19710
 
@@ -19545,12 +19725,12 @@ var GIT_VALUE_OPTIONS = new Set([
19545
19725
  "--config-env",
19546
19726
  "--super-prefix"
19547
19727
  ]);
19548
- var isAbsolute = (value) => path23.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
19728
+ var isAbsolute = (value) => path24.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
19549
19729
  var joinDir = (base, next) => {
19550
19730
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
19551
19731
  return;
19552
- const expanded = next === "~" || next.startsWith("~/") ? path23.join(os4.homedir(), next.slice(1)) : next;
19553
- return base === null || isAbsolute(expanded) ? expanded : path23.join(base, expanded);
19732
+ const expanded = next === "~" || next.startsWith("~/") ? path24.join(os4.homedir(), next.slice(1)) : next;
19733
+ return base === null || isAbsolute(expanded) ? expanded : path24.join(base, expanded);
19554
19734
  };
19555
19735
  var cdTarget = (words) => {
19556
19736
  const args = words.slice(1);
@@ -19803,7 +19983,7 @@ var repoAt = (dir) => {
19803
19983
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
19804
19984
  };
19805
19985
  var isWorkitWorkspace = (repo) => {
19806
- if (fs17.existsSync(repo.location.dir))
19986
+ if (fs18.existsSync(repo.location.dir))
19807
19987
  return true;
19808
19988
  try {
19809
19989
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -19834,17 +20014,17 @@ var headSha2 = (repo) => {
19834
20014
  if (!git || git.reftable)
19835
20015
  return null;
19836
20016
  try {
19837
- const head = fs17.readFileSync(path23.join(git.gitDir, "HEAD"), "utf8").trim();
20017
+ const head = fs18.readFileSync(path24.join(git.gitDir, "HEAD"), "utf8").trim();
19838
20018
  if (/^[0-9a-f]{40,64}$/.test(head))
19839
20019
  return head;
19840
20020
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
19841
20021
  if (!ref)
19842
20022
  return null;
19843
- const common = path23.dirname(repo.location.dir);
20023
+ const common = path24.dirname(repo.location.dir);
19844
20024
  try {
19845
- return fs17.readFileSync(path23.join(common, ref), "utf8").trim() || null;
20025
+ return fs18.readFileSync(path24.join(common, ref), "utf8").trim() || null;
19846
20026
  } catch {}
19847
- const packed = fs17.readFileSync(path23.join(common, "packed-refs"), "utf8");
20027
+ const packed = fs18.readFileSync(path24.join(common, "packed-refs"), "utf8");
19848
20028
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
19849
20029
  } catch {
19850
20030
  return null;
@@ -19881,7 +20061,7 @@ var actionsIn = (cwd, command, dialect) => {
19881
20061
  const action = rawAction(invocation);
19882
20062
  if (!action)
19883
20063
  continue;
19884
- const dir = path23.resolve(cwd, invocation.dir ?? ".");
20064
+ const dir = path24.resolve(cwd, invocation.dir ?? ".");
19885
20065
  if (!repos.has(dir)) {
19886
20066
  const repo = repoAt(dir);
19887
20067
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -19950,8 +20130,8 @@ function rawGitPre(input, command, options = {}) {
19950
20130
  var deny = (reason) => ({ kind: "deny", reason, unblock: null });
19951
20131
  var NEXT_COMMAND = "next";
19952
20132
  var MARKER_TTL_SECONDS = 3600;
19953
- var markerFile = (repo, session, key) => path23.join(repo.location.dir, "hooks", `commit-${createHash13("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
19954
- var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash13("sha256").update(command).digest("hex")}`;
20133
+ var markerFile = (repo, session, key) => path24.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
20134
+ var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
19955
20135
  function noteRawCommit(input, command, key) {
19956
20136
  try {
19957
20137
  if (!input.session.id || !/\bcommit\b/.test(command))
@@ -19961,8 +20141,8 @@ function noteRawCommit(input, command, key) {
19961
20141
  if (action.kind !== "commit")
19962
20142
  continue;
19963
20143
  const file = markerFile(repo, input.session.id, key);
19964
- fs17.mkdirSync(path23.dirname(file), { recursive: true });
19965
- fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
20144
+ fs18.mkdirSync(path24.dirname(file), { recursive: true });
20145
+ fs18.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
19966
20146
  `);
19967
20147
  }
19968
20148
  } catch {}
@@ -19985,11 +20165,11 @@ var headCommit = (dir) => {
19985
20165
  var settle2 = (input, repo, file) => {
19986
20166
  let marker;
19987
20167
  try {
19988
- marker = JSON.parse(fs17.readFileSync(file, "utf8"));
20168
+ marker = JSON.parse(fs18.readFileSync(file, "utf8"));
19989
20169
  } catch {
19990
20170
  return;
19991
20171
  }
19992
- fs17.rmSync(file, { force: true });
20172
+ fs18.rmSync(file, { force: true });
19993
20173
  const session = input.session.id;
19994
20174
  const now = Date.now() / 1000;
19995
20175
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -20038,7 +20218,7 @@ function settlePendingCommit(input) {
20038
20218
  if (!repo)
20039
20219
  return;
20040
20220
  const file = markerFile(repo, input.session.id, NEXT_COMMAND);
20041
- if (fs17.existsSync(file))
20221
+ if (fs18.existsSync(file))
20042
20222
  settle2(input, repo, file);
20043
20223
  } catch {}
20044
20224
  }
@@ -20496,6 +20676,8 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
20496
20676
  if (role !== "judge" || !event.agentId)
20497
20677
  return readOnly;
20498
20678
  const own = subagentSession(session, event.agentId);
20679
+ if (session)
20680
+ recordIssuedSession(input.cwd || process.cwd(), session, own);
20499
20681
  return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> --session ${own} ...\` so the ledger tells it apart from the author.`;
20500
20682
  };
20501
20683
  function handleHook(input, deps) {
@@ -20857,12 +21039,12 @@ var skillPromptNudge = (prompt, ctx) => process.env.WORKIT_PI_WORKER_SESSION ? n
20857
21039
  // packages/workit-pi/src/launches.ts
20858
21040
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
20859
21041
  import os5 from "node:os";
20860
- import path24 from "node:path";
21042
+ import path25 from "node:path";
20861
21043
  var launchDir = (root) => {
20862
21044
  const location = resolveStore(root);
20863
- return location instanceof Error ? null : path24.join(location.dir, "pi-launches");
21045
+ return location instanceof Error ? null : path25.join(location.dir, "pi-launches");
20864
21046
  };
20865
- var fileFor = (dir, workerId) => path24.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
21047
+ var fileFor = (dir, workerId) => path25.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
20866
21048
  var recordLaunch = (root, attempt) => {
20867
21049
  const dir = launchDir(root);
20868
21050
  if (!dir)
@@ -20908,7 +21090,7 @@ var abandonedLaunches = (root) => {
20908
21090
  const found = [];
20909
21091
  for (const name of names) {
20910
21092
  try {
20911
- const attempt = JSON.parse(readFileSync6(path24.join(dir, name), "utf8"));
21093
+ const attempt = JSON.parse(readFileSync6(path25.join(dir, name), "utf8"));
20912
21094
  if (typeof attempt.workerId === "string" && typeof attempt.session === "string") {
20913
21095
  if (!alive(attempt))
20914
21096
  found.push(attempt);
@@ -20994,7 +21176,7 @@ var roleTools = (role) => {
20994
21176
  return [...readOnly, "write", "edit", ...familyTools];
20995
21177
  return [...readOnly, ...familyTools];
20996
21178
  };
20997
- function workerCommand(runtime, assignment) {
21179
+ function workerCommand2(runtime, assignment) {
20998
21180
  return {
20999
21181
  command: runtime.node,
21000
21182
  args: [
@@ -21046,7 +21228,7 @@ function launchWorker(assignment, options) {
21046
21228
  const taskId = options.taskId ?? null;
21047
21229
  const workerId = options.workerId ?? null;
21048
21230
  const sessionId = options.sessionId ?? "pi-worker-" + randomUUID4();
21049
- const spec = workerCommand({ ...options.runtime, sessionId }, assignment);
21231
+ const spec = workerCommand2({ ...options.runtime, sessionId }, assignment);
21050
21232
  const handle = {
21051
21233
  id: randomUUID4(),
21052
21234
  taskId,
@@ -21082,7 +21264,7 @@ function launchWorker(assignment, options) {
21082
21264
  WORKIT_PI_WORKER_ID: workerId ?? "",
21083
21265
  WORKIT_PI_WORKER_SESSION: sessionId,
21084
21266
  WORKIT_HOST: "pi",
21085
- WORKIT_SESSION_ID: sessionId,
21267
+ WORKIT_SESSION_ID: options.leadSession ? childSessionId(options.leadSession, sessionId) : sessionId,
21086
21268
  WORKIT_PI_WORKER_ROLE: assignment.role,
21087
21269
  WORKIT_PI_WORKER_SCOPE: JSON.stringify(assignment.scope)
21088
21270
  };
@@ -21629,6 +21811,7 @@ function extension(pi) {
21629
21811
  const handle = launchSupervisedWorker(worker.data.assignment, {
21630
21812
  runtime: runtimeFor(ctx),
21631
21813
  binding,
21814
+ leadSession: ctx.sessionManager.getSessionId(),
21632
21815
  prompt: request.prompt,
21633
21816
  onPrepare: (pending) => {
21634
21817
  child = pending;