@brainervirus/workit-opencode 8.13.0 → 8.14.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/plugin.js CHANGED
@@ -6,7 +6,7 @@ function define(plugin) {
6
6
  // packages/workit-core/src/core.ts
7
7
  import { spawnSync as spawnSync16 } from "node:child_process";
8
8
  import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
9
- import path22 from "node:path";
9
+ import path24 from "node:path";
10
10
 
11
11
  // packages/workit-core/src/core/task-contract.ts
12
12
  import { createHash, randomUUID } from "node:crypto";
@@ -8992,9 +8992,9 @@ function parseAdvertisedOperation(family, input, host) {
8992
8992
  });
8993
8993
  }
8994
8994
  // packages/workit-core/src/core/task-store.ts
8995
- import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
8996
- import * as fs7 from "node:fs";
8997
- import path6 from "node:path";
8995
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
8996
+ import * as fs8 from "node:fs";
8997
+ import path8 from "node:path";
8998
8998
 
8999
8999
  // node_modules/@openclaw/fs-safe/dist/errors.js
9000
9000
  var OPERATIONAL_CODES = new Set([
@@ -9597,8 +9597,14 @@ function acquireFileLockSync(targetPath, options) {
9597
9597
  }
9598
9598
  }
9599
9599
  }
9600
+ // packages/workit-core/src/host-session.ts
9601
+ import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
9602
+ import fs5 from "node:fs";
9603
+ import path5 from "node:path";
9604
+
9600
9605
  // packages/workit-core/src/core/logger.ts
9601
9606
  import os from "node:os";
9607
+ import path3 from "node:path";
9602
9608
  var SENSITIVE_WORDS = new Set([
9603
9609
  "token",
9604
9610
  "secret",
@@ -9637,13 +9643,29 @@ var SENSITIVE_WORDS = new Set([
9637
9643
  ]);
9638
9644
  var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
9639
9645
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9646
+ var resolveStateDir = () => {
9647
+ const override = process.env.WORKFLOW_TOOLKIT_STATE;
9648
+ if (override)
9649
+ return override;
9650
+ const home = os.homedir();
9651
+ if (process.env.XDG_STATE_HOME)
9652
+ return path3.join(process.env.XDG_STATE_HOME, "workit");
9653
+ switch (os.platform()) {
9654
+ case "darwin":
9655
+ return path3.join(home, "Library", "Application Support", "workit");
9656
+ case "win32":
9657
+ return path3.join(process.env.LOCALAPPDATA ?? path3.join(home, "AppData", "Local"), "workit");
9658
+ default:
9659
+ return path3.join(process.env.HOME ?? home, ".local", "state", "workit");
9660
+ }
9661
+ };
9640
9662
  var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
9641
9663
 
9642
9664
  // packages/workit-core/src/store/paths.ts
9643
9665
  import { spawnSync } from "node:child_process";
9644
9666
  import { createHash as createHash2 } from "node:crypto";
9645
9667
  import fs4 from "node:fs";
9646
- import path3 from "node:path";
9668
+ import path4 from "node:path";
9647
9669
  var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
9648
9670
  var gitRun = (cwd, args) => {
9649
9671
  const run = spawnSync("git", args, {
@@ -9685,14 +9707,14 @@ var DISCOVERY_ENV = [
9685
9707
  ];
9686
9708
  var isGitDir = (dir) => {
9687
9709
  try {
9688
- return fs4.statSync(path3.join(dir, "HEAD")).isFile() && fs4.statSync(path3.join(dir, "objects")).isDirectory() && fs4.statSync(path3.join(dir, "refs")).isDirectory();
9710
+ return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
9689
9711
  } catch {
9690
9712
  return false;
9691
9713
  }
9692
9714
  };
9693
9715
  var reftableRefs = (common) => {
9694
9716
  try {
9695
- return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path3.join(common, "config"), "utf8"));
9717
+ return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
9696
9718
  } catch {
9697
9719
  return false;
9698
9720
  }
@@ -9712,7 +9734,7 @@ function fastGit(root) {
9712
9734
  if (device !== null && stat.dev !== device)
9713
9735
  return { kind: "none" };
9714
9736
  device = stat.dev;
9715
- const dotGit = path3.join(dir, ".git");
9737
+ const dotGit = path4.join(dir, ".git");
9716
9738
  let entry = null;
9717
9739
  try {
9718
9740
  entry = fs4.statSync(dotGit);
@@ -9733,19 +9755,19 @@ function fastGit(root) {
9733
9755
  const match = /^gitdir: (.+)$/mu.exec(text);
9734
9756
  if (!match)
9735
9757
  return null;
9736
- gitDir = path3.resolve(dir, match[1].trim());
9758
+ gitDir = path4.resolve(dir, match[1].trim());
9737
9759
  } else
9738
9760
  return null;
9739
- if (!fs4.existsSync(path3.join(gitDir, "HEAD")))
9761
+ if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
9740
9762
  return null;
9741
9763
  } else if (isGitDir(dir))
9742
9764
  gitDir = dir;
9743
9765
  if (gitDir) {
9744
9766
  let common = gitDir;
9745
9767
  try {
9746
- common = path3.resolve(gitDir, fs4.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
9768
+ common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
9747
9769
  } catch {}
9748
- if (!fs4.existsSync(path3.join(common, "objects")))
9770
+ if (!fs4.existsSync(path4.join(common, "objects")))
9749
9771
  return null;
9750
9772
  return {
9751
9773
  kind: "repo",
@@ -9755,26 +9777,26 @@ function fastGit(root) {
9755
9777
  reftable: reftableRefs(common)
9756
9778
  };
9757
9779
  }
9758
- const parent = path3.dirname(dir);
9780
+ const parent = path4.dirname(dir);
9759
9781
  if (parent === dir)
9760
9782
  return { kind: "none" };
9761
9783
  dir = parent;
9762
9784
  }
9763
9785
  }
9764
9786
  function resolveStore(root) {
9765
- root = path3.resolve(root);
9787
+ root = path4.resolve(root);
9766
9788
  let directory = false;
9767
9789
  try {
9768
9790
  directory = fs4.statSync(root).isDirectory();
9769
9791
  } catch {}
9770
9792
  if (!directory)
9771
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
9793
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
9772
9794
  const fast = fastGit(root);
9773
9795
  if (fast?.kind === "none")
9774
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
9796
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
9775
9797
  if (fast?.kind === "repo")
9776
9798
  return {
9777
- dir: path3.join(fast.common, "workit"),
9799
+ dir: path4.join(fast.common, "workit"),
9778
9800
  shared: true,
9779
9801
  top: fast.top,
9780
9802
  git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
@@ -9784,20 +9806,20 @@ function resolveStore(root) {
9784
9806
  if (run.ok && common) {
9785
9807
  const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
9786
9808
  return {
9787
- dir: path3.join(real(path3.resolve(root, common)), "workit"),
9809
+ dir: path4.join(real(path4.resolve(root, common)), "workit"),
9788
9810
  shared: true,
9789
9811
  top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
9790
9812
  };
9791
9813
  }
9792
9814
  if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
9793
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
9815
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
9794
9816
  return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
9795
9817
  `)[0] || "no output"}); fix the repository (git status should work here)`);
9796
9818
  }
9797
9819
  var rebasingBranch = (gitDir) => {
9798
9820
  for (const state of ["rebase-merge", "rebase-apply"]) {
9799
9821
  try {
9800
- const ref = fs4.readFileSync(path3.join(gitDir, state, "head-name"), "utf8").trim();
9822
+ const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
9801
9823
  const match = /^refs\/heads\/(.+)$/u.exec(ref);
9802
9824
  if (match)
9803
9825
  return match[1];
@@ -9812,7 +9834,7 @@ function resolveTaskKey(root, location) {
9812
9834
  if (location.git && !location.git.reftable) {
9813
9835
  let head = "";
9814
9836
  try {
9815
- head = fs4.readFileSync(path3.join(location.git.gitDir, "HEAD"), "utf8").trim();
9837
+ head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
9816
9838
  } catch {}
9817
9839
  const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
9818
9840
  if (ref && ref[1] !== ".invalid")
@@ -9843,7 +9865,7 @@ function resolveTaskKey(root, location) {
9843
9865
  return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
9844
9866
  }
9845
9867
  function checkoutRootOf(root, location) {
9846
- const resolved = path3.resolve(root);
9868
+ const resolved = path4.resolve(root);
9847
9869
  let canonical = resolved;
9848
9870
  try {
9849
9871
  canonical = fs4.realpathSync(resolved);
@@ -9874,13 +9896,13 @@ function branchRenames(location) {
9874
9896
  }
9875
9897
  for (const entry of entries)
9876
9898
  if (entry.isDirectory())
9877
- walk(path3.join(dir, entry.name));
9899
+ walk(path4.join(dir, entry.name));
9878
9900
  else
9879
- files.push(path3.join(dir, entry.name));
9901
+ files.push(path4.join(dir, entry.name));
9880
9902
  };
9881
- walk(path3.join(path3.dirname(location.dir), "logs", "refs", "heads"));
9903
+ walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
9882
9904
  if (location.git)
9883
- files.push(path3.join(location.git.gitDir, "logs", "HEAD"));
9905
+ files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
9884
9906
  const seen = new Set;
9885
9907
  const renames = [];
9886
9908
  for (const file of files) {
@@ -9915,7 +9937,7 @@ var checkoutSlug = (root) => {
9915
9937
  let canonical = real(root);
9916
9938
  if (process.platform === "win32")
9917
9939
  canonical = canonical.toLowerCase();
9918
- const base = path3.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
9940
+ const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
9919
9941
  return `${base}-${shortHash(canonical)}`;
9920
9942
  };
9921
9943
 
@@ -9947,6 +9969,89 @@ function hostSessionFromEnv(env) {
9947
9969
  }
9948
9970
  return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
9949
9971
  }
9972
+ var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
9973
+ var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
9974
+ var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
9975
+ function migrateRepoIssued(cwd) {
9976
+ if (cwd === undefined)
9977
+ return;
9978
+ let old;
9979
+ try {
9980
+ const location = resolveStore(cwd);
9981
+ if (location instanceof Error)
9982
+ return;
9983
+ old = path5.join(location.dir, "hooks", "issued");
9984
+ if (!fs5.existsSync(old))
9985
+ return;
9986
+ } catch {
9987
+ return;
9988
+ }
9989
+ for (const lead of safeList(old)) {
9990
+ try {
9991
+ const from = path5.join(old, lead);
9992
+ if (!fs5.statSync(from).isDirectory())
9993
+ continue;
9994
+ const to = path5.join(userIssuedRoot(), lead);
9995
+ fs5.mkdirSync(to, { recursive: true, mode: 448 });
9996
+ for (const id of fs5.readdirSync(from)) {
9997
+ try {
9998
+ fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
9999
+ } catch (error) {
10000
+ if (error.code !== "EEXIST")
10001
+ throw error;
10002
+ }
10003
+ fs5.rmSync(path5.join(from, id), { force: true });
10004
+ }
10005
+ removeIfEmpty(from);
10006
+ } catch {}
10007
+ }
10008
+ removeIfEmpty(old);
10009
+ }
10010
+ var safeList = (dir) => {
10011
+ try {
10012
+ return fs5.readdirSync(dir);
10013
+ } catch {
10014
+ return [];
10015
+ }
10016
+ };
10017
+ var removeIfEmpty = (dir) => {
10018
+ try {
10019
+ fs5.rmdirSync(dir);
10020
+ } catch {}
10021
+ };
10022
+ var issuedRecord = (cwd, claimed) => {
10023
+ const cut = claimed.lastIndexOf(":");
10024
+ if (cut <= 0)
10025
+ return null;
10026
+ migrateRepoIssued(cwd);
10027
+ try {
10028
+ return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
10029
+ } catch {
10030
+ return null;
10031
+ }
10032
+ };
10033
+ var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
10034
+ var issuedToken = (claimed) => {
10035
+ const cut = claimed.lastIndexOf(":");
10036
+ if (cut <= 0)
10037
+ return { stored: null, leadBound: false };
10038
+ const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
10039
+ try {
10040
+ const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
10041
+ return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
10042
+ } catch {
10043
+ return { stored: null, leadBound: safeList(dir).length > 0 };
10044
+ }
10045
+ };
10046
+ function issuedBinding(cwd, claimed) {
10047
+ const record = issuedRecord(cwd, claimed);
10048
+ const { stored, leadBound } = issuedToken(claimed);
10049
+ if (stored)
10050
+ return "token";
10051
+ if (record === null)
10052
+ return null;
10053
+ return leadBound ? "token" : "legacy";
10054
+ }
9950
10055
  var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
9951
10056
  var SESSION_UNSAFE = /[^A-Za-z0-9_.:@/+-]/g;
9952
10057
  function childSessionId(lead, child) {
@@ -9957,26 +10062,26 @@ function childSessionId(lead, child) {
9957
10062
 
9958
10063
  // packages/workit-core/src/core/package-root.ts
9959
10064
  import { existsSync } from "node:fs";
9960
- import path4 from "node:path";
10065
+ import path6 from "node:path";
9961
10066
  import { fileURLToPath } from "node:url";
9962
10067
  var packageRoot = () => {
9963
- let dir = path4.dirname(fileURLToPath(import.meta.url));
10068
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
9964
10069
  while (true) {
9965
- const parent = path4.dirname(dir);
9966
- if (existsSync(path4.join(dir, "package.json")) || parent === dir)
10070
+ const parent = path6.dirname(dir);
10071
+ if (existsSync(path6.join(dir, "package.json")) || parent === dir)
9967
10072
  return dir;
9968
10073
  dir = parent;
9969
10074
  }
9970
10075
  };
9971
10076
  var assetRoot = () => {
9972
10077
  const root = packageRoot();
9973
- const assets = path4.join(root, "assets");
10078
+ const assets = path6.join(root, "assets");
9974
10079
  return existsSync(assets) ? assets : root;
9975
10080
  };
9976
10081
 
9977
10082
  // packages/workit-core/src/core/store-lock.ts
9978
10083
  import { spawnSync as spawnSync2 } from "node:child_process";
9979
- import fs5 from "node:fs";
10084
+ import fs6 from "node:fs";
9980
10085
  import { hostname } from "node:os";
9981
10086
  var metadataLockSchema = object({
9982
10087
  pid: number2().int().nonnegative().safe(),
@@ -9988,8 +10093,10 @@ var metadataLockSchema = object({
9988
10093
  var FOREIGN_LOCK_TTL_MS = 10 * 60000;
9989
10094
  var UNREADABLE_LOCK_TTL_MS = 30000;
9990
10095
  var RECLAIM_GUARD_TTL_MS = 30000;
9991
- var defaultLockTimeoutMs = 250;
10096
+ var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
10097
+ var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
9992
10098
  var defaultLockTimeout = () => defaultLockTimeoutMs;
10099
+ var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
9993
10100
  var parseMetadataLock = (raw) => {
9994
10101
  let value;
9995
10102
  try {
@@ -10018,7 +10125,7 @@ var parseProcStatStart = (stat) => {
10018
10125
  var processStartOf = (pid) => {
10019
10126
  if (process.platform === "linux") {
10020
10127
  try {
10021
- return parseProcStatStart(fs5.readFileSync(`/proc/${pid}/stat`, "utf8"));
10128
+ return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
10022
10129
  } catch {
10023
10130
  return null;
10024
10131
  }
@@ -10039,7 +10146,7 @@ var processStartOf = (pid) => {
10039
10146
  };
10040
10147
  var readTrimmed = (file) => {
10041
10148
  try {
10042
- return fs5.readFileSync(file, "utf8").trim() || null;
10149
+ return fs6.readFileSync(file, "utf8").trim() || null;
10043
10150
  } catch {
10044
10151
  return null;
10045
10152
  }
@@ -10050,7 +10157,7 @@ var localLockHost = () => {
10050
10157
  return cachedLockHost;
10051
10158
  let pidns = null;
10052
10159
  try {
10053
- pidns = /\[(\d+)\]/.exec(fs5.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10160
+ pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10054
10161
  } catch {}
10055
10162
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
10056
10163
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -10096,7 +10203,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
10096
10203
  };
10097
10204
  var ageOf = (file, nowMs) => {
10098
10205
  try {
10099
- return nowMs - fs5.lstatSync(file).mtimeMs;
10206
+ return nowMs - fs6.lstatSync(file).mtimeMs;
10100
10207
  } catch {
10101
10208
  return null;
10102
10209
  }
@@ -10107,7 +10214,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10107
10214
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
10108
10215
  return false;
10109
10216
  try {
10110
- fs5.rmdirSync(guard);
10217
+ fs6.rmdirSync(guard);
10111
10218
  return true;
10112
10219
  } catch {
10113
10220
  return false;
@@ -10116,8 +10223,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10116
10223
 
10117
10224
  // packages/workit-core/src/store/event-log.ts
10118
10225
  import { randomUUID as randomUUID2 } from "node:crypto";
10119
- import fs6 from "node:fs";
10120
- import path5 from "node:path";
10226
+ import fs7 from "node:fs";
10227
+ import path7 from "node:path";
10121
10228
  var EVENT_VERSION = 1;
10122
10229
 
10123
10230
  class LogDamage extends Error {
@@ -10146,7 +10253,7 @@ var parseEvent = (line, file, offset) => {
10146
10253
  function readLog(file, from = 0, expectSeq = null) {
10147
10254
  let fd;
10148
10255
  try {
10149
- fd = fs6.openSync(file, "r");
10256
+ fd = fs7.openSync(file, "r");
10150
10257
  } catch (error) {
10151
10258
  if (error.code === "ENOENT")
10152
10259
  return null;
@@ -10155,13 +10262,13 @@ function readLog(file, from = 0, expectSeq = null) {
10155
10262
  let bytes;
10156
10263
  let size;
10157
10264
  try {
10158
- size = fs6.fstatSync(fd).size;
10265
+ size = fs7.fstatSync(fd).size;
10159
10266
  if (from > size)
10160
10267
  throw new LogDamage(`${file}: shorter than its snapshot`);
10161
10268
  bytes = Buffer.alloc(size - from);
10162
10269
  let read = 0;
10163
10270
  while (read < bytes.length) {
10164
- const got = fs6.readSync(fd, bytes, read, bytes.length - read, from + read);
10271
+ const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
10165
10272
  if (got === 0)
10166
10273
  break;
10167
10274
  read += got;
@@ -10169,7 +10276,7 @@ function readLog(file, from = 0, expectSeq = null) {
10169
10276
  bytes = bytes.subarray(0, read);
10170
10277
  size = from + read;
10171
10278
  } finally {
10172
- fs6.closeSync(fd);
10279
+ fs7.closeSync(fd);
10173
10280
  }
10174
10281
  const events = [];
10175
10282
  let start = 0;
@@ -10194,11 +10301,11 @@ function readLog(file, from = 0, expectSeq = null) {
10194
10301
  }
10195
10302
  var fsyncDirectory = (dir) => {
10196
10303
  try {
10197
- const fd = fs6.openSync(dir, "r");
10304
+ const fd = fs7.openSync(dir, "r");
10198
10305
  try {
10199
- fs6.fsyncSync(fd);
10306
+ fs7.fsyncSync(fd);
10200
10307
  } finally {
10201
- fs6.closeSync(fd);
10308
+ fs7.closeSync(fd);
10202
10309
  }
10203
10310
  } catch (error) {
10204
10311
  if (process.platform !== "win32")
@@ -10208,46 +10315,46 @@ var fsyncDirectory = (dir) => {
10208
10315
  function appendEvent(file, event, validEnd) {
10209
10316
  const line = Buffer.from(`${JSON.stringify(event)}
10210
10317
  `, "utf8");
10211
- const created = !fs6.existsSync(file);
10212
- if (!created && fs6.statSync(file).size !== validEnd) {
10213
- const cut = fs6.openSync(file, "r+");
10318
+ const created = !fs7.existsSync(file);
10319
+ if (!created && fs7.statSync(file).size !== validEnd) {
10320
+ const cut = fs7.openSync(file, "r+");
10214
10321
  try {
10215
- fs6.ftruncateSync(cut, validEnd);
10216
- fs6.fsyncSync(cut);
10322
+ fs7.ftruncateSync(cut, validEnd);
10323
+ fs7.fsyncSync(cut);
10217
10324
  } finally {
10218
- fs6.closeSync(cut);
10325
+ fs7.closeSync(cut);
10219
10326
  }
10220
10327
  }
10221
- const fd = fs6.openSync(file, "a", 384);
10328
+ const fd = fs7.openSync(file, "a", 384);
10222
10329
  try {
10223
10330
  let written = 0;
10224
10331
  while (written < line.length)
10225
- written += fs6.writeSync(fd, line, written, line.length - written);
10226
- fs6.fsyncSync(fd);
10332
+ written += fs7.writeSync(fd, line, written, line.length - written);
10333
+ fs7.fsyncSync(fd);
10227
10334
  } finally {
10228
- fs6.closeSync(fd);
10335
+ fs7.closeSync(fd);
10229
10336
  }
10230
10337
  if (created)
10231
- fsyncDirectory(path5.dirname(file));
10338
+ fsyncDirectory(path7.dirname(file));
10232
10339
  return validEnd;
10233
10340
  }
10234
10341
  function replaceFile(file, bytes, durable) {
10235
10342
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
10236
10343
  try {
10237
- const fd = fs6.openSync(temporary, "wx", 384);
10344
+ const fd = fs7.openSync(temporary, "wx", 384);
10238
10345
  try {
10239
- fs6.writeSync(fd, bytes);
10346
+ fs7.writeSync(fd, bytes);
10240
10347
  if (durable)
10241
- fs6.fsyncSync(fd);
10348
+ fs7.fsyncSync(fd);
10242
10349
  } finally {
10243
- fs6.closeSync(fd);
10350
+ fs7.closeSync(fd);
10244
10351
  }
10245
10352
  renameWithRetry(temporary, file);
10246
10353
  if (durable)
10247
- fsyncDirectory(path5.dirname(file));
10354
+ fsyncDirectory(path7.dirname(file));
10248
10355
  } catch (error) {
10249
10356
  try {
10250
- fs6.unlinkSync(temporary);
10357
+ fs7.unlinkSync(temporary);
10251
10358
  } catch {}
10252
10359
  throw error;
10253
10360
  }
@@ -10272,7 +10379,7 @@ var retryTransient = (run) => {
10272
10379
  }
10273
10380
  }
10274
10381
  };
10275
- var renameWithRetry = (from, to) => retryTransient(() => fs6.renameSync(from, to));
10382
+ var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
10276
10383
 
10277
10384
  // packages/workit-core/src/store/patch.ts
10278
10385
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -10385,7 +10492,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
10385
10492
  });
10386
10493
 
10387
10494
  // packages/workit-core/src/store/reduce.ts
10388
- import { createHash as createHash3 } from "node:crypto";
10495
+ import { createHash as createHash4 } from "node:crypto";
10389
10496
  var OPENING_TYPES = new Set([
10390
10497
  "task.opened",
10391
10498
  "task.imported",
@@ -10414,7 +10521,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
10414
10521
  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");
10415
10522
  var BLOB_REF = "$blob";
10416
10523
  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;
10417
- var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
10524
+ var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
10418
10525
  var toBlob = (value, blobs) => {
10419
10526
  if (!isObject4(value))
10420
10527
  return value;
@@ -10569,7 +10676,7 @@ var indexEntry = (task, key, legacy, file) => {
10569
10676
  };
10570
10677
  var fileSignature = (file) => {
10571
10678
  try {
10572
- const stat = fs7.statSync(file, { bigint: true });
10679
+ const stat = fs8.statSync(file, { bigint: true });
10573
10680
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
10574
10681
  } catch {
10575
10682
  return null;
@@ -10587,7 +10694,7 @@ var cachedRuntimeVersion = null;
10587
10694
  var runtimeVersion = () => {
10588
10695
  if (cachedRuntimeVersion === null) {
10589
10696
  try {
10590
- const pkg = JSON.parse(fs7.readFileSync(path6.join(packageRoot(), "package.json"), "utf8"));
10697
+ const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
10591
10698
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
10592
10699
  } catch {
10593
10700
  cachedRuntimeVersion = "0.0.0";
@@ -10613,14 +10720,14 @@ var isNewerVersion = (candidate, current) => {
10613
10720
  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);
10614
10721
  var readText = (file) => {
10615
10722
  try {
10616
- return fs7.readFileSync(file, "utf8");
10723
+ return fs8.readFileSync(file, "utf8");
10617
10724
  } catch {
10618
10725
  return "";
10619
10726
  }
10620
10727
  };
10621
10728
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
10622
10729
  var branchStart = (location, branch, aliases) => {
10623
- const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10730
+ const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10624
10731
  `, 1)[0];
10625
10732
  const created = REFLOG_LINE.exec(first);
10626
10733
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -10630,7 +10737,7 @@ var branchStart = (location, branch, aliases) => {
10630
10737
  return start;
10631
10738
  if (!location.git)
10632
10739
  return null;
10633
- for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
10740
+ for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
10634
10741
  `)) {
10635
10742
  const entry = REFLOG_LINE.exec(line);
10636
10743
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -10640,23 +10747,23 @@ var branchStart = (location, branch, aliases) => {
10640
10747
  }
10641
10748
  return null;
10642
10749
  };
10643
- var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
10750
+ var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
10644
10751
  `);
10645
10752
  var origins = new Map;
10646
10753
  var sameDirectoryIdentity = (left, right) => {
10647
- if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
10754
+ if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
10648
10755
  return false;
10649
- const normalizedLeft = path6.resolve(left);
10650
- const normalizedRight = path6.resolve(right);
10756
+ const normalizedLeft = path8.resolve(left);
10757
+ const normalizedRight = path8.resolve(right);
10651
10758
  try {
10652
- const a = fs7.statSync(normalizedLeft, { bigint: true });
10653
- const b = fs7.statSync(normalizedRight, { bigint: true });
10759
+ const a = fs8.statSync(normalizedLeft, { bigint: true });
10760
+ const b = fs8.statSync(normalizedRight, { bigint: true });
10654
10761
  if (!a.isDirectory() || !b.isDirectory())
10655
10762
  return false;
10656
10763
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
10657
10764
  return true;
10658
- const canonicalLeft = fs7.realpathSync(normalizedLeft);
10659
- const canonicalRight = fs7.realpathSync(normalizedRight);
10765
+ const canonicalLeft = fs8.realpathSync(normalizedLeft);
10766
+ const canonicalRight = fs8.realpathSync(normalizedRight);
10660
10767
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
10661
10768
  return true;
10662
10769
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -10693,8 +10800,8 @@ class TaskStore {
10693
10800
  migrationChecked = false;
10694
10801
  compactDue = new Set;
10695
10802
  constructor(root, options = {}) {
10696
- const resolved = path6.resolve(root);
10697
- this.origin = fs7.existsSync(resolved) ? fs7.realpathSync(resolved) : resolved;
10803
+ const resolved = path8.resolve(root);
10804
+ this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
10698
10805
  this.located = resolveStore(this.origin);
10699
10806
  this.root = checkoutRootOf(this.origin, this.located);
10700
10807
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -10702,7 +10809,7 @@ class TaskStore {
10702
10809
  this.migrateOnRead = options.migrateOnRead;
10703
10810
  }
10704
10811
  location() {
10705
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs7.existsSync(path6.join(this.root, ".git"))) {
10812
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
10706
10813
  this.located = undefined;
10707
10814
  this.migrationChecked = false;
10708
10815
  }
@@ -10766,7 +10873,7 @@ class TaskStore {
10766
10873
  const tasksDir = this.paths().tasks;
10767
10874
  let names;
10768
10875
  try {
10769
- names = fs7.existsSync(tasksDir) ? fs7.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10876
+ names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10770
10877
  } catch (error) {
10771
10878
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
10772
10879
  path: tasksDir
@@ -10807,7 +10914,7 @@ class TaskStore {
10807
10914
  const file = this.paths().workspace;
10808
10915
  let bytes;
10809
10916
  try {
10810
- bytes = retryTransient(() => fs7.readFileSync(file, "utf8"));
10917
+ bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
10811
10918
  } catch (error) {
10812
10919
  if (error.code === "ENOENT")
10813
10920
  return success(null, null, null);
@@ -10858,7 +10965,7 @@ class TaskStore {
10858
10965
  const moved = this.bindLocked(again.data.id, key, input.now);
10859
10966
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
10860
10967
  }
10861
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path6.basename(this.root) || this.root;
10968
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
10862
10969
  const intent = {
10863
10970
  objective: input.objective?.trim() || `Work on ${label}`,
10864
10971
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -10918,14 +11025,14 @@ class TaskStore {
10918
11025
  const branch = key.ok ? key.data.branch : null;
10919
11026
  if (!branch || !location.ok)
10920
11027
  return success(null, null, null);
10921
- const common = path6.dirname(location.data.dir);
10922
- const heads = path6.join(common, "logs", "refs", "heads");
10923
- const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
11028
+ const common = path8.dirname(location.data.dir);
11029
+ const heads = path8.join(common, "logs", "refs", "heads");
11030
+ const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
10924
11031
  const signatures = [
10925
- path6.join(heads, branch),
11032
+ path8.join(heads, branch),
10926
11033
  headLog,
10927
11034
  heads,
10928
- path6.join(common, "packed-refs")
11035
+ path8.join(common, "packed-refs")
10929
11036
  ].map((file) => fileSignature(file) ?? "-");
10930
11037
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
10931
11038
  let resolved = origins.get(slot);
@@ -10937,7 +11044,7 @@ class TaskStore {
10937
11044
  aliases.push(rename.from);
10938
11045
  const start = branchStart(location.data, branch, aliases);
10939
11046
  const slash = start?.indexOf("/") ?? -1;
10940
- const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path6.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
11047
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path8.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
10941
11048
  const name = start && (remote ? start.slice(slash + 1) : start);
10942
11049
  resolved = {
10943
11050
  signature: signatures.join("|"),
@@ -11175,13 +11282,13 @@ class TaskStore {
11175
11282
  if (!ready.ok)
11176
11283
  return ready;
11177
11284
  const dirs = this.paths();
11178
- const recovery = path6.join(this.root, ".workit", "recovery");
11179
- if (fs7.existsSync(recovery)) {
11285
+ const recovery = path8.join(this.root, ".workit", "recovery");
11286
+ if (fs8.existsSync(recovery)) {
11180
11287
  let files = 0;
11181
11288
  let bytes = 0;
11182
11289
  try {
11183
- for (const name of fs7.readdirSync(recovery)) {
11184
- const stat = fs7.lstatSync(path6.join(recovery, name));
11290
+ for (const name of fs8.readdirSync(recovery)) {
11291
+ const stat = fs8.lstatSync(path8.join(recovery, name));
11185
11292
  if (!stat.isFile())
11186
11293
  continue;
11187
11294
  files += 1;
@@ -11190,17 +11297,17 @@ class TaskStore {
11190
11297
  } catch {}
11191
11298
  const remove = options.pruneRecovery === true && !dryRun;
11192
11299
  if (remove)
11193
- fs7.rmSync(recovery, { recursive: true, force: true });
11300
+ fs8.rmSync(recovery, { recursive: true, force: true });
11194
11301
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
11195
11302
  }
11196
- if (!fs7.existsSync(dirs.tasks))
11303
+ if (!fs8.existsSync(dirs.tasks))
11197
11304
  return success(null, null, report);
11198
- const names = fs7.readdirSync(dirs.tasks).filter((name) => validId(name));
11305
+ const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
11199
11306
  for (const name of names) {
11200
11307
  const file = this.eventsPath(name);
11201
11308
  let size = 0;
11202
11309
  try {
11203
- size = fs7.statSync(file).size;
11310
+ size = fs8.statSync(file).size;
11204
11311
  } catch {
11205
11312
  continue;
11206
11313
  }
@@ -11228,16 +11335,16 @@ class TaskStore {
11228
11335
  const references = new Set;
11229
11336
  for (const name of names)
11230
11337
  try {
11231
- for (const match of fs7.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11338
+ for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11232
11339
  references.add(match[1]);
11233
11340
  } catch {}
11234
- if (fs7.existsSync(dirs.blobs))
11235
- for (const name of fs7.readdirSync(dirs.blobs)) {
11341
+ if (fs8.existsSync(dirs.blobs))
11342
+ for (const name of fs8.readdirSync(dirs.blobs)) {
11236
11343
  const digest = name.replace(/\.json$/, "");
11237
- const file = path6.join(dirs.blobs, name);
11344
+ const file = path8.join(dirs.blobs, name);
11238
11345
  let stat;
11239
11346
  try {
11240
- stat = fs7.lstatSync(file);
11347
+ stat = fs8.lstatSync(file);
11241
11348
  } catch {
11242
11349
  continue;
11243
11350
  }
@@ -11248,11 +11355,11 @@ class TaskStore {
11248
11355
  report.blobs.removed += 1;
11249
11356
  report.blobs.removedBytes += stat.size;
11250
11357
  if (!dryRun)
11251
- fs7.rmSync(file, { force: true });
11358
+ fs8.rmSync(file, { force: true });
11252
11359
  }
11253
11360
  return success(null, null, null);
11254
11361
  };
11255
- const swept = dryRun ? sweep() : this.withLock(path6.join(dirs.dir, "gc.lock"), sweep);
11362
+ const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
11256
11363
  if (!swept.ok)
11257
11364
  return swept;
11258
11365
  const temporaryDirs = [
@@ -11260,17 +11367,17 @@ class TaskStore {
11260
11367
  dirs.tasks,
11261
11368
  dirs.blobs,
11262
11369
  dirs.checkout,
11263
- ...names.map((name) => path6.join(dirs.tasks, name))
11370
+ ...names.map((name) => path8.join(dirs.tasks, name))
11264
11371
  ];
11265
11372
  for (const directory of temporaryDirs) {
11266
- if (!fs7.existsSync(directory))
11373
+ if (!fs8.existsSync(directory))
11267
11374
  continue;
11268
- for (const name of fs7.readdirSync(directory)) {
11375
+ for (const name of fs8.readdirSync(directory)) {
11269
11376
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
11270
11377
  continue;
11271
- const file = path6.join(directory, name);
11378
+ const file = path8.join(directory, name);
11272
11379
  try {
11273
- const stat = fs7.lstatSync(file);
11380
+ const stat = fs8.lstatSync(file);
11274
11381
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
11275
11382
  continue;
11276
11383
  } catch {
@@ -11278,37 +11385,37 @@ class TaskStore {
11278
11385
  }
11279
11386
  report.temporary.removed += 1;
11280
11387
  if (!dryRun)
11281
- fs7.rmSync(file, { force: true });
11388
+ fs8.rmSync(file, { force: true });
11282
11389
  }
11283
11390
  }
11284
11391
  return success(null, null, report);
11285
11392
  }
11286
11393
  paths() {
11287
11394
  const location = this.location();
11288
- const dir = location.ok ? location.data.dir : path6.join(this.root, ".workit");
11289
- const checkout = path6.join(dir, "checkouts", checkoutSlug(this.root));
11395
+ const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
11396
+ const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
11290
11397
  return {
11291
11398
  dir,
11292
11399
  shared: location.ok ? location.data.shared : false,
11293
- marker: path6.join(dir, "store.json"),
11400
+ marker: path8.join(dir, "store.json"),
11294
11401
  checkout,
11295
- workspace: path6.join(checkout, "workspace.json"),
11296
- checkoutLock: path6.join(checkout, "metadata.lock"),
11297
- tasks: path6.join(dir, "tasks"),
11298
- index: path6.join(dir, "task-index.json"),
11299
- blobs: path6.join(dir, "blobs", "candidates"),
11300
- legacy: path6.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11301
- v2: path6.join(this.root, ".workit")
11402
+ workspace: path8.join(checkout, "workspace.json"),
11403
+ checkoutLock: path8.join(checkout, "metadata.lock"),
11404
+ tasks: path8.join(dir, "tasks"),
11405
+ index: path8.join(dir, "task-index.json"),
11406
+ blobs: path8.join(dir, "blobs", "candidates"),
11407
+ legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11408
+ v2: path8.join(this.root, ".workit")
11302
11409
  };
11303
11410
  }
11304
11411
  eventsPath(taskId) {
11305
- return path6.join(this.paths().tasks, taskId, "events.jsonl");
11412
+ return path8.join(this.paths().tasks, taskId, "events.jsonl");
11306
11413
  }
11307
11414
  snapshotPath(taskId) {
11308
- return path6.join(this.paths().tasks, taskId, "snapshot.json");
11415
+ return path8.join(this.paths().tasks, taskId, "snapshot.json");
11309
11416
  }
11310
11417
  taskLockPath(taskId) {
11311
- return path6.join(this.paths().tasks, taskId, "lock");
11418
+ return path8.join(this.paths().tasks, taskId, "lock");
11312
11419
  }
11313
11420
  ready(write = false) {
11314
11421
  const location = this.location();
@@ -11318,7 +11425,7 @@ class TaskStore {
11318
11425
  return success(null, null, null);
11319
11426
  const dirs = this.paths();
11320
11427
  try {
11321
- const marker = JSON.parse(fs7.readFileSync(dirs.marker, "utf8"));
11428
+ const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
11322
11429
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
11323
11430
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
11324
11431
  } catch {}
@@ -11347,21 +11454,21 @@ class TaskStore {
11347
11454
  return {
11348
11455
  read: (digest) => {
11349
11456
  try {
11350
- return JSON.parse(fs7.readFileSync(path6.join(dir, `${digest}.json`), "utf8"));
11457
+ return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
11351
11458
  } catch (error) {
11352
11459
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
11353
11460
  }
11354
11461
  },
11355
11462
  write: (digest, value) => {
11356
- const file = path6.join(dir, `${digest}.json`);
11357
- if (fs7.existsSync(file)) {
11463
+ const file = path8.join(dir, `${digest}.json`);
11464
+ if (fs8.existsSync(file)) {
11358
11465
  const stamp = new Date;
11359
11466
  try {
11360
- fs7.utimesSync(file, stamp, stamp);
11467
+ fs8.utimesSync(file, stamp, stamp);
11361
11468
  return;
11362
11469
  } catch {}
11363
11470
  }
11364
- fs7.mkdirSync(dir, { recursive: true });
11471
+ fs8.mkdirSync(dir, { recursive: true });
11365
11472
  replaceFile(file, JSON.stringify(value), true);
11366
11473
  }
11367
11474
  };
@@ -11443,7 +11550,7 @@ class TaskStore {
11443
11550
  }
11444
11551
  readSnapshot(taskId) {
11445
11552
  try {
11446
- const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
11553
+ const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
11447
11554
  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))
11448
11555
  return null;
11449
11556
  return value;
@@ -11545,8 +11652,8 @@ class TaskStore {
11545
11652
  }
11546
11653
  }
11547
11654
  openTask(record, type, key, legacy = null) {
11548
- const dir = path6.join(this.paths().tasks, record.id);
11549
- fs7.mkdirSync(dir, { recursive: true });
11655
+ const dir = path8.join(this.paths().tasks, record.id);
11656
+ fs8.mkdirSync(dir, { recursive: true });
11550
11657
  const blobs = this.blobStore();
11551
11658
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
11552
11659
  const event = this.event(record.id, 1, type, {
@@ -11598,11 +11705,11 @@ class TaskStore {
11598
11705
  } catch {}
11599
11706
  if (!latest || latest.events[0]?.id !== last.id)
11600
11707
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
11601
- const appended = fs7.readFileSync(file).subarray(log.end, latest.end);
11708
+ const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
11602
11709
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
11603
11710
  replaceFile(file, bytes.toString("utf8"), true);
11604
11711
  try {
11605
- fs7.rmSync(this.snapshotPath(taskId), { force: true });
11712
+ fs8.rmSync(this.snapshotPath(taskId), { force: true });
11606
11713
  } catch {}
11607
11714
  const reloaded = this.load(taskId);
11608
11715
  if (reloaded.ok && reloaded.data)
@@ -11615,7 +11722,7 @@ class TaskStore {
11615
11722
  }
11616
11723
  countEvents(file) {
11617
11724
  try {
11618
- const bytes = fs7.readFileSync(file);
11725
+ const bytes = fs8.readFileSync(file);
11619
11726
  let count = 0;
11620
11727
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
11621
11728
  count += 1;
@@ -11637,8 +11744,8 @@ class TaskStore {
11637
11744
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
11638
11745
  }
11639
11746
  withKeyLock(key, operation) {
11640
- const digest = createHash4("sha256").update(key).digest("hex").slice(0, 32);
11641
- return this.withLock(path6.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11747
+ const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
11748
+ return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11642
11749
  }
11643
11750
  bindLocked(taskId, key, timestamp) {
11644
11751
  const current = this.readWorkspace();
@@ -11779,7 +11886,7 @@ class TaskStore {
11779
11886
  writeWorkspace(record) {
11780
11887
  const file = this.paths().workspace;
11781
11888
  try {
11782
- fs7.mkdirSync(path6.dirname(file), { recursive: true });
11889
+ fs8.mkdirSync(path8.dirname(file), { recursive: true });
11783
11890
  replaceFile(file, `${canonicalJson(record)}
11784
11891
  `, true);
11785
11892
  return success(null, null, null);
@@ -11789,7 +11896,7 @@ class TaskStore {
11789
11896
  }
11790
11897
  readIndex() {
11791
11898
  try {
11792
- const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
11899
+ const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
11793
11900
  if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
11794
11901
  return {};
11795
11902
  const tasks = {};
@@ -11803,7 +11910,7 @@ class TaskStore {
11803
11910
  }
11804
11911
  writeIndex(entries) {
11805
11912
  try {
11806
- fs7.mkdirSync(this.paths().tasks, { recursive: true });
11913
+ fs8.mkdirSync(this.paths().tasks, { recursive: true });
11807
11914
  replaceFile(this.paths().index, JSON.stringify({
11808
11915
  version: INDEX_VERSION,
11809
11916
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -11857,23 +11964,23 @@ class TaskStore {
11857
11964
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
11858
11965
  }
11859
11966
  v2Dirs() {
11860
- return [...new Set([this.root, this.origin])].map((dir) => path6.join(dir, ".workit"));
11967
+ return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
11861
11968
  }
11862
11969
  pendingMigrations() {
11863
11970
  return this.v2Dirs().filter((dir) => {
11864
- const raw = this.readV2Workspace(path6.join(dir, "workspace.json"));
11971
+ const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
11865
11972
  return raw !== null && raw !== TOMBSTONE;
11866
11973
  });
11867
11974
  }
11868
11975
  migrateV2(v2) {
11869
11976
  const dirs = this.paths();
11870
- const v2Workspace = path6.join(v2, "workspace.json");
11871
- const v2Tasks = path6.join(v2, "tasks");
11872
- const legacy = path6.join(dirs.dir, "legacy", checkoutSlug(path6.dirname(v2)), "v2");
11977
+ const v2Workspace = path8.join(v2, "workspace.json");
11978
+ const v2Tasks = path8.join(v2, "tasks");
11979
+ const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
11873
11980
  const raw = this.readV2Workspace(v2Workspace);
11874
11981
  if (raw === null || raw === TOMBSTONE)
11875
11982
  return success(null, null, null);
11876
- return this.withLock(dirs.checkoutLock, () => this.withLock(path6.join(v2, "metadata.lock"), () => {
11983
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
11877
11984
  const workspace = this.readV2Workspace(v2Workspace);
11878
11985
  if (workspace === null || workspace === TOMBSTONE)
11879
11986
  return success(null, null, null);
@@ -11882,7 +11989,7 @@ class TaskStore {
11882
11989
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
11883
11990
  let files = [];
11884
11991
  try {
11885
- files = fs7.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path6.join(v2Tasks, name)));
11992
+ files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
11886
11993
  } catch {}
11887
11994
  const current = this.readWorkspace();
11888
11995
  if (!current.ok)
@@ -11900,22 +12007,22 @@ class TaskStore {
11900
12007
  let skipped = 0;
11901
12008
  const done = new Map;
11902
12009
  try {
11903
- fs7.mkdirSync(path6.join(legacy, "tasks"), { recursive: true });
12010
+ fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
11904
12011
  for (const name of ["workspace.json", "index.json"])
11905
- if (fs7.existsSync(path6.join(v2, name)))
11906
- fs7.copyFileSync(path6.join(v2, name), path6.join(legacy, name));
12012
+ if (fs8.existsSync(path8.join(v2, name)))
12013
+ fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
11907
12014
  for (const name of files) {
11908
12015
  const id = name.slice(0, -5);
11909
- const file = path6.join(v2Tasks, name);
11910
- const bytes = fs7.readFileSync(file, "utf8");
12016
+ const file = path8.join(v2Tasks, name);
12017
+ const bytes = fs8.readFileSync(file, "utf8");
11911
12018
  const digest = sha256(bytes);
11912
- fs7.writeFileSync(path6.join(legacy, "tasks", name), bytes);
12019
+ fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
11913
12020
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
11914
12021
  if (!parsed.ok || parsed.data.id !== id)
11915
12022
  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 });
11916
12023
  const record = { ...parsed.data, workspaceId: target.id };
11917
- const origin = { path: path6.join(legacy, "tasks", name), digest };
11918
- if (!fs7.existsSync(this.eventsPath(id))) {
12024
+ const origin = { path: path8.join(legacy, "tasks", name), digest };
12025
+ if (!fs8.existsSync(this.eventsPath(id))) {
11919
12026
  this.openTask(record, "migrated.from_v2", null, origin);
11920
12027
  migrated += 1;
11921
12028
  } else {
@@ -11950,8 +12057,8 @@ class TaskStore {
11950
12057
  try {
11951
12058
  let complete = true;
11952
12059
  for (const [name, digest] of done) {
11953
- const file = path6.join(v2Tasks, name);
11954
- if (sha256(fs7.readFileSync(file, "utf8")) !== digest) {
12060
+ const file = path8.join(v2Tasks, name);
12061
+ if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
11955
12062
  complete = false;
11956
12063
  continue;
11957
12064
  }
@@ -11961,7 +12068,7 @@ class TaskStore {
11961
12068
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
11962
12069
  path: v2
11963
12070
  });
11964
- fs7.rmSync(path6.join(v2, "index.json"), { force: true });
12071
+ fs8.rmSync(path8.join(v2, "index.json"), { force: true });
11965
12072
  this.writeTombstone(v2, target.id);
11966
12073
  } catch (error) {
11967
12074
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -11981,45 +12088,45 @@ class TaskStore {
11981
12088
  }
11982
12089
  adoptLocalStore() {
11983
12090
  const dirs = this.paths();
11984
- const marker = path6.join(dirs.v2, "store.json");
11985
- if (!dirs.shared || !fs7.existsSync(marker))
12091
+ const marker = path8.join(dirs.v2, "store.json");
12092
+ if (!dirs.shared || !fs8.existsSync(marker))
11986
12093
  return success(null, null, null);
11987
12094
  return this.withLock(dirs.checkoutLock, () => {
11988
- if (!fs7.existsSync(marker))
12095
+ if (!fs8.existsSync(marker))
11989
12096
  return success(null, null, null);
11990
12097
  let tasks = 0;
11991
12098
  try {
11992
12099
  const merge = (from, to) => {
11993
- if (!fs7.existsSync(to)) {
11994
- fs7.mkdirSync(path6.dirname(to), { recursive: true });
12100
+ if (!fs8.existsSync(to)) {
12101
+ fs8.mkdirSync(path8.dirname(to), { recursive: true });
11995
12102
  try {
11996
- fs7.renameSync(from, to);
12103
+ fs8.renameSync(from, to);
11997
12104
  } catch {
11998
- fs7.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
11999
- fs7.rmSync(from, { recursive: true, force: true });
12105
+ fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
12106
+ fs8.rmSync(from, { recursive: true, force: true });
12000
12107
  }
12001
12108
  return;
12002
12109
  }
12003
- if (fs7.lstatSync(from).isDirectory() && fs7.lstatSync(to).isDirectory())
12004
- for (const name of fs7.readdirSync(from))
12005
- merge(path6.join(from, name), path6.join(to, name));
12110
+ if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
12111
+ for (const name of fs8.readdirSync(from))
12112
+ merge(path8.join(from, name), path8.join(to, name));
12006
12113
  };
12007
12114
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
12008
- const from = path6.join(dirs.v2, sub);
12009
- if (!fs7.existsSync(from))
12115
+ const from = path8.join(dirs.v2, sub);
12116
+ if (!fs8.existsSync(from))
12010
12117
  continue;
12011
12118
  if (sub === "tasks") {
12012
- for (const name of fs7.readdirSync(from))
12013
- if (validId(name) && !fs7.existsSync(path6.join(dirs.dir, sub, name)))
12119
+ for (const name of fs8.readdirSync(from))
12120
+ if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
12014
12121
  tasks += 1;
12015
12122
  }
12016
- merge(from, path6.join(dirs.dir, sub));
12123
+ merge(from, path8.join(dirs.dir, sub));
12017
12124
  }
12018
- const ledger = path6.join(dirs.v2, "ledger");
12019
- if (fs7.existsSync(ledger) && !fs7.existsSync(path6.join(dirs.dir, "ledger")))
12020
- merge(ledger, path6.join(dirs.dir, "ledger"));
12021
- fs7.rmSync(path6.join(dirs.v2, "task-index.json"), { force: true });
12022
- fs7.renameSync(marker, path6.join(dirs.v2, "store.moved.json"));
12125
+ const ledger = path8.join(dirs.v2, "ledger");
12126
+ if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
12127
+ merge(ledger, path8.join(dirs.dir, "ledger"));
12128
+ fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
12129
+ fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
12023
12130
  } catch (error) {
12024
12131
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
12025
12132
  path: dirs.v2
@@ -12039,7 +12146,7 @@ class TaskStore {
12039
12146
  readV2Workspace(file) {
12040
12147
  let bytes;
12041
12148
  try {
12042
- bytes = fs7.readFileSync(file, "utf8");
12149
+ bytes = fs8.readFileSync(file, "utf8");
12043
12150
  } catch {
12044
12151
  return null;
12045
12152
  }
@@ -12057,7 +12164,7 @@ class TaskStore {
12057
12164
  schemaVersion: SCHEMA_VERSION,
12058
12165
  id: workspaceId ?? newId(),
12059
12166
  revision: newRevision(),
12060
- root: path6.dirname(v2),
12167
+ root: path8.dirname(v2),
12061
12168
  runtime: { createdWith: version, updatedWith: version },
12062
12169
  writer: null,
12063
12170
  store: {
@@ -12068,12 +12175,12 @@ class TaskStore {
12068
12175
  },
12069
12176
  critical: ["store"]
12070
12177
  };
12071
- fs7.mkdirSync(v2, { recursive: true });
12072
- const ignore = path6.join(v2, ".gitignore");
12073
- if (!fs7.existsSync(ignore))
12074
- fs7.writeFileSync(ignore, `*
12178
+ fs8.mkdirSync(v2, { recursive: true });
12179
+ const ignore = path8.join(v2, ".gitignore");
12180
+ if (!fs8.existsSync(ignore))
12181
+ fs8.writeFileSync(ignore, `*
12075
12182
  `);
12076
- replaceFile(path6.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
12183
+ replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
12077
12184
  `, true);
12078
12185
  }
12079
12186
  stubFor(taskId) {
@@ -12089,7 +12196,7 @@ class TaskStore {
12089
12196
  }
12090
12197
  isStub(file) {
12091
12198
  try {
12092
- const value = JSON.parse(fs7.readFileSync(file, "utf8"));
12199
+ const value = JSON.parse(fs8.readFileSync(file, "utf8"));
12093
12200
  return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
12094
12201
  } catch {
12095
12202
  return false;
@@ -12097,32 +12204,32 @@ class TaskStore {
12097
12204
  }
12098
12205
  writeTombstoneIfMissing() {
12099
12206
  try {
12100
- const v2 = path6.join(this.root, ".workit");
12101
- if (fs7.existsSync(path6.join(v2, "workspace.json")))
12207
+ const v2 = path8.join(this.root, ".workit");
12208
+ if (fs8.existsSync(path8.join(v2, "workspace.json")))
12102
12209
  return;
12103
12210
  let id = null;
12104
12211
  try {
12105
- id = JSON.parse(fs7.readFileSync(this.paths().workspace, "utf8")).id ?? null;
12212
+ id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
12106
12213
  } catch {}
12107
12214
  this.writeTombstone(v2, id);
12108
12215
  } catch {}
12109
12216
  }
12110
12217
  initializeStorage() {
12111
12218
  const dirs = this.paths();
12112
- retryTransient(() => fs7.mkdirSync(dirs.checkout, { recursive: true }));
12113
- retryTransient(() => fs7.mkdirSync(dirs.tasks, { recursive: true }));
12114
- if (!fs7.existsSync(dirs.marker))
12219
+ retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
12220
+ retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
12221
+ if (!fs8.existsSync(dirs.marker))
12115
12222
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
12116
12223
  `, false);
12117
12224
  if (!dirs.shared) {
12118
- const ignore = path6.join(dirs.dir, ".gitignore");
12225
+ const ignore = path8.join(dirs.dir, ".gitignore");
12119
12226
  let current = null;
12120
12227
  try {
12121
- current = fs7.readFileSync(ignore, "utf8");
12228
+ current = fs8.readFileSync(ignore, "utf8");
12122
12229
  } catch {}
12123
12230
  if (current !== `*
12124
12231
  `)
12125
- retryTransient(() => fs7.writeFileSync(ignore, `*
12232
+ retryTransient(() => fs8.writeFileSync(ignore, `*
12126
12233
  `));
12127
12234
  }
12128
12235
  this.writeTombstoneIfMissing();
@@ -12138,7 +12245,7 @@ class TaskStore {
12138
12245
  shouldReclaim: ({ payload, nowMs }) => {
12139
12246
  let ageMs = null;
12140
12247
  try {
12141
- ageMs = nowMs - fs7.lstatSync(lockPath).mtimeMs;
12248
+ ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
12142
12249
  } catch {}
12143
12250
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
12144
12251
  },
@@ -12175,7 +12282,7 @@ class TaskStore {
12175
12282
  withLock(lockPath, operation) {
12176
12283
  try {
12177
12284
  this.initializeStorage();
12178
- fs7.mkdirSync(path6.dirname(lockPath), { recursive: true });
12285
+ fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
12179
12286
  } catch (error) {
12180
12287
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
12181
12288
  path: this.paths().dir
@@ -12215,18 +12322,18 @@ class TaskStore {
12215
12322
  lockDirWritable(lockPath) {
12216
12323
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
12217
12324
  try {
12218
- fs7.closeSync(fs7.openSync(probe, "wx", 384));
12325
+ fs8.closeSync(fs8.openSync(probe, "wx", 384));
12219
12326
  } catch {
12220
12327
  return false;
12221
12328
  }
12222
12329
  try {
12223
- fs7.rmSync(probe, { force: true });
12330
+ fs8.rmSync(probe, { force: true });
12224
12331
  } catch {}
12225
12332
  return true;
12226
12333
  }
12227
12334
  lockHolderPresent(lockPath) {
12228
12335
  try {
12229
- fs7.lstatSync(lockPath);
12336
+ fs8.lstatSync(lockPath);
12230
12337
  return true;
12231
12338
  } catch (error) {
12232
12339
  return error?.code !== "ENOENT";
@@ -12238,12 +12345,12 @@ class TaskStore {
12238
12345
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
12239
12346
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
12240
12347
  path: lockPath,
12241
- guidance: `Check permissions and the read-only attribute on ${path6.dirname(lockPath)}.`
12348
+ guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
12242
12349
  });
12243
12350
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
12244
12351
  let lock = null;
12245
12352
  try {
12246
- lock = parseMetadataLockOrNull(fs7.readFileSync(lockPath, "utf8"));
12353
+ lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
12247
12354
  } catch {}
12248
12355
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
12249
12356
  outcome: "not_started",
@@ -12261,15 +12368,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
12261
12368
  var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
12262
12369
 
12263
12370
  // packages/workit-core/src/core/task-evaluation.ts
12264
- import { createHash as createHash9 } from "node:crypto";
12265
- import * as fs12 from "node:fs";
12371
+ import { createHash as createHash10 } from "node:crypto";
12372
+ import * as fs13 from "node:fs";
12266
12373
  import { spawnSync as spawnSync10 } from "node:child_process";
12267
- import path14 from "node:path";
12374
+ import path16 from "node:path";
12268
12375
 
12269
12376
  // packages/workit-core/src/check-config.ts
12270
12377
  import { spawnSync as spawnSync3 } from "node:child_process";
12271
- import fs8 from "node:fs";
12272
- import path7 from "node:path";
12378
+ import fs9 from "node:fs";
12379
+ import path9 from "node:path";
12273
12380
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12274
12381
  var CHECKS_FILE = "workit.checks.json";
12275
12382
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -12322,10 +12429,10 @@ var git = (cwd, args) => {
12322
12429
  return value || null;
12323
12430
  };
12324
12431
  function checkRoot(cwd) {
12325
- return path7.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12432
+ return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12326
12433
  }
12327
12434
  var packageManager = (root) => {
12328
- const has = (file) => fs8.existsSync(path7.join(root, file));
12435
+ const has = (file) => fs9.existsSync(path9.join(root, file));
12329
12436
  if (has("bun.lock") || has("bun.lockb"))
12330
12437
  return "bun";
12331
12438
  if (has("pnpm-lock.yaml"))
@@ -12345,7 +12452,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
12345
12452
  function detectedChecks(root) {
12346
12453
  const read = (file) => {
12347
12454
  try {
12348
- return fs8.readFileSync(path7.join(root, file), "utf8");
12455
+ return fs9.readFileSync(path9.join(root, file), "utf8");
12349
12456
  } catch {
12350
12457
  return null;
12351
12458
  }
@@ -12384,7 +12491,7 @@ function detectedChecks(root) {
12384
12491
  var GATES = ["testing", "verification"];
12385
12492
  function loadCheckConfig(cwd) {
12386
12493
  const root = checkRoot(cwd);
12387
- const file = path7.join(root, CHECKS_FILE);
12494
+ const file = path9.join(root, CHECKS_FILE);
12388
12495
  const none = (error = null) => ({
12389
12496
  source: error ? CHECKS_FILE : "none",
12390
12497
  root,
@@ -12394,7 +12501,7 @@ function loadCheckConfig(cwd) {
12394
12501
  });
12395
12502
  let text = null;
12396
12503
  try {
12397
- text = fs8.readFileSync(file, "utf8");
12504
+ text = fs9.readFileSync(file, "utf8");
12398
12505
  } catch (error) {
12399
12506
  if (error.code !== "ENOENT")
12400
12507
  return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
@@ -12457,10 +12564,10 @@ function workitAvailable(env = process.env) {
12457
12564
  return workitOnPath;
12458
12565
  const windows = process.platform === "win32";
12459
12566
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
12460
- const dirs = (env.PATH ?? env.Path ?? "").split(path7.delimiter).filter(Boolean);
12567
+ const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
12461
12568
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
12462
12569
  try {
12463
- return fs8.statSync(path7.join(dir, `workit${ext}`)).isFile();
12570
+ return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
12464
12571
  } catch {
12465
12572
  return false;
12466
12573
  }
@@ -12473,10 +12580,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
12473
12580
 
12474
12581
  // packages/workit-core/src/git/rev.ts
12475
12582
  import { spawnSync as spawnSync4 } from "node:child_process";
12476
- import { createHash as createHash5 } from "node:crypto";
12477
- import fs9 from "node:fs";
12583
+ import { createHash as createHash6 } from "node:crypto";
12584
+ import fs10 from "node:fs";
12478
12585
  import os2 from "node:os";
12479
- import path8 from "node:path";
12586
+ import path10 from "node:path";
12480
12587
  var MAX_BUFFER = 256 * 1024 * 1024;
12481
12588
  var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
12482
12589
  var git2 = (cwd, args, options = {}) => {
@@ -12511,10 +12618,10 @@ function headSha(cwd) {
12511
12618
  var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
12512
12619
  var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
12513
12620
  var seedIndex = (from, to) => {
12514
- fs9.copyFileSync(from, to);
12515
- const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
12621
+ fs10.copyFileSync(from, to);
12622
+ const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
12516
12623
  const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
12517
- fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
12624
+ fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
12518
12625
  };
12519
12626
  function worktreeTree(cwd, options = {}) {
12520
12627
  const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
@@ -12523,10 +12630,10 @@ function worktreeTree(cwd, options = {}) {
12523
12630
  return null;
12524
12631
  const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
12525
12632
  const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
12526
- const scratch = fs9.mkdtempSync(path8.join(os2.tmpdir(), "workit-tree-"));
12633
+ const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
12527
12634
  try {
12528
- const tempIndex = path8.join(scratch, "index");
12529
- if (fs9.existsSync(indexPath))
12635
+ const tempIndex = path10.join(scratch, "index");
12636
+ if (fs10.existsSync(indexPath))
12530
12637
  seedIndex(indexPath, tempIndex);
12531
12638
  const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
12532
12639
  const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
@@ -12538,7 +12645,7 @@ function worktreeTree(cwd, options = {}) {
12538
12645
  const skipped = [];
12539
12646
  for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
12540
12647
  try {
12541
- const stat = fs9.statSync(path8.join(top, file));
12648
+ const stat = fs10.statSync(path10.join(top, file));
12542
12649
  if (stat.isFile() && stat.size > cap)
12543
12650
  skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
12544
12651
  } catch {}
@@ -12555,10 +12662,10 @@ function worktreeTree(cwd, options = {}) {
12555
12662
  if (!tree)
12556
12663
  return null;
12557
12664
  const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
12558
- const key = skipped.length ? `sha256:${createHash5("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
12665
+ const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
12559
12666
  return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
12560
12667
  } finally {
12561
- fs9.rmSync(scratch, { recursive: true, force: true });
12668
+ fs10.rmSync(scratch, { recursive: true, force: true });
12562
12669
  }
12563
12670
  }
12564
12671
  function worktreeSignal(cwd, options = {}) {
@@ -12578,16 +12685,16 @@ function worktreeSignal(cwd, options = {}) {
12578
12685
  });
12579
12686
  if (!status.ok)
12580
12687
  return null;
12581
- const hash = createHash5("sha256");
12688
+ const hash = createHash6("sha256");
12582
12689
  hash.update(`${headSha(top) ?? "unborn"}\x00`);
12583
12690
  for (const entry of status.stdout.split("\x00")) {
12584
12691
  if (entry.length < 4)
12585
12692
  continue;
12586
12693
  const file = entry.slice(3);
12587
- const absolute = path8.join(top, file);
12694
+ const absolute = path10.join(top, file);
12588
12695
  let stat = "-";
12589
12696
  try {
12590
- const info = fs9.lstatSync(absolute, { bigint: true });
12697
+ const info = fs10.lstatSync(absolute, { bigint: true });
12591
12698
  stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
12592
12699
  if (info.isDirectory())
12593
12700
  stat += `:${headSha(absolute) ?? "-"}`;
@@ -12654,17 +12761,17 @@ function pushRemoteName(cwd, branch) {
12654
12761
 
12655
12762
  // packages/workit-core/src/core/policy/derive.ts
12656
12763
  import { existsSync as existsSync4 } from "node:fs";
12657
- import path13 from "node:path";
12764
+ import path15 from "node:path";
12658
12765
 
12659
12766
  // packages/workit-core/src/ledger.ts
12660
12767
  import { spawnSync as spawnSync9 } from "node:child_process";
12661
- import { createHash as createHash8, randomBytes as randomBytes2 } from "node:crypto";
12662
- import fs11 from "node:fs";
12663
- import path12 from "node:path";
12768
+ import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
12769
+ import fs12 from "node:fs";
12770
+ import path14 from "node:path";
12664
12771
 
12665
12772
  // packages/workit-core/src/core/vcs-config.ts
12666
- import fs10 from "node:fs";
12667
- import path11 from "node:path";
12773
+ import fs11 from "node:fs";
12774
+ import path13 from "node:path";
12668
12775
  import { spawnSync as spawnSync8 } from "node:child_process";
12669
12776
 
12670
12777
  // packages/workit-core/src/core/config.ts
@@ -12678,7 +12785,7 @@ import {
12678
12785
  writeFileSync as writeFileSync2
12679
12786
  } from "node:fs";
12680
12787
  import os3 from "node:os";
12681
- import path9 from "node:path";
12788
+ import path11 from "node:path";
12682
12789
 
12683
12790
  // packages/workit-core/src/core/boundary.ts
12684
12791
  import { statSync as statSync2 } from "node:fs";
@@ -12737,7 +12844,7 @@ var mergePreset = (preset, input = {}, current = {
12737
12844
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
12738
12845
  };
12739
12846
  };
12740
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path9.join(process.env.XDG_CONFIG_HOME || path9.join(os3.homedir(), ".config"), "workit");
12847
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workit");
12741
12848
  var migratedDir = null;
12742
12849
  var migrationFailed = false;
12743
12850
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -12747,7 +12854,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12747
12854
  migratedDir = dir;
12748
12855
  return dir;
12749
12856
  }
12750
- const legacy = path9.join(process.env.XDG_CONFIG_HOME || path9.join(os3.homedir(), ".config"), "workflow-toolkit");
12857
+ const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
12751
12858
  if (!existsSync3(legacy)) {
12752
12859
  migratedDir = dir;
12753
12860
  return dir;
@@ -12760,8 +12867,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12760
12867
  mkdirSync2(dir, { recursive: true });
12761
12868
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
12762
12869
  for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
12763
- const src = path9.join(legacy, entry.name);
12764
- const dest = path9.join(dir, entry.name);
12870
+ const src = path11.join(legacy, entry.name);
12871
+ const dest = path11.join(dir, entry.name);
12765
12872
  if (existsSync3(dest))
12766
12873
  continue;
12767
12874
  try {
@@ -12833,7 +12940,7 @@ var parseConfigResult = (raw, file) => {
12833
12940
  return { status: "valid", path: file, config };
12834
12941
  };
12835
12942
  var readConfigTyped = (dir) => {
12836
- const file = path9.join(dir ?? configDir(), "config.json");
12943
+ const file = path11.join(dir ?? configDir(), "config.json");
12837
12944
  return parseConfigResult(readSafe(file), file);
12838
12945
  };
12839
12946
  var readConfig = () => {
@@ -12851,10 +12958,10 @@ var mergeConfigValues = (input, current) => ({
12851
12958
  var writeConfig = (config) => {
12852
12959
  const dir = configDir();
12853
12960
  mkdirSync2(dir, { recursive: true });
12854
- writeFileSync2(path9.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
12961
+ writeFileSync2(path11.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
12855
12962
  `, "utf8");
12856
12963
  };
12857
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
12964
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
12858
12965
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
12859
12966
  var resolveBranchPolicy = (config, workspace, repoPreset) => {
12860
12967
  const wp = workspace?.branchPolicy ?? {};
@@ -12896,9 +13003,9 @@ var resolveCommitPolicy = (config, workspace) => {
12896
13003
 
12897
13004
  // packages/workit-core/src/core/workspaces.ts
12898
13005
  import { spawnSync as spawnSync6 } from "node:child_process";
12899
- import { createHash as createHash6 } from "node:crypto";
13006
+ import { createHash as createHash7 } from "node:crypto";
12900
13007
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
12901
- import path10 from "node:path";
13008
+ import path12 from "node:path";
12902
13009
 
12903
13010
  // packages/workit-core/src/core/branch-policy.ts
12904
13011
  import { spawnSync as spawnSync5 } from "node:child_process";
@@ -13033,7 +13140,7 @@ var releaseTrackSchema = object({
13033
13140
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
13034
13141
  }
13035
13142
  }
13036
- 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(".."))) {
13143
+ if (track.versionSource?.kind === "package-json" && (path12.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
13037
13144
  context.addIssue({
13038
13145
  code: "custom",
13039
13146
  path: ["versionSource", "path"],
@@ -13054,6 +13161,8 @@ var workspaceConfigSchema = object({
13054
13161
  defaultEndpoint: unknown().optional(),
13055
13162
  verification: _enum(["self", "independent"]).optional(),
13056
13163
  trailers: boolean2().optional(),
13164
+ liveSessionGuard: boolean2().optional(),
13165
+ fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
13057
13166
  autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
13058
13167
  kind,
13059
13168
  union([boolean2(), literal("verified")]).optional()
@@ -13094,7 +13203,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
13094
13203
  names.add(workspace.name);
13095
13204
  }
13096
13205
  });
13097
- var workspacesRevision = (contents) => createHash6("sha256").update(contents ?? "<missing>").digest("hex");
13206
+ var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
13098
13207
  var validateWorkspacesDocument = (parsed, file) => {
13099
13208
  if (!isConfigObject(parsed)) {
13100
13209
  return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
@@ -13132,7 +13241,7 @@ var validateWorkspacesDocument = (parsed, file) => {
13132
13241
  };
13133
13242
  };
13134
13243
  var readWorkspacesResult = (dir = configDir()) => {
13135
- const file = path10.join(dir, "workspaces.json");
13244
+ const file = path12.join(dir, "workspaces.json");
13136
13245
  let raw;
13137
13246
  try {
13138
13247
  raw = readFileSync3(file, "utf8");
@@ -13243,9 +13352,9 @@ var mainCheckoutOf = (cwd) => {
13243
13352
  let main = null;
13244
13353
  const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
13245
13354
  const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
13246
- if (gitDir && common && path10.resolve(gitDir) !== path10.resolve(common)) {
13355
+ if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
13247
13356
  const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
13248
- main = listed?.trim() || (path10.basename(common) === ".git" ? path10.dirname(common) : common);
13357
+ main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
13249
13358
  }
13250
13359
  mainCheckouts.set(cwd, main);
13251
13360
  return main;
@@ -13336,7 +13445,7 @@ var runtimeWorkspaceIndexSchema = object({
13336
13445
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
13337
13446
  }).passthrough();
13338
13447
  var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
13339
- const file = path10.join(dir, "workspaces.json");
13448
+ const file = path12.join(dir, "workspaces.json");
13340
13449
  let raw;
13341
13450
  try {
13342
13451
  raw = readFileSync3(file, "utf8");
@@ -13371,6 +13480,31 @@ var resolveRuntimeReleaseTracks = (cwd) => {
13371
13480
  const matched = matchRuntimeWorkspace(cwd);
13372
13481
  return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
13373
13482
  };
13483
+ var inheritedSetting = (cwd, dir, has) => {
13484
+ let matched;
13485
+ try {
13486
+ matched = matchRuntimeWorkspace(cwd, dir);
13487
+ } catch {
13488
+ return null;
13489
+ }
13490
+ if (!matched)
13491
+ return null;
13492
+ const { selected, matches } = matched;
13493
+ const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
13494
+ return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
13495
+ };
13496
+ var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
13497
+ var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
13498
+ var TIER_HOSTS = new Set([
13499
+ "claude_code",
13500
+ "opencode",
13501
+ "codex",
13502
+ "codex_cli",
13503
+ "codex_desktop",
13504
+ "cursor",
13505
+ "pi",
13506
+ "default"
13507
+ ]);
13374
13508
  var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
13375
13509
  const matched = matchRuntimeWorkspace(cwd);
13376
13510
  if (!matched)
@@ -13411,7 +13545,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
13411
13545
  }
13412
13546
 
13413
13547
  // packages/workit-core/src/core/release-tracks.ts
13414
- import { createHash as createHash7 } from "node:crypto";
13548
+ import { createHash as createHash8 } from "node:crypto";
13415
13549
  import { spawnSync as spawnSync7 } from "node:child_process";
13416
13550
  class ReleaseTrackError extends Error {
13417
13551
  }
@@ -13794,7 +13928,7 @@ function resolveTrackFor(cwd, options) {
13794
13928
  ]) ?? "",
13795
13929
  git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
13796
13930
  ] : [];
13797
- key = createHash7("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13931
+ key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13798
13932
  const hit = memo2.get(key);
13799
13933
  if (hit)
13800
13934
  return hit;
@@ -13809,7 +13943,7 @@ function resolveTrackFor(cwd, options) {
13809
13943
  }
13810
13944
 
13811
13945
  // packages/workit-core/src/core/vcs-config.ts
13812
- var workspacesPath = () => path11.join(configDir(), "workspaces.json");
13946
+ var workspacesPath = () => path13.join(configDir(), "workspaces.json");
13813
13947
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
13814
13948
  var remoteProvider = (cwd) => {
13815
13949
  const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
@@ -13899,7 +14033,7 @@ var readVcsConfig = () => {
13899
14033
  const file = vcsConfigPath();
13900
14034
  let raw;
13901
14035
  try {
13902
- raw = fs10.readFileSync(file, "utf8");
14036
+ raw = fs11.readFileSync(file, "utf8");
13903
14037
  } catch {
13904
14038
  return { status: "missing", path: file, config: {} };
13905
14039
  }
@@ -13984,7 +14118,7 @@ function vcsConfig(mode, cwd, options = {}) {
13984
14118
  const prov = cfg[provider] ?? {};
13985
14119
  const out = {
13986
14120
  ok: true,
13987
- configPath: path11.resolve(cfgPath),
14121
+ configPath: path13.resolve(cfgPath),
13988
14122
  provider,
13989
14123
  defaultTargetBranch: workspaceDefault,
13990
14124
  pr: cfg.pr ?? {},
@@ -14195,7 +14329,7 @@ function diffHash(cwd, base, head) {
14195
14329
  ], GIT_TIMEOUTS.worktree);
14196
14330
  if (!diff.ok || !diff.stdout)
14197
14331
  return null;
14198
- const hash = createHash8("sha256");
14332
+ const hash = createHash9("sha256");
14199
14333
  for (const line of diff.stdout.split(`
14200
14334
  `)) {
14201
14335
  if (line.startsWith("index "))
@@ -14207,16 +14341,16 @@ function diffHash(cwd, base, head) {
14207
14341
  return `sha256:${hash.digest("hex")}`;
14208
14342
  }
14209
14343
  function storeRoot(cwd) {
14210
- const location = resolveStore(path12.resolve(cwd));
14344
+ const location = resolveStore(path14.resolve(cwd));
14211
14345
  if (location instanceof Error)
14212
14346
  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");
14213
14347
  return { ok: true, value: { root: location.dir, shared: location.shared } };
14214
14348
  }
14215
14349
  function ledgerPath(cwd) {
14216
14350
  const root = storeRoot(cwd);
14217
- return root.ok ? { ok: true, value: path12.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14351
+ return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14218
14352
  }
14219
- var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
14353
+ var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
14220
14354
  var NETWORK_FS_TYPES = new Set([
14221
14355
  26985,
14222
14356
  20859,
@@ -14234,7 +14368,7 @@ var onNetworkFs = (dir) => {
14234
14368
  if (process.platform !== "linux")
14235
14369
  return false;
14236
14370
  try {
14237
- return isNetworkFsType(fs11.statfsSync(dir).type);
14371
+ return isNetworkFsType(fs12.statfsSync(dir).type);
14238
14372
  } catch {
14239
14373
  return false;
14240
14374
  }
@@ -14244,15 +14378,15 @@ var LOCK_STALE_MS = 30000;
14244
14378
  var LOCK_OWNER = "owner";
14245
14379
  var lockToken = (lock) => {
14246
14380
  try {
14247
- return fs11.readFileSync(path12.join(lock, LOCK_OWNER), "utf8");
14381
+ return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
14248
14382
  } catch {
14249
14383
  return null;
14250
14384
  }
14251
14385
  };
14252
14386
  var lockAgeMs = (lock) => {
14253
- for (const target of [path12.join(lock, LOCK_OWNER), lock]) {
14387
+ for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
14254
14388
  try {
14255
- return Date.now() - fs11.statSync(target).mtimeMs;
14389
+ return Date.now() - fs12.statSync(target).mtimeMs;
14256
14390
  } catch {}
14257
14391
  }
14258
14392
  return null;
@@ -14260,55 +14394,55 @@ var lockAgeMs = (lock) => {
14260
14394
  var ledgerLock = {
14261
14395
  acquire(lock) {
14262
14396
  try {
14263
- fs11.mkdirSync(lock);
14397
+ fs12.mkdirSync(lock);
14264
14398
  } catch (error) {
14265
14399
  if (error.code === "EEXIST")
14266
14400
  return null;
14267
14401
  throw error;
14268
14402
  }
14269
- const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
14270
- fs11.writeFileSync(path12.join(lock, LOCK_OWNER), token);
14403
+ const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
14404
+ fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
14271
14405
  return token;
14272
14406
  },
14273
14407
  release(lock, token) {
14274
14408
  if (lockToken(lock) !== token)
14275
14409
  return false;
14276
- const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
14410
+ const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
14277
14411
  try {
14278
- fs11.renameSync(lock, tomb);
14412
+ fs12.renameSync(lock, tomb);
14279
14413
  } catch {
14280
14414
  return false;
14281
14415
  }
14282
14416
  if (lockToken(tomb) !== token) {
14283
14417
  try {
14284
- fs11.renameSync(tomb, lock);
14418
+ fs12.renameSync(tomb, lock);
14285
14419
  } catch {}
14286
14420
  return false;
14287
14421
  }
14288
- fs11.rmSync(tomb, { recursive: true, force: true });
14422
+ fs12.rmSync(tomb, { recursive: true, force: true });
14289
14423
  return true;
14290
14424
  },
14291
14425
  token: lockToken,
14292
14426
  reap(lock, observed) {
14293
- const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
14427
+ const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
14294
14428
  try {
14295
- fs11.renameSync(lock, tomb);
14429
+ fs12.renameSync(lock, tomb);
14296
14430
  } catch {
14297
14431
  return false;
14298
14432
  }
14299
14433
  if (lockToken(tomb) === observed) {
14300
- fs11.rmSync(tomb, { recursive: true, force: true });
14434
+ fs12.rmSync(tomb, { recursive: true, force: true });
14301
14435
  return true;
14302
14436
  }
14303
14437
  try {
14304
- fs11.renameSync(tomb, lock);
14438
+ fs12.renameSync(tomb, lock);
14305
14439
  } catch {
14306
- fs11.rmSync(tomb, { recursive: true, force: true });
14440
+ fs12.rmSync(tomb, { recursive: true, force: true });
14307
14441
  }
14308
14442
  return false;
14309
14443
  }
14310
14444
  };
14311
- function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
14445
+ function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
14312
14446
  const lock = `${file}.lock`;
14313
14447
  const deadline = Date.now() + waitMs;
14314
14448
  let token = null;
@@ -14330,14 +14464,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
14330
14464
  ledgerLock.release(lock, token);
14331
14465
  }
14332
14466
  }
14333
- var sha2562 = (text) => createHash8("sha256").update(text).digest("hex");
14467
+ var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
14334
14468
  var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
14335
14469
  ${body}`);
14336
14470
  var TAIL_BYTES = 64 * 1024;
14337
14471
  var lastLine = (fd, size) => {
14338
14472
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
14339
14473
  const buffer = Buffer.alloc(span);
14340
- fs11.readSync(fd, buffer, 0, span, size - span);
14474
+ fs12.readSync(fd, buffer, 0, span, size - span);
14341
14475
  const end = buffer.lastIndexOf(10);
14342
14476
  if (end < 0) {
14343
14477
  if (span === size)
@@ -14366,15 +14500,23 @@ var lastLine = (fd, size) => {
14366
14500
  }
14367
14501
  return null;
14368
14502
  };
14503
+ var tighten = (fd) => {
14504
+ try {
14505
+ if ((fs12.fstatSync(fd).mode & 63) !== 0)
14506
+ fs12.fchmodSync(fd, 384);
14507
+ } catch {}
14508
+ };
14369
14509
  function appendLine(file, build, options) {
14370
14510
  const write = () => {
14371
- const fd = fs11.openSync(file, "a+");
14511
+ const fd = fs12.openSync(file, "a+", 384);
14372
14512
  try {
14373
- const size = fs11.fstatSync(fd).size;
14513
+ if (process.platform !== "win32")
14514
+ tighten(fd);
14515
+ const size = fs12.fstatSync(fd).size;
14374
14516
  let fence = "";
14375
14517
  if (size > 0) {
14376
14518
  const last = Buffer.alloc(1);
14377
- fs11.readSync(fd, last, 0, 1, size - 1);
14519
+ fs12.readSync(fd, last, 0, 1, size - 1);
14378
14520
  if (last[0] !== 10)
14379
14521
  fence = `
14380
14522
  `;
@@ -14383,17 +14525,17 @@ function appendLine(file, build, options) {
14383
14525
  if (!line.ok)
14384
14526
  return line;
14385
14527
  const bytes = Buffer.from(fence + line.value);
14386
- const written = fs11.writeSync(fd, bytes);
14528
+ const written = fs12.writeSync(fd, bytes);
14387
14529
  if (written !== bytes.length)
14388
14530
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
14389
14531
  return { ok: true, value: undefined };
14390
14532
  } finally {
14391
- fs11.closeSync(fd);
14533
+ fs12.closeSync(fd);
14392
14534
  }
14393
14535
  };
14394
14536
  try {
14395
- fs11.mkdirSync(path12.dirname(file), { recursive: true });
14396
- if (!options.forceLock && !onNetworkFs(path12.dirname(file)))
14537
+ fs12.mkdirSync(path14.dirname(file), { recursive: true });
14538
+ if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
14397
14539
  return write();
14398
14540
  const locked = withLedgerLock(file, write);
14399
14541
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -14464,8 +14606,10 @@ function readLedger(cwd, options = {}) {
14464
14606
  if (!file.ok)
14465
14607
  return file;
14466
14608
  let text = "";
14609
+ let stat = null;
14467
14610
  try {
14468
- text = fs11.readFileSync(file.value, "utf8");
14611
+ stat = fs12.statSync(file.value);
14612
+ text = fs12.readFileSync(file.value, "utf8");
14469
14613
  } catch (error) {
14470
14614
  if (error.code === "ENOENT")
14471
14615
  return {
@@ -14484,6 +14628,7 @@ function readLedger(cwd, options = {}) {
14484
14628
  const integrity = { chained: 0, legacy: 0, unverified: [] };
14485
14629
  const earlier = new Set([null]);
14486
14630
  let chainStarted = false;
14631
+ const ids = new Set;
14487
14632
  for (const raw of text.split(`
14488
14633
  `)) {
14489
14634
  const trimmed = raw.trim();
@@ -14517,6 +14662,10 @@ function readLedger(cwd, options = {}) {
14517
14662
  reason = "unsigned";
14518
14663
  else
14519
14664
  rowIntegrity = "legacy";
14665
+ if (options.integrity && !reason && ids.has(parsed.id))
14666
+ reason = "duplicate_id";
14667
+ if (options.integrity)
14668
+ ids.add(parsed.id);
14520
14669
  if (reason)
14521
14670
  rowIntegrity = "unverified";
14522
14671
  const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
@@ -14540,7 +14689,8 @@ function readLedger(cwd, options = {}) {
14540
14689
  seq: rows.length + 1,
14541
14690
  superseded: false,
14542
14691
  supersedeIgnored: false,
14543
- ...rowIntegrity ? { integrity: rowIntegrity } : {}
14692
+ ...rowIntegrity ? { integrity: rowIntegrity } : {},
14693
+ ...reason ? { integrityReason: reason } : {}
14544
14694
  };
14545
14695
  rows.push(row);
14546
14696
  if (!options.integrity)
@@ -14568,18 +14718,48 @@ function readLedger(cwd, options = {}) {
14568
14718
  const byId = new Map;
14569
14719
  for (const row of rows) {
14570
14720
  const target = row.supersedes ? byId.get(row.supersedes) : undefined;
14571
- if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
14721
+ if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
14572
14722
  target.superseded = true;
14573
14723
  else if (row.supersedes)
14574
14724
  row.supersedeIgnored = true;
14575
14725
  if (!byId.has(row.id))
14576
14726
  byId.set(row.id, row);
14577
14727
  }
14728
+ if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
14729
+ tamperCache.set(file.value, {
14730
+ size: stat.size,
14731
+ stamp: fileStamp(stat),
14732
+ breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
14733
+ });
14578
14734
  return {
14579
14735
  ok: true,
14580
14736
  value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
14581
14737
  };
14582
14738
  }
14739
+ var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
14740
+ var tamperCache = new Map;
14741
+ var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
14742
+ function ledgerTampering(cwd) {
14743
+ const file = ledgerPath(cwd);
14744
+ if (!file.ok)
14745
+ return [];
14746
+ try {
14747
+ const stat = fs12.statSync(file.value);
14748
+ const cached = tamperCache.get(file.value);
14749
+ if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
14750
+ return cached.breaks;
14751
+ } catch {
14752
+ return [];
14753
+ }
14754
+ const read = readLedger(cwd, { integrity: true });
14755
+ return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
14756
+ }
14757
+ function tamperedUnblock(cwd) {
14758
+ const file = ledgerPath(cwd);
14759
+ const where = file.ok ? file.value : "the ledger file";
14760
+ return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
14761
+ }
14762
+ var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
14583
14763
  function verdictBasis(verdict, current) {
14584
14764
  if (verdict.head && verdict.head === current.head)
14585
14765
  return "fresh";
@@ -14587,7 +14767,7 @@ function verdictBasis(verdict, current) {
14587
14767
  return "carried";
14588
14768
  return "stale";
14589
14769
  }
14590
- var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.superseded;
14770
+ var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
14591
14771
  function trailerSessions(cwd, base, head) {
14592
14772
  if (!base || !head || !safeRef(base) || !safeRef(head))
14593
14773
  return [];
@@ -14668,6 +14848,56 @@ var authoredBy = (authors, session) => {
14668
14848
  return true;
14669
14849
  return false;
14670
14850
  };
14851
+ var TRUST_REASONS = new Set([
14852
+ "unsigned",
14853
+ "hash_mismatch",
14854
+ "broken_link",
14855
+ "duplicate_id",
14856
+ "unissued_session",
14857
+ "unbound_session"
14858
+ ]);
14859
+ var SHELL_VOUCHED_HOSTS = new Set([
14860
+ "cli",
14861
+ "opencode",
14862
+ "pi",
14863
+ "codex_cli",
14864
+ "codex_desktop"
14865
+ ]);
14866
+ function sessionTrust(cwd, row) {
14867
+ const session = row.actor.session;
14868
+ if (!session || row.self === true)
14869
+ return null;
14870
+ const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
14871
+ const host = row.actor.host;
14872
+ const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
14873
+ if (!issuedOnly)
14874
+ return null;
14875
+ const binding = issuedBinding(cwd, session);
14876
+ if (binding === null)
14877
+ return "unissued_session";
14878
+ return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
14879
+ }
14880
+ function trustReasons(cwd, row) {
14881
+ if (row.integrity === "unverified")
14882
+ return [row.integrityReason ?? "unsigned"];
14883
+ if (row.integrity !== "verified")
14884
+ return ["unsigned"];
14885
+ const session = sessionTrust(cwd, row);
14886
+ return session ? [session] : [];
14887
+ }
14888
+ function withIntegrity(cwd, rows) {
14889
+ if (rows.every((row) => row.integrity !== undefined))
14890
+ return rows;
14891
+ const read = readLedger(cwd, { integrity: true });
14892
+ const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
14893
+ return rows.map((row) => {
14894
+ if (row.integrity !== undefined)
14895
+ return row;
14896
+ const fresh = bySeq.get(row.seq);
14897
+ return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
14898
+ });
14899
+ }
14900
+ var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
14671
14901
  var independenceReasons = (row, authors) => {
14672
14902
  const reasons = [];
14673
14903
  if (!row.actor.session)
@@ -14678,15 +14908,17 @@ var independenceReasons = (row, authors) => {
14678
14908
  reasons.push("self");
14679
14909
  return reasons;
14680
14910
  };
14681
- function effectiveOf(rows, authors, sameCode, isCurrent) {
14911
+ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
14682
14912
  let effective = null;
14683
14913
  for (const row of rows) {
14684
14914
  if (!effective) {
14685
14915
  effective = row;
14686
14916
  continue;
14687
14917
  }
14688
- const rowIndependent = independenceReasons(row, authors).length === 0;
14689
- const effIndependent = independenceReasons(effective, authors).length === 0;
14918
+ if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
14919
+ continue;
14920
+ const rowIndependent = reasonsOf(row).length === 0;
14921
+ const effIndependent = reasonsOf(effective).length === 0;
14690
14922
  if (effIndependent && !rowIndependent && isCurrent(effective))
14691
14923
  continue;
14692
14924
  if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
@@ -14700,7 +14932,7 @@ function restackCarries(rows, branch, from, to) {
14700
14932
  return false;
14701
14933
  const edges = new Map;
14702
14934
  for (const row of rows)
14703
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
14935
+ if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
14704
14936
  edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
14705
14937
  const seen = new Set([from]);
14706
14938
  const queue = [from];
@@ -14716,7 +14948,8 @@ function restackCarries(rows, branch, from, to) {
14716
14948
  }
14717
14949
  return false;
14718
14950
  }
14719
- function checkVerdicts(cwd, branch, rows) {
14951
+ function checkVerdicts(cwd, branch, ledgerRows) {
14952
+ const rows = withIntegrity(cwd, ledgerRows);
14720
14953
  const head = branchHead(cwd, branch);
14721
14954
  const fallbackBase = defaultBase(cwd);
14722
14955
  const scope = branchScope(cwd, branch);
@@ -14746,21 +14979,47 @@ function checkVerdicts(cwd, branch, rows) {
14746
14979
  return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
14747
14980
  };
14748
14981
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
14982
+ const failureAuthorCache = new Map;
14983
+ const failureAuthors = (row) => {
14984
+ let set = failureAuthorCache.get(row.seq);
14985
+ if (!set) {
14986
+ set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
14987
+ failureAuthorCache.set(row.seq, set);
14988
+ }
14989
+ return set;
14990
+ };
14991
+ const reasonCache = new Map;
14992
+ const reasonsOf = (row) => {
14993
+ let reasons = reasonCache.get(row);
14994
+ if (!reasons) {
14995
+ const failing = FAILING.has(String(row.result));
14996
+ reasons = [
14997
+ ...trustReasons(cwd, row),
14998
+ ...independenceReasons(row, failing ? failureAuthors(row) : authors)
14999
+ ];
15000
+ reasonCache.set(row, reasons);
15001
+ }
15002
+ return reasons;
15003
+ };
15004
+ const retired = new Set;
15005
+ for (const row of rows)
15006
+ if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
15007
+ retired.add(row.supersedes);
14749
15008
  const byKind = new Map;
14750
15009
  for (const row of rows)
14751
- if (isLiveVerdict(row) && scope(row)) {
15010
+ if (isLiveVerdict(row, retired) && scope(row)) {
14752
15011
  const kind = typeof row.kind === "string" ? row.kind : "review";
14753
15012
  byKind.set(kind, [...byKind.get(kind) ?? [], row]);
14754
15013
  }
14755
15014
  const verdicts = [];
14756
15015
  const warnings = [];
14757
15016
  for (const [kind, list] of byKind) {
14758
- const row = effectiveOf(list, authors, sameCode, (candidate) => basisOf(candidate) !== "stale");
15017
+ const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
14759
15018
  if (!row)
14760
15019
  continue;
14761
15020
  const basis = basisOf(row);
14762
15021
  const current = basis !== "stale";
14763
- const independence = independenceReasons(row, authors);
15022
+ const independence = reasonsOf(row);
14764
15023
  const reasons = [
14765
15024
  ...current ? [] : ["stale"],
14766
15025
  ...PASSING.has(String(row.result)) ? [] : ["not_passing"],
@@ -14777,23 +15036,31 @@ function checkVerdicts(cwd, branch, rows) {
14777
15036
  });
14778
15037
  if (reasons.length === 0) {
14779
15038
  for (const later of list)
14780
- if (later.seq > row.seq && independenceReasons(later, authors).length > 0 && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
15039
+ if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
14781
15040
  warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
14782
15041
  }
14783
15042
  }
14784
15043
  for (const row of rows)
14785
15044
  if (row.integrity === "unverified" && scope(row))
14786
15045
  warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
15046
+ const tampered = ledgerTampering(cwd);
15047
+ if (tampered.length) {
15048
+ warnings.push(tamperedWarning(cwd, tampered));
15049
+ for (const entry of verdicts) {
15050
+ entry.accepted = false;
15051
+ entry.reasons = [...entry.reasons, "ledger_tampered"];
15052
+ }
15053
+ }
14787
15054
  verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
14788
15055
  const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
14789
15056
  const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
14790
15057
  const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
14791
- const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
15058
+ const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
14792
15059
  accepted: false,
14793
15060
  verdict: failing?.verdict ?? null,
14794
15061
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
14795
15062
  };
14796
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
15063
+ const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14797
15064
  return {
14798
15065
  branch,
14799
15066
  head,
@@ -14956,7 +15223,7 @@ var productDecision = (task, ledger) => {
14956
15223
  };
14957
15224
  };
14958
15225
  var planRecorded = (task, root) => {
14959
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path13.resolve(root, ref.path)));
15226
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
14960
15227
  if (cited)
14961
15228
  return {
14962
15229
  met: true,
@@ -15015,15 +15282,15 @@ function writeBlockers(task, root) {
15015
15282
  }
15016
15283
 
15017
15284
  // packages/workit-core/src/core/task-evaluation.ts
15018
- var digestBytes = (value) => createHash9("sha256").update(value).digest("hex");
15019
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path14.sep}`);
15020
- var relative = (root, candidate) => path14.relative(root, candidate).split(path14.sep).join("/") || ".";
15285
+ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
15286
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
15287
+ var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
15021
15288
  var canonicalPath = (value) => {
15022
15289
  if (typeof value !== "string" || !value)
15023
15290
  return null;
15024
15291
  if (value.includes("\\") || value.split("/").includes(".."))
15025
15292
  return null;
15026
- const normalized = path14.posix.normalize(value);
15293
+ const normalized = path16.posix.normalize(value);
15027
15294
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
15028
15295
  return null;
15029
15296
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -15071,21 +15338,21 @@ var gitPaths = (root) => {
15071
15338
  git: !/not a git repository/i.test(stderr)
15072
15339
  };
15073
15340
  }
15074
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")) : [];
15341
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
15075
15342
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
15076
15343
  if (stagedDeleted.status !== 0) {
15077
15344
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
15078
15345
  if (!/not a git repository/i.test(stderr))
15079
15346
  return { paths, uncertain: true, git: true };
15080
15347
  } else if (stagedDeleted.stdout) {
15081
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")));
15348
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
15082
15349
  }
15083
15350
  return { paths: [...new Set(paths)], uncertain: false, git: true };
15084
15351
  };
15085
15352
  var walk = (root, directory, output) => {
15086
15353
  let names;
15087
15354
  try {
15088
- names = fs12.readdirSync(directory);
15355
+ names = fs13.readdirSync(directory);
15089
15356
  } catch {
15090
15357
  return true;
15091
15358
  }
@@ -15093,10 +15360,10 @@ var walk = (root, directory, output) => {
15093
15360
  for (const name of names) {
15094
15361
  if (name === ".git" || name === ".workit")
15095
15362
  continue;
15096
- const target = path14.join(directory, name);
15363
+ const target = path16.join(directory, name);
15097
15364
  let stat;
15098
15365
  try {
15099
- stat = fs12.lstatSync(target);
15366
+ stat = fs13.lstatSync(target);
15100
15367
  } catch {
15101
15368
  uncertain = true;
15102
15369
  continue;
@@ -15111,14 +15378,14 @@ var walk = (root, directory, output) => {
15111
15378
  var scopeRoots = (root, scope) => {
15112
15379
  const roots = [];
15113
15380
  for (const item of scope.paths.length ? scope.paths : ["."]) {
15114
- const target = path14.resolve(root, item);
15381
+ const target = path16.resolve(root, item);
15115
15382
  if (!inside(root, target))
15116
15383
  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` }] });
15117
15384
  let ancestor = target;
15118
- while (!fs12.existsSync(ancestor) && ancestor !== root)
15119
- ancestor = path14.dirname(ancestor);
15385
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
15386
+ ancestor = path16.dirname(ancestor);
15120
15387
  try {
15121
- if (!inside(root, fs12.realpathSync(ancestor)))
15388
+ if (!inside(root, fs13.realpathSync(ancestor)))
15122
15389
  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` }] });
15123
15390
  } catch {
15124
15391
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -15130,7 +15397,7 @@ var scopeRoots = (root, scope) => {
15130
15397
  function captureCandidate(root, scope, environment = []) {
15131
15398
  let checkout;
15132
15399
  try {
15133
- checkout = fs12.realpathSync(root);
15400
+ checkout = fs13.realpathSync(root);
15134
15401
  } catch {
15135
15402
  return failure2("invalid_input", "candidate root does not exist");
15136
15403
  }
@@ -15148,11 +15415,11 @@ function captureCandidate(root, scope, environment = []) {
15148
15415
  if (item === ".git" || item === ".workit")
15149
15416
  continue;
15150
15417
  names.add(item);
15151
- if (!fs12.existsSync(target))
15418
+ if (!fs13.existsSync(target))
15152
15419
  continue;
15153
15420
  let stat;
15154
15421
  try {
15155
- stat = fs12.lstatSync(target);
15422
+ stat = fs13.lstatSync(target);
15156
15423
  } catch {
15157
15424
  uncertain = true;
15158
15425
  continue;
@@ -15168,17 +15435,17 @@ function captureCandidate(root, scope, environment = []) {
15168
15435
  for (const item of [...names].toSorted()) {
15169
15436
  if (!scopeMatches(item, normalizedScope))
15170
15437
  continue;
15171
- const target = path14.join(checkout, item);
15438
+ const target = path16.join(checkout, item);
15172
15439
  try {
15173
- const stat = fs12.lstatSync(target);
15440
+ const stat = fs13.lstatSync(target);
15174
15441
  if (stat.isSymbolicLink()) {
15175
- const link = fs12.readlinkSync(target);
15442
+ const link = fs13.readlinkSync(target);
15176
15443
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
15177
15444
  } else if (stat.isFile()) {
15178
15445
  files.push({
15179
15446
  path: item,
15180
15447
  kind: "file",
15181
- digest: digestBytes(fs12.readFileSync(target)),
15448
+ digest: digestBytes(fs13.readFileSync(target)),
15182
15449
  executable: (stat.mode & 73) !== 0
15183
15450
  });
15184
15451
  } else if (!stat.isDirectory()) {
@@ -15343,7 +15610,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
15343
15610
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15344
15611
  let root;
15345
15612
  try {
15346
- root = fs12.realpathSync(checkoutRoot);
15613
+ root = fs13.realpathSync(checkoutRoot);
15347
15614
  } catch {
15348
15615
  return failure2("permission_denied", "checkout root is unavailable");
15349
15616
  }
@@ -15352,23 +15619,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15352
15619
  continue;
15353
15620
  if (!reference.digest)
15354
15621
  return failure2("invalid_input", "document references require a byte digest");
15355
- const target = path14.resolve(root, reference.path);
15356
- if (target !== root && !target.startsWith(`${root}${path14.sep}`))
15622
+ const target = path16.resolve(root, reference.path);
15623
+ if (target !== root && !target.startsWith(`${root}${path16.sep}`))
15357
15624
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15358
15625
  fields: [
15359
15626
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
15360
15627
  ]
15361
15628
  });
15362
15629
  try {
15363
- const below = path14.relative(root, target);
15630
+ const below = path16.relative(root, target);
15364
15631
  let current = root;
15365
- for (const segment of below.split(path14.sep)) {
15632
+ for (const segment of below.split(path16.sep)) {
15366
15633
  if (!segment)
15367
15634
  continue;
15368
- current = path14.join(current, segment);
15369
- const stat = fs12.lstatSync(current);
15370
- const real = fs12.realpathSync(current);
15371
- if (real !== root && !real.startsWith(`${root}${path14.sep}`))
15635
+ current = path16.join(current, segment);
15636
+ const stat = fs13.lstatSync(current);
15637
+ const real = fs13.realpathSync(current);
15638
+ if (real !== root && !real.startsWith(`${root}${path16.sep}`))
15372
15639
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15373
15640
  fields: [
15374
15641
  {
@@ -15384,7 +15651,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15384
15651
  if (current !== target && !stat.isDirectory())
15385
15652
  return failure2("invalid_input", "document reference ancestor is not a directory");
15386
15653
  }
15387
- if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
15654
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
15388
15655
  return failure2("permission_denied", "decision document bytes have changed");
15389
15656
  } catch {
15390
15657
  return failure2("invalid_input", "decision document is unavailable");
@@ -15916,17 +16183,17 @@ var exportDigest = (bundle) => sha256({
15916
16183
  // packages/workit-core/src/core/route-intent.ts
15917
16184
  import { spawnSync as spawnSync12 } from "node:child_process";
15918
16185
  import { existsSync as existsSync6 } from "node:fs";
15919
- import path17 from "node:path";
16186
+ import path19 from "node:path";
15920
16187
 
15921
16188
  // packages/workit-core/src/hooks/raw-git.ts
15922
16189
  import { spawnSync as spawnSync11 } from "node:child_process";
15923
- import { createHash as createHash12 } from "node:crypto";
15924
- import fs14 from "node:fs";
16190
+ import { createHash as createHash13 } from "node:crypto";
16191
+ import fs15 from "node:fs";
15925
16192
  import os4 from "node:os";
15926
- import path16 from "node:path";
16193
+ import path18 from "node:path";
15927
16194
 
15928
16195
  // packages/workit-core/src/core/branch.ts
15929
- import { createHash as createHash10 } from "node:crypto";
16196
+ import { createHash as createHash11 } from "node:crypto";
15930
16197
 
15931
16198
  // packages/workit-core/src/core/git.ts
15932
16199
  import { execFileSync } from "node:child_process";
@@ -16120,7 +16387,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
16120
16387
  const source = resolved.provenance.branchPolicy;
16121
16388
  const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
16122
16389
  const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
16123
- const fingerprint = createHash10("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
16390
+ const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
16124
16391
  return {
16125
16392
  branchPolicy,
16126
16393
  namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
@@ -16174,9 +16441,9 @@ var releaseBranchesOf = (cwd) => {
16174
16441
  var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
16175
16442
 
16176
16443
  // packages/workit-core/src/hooks/session-marker.ts
16177
- import { createHash as createHash11 } from "node:crypto";
16178
- import fs13 from "node:fs";
16179
- import path15 from "node:path";
16444
+ import { createHash as createHash12 } from "node:crypto";
16445
+ import fs14 from "node:fs";
16446
+ import path17 from "node:path";
16180
16447
  var sessionMarkerFile = (input, session, store) => {
16181
16448
  let dir = store;
16182
16449
  if (dir === undefined) {
@@ -16185,13 +16452,13 @@ var sessionMarkerFile = (input, session, store) => {
16185
16452
  return null;
16186
16453
  dir = location.dir;
16187
16454
  }
16188
- const name = createHash11("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16189
- return path15.join(dir, "hooks", `skills-${name}.json`);
16455
+ const name = createHash12("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16456
+ return path17.join(dir, "hooks", `skills-${name}.json`);
16190
16457
  };
16191
16458
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16192
16459
  var readSessionMarker = (file) => {
16193
16460
  try {
16194
- const value = JSON.parse(fs13.readFileSync(file, "utf8"));
16461
+ const value = JSON.parse(fs14.readFileSync(file, "utf8"));
16195
16462
  return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
16196
16463
  } catch {
16197
16464
  return { nudged: [], loaded: [], raw: [] };
@@ -16199,8 +16466,8 @@ var readSessionMarker = (file) => {
16199
16466
  };
16200
16467
  var writeSessionMarker = (file, marker) => {
16201
16468
  try {
16202
- fs13.mkdirSync(path15.dirname(file), { recursive: true });
16203
- fs13.writeFileSync(file, JSON.stringify(marker));
16469
+ fs14.mkdirSync(path17.dirname(file), { recursive: true });
16470
+ fs14.writeFileSync(file, JSON.stringify(marker));
16204
16471
  } catch {}
16205
16472
  };
16206
16473
 
@@ -16697,14 +16964,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
16697
16964
  var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
16698
16965
  var MAX_DEPTH = 4;
16699
16966
  var MAX_EXPANSIONS = 16;
16700
- var isAbsolute = (value) => path16.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16967
+ var isAbsolute = (value) => path18.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16701
16968
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
16702
16969
  var joinDir = (base, raw) => {
16703
16970
  const next = raw.replace(HERE, ".");
16704
16971
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
16705
16972
  return;
16706
- const expanded = next === "~" || next.startsWith("~/") ? path16.join(os4.homedir(), next.slice(1)) : next;
16707
- return base === null || isAbsolute(expanded) ? expanded : path16.join(base, expanded);
16973
+ const expanded = next === "~" || next.startsWith("~/") ? path18.join(os4.homedir(), next.slice(1)) : next;
16974
+ return base === null || isAbsolute(expanded) ? expanded : path18.join(base, expanded);
16708
16975
  };
16709
16976
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
16710
16977
  var cdTarget = (words) => {
@@ -17455,7 +17722,7 @@ var repoAt = (dir) => {
17455
17722
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
17456
17723
  };
17457
17724
  var isWorkitWorkspace = (repo) => {
17458
- if (fs14.existsSync(repo.location.dir))
17725
+ if (fs15.existsSync(repo.location.dir))
17459
17726
  return true;
17460
17727
  try {
17461
17728
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -17476,17 +17743,17 @@ var headSha2 = (repo) => {
17476
17743
  if (!git || git.reftable)
17477
17744
  return null;
17478
17745
  try {
17479
- const head = fs14.readFileSync(path16.join(git.gitDir, "HEAD"), "utf8").trim();
17746
+ const head = fs15.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
17480
17747
  if (/^[0-9a-f]{40,64}$/.test(head))
17481
17748
  return head;
17482
17749
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
17483
17750
  if (!ref)
17484
17751
  return null;
17485
- const common = path16.dirname(repo.location.dir);
17752
+ const common = path18.dirname(repo.location.dir);
17486
17753
  try {
17487
- return fs14.readFileSync(path16.join(common, ref), "utf8").trim() || null;
17754
+ return fs15.readFileSync(path18.join(common, ref), "utf8").trim() || null;
17488
17755
  } catch {}
17489
- const packed = fs14.readFileSync(path16.join(common, "packed-refs"), "utf8");
17756
+ const packed = fs15.readFileSync(path18.join(common, "packed-refs"), "utf8");
17490
17757
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
17491
17758
  } catch {
17492
17759
  return null;
@@ -17574,8 +17841,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
17574
17841
  const sub = invocation.args[0];
17575
17842
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
17576
17843
  return [invocation];
17577
- const dir = path16.resolve(root, invocation.dir ?? ".");
17578
- if (!fs14.existsSync(dir))
17844
+ const dir = path18.resolve(root, invocation.dir ?? ".");
17845
+ if (!fs15.existsSync(dir))
17579
17846
  return [invocation];
17580
17847
  const config = gitConfig(dir, invocation, cache);
17581
17848
  const alias = configValue(config, `alias.${sub.toLowerCase()}`);
@@ -17597,11 +17864,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
17597
17864
  var normalizedUrl = (url, dir) => {
17598
17865
  const trimmed = url.trim();
17599
17866
  if (/^file:\/\//i.test(trimmed))
17600
- return path16.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17867
+ return path18.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17601
17868
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
17602
17869
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
17603
17870
  if (!scheme && !scp)
17604
- return path16.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17871
+ return path18.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17605
17872
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
17606
17873
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
17607
17874
  };
@@ -17825,7 +18092,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
17825
18092
  const out = [];
17826
18093
  const repos = new Map;
17827
18094
  const repoFor = (relative) => {
17828
- const dir = path16.resolve(cwd, relative ?? ".");
18095
+ const dir = path18.resolve(cwd, relative ?? ".");
17829
18096
  if (!repos.has(dir)) {
17830
18097
  const repo = repoAt(dir);
17831
18098
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -17910,8 +18177,8 @@ var analysisDenial = (error) => ({
17910
18177
  unblock: SPLIT_IT
17911
18178
  });
17912
18179
  var MARKER_TTL_SECONDS = 3600;
17913
- var markerFile = (repo, session, key) => path16.join(repo.location.dir, "hooks", `commit-${createHash12("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
17914
- var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash12("sha256").update(command).digest("hex")}`;
18180
+ var markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${createHash13("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18181
+ var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash13("sha256").update(command).digest("hex")}`;
17915
18182
  function noteRawCommit(input, command, key) {
17916
18183
  try {
17917
18184
  if (!input.session.id || !/\bcommit\b/.test(command))
@@ -17921,8 +18188,8 @@ function noteRawCommit(input, command, key) {
17921
18188
  if (action.kind !== "commit")
17922
18189
  continue;
17923
18190
  const file = markerFile(repo, input.session.id, key);
17924
- fs14.mkdirSync(path16.dirname(file), { recursive: true });
17925
- fs14.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18191
+ fs15.mkdirSync(path18.dirname(file), { recursive: true });
18192
+ fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
17926
18193
  `);
17927
18194
  }
17928
18195
  } catch {}
@@ -17945,11 +18212,11 @@ var headCommit = (dir) => {
17945
18212
  var settle2 = (input, repo, file) => {
17946
18213
  let marker;
17947
18214
  try {
17948
- marker = JSON.parse(fs14.readFileSync(file, "utf8"));
18215
+ marker = JSON.parse(fs15.readFileSync(file, "utf8"));
17949
18216
  } catch {
17950
18217
  return;
17951
18218
  }
17952
- fs14.rmSync(file, { force: true });
18219
+ fs15.rmSync(file, { force: true });
17953
18220
  const session = input.session.id;
17954
18221
  const now = Date.now() / 1000;
17955
18222
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -18140,7 +18407,7 @@ var judgedDir = (root, dir) => {
18140
18407
  return null;
18141
18408
  if (dir === null)
18142
18409
  return root;
18143
- const resolved = path17.resolve(root, dir);
18410
+ const resolved = path19.resolve(root, dir);
18144
18411
  return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18145
18412
  };
18146
18413
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
@@ -18376,7 +18643,7 @@ var judgeInputSummary = (raw) => {
18376
18643
 
18377
18644
  // packages/workit-core/src/autonomy.ts
18378
18645
  import os5 from "node:os";
18379
- import path18 from "node:path";
18646
+ import path20 from "node:path";
18380
18647
 
18381
18648
  // packages/workit-core/src/core/safe-write.ts
18382
18649
  import {
@@ -18410,10 +18677,10 @@ var accountHome = () => {
18410
18677
  }
18411
18678
  };
18412
18679
  var grantsHome = { resolve: accountHome };
18413
- var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
18680
+ var grantsDir = () => path20.join(grantsHome.resolve(), ".config", "workit");
18414
18681
  var grantsOverride = () => {
18415
- const configured = path18.resolve(resolveConfigDir());
18416
- return configured === path18.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 ${path18.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
18682
+ const configured = path20.resolve(resolveConfigDir());
18683
+ return configured === path20.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 ${path20.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
18417
18684
  };
18418
18685
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
18419
18686
  var DEFAULT_GRANTS = Object.freeze({
@@ -20185,7 +20452,7 @@ class WorkitCore {
20185
20452
  // packages/workit-core/src/core/repo-context.ts
20186
20453
  import { spawnSync as spawnSync13 } from "node:child_process";
20187
20454
  import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
20188
- import path19 from "node:path";
20455
+ import path21 from "node:path";
20189
20456
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
20190
20457
  try {
20191
20458
  gitRevisionParts(value);
@@ -20319,7 +20586,7 @@ var findPrTemplate = (cwd) => {
20319
20586
  ]) {
20320
20587
  const parts = rel.split("/");
20321
20588
  const base = parts[parts.length - 1];
20322
- const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
20589
+ const dir = parts.length > 1 ? path21.join(cwd, ...parts.slice(0, -1)) : cwd;
20323
20590
  const actual = probeCaseInsensitive(dir, base);
20324
20591
  if (actual)
20325
20592
  return [...parts.slice(0, -1), actual].join("/");
@@ -20350,7 +20617,7 @@ var diffStatForRange = (cwd, range) => {
20350
20617
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
20351
20618
  };
20352
20619
  var packageScripts = (cwd, keys) => {
20353
- const pkgPath = path19.join(cwd, "package.json");
20620
+ const pkgPath = path21.join(cwd, "package.json");
20354
20621
  if (!existsSync7(pkgPath))
20355
20622
  return [];
20356
20623
  try {
@@ -20376,9 +20643,9 @@ var documentationFiles = (cwd) => {
20376
20643
  if (entry.isDirectory()) {
20377
20644
  if (excluded.has(entry.name))
20378
20645
  continue;
20379
- walk(path19.join(dir, entry.name), depth + 1);
20646
+ walk(path21.join(dir, entry.name), depth + 1);
20380
20647
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
20381
- matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
20648
+ matches.push("./" + path21.relative(cwd, path21.join(dir, entry.name)).replaceAll("\\", "/"));
20382
20649
  }
20383
20650
  }
20384
20651
  };
@@ -20394,7 +20661,7 @@ var readTrimmed2 = (file, maxLines) => {
20394
20661
  `);
20395
20662
  };
20396
20663
  function prReadyContext(root, range) {
20397
- const cwd = path19.resolve(repoRoot(root));
20664
+ const cwd = path21.resolve(repoRoot(root));
20398
20665
  if (range !== undefined && !isResolvableContextRange(cwd, range))
20399
20666
  return invalidRange(cwd);
20400
20667
  let stdout = "";
@@ -20452,7 +20719,7 @@ ${status.stderr}` : "");
20452
20719
  stdout += `template_path: ${template}
20453
20720
 
20454
20721
  `;
20455
- stdout += readTrimmed2(path19.join(cwd, template), 220);
20722
+ stdout += readTrimmed2(path21.join(cwd, template), 220);
20456
20723
  } else {
20457
20724
  stdout += `template_path: none
20458
20725
 
@@ -20462,7 +20729,7 @@ ${status.stderr}` : "");
20462
20729
  stdout += `
20463
20730
  `;
20464
20731
  stdout += printSection("Recent Validation Signals");
20465
- if (existsSync7(path19.join(cwd, "package.json"))) {
20732
+ if (existsSync7(path21.join(cwd, "package.json"))) {
20466
20733
  stdout += `package.json detected. Common scripts:
20467
20734
  `;
20468
20735
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -20471,7 +20738,7 @@ ${status.stderr}` : "");
20471
20738
  `) + `
20472
20739
  `;
20473
20740
  }
20474
- if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
20741
+ if (existsSync7(path21.join(cwd, "Cargo.toml")) || existsSync7(path21.join(cwd, "src-tauri/Cargo.toml"))) {
20475
20742
  stdout += `Rust project detected.
20476
20743
  `;
20477
20744
  }
@@ -20504,7 +20771,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
20504
20771
  - Apply with the host-native editor, preserving the repository changelog conventions.
20505
20772
  - If Unreleased already has duplicate category headings, normalize_only first.`;
20506
20773
  function changelogContext(root, range) {
20507
- const cwd = path19.resolve(repoRoot(root));
20774
+ const cwd = path21.resolve(repoRoot(root));
20508
20775
  if (range !== undefined && !isResolvableContextRange(cwd, range))
20509
20776
  return invalidRange(cwd);
20510
20777
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -20522,7 +20789,7 @@ function changelogContext(root, range) {
20522
20789
  stdout += CHANGELOG_RULES + `
20523
20790
  `;
20524
20791
  stdout += printSection("Existing CHANGELOG.md");
20525
- const changelogPath = path19.join(cwd, "CHANGELOG.md");
20792
+ const changelogPath = path21.join(cwd, "CHANGELOG.md");
20526
20793
  if (existsSync7(changelogPath)) {
20527
20794
  stdout += readTrimmed2(changelogPath, 260) + `
20528
20795
  `;
@@ -20542,7 +20809,7 @@ function changelogContext(root, range) {
20542
20809
  return { stdout, stderr: "", exitCode: 0, cwd };
20543
20810
  }
20544
20811
  function docsRefreshContext(root, range) {
20545
- const cwd = path19.resolve(repoRoot(root));
20812
+ const cwd = path21.resolve(repoRoot(root));
20546
20813
  if (range !== undefined && !isResolvableContextRange(cwd, range))
20547
20814
  return invalidRange(cwd);
20548
20815
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -20564,7 +20831,7 @@ function docsRefreshContext(root, range) {
20564
20831
  `) + `
20565
20832
  `;
20566
20833
  stdout += printSection("README Preview");
20567
- const readme = path19.join(cwd, "README.md");
20834
+ const readme = path21.join(cwd, "README.md");
20568
20835
  if (existsSync7(readme)) {
20569
20836
  stdout += readTrimmed2(readme, 220) + `
20570
20837
  `;
@@ -20573,7 +20840,7 @@ function docsRefreshContext(root, range) {
20573
20840
  `;
20574
20841
  }
20575
20842
  stdout += printSection("Package Scripts");
20576
- const pkgPath = path19.join(cwd, "package.json");
20843
+ const pkgPath = path21.join(cwd, "package.json");
20577
20844
  if (existsSync7(pkgPath)) {
20578
20845
  try {
20579
20846
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
@@ -20587,7 +20854,7 @@ function docsRefreshContext(root, range) {
20587
20854
  return { stdout, stderr: "", exitCode: 0, cwd };
20588
20855
  }
20589
20856
  function releaseNotesContext(root, rangeOrTag) {
20590
- const cwd = path19.resolve(repoRoot(root));
20857
+ const cwd = path21.resolve(repoRoot(root));
20591
20858
  if (!rangeOrTag) {
20592
20859
  return { stdout: "", stderr: `ERROR: release tag or range required
20593
20860
  `, exitCode: 1, cwd };
@@ -20625,7 +20892,7 @@ function releaseNotesContext(root, rangeOrTag) {
20625
20892
  `;
20626
20893
  stdout += printSection("Existing Release Files");
20627
20894
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
20628
- if (existsSync7(path19.join(cwd, rel)))
20895
+ if (existsSync7(path21.join(cwd, rel)))
20629
20896
  stdout += rel + `
20630
20897
  `;
20631
20898
  }
@@ -20656,8 +20923,8 @@ function releaseNotesContext(root, rangeOrTag) {
20656
20923
  import { spawnSync as spawnSync15 } from "node:child_process";
20657
20924
 
20658
20925
  // packages/workit-core/src/core/pr-create.ts
20659
- import fs15 from "node:fs";
20660
- import path20 from "node:path";
20926
+ import fs16 from "node:fs";
20927
+ import path22 from "node:path";
20661
20928
  import { spawnSync as spawnSync14 } from "node:child_process";
20662
20929
  // packages/workit-core/src/forge/pr-body.ts
20663
20930
  function parseGhIssue(value) {
@@ -20668,13 +20935,13 @@ function parseGhIssue(value) {
20668
20935
  // packages/workit-core/src/core/pr-create.ts
20669
20936
  function whichOnPath(tool) {
20670
20937
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
20671
- for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
20938
+ for (const dir of (process.env.PATH ?? "").split(path22.delimiter)) {
20672
20939
  if (!dir)
20673
20940
  continue;
20674
20941
  for (const name of names) {
20675
- const candidate = path20.join(dir, name);
20942
+ const candidate = path22.join(dir, name);
20676
20943
  try {
20677
- fs15.accessSync(candidate, fs15.constants.X_OK);
20944
+ fs16.accessSync(candidate, fs16.constants.X_OK);
20678
20945
  return candidate;
20679
20946
  } catch {}
20680
20947
  }
@@ -20792,8 +21059,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
20792
21059
  };
20793
21060
 
20794
21061
  // packages/workit-core/src/core/youtrack.ts
20795
- import fs16 from "node:fs";
20796
- import path21 from "node:path";
21062
+ import fs17 from "node:fs";
21063
+ import path23 from "node:path";
20797
21064
 
20798
21065
  // packages/workit-core/src/core/templates.ts
20799
21066
  var repoRoot2 = assetRoot();
@@ -20806,22 +21073,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
20806
21073
  // packages/workit-core/src/core/youtrack.ts
20807
21074
  var ISSUE_RE = /^[A-Z]+-\d+$/;
20808
21075
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
20809
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(configDir(), "youtrack.json");
20810
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(resolveConfigDir(), "youtrack.json");
21076
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path23.join(configDir(), "youtrack.json");
21077
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path23.join(resolveConfigDir(), "youtrack.json");
20811
21078
  var youTrackTokenModeOk = (p) => {
20812
21079
  if (process.platform === "win32")
20813
21080
  return true;
20814
- const mode = fs16.statSync(p).mode & 511;
21081
+ const mode = fs17.statSync(p).mode & 511;
20815
21082
  return mode === 384;
20816
21083
  };
20817
21084
  function readYouTrackConfig(required) {
20818
21085
  const cfgPath = youTrackConfigPath();
20819
- if (!fs16.existsSync(cfgPath)) {
21086
+ if (!fs17.existsSync(cfgPath)) {
20820
21087
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
20821
21088
  }
20822
21089
  let parsed;
20823
21090
  try {
20824
- parsed = JSON.parse(fs16.readFileSync(cfgPath, "utf8"));
21091
+ parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
20825
21092
  } catch {
20826
21093
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
20827
21094
  }
@@ -20832,11 +21099,11 @@ function readYouTrackConfig(required) {
20832
21099
  }
20833
21100
  var readYouTrackContextConfig = () => {
20834
21101
  const cfgPath = youTrackReadOnlyConfigPath();
20835
- if (!fs16.existsSync(cfgPath))
21102
+ if (!fs17.existsSync(cfgPath))
20836
21103
  return { data: {} };
20837
21104
  let parsed;
20838
21105
  try {
20839
- parsed = JSON.parse(fs16.readFileSync(cfgPath, "utf8"));
21106
+ parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
20840
21107
  } catch {
20841
21108
  return { error: "YouTrack configuration is unavailable" };
20842
21109
  }
@@ -20875,12 +21142,12 @@ var youTrackToken = () => {
20875
21142
  if ("error" in loaded)
20876
21143
  return loaded;
20877
21144
  const tokenFile = String(loaded.config.tokenFile ?? "");
20878
- const tokenPath = tokenFile ? path21.isAbsolute(tokenFile) ? path21.resolve(tokenFile) : path21.resolve(process.cwd(), tokenFile) : "";
20879
- if (!tokenPath || !fs16.existsSync(tokenPath))
21145
+ const tokenPath = tokenFile ? path23.isAbsolute(tokenFile) ? path23.resolve(tokenFile) : path23.resolve(process.cwd(), tokenFile) : "";
21146
+ if (!tokenPath || !fs17.existsSync(tokenPath))
20880
21147
  return { error: "missing youtrack.token" };
20881
21148
  if (!youTrackTokenModeOk(tokenPath))
20882
21149
  return { error: "youtrack.token mode must be 0600" };
20883
- const token = fs16.readFileSync(tokenPath, "utf8").trim();
21150
+ const token = fs17.readFileSync(tokenPath, "utf8").trim();
20884
21151
  if (!token)
20885
21152
  return { error: "empty token file" };
20886
21153
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -20926,7 +21193,7 @@ function youTrackTokenCreateUrl() {
20926
21193
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
20927
21194
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
20928
21195
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
20929
- const tokenFile = String(config.tokenFile ?? path21.join(path21.dirname(loaded.path), "youtrack.token"));
21196
+ const tokenFile = String(config.tokenFile ?? path23.join(path23.dirname(loaded.path), "youtrack.token"));
20930
21197
  const tab = String(defaults.profileTab ?? "account-security");
20931
21198
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
20932
21199
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -20935,7 +21202,7 @@ function youTrackTokenCreateUrl() {
20935
21202
  tokenName: name,
20936
21203
  tokenDescription: desc,
20937
21204
  scopes,
20938
- tokenFile: path21.resolve(tokenFile),
21205
+ tokenFile: path23.resolve(tokenFile),
20939
21206
  createUrl,
20940
21207
  docsUrl,
20941
21208
  prefillSupported: false,
@@ -20981,9 +21248,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
20981
21248
  } catch {
20982
21249
  continue;
20983
21250
  }
20984
- if (!fs16.existsSync(full))
21251
+ if (!fs17.existsSync(full))
20985
21252
  continue;
20986
- const text = fs16.readFileSync(full, "utf8");
21253
+ const text = fs17.readFileSync(full, "utf8");
20987
21254
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
20988
21255
  if (m)
20989
21256
  return m[1];
@@ -21275,15 +21542,15 @@ function resolveGitRevision(root, value) {
21275
21542
  }
21276
21543
  function resolveInside(root, candidate) {
21277
21544
  const base = realpathSync5(root);
21278
- const target = path22.resolve(base, candidate);
21279
- if (target !== base && !target.startsWith(base + path22.sep)) {
21545
+ const target = path24.resolve(base, candidate);
21546
+ if (target !== base && !target.startsWith(base + path24.sep)) {
21280
21547
  throw new Error("path must stay inside repository root");
21281
21548
  }
21282
21549
  let ancestor = target;
21283
21550
  while (!existsSync8(ancestor))
21284
- ancestor = path22.dirname(ancestor);
21551
+ ancestor = path24.dirname(ancestor);
21285
21552
  const canonicalAncestor = realpathSync5(ancestor);
21286
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path22.sep)) {
21553
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path24.sep)) {
21287
21554
  throw new Error("path must stay inside repository root");
21288
21555
  }
21289
21556
  return target;
@@ -21327,7 +21594,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
21327
21594
  };
21328
21595
  });
21329
21596
  // packages/workit-core/src/hooks/context.ts
21330
- import path23 from "node:path";
21597
+ import path25 from "node:path";
21331
21598
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
21332
21599
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
21333
21600
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -21412,7 +21679,7 @@ function entryCompactContext(store, entry, session, context) {
21412
21679
  return null;
21413
21680
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
21414
21681
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
21415
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path23.resolve(store.root, ref.path)] : []));
21682
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path25.resolve(store.root, ref.path)] : []));
21416
21683
  const observedFiles = filesKey(files);
21417
21684
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
21418
21685
  if (!compact.ok || !compact.data)
@@ -21444,27 +21711,231 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
21444
21711
  };
21445
21712
  // packages/workit-core/src/hooks/write-gate.ts
21446
21713
  import { realpathSync as realpathSync6 } from "node:fs";
21447
- import path24 from "node:path";
21714
+ import os7 from "node:os";
21715
+ import path27 from "node:path";
21716
+
21717
+ // packages/workit-core/src/core/live-session.ts
21718
+ import { spawnSync as spawnSync17 } from "node:child_process";
21719
+ import { createHash as createHash14 } from "node:crypto";
21720
+ import fs18 from "node:fs";
21721
+ import os6 from "node:os";
21722
+ import path26 from "node:path";
21723
+ var LIVE_SESSION_TTL_MS = 15 * 60000;
21724
+ var HEARTBEAT_EVERY_MS = 30000;
21725
+ var liveProbe = {
21726
+ platform: process.platform,
21727
+ run: (args) => spawnSync17("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
21728
+ start: (pid) => processStartOf(pid)
21729
+ };
21730
+ var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
21731
+ var familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, "");
21732
+ var processAlive = (pid) => {
21733
+ try {
21734
+ process.kill(pid, 0);
21735
+ return true;
21736
+ } catch (error) {
21737
+ return error.code === "EPERM";
21738
+ }
21739
+ };
21740
+ var processInfo = (pid) => {
21741
+ try {
21742
+ if (liveProbe.platform === "linux") {
21743
+ const stat = fs18.readFileSync(`/proc/${pid}/stat`, "utf8");
21744
+ const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
21745
+ const command = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
21746
+ return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
21747
+ }
21748
+ if (liveProbe.platform === "win32")
21749
+ return null;
21750
+ const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
21751
+ return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
21752
+ } catch {
21753
+ return null;
21754
+ }
21755
+ };
21756
+ var WRAPPER = /^(?:\S*\/)?(?:sh|bash|dash|zsh|ash|ksh|fish|env|timeout|npx|npm|bunx|cmd\.exe|pwsh|powershell)(?:\s|$)|launch\.mjs|npx-cli\.js/u;
21757
+ var PIDS = new Map;
21758
+ var hostPid = (transport) => {
21759
+ if (transport !== "hook-process")
21760
+ return process.pid;
21761
+ const key = `${liveProbe.platform}\x00${process.ppid}`;
21762
+ if (PIDS.has(key))
21763
+ return PIDS.get(key) ?? null;
21764
+ if (liveProbe.platform === "win32") {
21765
+ PIDS.set(key, null);
21766
+ return null;
21767
+ }
21768
+ let pid = process.ppid;
21769
+ for (let hop = 0;hop < 4; hop++) {
21770
+ const info = processInfo(pid);
21771
+ if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
21772
+ break;
21773
+ pid = info.ppid;
21774
+ }
21775
+ PIDS.set(key, pid);
21776
+ return pid;
21777
+ };
21778
+ var sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-");
21779
+ var liveOwner = (input, transport, env = process.env) => {
21780
+ const session = input.session.id?.trim();
21781
+ if (!session)
21782
+ return null;
21783
+ const leads = new Set([leadOf(sessionSafe(session))]);
21784
+ if (input.session.parentId)
21785
+ leads.add(leadOf(sessionSafe(input.session.parentId)));
21786
+ if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
21787
+ leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
21788
+ return {
21789
+ host: input.host,
21790
+ session: sessionSafe(session),
21791
+ leads: [...leads],
21792
+ pid: () => hostPid(transport)
21793
+ };
21794
+ };
21795
+ var checkoutOf = (target) => {
21796
+ let dir = path26.resolve(target);
21797
+ while (!fs18.existsSync(dir) && path26.dirname(dir) !== dir)
21798
+ dir = path26.dirname(dir);
21799
+ if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
21800
+ dir = path26.dirname(dir);
21801
+ const parts = dir.split(path26.sep);
21802
+ const dotGit = parts.lastIndexOf(".git");
21803
+ if (dotGit > 0)
21804
+ dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
21805
+ const location = resolveStore(dir);
21806
+ if (location instanceof Error || !location.shared || !location.top)
21807
+ return null;
21808
+ let top = location.top;
21809
+ try {
21810
+ top = fs18.realpathSync.native(top);
21811
+ } catch {}
21812
+ return top;
21813
+ };
21814
+ var markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash14("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
21815
+ var checkoutKey = (top, platform = process.platform) => {
21816
+ let key = path26.resolve(top);
21817
+ try {
21818
+ key = fs18.realpathSync.native(key);
21819
+ } catch {}
21820
+ return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
21821
+ };
21822
+ var readMarker = (file) => {
21823
+ try {
21824
+ const value = JSON.parse(fs18.readFileSync(file, "utf8"));
21825
+ if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
21826
+ return null;
21827
+ return {
21828
+ version: 1,
21829
+ session: value.session,
21830
+ leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
21831
+ host: value.host,
21832
+ pid: typeof value.pid === "number" ? value.pid : null,
21833
+ pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
21834
+ machine: typeof value.machine === "string" ? value.machine : "",
21835
+ checkout: typeof value.checkout === "string" ? value.checkout : "",
21836
+ heartbeatAt: value.heartbeatAt
21837
+ };
21838
+ } catch {
21839
+ return null;
21840
+ }
21841
+ };
21842
+ var ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt);
21843
+ var isLive = (marker, deps) => {
21844
+ const age = ageOf2(marker, deps.now ?? new Date);
21845
+ if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
21846
+ return false;
21847
+ if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
21848
+ return true;
21849
+ if (!(deps.alive ?? processAlive)(marker.pid))
21850
+ return false;
21851
+ if (marker.pidStart === null)
21852
+ return true;
21853
+ const start = liveProbe.start(marker.pid);
21854
+ return start === null || start === marker.pidStart;
21855
+ };
21856
+ var sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead));
21857
+ var sameOwner = (marker, owner, machine) => {
21858
+ if (sameLead(marker, owner))
21859
+ return true;
21860
+ if (marker.machine !== machine)
21861
+ return false;
21862
+ const pid = owner.pid();
21863
+ if (pid === null || marker.pid === null || marker.pidStart === null)
21864
+ return familyOf(marker.host) === familyOf(owner.host);
21865
+ return marker.pid === pid;
21866
+ };
21867
+ var liveConflict = (target, owner, deps = {}) => {
21868
+ try {
21869
+ const checkout = checkoutOf(target);
21870
+ if (!checkout)
21871
+ return null;
21872
+ const file = markerFile2(checkout);
21873
+ const marker = readMarker(file);
21874
+ if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
21875
+ return null;
21876
+ if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
21877
+ return null;
21878
+ return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
21879
+ } catch {
21880
+ return null;
21881
+ }
21882
+ };
21883
+ var claimLiveSession = (target, owner, deps = {}) => {
21884
+ try {
21885
+ const checkout = checkoutOf(target);
21886
+ if (!checkout)
21887
+ return;
21888
+ const file = markerFile2(checkout);
21889
+ const now = deps.now ?? new Date;
21890
+ const machine = deps.machine ?? os6.hostname();
21891
+ const current = readMarker(file);
21892
+ if (current) {
21893
+ const age = ageOf2(current, now);
21894
+ if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
21895
+ return;
21896
+ if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
21897
+ return;
21898
+ }
21899
+ const pid = owner.pid();
21900
+ const marker = {
21901
+ version: 1,
21902
+ session: owner.session,
21903
+ leads: owner.leads,
21904
+ host: owner.host,
21905
+ pid,
21906
+ pidStart: pid === null ? null : liveProbe.start(pid),
21907
+ machine,
21908
+ checkout,
21909
+ heartbeatAt: now.toISOString()
21910
+ };
21911
+ fs18.mkdirSync(path26.dirname(file), { recursive: true });
21912
+ const temp = `${file}.${process.pid}.tmp`;
21913
+ fs18.writeFileSync(temp, JSON.stringify(marker));
21914
+ fs18.renameSync(temp, file);
21915
+ } catch {}
21916
+ };
21917
+
21918
+ // packages/workit-core/src/hooks/write-gate.ts
21448
21919
  var NONE3 = { kind: "none" };
21449
21920
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
21450
21921
  var canonical2 = (file) => {
21451
21922
  let rest = "";
21452
- for (let dir = path24.resolve(file);; dir = path24.dirname(dir)) {
21923
+ for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
21453
21924
  try {
21454
- return path24.join(realpathSync6(dir), rest);
21925
+ return path27.join(realpathSync6(dir), rest);
21455
21926
  } catch {
21456
- if (path24.dirname(dir) === dir)
21457
- return path24.resolve(file);
21458
- rest = rest ? path24.join(path24.basename(dir), rest) : path24.basename(dir);
21927
+ if (path27.dirname(dir) === dir)
21928
+ return path27.resolve(file);
21929
+ rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
21459
21930
  }
21460
21931
  }
21461
21932
  };
21462
21933
  var exempt = (cwd, root, file, plans) => {
21463
21934
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
21464
- const relative = path24.relative(fold(canonical2(root)), fold(canonical2(path24.resolve(cwd, file))));
21465
- if (relative.startsWith("..") || path24.isAbsolute(relative))
21935
+ const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
21936
+ if (relative.startsWith("..") || path27.isAbsolute(relative))
21466
21937
  return true;
21467
- const posix = relative.split(path24.sep).join("/");
21938
+ const posix = relative.split(path27.sep).join("/");
21468
21939
  const segments = posix.split("/");
21469
21940
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
21470
21941
  };
@@ -21562,7 +22033,7 @@ var shellWrites = (command) => {
21562
22033
  return { writes: true, targets: unknown ? null : targets };
21563
22034
  };
21564
22035
  var cache2 = new Map;
21565
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path24.resolve(root, file)) ?? "-").join("\x00");
22036
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00");
21566
22037
  var gateFor = (cwd) => {
21567
22038
  const store = new TaskStore(cwd);
21568
22039
  const listed = store.listTaskIndex();
@@ -21600,7 +22071,218 @@ var gateFor = (cwd) => {
21600
22071
  cache2.delete(cache2.keys().next().value);
21601
22072
  return gate;
21602
22073
  };
21603
- function writeGate(cwd, paths) {
22074
+ var MUTATING_GIT = new Set([
22075
+ "add",
22076
+ "am",
22077
+ "apply",
22078
+ "checkout",
22079
+ "cherry-pick",
22080
+ "clean",
22081
+ "commit",
22082
+ "merge",
22083
+ "mv",
22084
+ "pull",
22085
+ "rebase",
22086
+ "reset",
22087
+ "restore",
22088
+ "revert",
22089
+ "rm",
22090
+ "stash",
22091
+ "switch"
22092
+ ]);
22093
+ var DRY_N = new Set(["add", "clean", "rm", "mv"]);
22094
+ var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
22095
+ var DIR_TARGET = ".workit-live-target";
22096
+ var OVERRIDE = "WORKIT_LIVE_SESSION=override";
22097
+ var inert = (args, sub) => args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run" || DRY_N.has(sub) && /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg) && !arg.startsWith("--"));
22098
+ var gitMutationDirs = (cwd, command, dialect) => {
22099
+ let invocations;
22100
+ try {
22101
+ invocations = rawInvocations(command, dialect);
22102
+ } catch {
22103
+ return [];
22104
+ }
22105
+ return invocations.flatMap((invocation) => {
22106
+ if (invocation.tool !== "git" || invocation.dir === undefined)
22107
+ return [];
22108
+ const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
22109
+ const sub = invocation.args[at] ?? "";
22110
+ const rest = invocation.args.slice(at + 1);
22111
+ if (!MUTATING_GIT.has(sub) || inert(rest, sub))
22112
+ return [];
22113
+ if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
22114
+ return [];
22115
+ return [path27.resolve(cwd, invocation.dir ?? ".")];
22116
+ });
22117
+ };
22118
+ var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
22119
+ var PUSHD2 = new Set(["pushd", "push-location"]);
22120
+ var POPD2 = new Set(["popd", "pop-location"]);
22121
+ var cdInto = (dir, words) => {
22122
+ const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
22123
+ if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
22124
+ return;
22125
+ return target === undefined ? os7.homedir() : path27.resolve(dir, target);
22126
+ };
22127
+ var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
22128
+ var segmentTargets = (cwd, command, dialect) => {
22129
+ const targets = [];
22130
+ let dir = cwd;
22131
+ let stack = [];
22132
+ const groups = [];
22133
+ const at = (file) => path27.isAbsolute(file) ? file : dir === undefined ? null : path27.resolve(dir, file);
22134
+ for (const segment of segmentsOf(command, dialect)) {
22135
+ if (segment.group === "open") {
22136
+ groups.push({ dir, stack: [...stack] });
22137
+ continue;
22138
+ }
22139
+ if (segment.group === "close") {
22140
+ const saved = groups.pop();
22141
+ if (saved)
22142
+ ({ dir, stack } = saved);
22143
+ continue;
22144
+ }
22145
+ const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
22146
+ const words = lead < 0 ? [] : segment.words.slice(lead);
22147
+ const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
22148
+ if (CD2.has(name)) {
22149
+ dir = cdInto(dir, words);
22150
+ continue;
22151
+ }
22152
+ if (PUSHD2.has(name)) {
22153
+ stack.push(dir);
22154
+ dir = cdInto(dir, words);
22155
+ continue;
22156
+ }
22157
+ if (POPD2.has(name)) {
22158
+ dir = stack.length ? stack.pop() : undefined;
22159
+ continue;
22160
+ }
22161
+ for (const file of segment.redirects)
22162
+ if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
22163
+ const resolved = at(file);
22164
+ if (resolved)
22165
+ targets.push(resolved);
22166
+ }
22167
+ const edits = editsOf(segment.words);
22168
+ if (edits === null) {
22169
+ if (dir !== undefined)
22170
+ targets.push(path27.join(dir, DIR_TARGET));
22171
+ } else
22172
+ for (const file of edits) {
22173
+ const resolved = at(file);
22174
+ if (resolved)
22175
+ targets.push(resolved);
22176
+ }
22177
+ if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
22178
+ targets.push(path27.join(dir, DIR_TARGET));
22179
+ }
22180
+ return targets;
22181
+ };
22182
+ var overridden = (command, dialect) => {
22183
+ const segments = segmentsOf(command, dialect);
22184
+ if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
22185
+ return false;
22186
+ const words = segments[0].words;
22187
+ const lead = words.findIndex((word) => !/^\w+=/.test(word));
22188
+ return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
22189
+ };
22190
+ var HOST_LABELS = {
22191
+ claude_code: "Claude Code",
22192
+ opencode: "OpenCode",
22193
+ codex_cli: "Codex CLI",
22194
+ codex_desktop: "Codex Desktop",
22195
+ cursor: "Cursor",
22196
+ pi: "Pi"
22197
+ };
22198
+ var ageText = (ms) => {
22199
+ const seconds = Math.max(0, Math.round(ms / 1000));
22200
+ return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
22201
+ };
22202
+ var liveDeny = ({ marker, checkout, ageMs, file }) => {
22203
+ const unblock = `Work in your own worktree (\`git worktree add <path> -b <branch>\`, or \`workit fanout worktree create <slice>\`). Only if the user confirms that session is finished: release it with \`rm '${file}'\`, or prefix one command with ${OVERRIDE}.`;
22204
+ return {
22205
+ kind: "deny",
22206
+ reason: `workit live-session guard: ${checkout} belongs to another live agent session, ${HOST_LABELS[marker.host] ?? marker.host} session ${marker.session}${marker.pid === null ? "" : ` (pid ${marker.pid})`}, last active ${ageText(ageMs)} ago. Do not edit a checkout another session is working in; read-only commands stay allowed. ${unblock}`,
22207
+ unblock
22208
+ };
22209
+ };
22210
+ var guarded = (targets) => {
22211
+ const seen = new Set;
22212
+ return targets.filter((target) => {
22213
+ const dir = path27.dirname(target);
22214
+ if (seen.has(dir))
22215
+ return false;
22216
+ seen.add(dir);
22217
+ return true;
22218
+ });
22219
+ };
22220
+ var liveGate = (owner, targets, deps) => {
22221
+ try {
22222
+ for (const target of guarded(targets)) {
22223
+ const conflict = liveConflict(target, owner, deps);
22224
+ if (conflict && workspaceLiveSessionGuard(conflict.checkout))
22225
+ return liveDeny(conflict);
22226
+ }
22227
+ } catch {}
22228
+ return NONE3;
22229
+ };
22230
+ var claimAll = (owner, targets, deps) => {
22231
+ for (const target of guarded(targets))
22232
+ claimLiveSession(target, owner, deps);
22233
+ };
22234
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)];
22235
+ function writeGate(cwd, paths, owner, deps) {
22236
+ if (!owner)
22237
+ return beforeWriteGate(cwd, paths);
22238
+ const targets = liveTargets(cwd, paths);
22239
+ const live = liveGate(owner, targets, deps);
22240
+ if (live.kind !== "none")
22241
+ return live;
22242
+ const decision = beforeWriteGate(cwd, paths);
22243
+ if (decision.kind === "none")
22244
+ claimAll(owner, targets, deps);
22245
+ return decision;
22246
+ }
22247
+ function shellGate(cwd, command, owner, dialect = "posix", deps) {
22248
+ const writes = shellWrites(command);
22249
+ let targets = [];
22250
+ if (owner) {
22251
+ try {
22252
+ targets = [
22253
+ ...segmentTargets(cwd, command, dialect),
22254
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
22255
+ ];
22256
+ } catch {
22257
+ targets = [];
22258
+ }
22259
+ }
22260
+ if (owner && targets.length) {
22261
+ const live = liveGate(owner, targets, deps);
22262
+ if (live.kind !== "none") {
22263
+ if (!overridden(command, dialect))
22264
+ return live;
22265
+ noteOverride(cwd, owner, command);
22266
+ }
22267
+ }
22268
+ const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
22269
+ if (owner && targets.length && decision.kind === "none")
22270
+ claimAll(owner, targets, deps);
22271
+ return decision;
22272
+ }
22273
+ var noteOverride = (cwd, owner, command) => {
22274
+ try {
22275
+ if (!inWorkitWorkspace(cwd))
22276
+ return;
22277
+ appendHookObserved(cwd, {
22278
+ type: "live_session.override",
22279
+ actor: { host: owner.host, session: owner.session, agentId: null },
22280
+ session: owner.session,
22281
+ command: command.slice(0, 200)
22282
+ });
22283
+ } catch {}
22284
+ };
22285
+ function beforeWriteGate(cwd, paths) {
21604
22286
  try {
21605
22287
  const gate = gateFor(cwd);
21606
22288
  if (!gate || gate.blockers.length === 0)
@@ -21618,7 +22300,7 @@ function writeGate(cwd, paths) {
21618
22300
  }
21619
22301
  }
21620
22302
  // packages/workit-core/src/hooks/stop.ts
21621
- import { spawnSync as spawnSync17 } from "node:child_process";
22303
+ import { spawnSync as spawnSync18 } from "node:child_process";
21622
22304
  var NONE4 = { kind: "none" };
21623
22305
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
21624
22306
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -21639,7 +22321,7 @@ var asksUser = (message) => {
21639
22321
  };
21640
22322
  var short = (sha) => (sha ?? "?").slice(0, 12);
21641
22323
  var gitList = (cwd, args) => {
21642
- const run = spawnSync17("git", args, {
22324
+ const run = spawnSync18("git", args, {
21643
22325
  cwd,
21644
22326
  encoding: "utf8",
21645
22327
  timeout: 3000,
@@ -21730,10 +22412,10 @@ function stopObligation(input, descriptor) {
21730
22412
  const branch = currentBranch(input.cwd);
21731
22413
  if (!branch || guardedBranch(input.cwd, branch))
21732
22414
  return null;
21733
- const ledger = readLedger(input.cwd);
22415
+ const ledger = readLedger(input.cwd, { integrity: true });
21734
22416
  if (!ledger.ok)
21735
22417
  return null;
21736
- const all = ledger.value.rows;
22418
+ const all = ledger.value.rows.filter((row) => row.integrity !== "unverified");
21737
22419
  const rows = all.filter((row) => row.branch === branch);
21738
22420
  const mine = rows.filter((row) => sessionOf(row) === session);
21739
22421
  const commits = new Set(mine.filter((row) => row.type === "commit.recorded").map((row) => typeof row.sha === "string" ? row.sha : row.head).filter((sha) => typeof sha === "string"));
@@ -22101,11 +22783,11 @@ var OPENCODE_DESCRIPTOR = {
22101
22783
  ]
22102
22784
  };
22103
22785
  // packages/workit-opencode/src/shared/init-apply.ts
22104
- import path29 from "node:path";
22786
+ import path32 from "node:path";
22105
22787
 
22106
22788
  // packages/workit-core/src/core/gitignore.ts
22107
22789
  import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
22108
- import path25 from "node:path";
22790
+ import path28 from "node:path";
22109
22791
  var GITIGNORE_ENTRIES = [
22110
22792
  "# workit: SDD working state (never commit)",
22111
22793
  "docs/*/sdd/",
@@ -22125,7 +22807,7 @@ var GITIGNORE_ENTRIES = [
22125
22807
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22126
22808
  if (!confirmed)
22127
22809
  return { ok: false, error: "confirmed: true required" };
22128
- const file = path25.join(workspaceRoot, ".gitignore");
22810
+ const file = path28.join(workspaceRoot, ".gitignore");
22129
22811
  const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
22130
22812
  const existingLines = new Set(existing.split(`
22131
22813
  `).map((l) => l.trim()).filter(Boolean));
@@ -22152,26 +22834,26 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22152
22834
  };
22153
22835
 
22154
22836
  // packages/workit-core/src/core/hygiene.ts
22155
- import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
22156
- import path26 from "node:path";
22837
+ import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
22838
+ import path29 from "node:path";
22157
22839
  var repoRoot3 = assetRoot();
22158
- var templatesDir = () => path26.join(repoRoot3, "templates", "hygiene");
22840
+ var templatesDir = () => path29.join(repoRoot3, "templates", "hygiene");
22159
22841
  var packageJson = (root) => {
22160
- if (!existsSync10(path26.join(root, "package.json")))
22842
+ if (!existsSync10(path29.join(root, "package.json")))
22161
22843
  return null;
22162
22844
  try {
22163
- return JSON.parse(readFileSync7(path26.join(root, "package.json"), "utf8"));
22845
+ return JSON.parse(readFileSync7(path29.join(root, "package.json"), "utf8"));
22164
22846
  } catch {
22165
22847
  return null;
22166
22848
  }
22167
22849
  };
22168
22850
  var isOpenSource = (root) => {
22169
- if (existsSync10(path26.join(root, "LICENSE")))
22851
+ if (existsSync10(path29.join(root, "LICENSE")))
22170
22852
  return true;
22171
22853
  const pkg = packageJson(root);
22172
22854
  if (pkg && !pkg.private)
22173
22855
  return true;
22174
- return path26.basename(root) === "workflow-toolkit";
22856
+ return path29.basename(root) === "workflow-toolkit";
22175
22857
  };
22176
22858
  var licenseHolder = (root) => {
22177
22859
  const pkg = packageJson(root);
@@ -22194,13 +22876,13 @@ var planHygieneFiles = (root, opts = {}) => {
22194
22876
  files.push("LICENSE", "CONTRIBUTING.md");
22195
22877
  const planned = [];
22196
22878
  for (const file of files) {
22197
- if (existsSync10(path26.join(root, file)))
22879
+ if (existsSync10(path29.join(root, file)))
22198
22880
  continue;
22199
- const tpl = path26.join(templatesDir(), file);
22881
+ const tpl = path29.join(templatesDir(), file);
22200
22882
  if (!existsSync10(tpl))
22201
22883
  continue;
22202
- const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path26.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22203
- planned.push({ path: path26.join(root, file), content });
22884
+ const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path29.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22885
+ planned.push({ path: path29.join(root, file), content });
22204
22886
  }
22205
22887
  return planned;
22206
22888
  };
@@ -22210,19 +22892,19 @@ var ensureHygieneFiles = (root, opts) => {
22210
22892
  const created = [];
22211
22893
  for (const planned of planHygieneFiles(root, opts)) {
22212
22894
  writeFileSync5(planned.path, planned.content, "utf8");
22213
- created.push(path26.basename(planned.path));
22895
+ created.push(path29.basename(planned.path));
22214
22896
  }
22215
22897
  return { ok: true, created };
22216
22898
  };
22217
22899
 
22218
22900
  // packages/workit-core/src/core/init.ts
22219
- import fs17 from "node:fs";
22220
- import path28 from "node:path";
22221
- import { spawnSync as spawnSync18 } from "node:child_process";
22901
+ import fs19 from "node:fs";
22902
+ import path31 from "node:path";
22903
+ import { spawnSync as spawnSync19 } from "node:child_process";
22222
22904
 
22223
22905
  // packages/workit-core/src/core/workspace-branch-policy.ts
22224
22906
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
22225
- import path27 from "node:path";
22907
+ import path30 from "node:path";
22226
22908
  import { isDeepStrictEqual } from "node:util";
22227
22909
  var readFileSafe = (p) => {
22228
22910
  try {
@@ -22233,7 +22915,7 @@ var readFileSafe = (p) => {
22233
22915
  };
22234
22916
  function applyWorkspaceBranchPolicy(opts) {
22235
22917
  const { workspace_root, env = process.env } = opts;
22236
- const dir = path27.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22918
+ const dir = path30.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22237
22919
  const {
22238
22920
  status,
22239
22921
  path: wsPath,
@@ -22245,7 +22927,7 @@ function applyWorkspaceBranchPolicy(opts) {
22245
22927
  if (status === "malformed" || status === "invalid")
22246
22928
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
22247
22929
  const detection = detectBranchPolicy(workspace_root);
22248
- const name = String(env.WORKFLOW_BP_NAME ?? path27.basename(workspace_root));
22930
+ const name = String(env.WORKFLOW_BP_NAME ?? path30.basename(workspace_root));
22249
22931
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
22250
22932
  const policy = {
22251
22933
  preset: detection.preset,
@@ -22294,7 +22976,7 @@ function applyWorkspaceBranchPolicy(opts) {
22294
22976
  ok: false,
22295
22977
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
22296
22978
  };
22297
- mkdirSync4(path27.dirname(wsPath), { recursive: true });
22979
+ mkdirSync4(path30.dirname(wsPath), { recursive: true });
22298
22980
  const tmp = `${wsPath}.${process.pid}.tmp`;
22299
22981
  try {
22300
22982
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -22329,7 +23011,7 @@ function applyWorkspaceBranchPolicy(opts) {
22329
23011
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
22330
23012
  var readJson = (p) => {
22331
23013
  try {
22332
- const parsed = JSON.parse(fs17.readFileSync(p, "utf8"));
23014
+ const parsed = JSON.parse(fs19.readFileSync(p, "utf8"));
22333
23015
  return isConfigObject(parsed) ? parsed : null;
22334
23016
  } catch {
22335
23017
  return null;
@@ -22341,7 +23023,7 @@ var resolveInitProvider = (cwd) => {
22341
23023
  return env.toLowerCase();
22342
23024
  const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
22343
23025
  try {
22344
- const out = spawnSync18("git", ["remote", "get-url", "origin"], {
23026
+ const out = spawnSync19("git", ["remote", "get-url", "origin"], {
22345
23027
  cwd: root,
22346
23028
  encoding: "utf8"
22347
23029
  });
@@ -22359,7 +23041,7 @@ var youtrackJsonContent = (dir) => {
22359
23041
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
22360
23042
  return {
22361
23043
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
22362
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path28.join(dir, "youtrack.token"),
23044
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path31.join(dir, "youtrack.token"),
22363
23045
  ...meetingIssue ? {
22364
23046
  meetingIssue,
22365
23047
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -22389,19 +23071,19 @@ var vcsJsonContent = () => {
22389
23071
  };
22390
23072
  function initApplyData(action, env = process.env) {
22391
23073
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22392
- fs17.mkdirSync(dir, { recursive: true });
23074
+ fs19.mkdirSync(dir, { recursive: true });
22393
23075
  switch (action) {
22394
23076
  case "youtrack_json": {
22395
- const out = path28.join(dir, "youtrack.json");
22396
- fs17.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23077
+ const out = path31.join(dir, "youtrack.json");
23078
+ fs19.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22397
23079
  `, "utf8");
22398
23080
  return { action, ok: true, path: out };
22399
23081
  }
22400
23082
  case "youtrack_token_placeholder": {
22401
- const p = path28.join(dir, "youtrack.token");
23083
+ const p = path31.join(dir, "youtrack.token");
22402
23084
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
22403
23085
  `, 384) === "preserved";
22404
- const abs = path28.resolve(p);
23086
+ const abs = path31.resolve(p);
22405
23087
  return {
22406
23088
  action,
22407
23089
  ok: true,
@@ -22413,14 +23095,14 @@ function initApplyData(action, env = process.env) {
22413
23095
  };
22414
23096
  }
22415
23097
  case "youtrack_scaffold": {
22416
- const jsonOut = path28.join(dir, "youtrack.json");
22417
- const tokenOut = path28.join(dir, "youtrack.token");
22418
- fs17.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23098
+ const jsonOut = path31.join(dir, "youtrack.json");
23099
+ const tokenOut = path31.join(dir, "youtrack.token");
23100
+ fs19.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22419
23101
  `, "utf8");
22420
23102
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
22421
23103
  `, 384) === "preserved";
22422
- const configPath = path28.resolve(jsonOut);
22423
- const tokenPath = path28.resolve(tokenOut);
23104
+ const configPath = path31.resolve(jsonOut);
23105
+ const tokenPath = path31.resolve(tokenOut);
22424
23106
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
22425
23107
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
22426
23108
  let tokenCreate = {};
@@ -22472,10 +23154,10 @@ function initApplyData(action, env = process.env) {
22472
23154
  }
22473
23155
  }
22474
23156
  case "vcs_scaffold": {
22475
- const jsonOut = path28.join(dir, "vcs.json");
22476
- fs17.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
23157
+ const jsonOut = path31.join(dir, "vcs.json");
23158
+ fs19.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
22477
23159
  `, "utf8");
22478
- const configPath = path28.resolve(jsonOut);
23160
+ const configPath = path31.resolve(jsonOut);
22479
23161
  const prev = process.env.WORKFLOW_VCS_CONFIG;
22480
23162
  process.env.WORKFLOW_VCS_CONFIG = configPath;
22481
23163
  try {
@@ -22594,7 +23276,7 @@ var executeInitApply = (args, directory, runtime) => {
22594
23276
  protectedNames: args.branch_policy_protected
22595
23277
  }, current);
22596
23278
  writeConfig(next);
22597
- return output(ok({ action: "config", path: path29.join(configDir(), "config.json"), ...next }));
23279
+ return output(ok({ action: "config", path: path32.join(configDir(), "config.json"), ...next }));
22598
23280
  }
22599
23281
  const env = Object.fromEntries(Object.entries({
22600
23282
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -22611,14 +23293,14 @@ var executeInitApply = (args, directory, runtime) => {
22611
23293
 
22612
23294
  // packages/workit-opencode/src/shared/session.ts
22613
23295
  import { realpathSync as realpathSync7 } from "node:fs";
22614
- import path30 from "node:path";
23296
+ import path33 from "node:path";
22615
23297
  var sameWorkspace = (expected, observed) => {
22616
23298
  if (typeof observed !== "string" || !observed)
22617
23299
  return false;
22618
23300
  try {
22619
23301
  return realpathSync7(expected) === realpathSync7(observed);
22620
23302
  } catch {
22621
- return path30.resolve(expected) === path30.resolve(observed);
23303
+ return path33.resolve(expected) === path33.resolve(observed);
22622
23304
  }
22623
23305
  };
22624
23306
 
@@ -22690,7 +23372,7 @@ var workitFamilyOf = (toolName) => {
22690
23372
 
22691
23373
  // packages/workit-opencode/src/tools/context.ts
22692
23374
  import { realpathSync as realpathSync8 } from "node:fs";
22693
- import path31 from "node:path";
23375
+ import path34 from "node:path";
22694
23376
  var createContextTool = () => ({
22695
23377
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
22696
23378
  execute: async (args, context) => {
@@ -22700,7 +23382,7 @@ var createContextTool = () => ({
22700
23382
  let root;
22701
23383
  try {
22702
23384
  const cwd = parsed.data.cwd;
22703
- root = realpathSync8(cwd ? path31.resolve(context.directory, cwd) : context.directory);
23385
+ root = realpathSync8(cwd ? path34.resolve(context.directory, cwd) : context.directory);
22704
23386
  } catch {
22705
23387
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
22706
23388
  }
@@ -23244,8 +23926,9 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
23244
23926
  event.message = reason;
23245
23927
  };
23246
23928
  const resources = event.resources.filter((resource) => typeof resource === "string");
23929
+ const owner = liveOwner({ host: "opencode", session: { id: event.sessionID ?? "", parentId: null } }, "in-process-plugin");
23247
23930
  if (WRITE_ACTIONS.has(event.action)) {
23248
- const decision = writeGate(root, resources);
23931
+ const decision = writeGate(root, resources, owner);
23249
23932
  if (decision.kind === "deny")
23250
23933
  deny(decision.reason);
23251
23934
  return;
@@ -23267,9 +23950,8 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
23267
23950
  }, resource, { nudge: false });
23268
23951
  if (raw.kind === "deny")
23269
23952
  return deny(raw.reason);
23270
- const writes = shellWrites(resource);
23271
- const gated = writes.writes ? writeGate(root, writes.targets) : null;
23272
- if (gated?.kind === "deny")
23953
+ const gated = shellGate(cwd, resource, owner);
23954
+ if (gated.kind === "deny")
23273
23955
  return deny(gated.reason);
23274
23956
  }
23275
23957
  };
@@ -23293,7 +23975,7 @@ var childTaskMissing = (store, input) => {
23293
23975
  var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
23294
23976
 
23295
23977
  // packages/workit-opencode/src/v2/shell.ts
23296
- import path32 from "node:path";
23978
+ import path35 from "node:path";
23297
23979
  var commandOf = (input) => {
23298
23980
  if (input === null || typeof input !== "object")
23299
23981
  return null;
@@ -23302,7 +23984,7 @@ var commandOf = (input) => {
23302
23984
  };
23303
23985
  var hookInput = (root, sessionID, id, shell) => ({
23304
23986
  host: "opencode",
23305
- cwd: shell.workdir ? path32.resolve(root, shell.workdir) : root,
23987
+ cwd: shell.workdir ? path35.resolve(root, shell.workdir) : root,
23306
23988
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
23307
23989
  permissionMode: null,
23308
23990
  transcriptPath: null,
@@ -23487,37 +24169,37 @@ var createStopControl = (root, port) => {
23487
24169
 
23488
24170
  // packages/workit-opencode/src/v2/registry.ts
23489
24171
  import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
23490
- import path34 from "node:path";
24172
+ import path37 from "node:path";
23491
24173
 
23492
24174
  // packages/workit-opencode/src/shared/assets.ts
23493
24175
  import { existsSync as existsSync11, realpathSync as realpathSync9 } from "node:fs";
23494
- import path33 from "node:path";
24176
+ import path36 from "node:path";
23495
24177
  import { fileURLToPath as fileURLToPath2 } from "node:url";
23496
24178
  var moduleDir = () => {
23497
24179
  try {
23498
- return realpathSync9(path33.dirname(fileURLToPath2(import.meta.url)));
24180
+ return realpathSync9(path36.dirname(fileURLToPath2(import.meta.url)));
23499
24181
  } catch {
23500
- return path33.dirname(fileURLToPath2(import.meta.url));
24182
+ return path36.dirname(fileURLToPath2(import.meta.url));
23501
24183
  }
23502
24184
  };
23503
24185
  var packageRoot2 = () => {
23504
24186
  const dir = moduleDir();
23505
24187
  for (const candidate of [
23506
- path33.resolve(dir, ".."),
23507
- path33.resolve(dir, "..", ".."),
23508
- path33.resolve(dir, "..", "..", "..")
24188
+ path36.resolve(dir, ".."),
24189
+ path36.resolve(dir, "..", ".."),
24190
+ path36.resolve(dir, "..", "..", "..")
23509
24191
  ]) {
23510
- if (existsSync11(path33.join(candidate, "package.json")))
24192
+ if (existsSync11(path36.join(candidate, "package.json")))
23511
24193
  return candidate;
23512
24194
  }
23513
- return path33.resolve(dir, "..");
24195
+ return path36.resolve(dir, "..");
23514
24196
  };
23515
24197
  var skillsRoot = () => {
23516
24198
  const root = packageRoot2();
23517
- const canonical = path33.resolve(root, "..", "workit-core", "skills");
23518
- if (existsSync11(path33.join(root, "src")) && existsSync11(canonical))
24199
+ const canonical = path36.resolve(root, "..", "workit-core", "skills");
24200
+ if (existsSync11(path36.join(root, "src")) && existsSync11(canonical))
23519
24201
  return canonical;
23520
- return path33.join(root, "assets", "skills");
24202
+ return path36.join(root, "assets", "skills");
23521
24203
  };
23522
24204
 
23523
24205
  // packages/workit-opencode/src/v2/registry.ts
@@ -23544,11 +24226,11 @@ var registerSkills = async (ctx) => {
23544
24226
  if (!existsSync12(skillsDir))
23545
24227
  return new Set;
23546
24228
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
23547
- const own = (skill) => typeof skill.path === "string" && path34.resolve(skill.path).startsWith(skillsDir + path34.sep);
24229
+ const own = (skill) => typeof skill.path === "string" && path37.resolve(skill.path).startsWith(skillsDir + path37.sep);
23548
24230
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
23549
24231
  const packaged = new Map;
23550
24232
  for (const id of WORKIT_METHOD_SKILLS) {
23551
- const file = path34.join(skillsDir, id, "SKILL.md");
24233
+ const file = path37.join(skillsDir, id, "SKILL.md");
23552
24234
  if (!userSkills.has(id) && existsSync12(file))
23553
24235
  packaged.set(id, file);
23554
24236
  }
@@ -23595,23 +24277,23 @@ var registerCommands = async (ctx, registeredSkills) => {
23595
24277
  };
23596
24278
 
23597
24279
  // packages/workit-opencode/src/stale-sources.ts
23598
- import { existsSync as existsSync13, readdirSync as readdirSync5 } from "node:fs";
23599
- import path35 from "node:path";
24280
+ import { existsSync as existsSync13, readdirSync as readdirSync6 } from "node:fs";
24281
+ import path38 from "node:path";
23600
24282
  import { fileURLToPath as fileURLToPath3 } from "node:url";
23601
24283
  var typescriptFiles = (directory) => {
23602
24284
  if (!existsSync13(directory))
23603
24285
  return [];
23604
- return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
23605
- const file = path35.join(directory, entry.name);
24286
+ return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
24287
+ const file = path38.join(directory, entry.name);
23606
24288
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
23607
24289
  });
23608
24290
  };
23609
24291
  var pluginSourceFiles = [
23610
24292
  ...new Set([
23611
24293
  fileURLToPath3(import.meta.url),
23612
- ...typescriptFiles(path35.join(packageRoot2(), "src")),
23613
- path35.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
23614
- ...typescriptFiles(path35.join(packageRoot2(), "..", "workit-core", "src", "core"))
24294
+ ...typescriptFiles(path38.join(packageRoot2(), "src")),
24295
+ path38.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
24296
+ ...typescriptFiles(path38.join(packageRoot2(), "..", "workit-core", "src", "core"))
23615
24297
  ])
23616
24298
  ].filter((file) => existsSync13(file)).toSorted();
23617
24299