kith-coord 0.1.0 → 0.1.2

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/index.d.ts CHANGED
@@ -73,6 +73,7 @@ declare class FencingAndAgentManager {
73
73
  private readonly coordDb;
74
74
  static readonly DEFAULT_LEASE_MS: number;
75
75
  private readonly capabilities;
76
+ private static ownWindowsStart;
76
77
  constructor(coordDb: CoordDatabase);
77
78
  static validateLease(duration: number): number;
78
79
  /** A real kernel process identity; tests must not supply liveness sentinels. */
@@ -977,7 +978,7 @@ declare function recoveryReport(directory?: string): {
977
978
  error: string | undefined;
978
979
  };
979
980
 
980
- declare const KITH_VERSION = "0.1.0";
981
- declare const KITH_PROTOCOL_VERSION = "1.0.0";
981
+ declare const KITH_VERSION = "0.1.2";
982
+ declare const KITH_PROTOCOL_VERSION = "1.1.0";
982
983
 
983
984
  export { type AcquireLockResult, type AgentRecord, type AgentUnreadDelta, AstAnalyzer, type BashGuardPostReport, BashMerkleGuard, type BlastRadiusAlert, CoWWorktreeManager, type ConflictRecord, type ContractRecord, CoordDatabase, type CoordEvent, type DeclareIntentResult, DependencyAndContractEngine, type DirtyMergeProbeResult, type ExtractedImport, type ExtractedSymbol, FencingAndAgentManager, type FileAstAnalysis, type FileModificationReport, type FileSnapshotEntry, IntentAndTaskEngine, KITH_PROTOCOL_VERSION, KITH_VERSION, KithEngine, type LockConflict, LockEngine, type LockRecord, type MergeTrainEntry, type PostFileEditHookResult, type QuarantineRecord, type RepositoryLayout, ResourceBroker, type ResourceRecord, SessionOwnershipError, type SessionRegistration, ShadowGitEngine, type SpawnedWorktree, type SpeculativeMergeResult, SpeculativeMergeTrain, StaleFencingTokenError, type SymbolKind, type TaskRecord, type WorkspaceObservation, type WorktreeChange, caseSensitivePaths, discoverRepository, isWithin, observeWorkspaces, physicalPath, previewIntegration, readCoordinationState, recoveryReport, repositoryFile, worktreeChanges };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
- var KITH_VERSION = "0.1.0";
3
- var KITH_PROTOCOL_VERSION = "1.0.0";
2
+ var KITH_VERSION = "0.1.2";
3
+ var KITH_PROTOCOL_VERSION = "1.1.0";
4
4
 
5
5
  // src/core/workspace-observation.ts
6
6
  import { execFileSync as execFileSync2, spawnSync } from "child_process";
@@ -10,6 +10,7 @@ import { execFileSync } from "child_process";
10
10
  import fs from "fs";
11
11
  import path from "path";
12
12
  function physicalPath(input) {
13
+ if (process.platform === "win32") input = input.replaceAll("/", path.sep);
13
14
  const absolute = path.isAbsolute(input) ? input : process.cwd() + path.sep + input;
14
15
  let current = path.parse(absolute).root;
15
16
  for (const component of absolute.slice(current.length).split(path.sep)) {
@@ -40,7 +41,7 @@ function isWithin(root, target) {
40
41
  return relative !== ".." && !relative.startsWith(".." + path.sep) && !path.isAbsolute(relative);
41
42
  }
42
43
  function repositoryFile(root, file) {
43
- if (!file || /[\\\x00-\x1f\x7f]/u.test(file)) throw new Error(`Invalid file path: ${file}`);
44
+ if (!file || /[\x00-\x1f\x7f]/u.test(file) || process.platform !== "win32" && file.includes("\\")) throw new Error(`Invalid file path: ${file}`);
44
45
  const canonicalRoot = physicalPath(root);
45
46
  const target = physicalPath(path.isAbsolute(file) ? file : canonicalRoot + path.sep + file);
46
47
  if (!isWithin(canonicalRoot, target) || target === canonicalRoot) {
@@ -230,7 +231,7 @@ function previewIntegration(repoRoot, agent) {
230
231
 
231
232
  // src/core/engine.ts
232
233
  import fs12 from "fs";
233
- import path10 from "path";
234
+ import path11 from "path";
234
235
 
235
236
  // src/core/ast-analyzer.ts
236
237
  import crypto from "crypto";
@@ -511,12 +512,12 @@ var AstAnalyzer = class _AstAnalyzer {
511
512
  // src/core/bash-guard.ts
512
513
  import crypto4 from "crypto";
513
514
  import fs5 from "fs";
514
- import path4 from "path";
515
+ import path5 from "path";
515
516
 
516
517
  // src/core/locks.ts
517
518
  import crypto3 from "crypto";
518
519
  import fs4 from "fs";
519
- import path3 from "path";
520
+ import path4 from "path";
520
521
 
521
522
  // src/core/glob.ts
522
523
  function scopeSegments(pattern, literal = false) {
@@ -587,6 +588,7 @@ function scopesOverlap(a, b, literalA = false, literalB = false) {
587
588
  import { execFileSync as execFileSync3 } from "child_process";
588
589
  import crypto2 from "crypto";
589
590
  import fs3 from "fs";
591
+ import path3 from "path";
590
592
  var SessionOwnershipError = class extends Error {
591
593
  constructor(agentId) {
592
594
  super(`No valid active session capability for '${agentId}'. Start a session, or resume it with its session token.`);
@@ -612,6 +614,7 @@ var FencingAndAgentManager = class _FencingAndAgentManager {
612
614
  coordDb;
613
615
  static DEFAULT_LEASE_MS = 5 * 60 * 1e3;
614
616
  capabilities = /* @__PURE__ */ new Map();
617
+ static ownWindowsStart = null;
615
618
  static validateLease(duration) {
616
619
  if (!Number.isSafeInteger(duration) || duration < 100 || duration > 864e5) {
617
620
  throw new Error("Lease duration must be an integer between 100 and 86400000 milliseconds");
@@ -626,6 +629,24 @@ var FencingAndAgentManager = class _FencingAndAgentManager {
626
629
  const stat = fs3.readFileSync(`/proc/${pid}/stat`, "utf8");
627
630
  return `linux:${stat.slice(stat.lastIndexOf(")") + 2).split(" ")[19]}`;
628
631
  }
632
+ if (process.platform === "win32") {
633
+ if (pid === process.pid && this.ownWindowsStart) return this.ownWindowsStart;
634
+ const powershell = path3.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
635
+ const ticks = execFileSync3(
636
+ powershell,
637
+ [
638
+ "-NoProfile",
639
+ "-NonInteractive",
640
+ "-Command",
641
+ `(Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks.ToString([System.Globalization.CultureInfo]::InvariantCulture)`
642
+ ],
643
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3, windowsHide: true }
644
+ ).trim();
645
+ if (!/^\d+$/.test(ticks)) return null;
646
+ const identity = `windows:${ticks}`;
647
+ if (pid === process.pid) this.ownWindowsStart = identity;
648
+ return identity;
649
+ }
629
650
  const out = execFileSync3("ps", ["-o", "lstart=", "-p", String(pid)], {
630
651
  encoding: "utf8",
631
652
  stdio: ["ignore", "pipe", "ignore"],
@@ -815,9 +836,9 @@ var LockEngine = class _LockEngine {
815
836
  const parts = rawPattern.split("/");
816
837
  const magic = parts.findIndex((p) => /[?*]/u.test(p));
817
838
  const prefix = magic < 0 ? rawPattern : parts.slice(0, magic).join("/") || ".";
818
- const absolute = physicalPath(path3.isAbsolute(prefix) ? prefix : root + "/" + prefix);
839
+ const absolute = physicalPath(path4.isAbsolute(prefix) ? prefix : root + "/" + prefix);
819
840
  if (!isWithin(root, absolute)) throw new Error(`Scope is outside repository: ${rawPattern}`);
820
- const relative = path3.relative(root, absolute).split(path3.sep).join("/");
841
+ const relative = path4.relative(root, absolute).split(path4.sep).join("/");
821
842
  const suffix = magic < 0 ? "" : parts.slice(magic).join("/");
822
843
  const normalized = [relative, suffix].filter(Boolean).join("/");
823
844
  scopeSegments(normalized);
@@ -852,7 +873,7 @@ var LockEngine = class _LockEngine {
852
873
  let budget = 1e4;
853
874
  while (pending.length) {
854
875
  const { root, relative } = pending.pop();
855
- const dir = path3.join(root, relative);
876
+ const dir = path4.join(root, relative);
856
877
  let entries;
857
878
  try {
858
879
  entries = fs4.readdirSync(dir, { withFileTypes: true });
@@ -1111,7 +1132,7 @@ var BashMerkleGuard = class {
1111
1132
  getSnapshotFilePath(agentId) {
1112
1133
  const epoch = this.fencing.assertSession(agentId);
1113
1134
  const safeId = crypto4.createHash("sha256").update(`${agentId}:${epoch}`).digest("hex");
1114
- return path4.join(this.coordDb.coordDir, "snapshots", `${safeId}.json`);
1135
+ return path5.join(this.coordDb.coordDir, "snapshots", `${safeId}.json`);
1115
1136
  }
1116
1137
  /**
1117
1138
  * Captures a Merkle snapshot of workspace files before a Bash tool execution.
@@ -1123,7 +1144,7 @@ var BashMerkleGuard = class {
1123
1144
  const entries = {};
1124
1145
  const files = this.walkWorkspaceFiles(rootDir);
1125
1146
  for (const absPath of files) {
1126
- const relPath = path4.relative(rootDir, absPath).replace(/\\/g, "/");
1147
+ const relPath = path5.relative(rootDir, absPath).replace(/\\/g, "/");
1127
1148
  const stat = fs5.statSync(absPath);
1128
1149
  if (stat.size > 5 * 1024 * 1024) continue;
1129
1150
  const buf = fs5.readFileSync(absPath);
@@ -1166,7 +1187,7 @@ var BashMerkleGuard = class {
1166
1187
  const authorizedModifiedFiles = [];
1167
1188
  const quarantinedViolations = [];
1168
1189
  for (const absPath of currentFiles) {
1169
- const relPath = path4.relative(rootDir, absPath).replace(/\\/g, "/");
1190
+ const relPath = path5.relative(rootDir, absPath).replace(/\\/g, "/");
1170
1191
  seenCurrentRelPaths.add(relPath);
1171
1192
  const stat = fs5.statSync(absPath);
1172
1193
  if (stat.size > 5 * 1024 * 1024) continue;
@@ -1228,7 +1249,7 @@ var BashMerkleGuard = class {
1228
1249
  }
1229
1250
  if (authorizedModifiedFiles.length > 0) {
1230
1251
  for (const relFile of authorizedModifiedFiles) {
1231
- const absFile = path4.join(rootDir, relFile);
1252
+ const absFile = path5.join(rootDir, relFile);
1232
1253
  if (fs5.existsSync(absFile) && /\.(ts|tsx|js|jsx|mjs|py)$/.test(relFile)) {
1233
1254
  this.depGraph.recordFileModification(params.agentId, relFile, fs5.readFileSync(absFile, "utf8"));
1234
1255
  }
@@ -1249,7 +1270,7 @@ var BashMerkleGuard = class {
1249
1270
  const id = `quar_${crypto4.randomBytes(6).toString("hex")}`;
1250
1271
  const now = Date.now();
1251
1272
  const safeName = params.relPath.replace(/[^a-zA-Z0-9._-]/g, "_");
1252
- const patchPath = path4.join(
1273
+ const patchPath = path5.join(
1253
1274
  this.coordDb.coordDir,
1254
1275
  "quarantine",
1255
1276
  `${id}_${safeName}.quarantine`
@@ -1263,13 +1284,13 @@ var BashMerkleGuard = class {
1263
1284
  if (params.prevEntry?.backupContentBase64 !== void 0) {
1264
1285
  fs5.writeFileSync(patchPath + ".before", Buffer.from(params.prevEntry.backupContentBase64, "base64"), { mode: 384 });
1265
1286
  }
1266
- const absTarget = path4.join(params.rootDir, params.relPath);
1287
+ const absTarget = path5.join(params.rootDir, params.relPath);
1267
1288
  if (params.restoreLockedFiles && params.prevEntry === null) {
1268
1289
  if (fs5.existsSync(absTarget)) {
1269
1290
  fs5.unlinkSync(absTarget);
1270
1291
  }
1271
1292
  } else if (params.restoreLockedFiles && params.prevEntry?.backupContentBase64 !== void 0) {
1272
- fs5.mkdirSync(path4.dirname(absTarget), { recursive: true });
1293
+ fs5.mkdirSync(path5.dirname(absTarget), { recursive: true });
1273
1294
  fs5.writeFileSync(
1274
1295
  absTarget,
1275
1296
  Buffer.from(params.prevEntry.backupContentBase64, "base64")
@@ -1313,7 +1334,7 @@ var BashMerkleGuard = class {
1313
1334
  if (entry.name === ".git" || entry.name === ".coord" || entry.name === "node_modules" || entry.name === "dist" || entry.name === "coverage" || entry.name === ".next" || entry.name === "target") {
1314
1335
  continue;
1315
1336
  }
1316
- const fullPath = path4.join(dir, entry.name);
1337
+ const fullPath = path5.join(dir, entry.name);
1317
1338
  if (entry.isDirectory()) {
1318
1339
  this.walkWorkspaceFiles(fullPath, acc);
1319
1340
  } else if (entry.isFile()) {
@@ -1750,7 +1771,7 @@ function delay(ms) {
1750
1771
  // src/core/db.ts
1751
1772
  import { createRequire } from "module";
1752
1773
  import fs7 from "fs";
1753
- import path5 from "path";
1774
+ import path6 from "path";
1754
1775
  var require2 = createRequire(import.meta.url);
1755
1776
  var { DatabaseSync } = require2("node:sqlite");
1756
1777
  var CoordDatabase = class _CoordDatabase {
@@ -1766,19 +1787,19 @@ var CoordDatabase = class _CoordDatabase {
1766
1787
  this.repoRoot = layout.checkoutRoot;
1767
1788
  this.commonGitDir = layout.commonGitDir;
1768
1789
  this.isLinkedWorktree = layout.isLinkedWorktree;
1769
- for (let current = fs7.realpathSync.native(repoRoot); isWithin(this.repoRoot, current); current = path5.dirname(current)) {
1770
- const local = path5.join(current, ".coord");
1771
- if (local !== layout.coordDir && fs7.existsSync(path5.join(local, "state.db"))) {
1790
+ for (let current = fs7.realpathSync.native(repoRoot); isWithin(this.repoRoot, current); current = path6.dirname(current)) {
1791
+ const local = path6.join(current, ".coord");
1792
+ if (local !== layout.coordDir && fs7.existsSync(path6.join(local, "state.db"))) {
1772
1793
  throw new Error(`Legacy checkout-local coordination state found at ${local}. Stop clients and preserve that directory before consolidating shared state.`);
1773
1794
  }
1774
1795
  if (current === this.repoRoot) break;
1775
1796
  }
1776
1797
  this.coordDir = layout.coordDir;
1777
1798
  if (physicalPath(this.coordDir) !== this.coordDir) throw new Error("Coordination directory must not be a symlink");
1778
- this.dbPath = customDbPath ?? path5.join(this.coordDir, "state.db");
1799
+ this.dbPath = customDbPath ?? path6.join(this.coordDir, "state.db");
1779
1800
  if (!customDbPath && physicalPath(this.dbPath) !== this.dbPath) throw new Error("Coordination database must not be a symlink");
1780
- const recoveryFiles = !customDbPath && (fs7.existsSync(path5.join(this.coordDir, "merge-train.lock")) || fs7.existsSync(path5.join(this.coordDir, "setup.lock")) || ["shadow-index", "quarantine", "snapshots", "worktrees", "setup"].some((name) => {
1781
- const dir = path5.join(this.coordDir, name);
1801
+ const recoveryFiles = !customDbPath && (fs7.existsSync(path6.join(this.coordDir, "merge-train.lock")) || fs7.existsSync(path6.join(this.coordDir, "setup.lock")) || ["shadow-index", "quarantine", "snapshots", "worktrees", "setup"].some((name) => {
1802
+ const dir = path6.join(this.coordDir, name);
1782
1803
  return fs7.existsSync(dir) && (!fs7.lstatSync(dir).isDirectory() || fs7.readdirSync(dir).length > 0);
1783
1804
  }));
1784
1805
  const sidecars = ["-wal", "-shm", "-journal"].some((suffix) => fs7.existsSync(this.dbPath + suffix));
@@ -1798,7 +1819,7 @@ var CoordDatabase = class _CoordDatabase {
1798
1819
  }
1799
1820
  fs7.mkdirSync(this.coordDir, { recursive: true, mode: 448 });
1800
1821
  for (const name of ["shadow-index", "quarantine", "snapshots", "worktrees"]) {
1801
- const target = path5.join(this.coordDir, name);
1822
+ const target = path6.join(this.coordDir, name);
1802
1823
  if (!isWithin(this.coordDir, physicalPath(target)) || physicalPath(target) !== target) {
1803
1824
  throw new Error(`Coordination subdirectory must not be a symlink: ${name}`);
1804
1825
  }
@@ -2159,7 +2180,7 @@ var CoordDatabase = class _CoordDatabase {
2159
2180
  // src/core/dep-graph.ts
2160
2181
  import crypto6 from "crypto";
2161
2182
  import fs8 from "fs";
2162
- import path6 from "path";
2183
+ import path7 from "path";
2163
2184
  var DependencyAndContractEngine = class {
2164
2185
  constructor(coordDb, lockEngine, fencing) {
2165
2186
  this.coordDb = coordDb;
@@ -2182,13 +2203,13 @@ var DependencyAndContractEngine = class {
2182
2203
  return indexedCount;
2183
2204
  }
2184
2205
  walkSourceFiles(dir, acc = []) {
2185
- if (!fs8.existsSync(dir) || dir !== this.coordDb.repoRoot && fs8.existsSync(path6.join(dir, ".git"))) return acc;
2206
+ if (!fs8.existsSync(dir) || dir !== this.coordDb.repoRoot && fs8.existsSync(path7.join(dir, ".git"))) return acc;
2186
2207
  const entries = fs8.readdirSync(dir, { withFileTypes: true });
2187
2208
  for (const entry of entries) {
2188
2209
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target" || entry.name === "coverage") {
2189
2210
  continue;
2190
2211
  }
2191
- const fullPath = path6.join(dir, entry.name);
2212
+ const fullPath = path7.join(dir, entry.name);
2192
2213
  if (entry.isDirectory()) {
2193
2214
  this.walkSourceFiles(fullPath, acc);
2194
2215
  } else if (entry.isFile() && /\.(ts|tsx|js|jsx|mjs|py)$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
@@ -2249,6 +2270,7 @@ var DependencyAndContractEngine = class {
2249
2270
  }
2250
2271
  for (const exp of analysis.exports) {
2251
2272
  const prev = existingBySymbol.get(exp.symbolName);
2273
+ if (prev && prev.kind === exp.kind && prev.signature === exp.signature && prev.ast_hash === exp.astHash && prev.params_fingerprint === exp.paramsFingerprint && prev.doc_comment === exp.docComment) continue;
2252
2274
  const isBreaking = prev !== void 0 && prev.ast_hash !== exp.astHash;
2253
2275
  if (isBreaking && prev) {
2254
2276
  changedSymbols.push({
@@ -2720,7 +2742,7 @@ var IntentAndTaskEngine = class {
2720
2742
  import { execFileSync as execFileSync4, execSync } from "child_process";
2721
2743
  import crypto8 from "crypto";
2722
2744
  import fs9 from "fs";
2723
- import path7 from "path";
2745
+ import path8 from "path";
2724
2746
  var SpeculativeMergeTrain = class {
2725
2747
  constructor(coordDb, fencing) {
2726
2748
  this.coordDb = coordDb;
@@ -2759,8 +2781,8 @@ var SpeculativeMergeTrain = class {
2759
2781
  now
2760
2782
  );
2761
2783
  });
2762
- const stageDir = path7.join(this.coordDb.coordDir, "worktrees", `_merge_train_${id}`);
2763
- const lockPath = path7.join(this.coordDb.coordDir, "merge-train.lock");
2784
+ const stageDir = path8.join(this.coordDb.coordDir, "worktrees", `_merge_train_${id}`);
2785
+ const lockPath = path8.join(this.coordDb.coordDir, "merge-train.lock");
2764
2786
  let lockFd;
2765
2787
  try {
2766
2788
  try {
@@ -2811,8 +2833,8 @@ var SpeculativeMergeTrain = class {
2811
2833
  cwd: this.coordDb.repoRoot,
2812
2834
  stdio: "ignore"
2813
2835
  });
2814
- const rootNodeModules = path7.join(this.coordDb.repoRoot, "node_modules");
2815
- const stageNodeModules = path7.join(stageDir, "node_modules");
2836
+ const rootNodeModules = path8.join(this.coordDb.repoRoot, "node_modules");
2837
+ const stageNodeModules = path8.join(stageDir, "node_modules");
2816
2838
  const hydratedNodeModules = fs9.existsSync(rootNodeModules) && !fs9.existsSync(stageNodeModules);
2817
2839
  if (hydratedNodeModules) {
2818
2840
  fs9.symlinkSync(rootNodeModules, stageNodeModules, "dir");
@@ -2977,7 +2999,7 @@ ${combined}`
2977
2999
  import { execFileSync as execFileSync5 } from "child_process";
2978
3000
  import crypto9 from "crypto";
2979
3001
  import fs10 from "fs";
2980
- import path8 from "path";
3002
+ import path9 from "path";
2981
3003
  var ShadowGitEngine = class {
2982
3004
  constructor(coordDb, fencing) {
2983
3005
  this.coordDb = coordDb;
@@ -3010,7 +3032,7 @@ var ShadowGitEngine = class {
3010
3032
  getShadowIndexPath(agentId) {
3011
3033
  const safeId = agentId.replace(/[^a-zA-Z0-9_-]/g, "_");
3012
3034
  const epoch = this.fencing.assertSession(agentId);
3013
- return path8.join(this.coordDb.coordDir, "shadow-index", `${safeId}-${epoch}.index`);
3035
+ return path9.join(this.coordDb.coordDir, "shadow-index", `${safeId}-${epoch}.index`);
3014
3036
  }
3015
3037
  /**
3016
3038
  * Updates an agent's ephemeral shadow Git index (`.coord/shadow-index/<agent-id>.index`)
@@ -3047,7 +3069,7 @@ var ShadowGitEngine = class {
3047
3069
  } else if (params.modifiedFiles && params.modifiedFiles.length > 0) {
3048
3070
  for (const mod of params.modifiedFiles) {
3049
3071
  const relPath = LockEngine.normalizeFile(targetDir, mod.filePath);
3050
- const absPath = path8.join(targetDir, relPath);
3072
+ const absPath = path9.join(targetDir, relPath);
3051
3073
  const content = mod.content ?? (fs10.existsSync(absPath) ? fs10.readFileSync(absPath, "utf8") : null);
3052
3074
  if (content === null) {
3053
3075
  {
@@ -3248,7 +3270,7 @@ var ShadowGitEngine = class {
3248
3270
  // src/core/worktree-cow.ts
3249
3271
  import { execFileSync as execFileSync6 } from "child_process";
3250
3272
  import fs11 from "fs";
3251
- import path9 from "path";
3273
+ import path10 from "path";
3252
3274
  var CoWWorktreeManager = class _CoWWorktreeManager {
3253
3275
  constructor(coordDb, fencing) {
3254
3276
  this.coordDb = coordDb;
@@ -3268,13 +3290,13 @@ var CoWWorktreeManager = class _CoWWorktreeManager {
3268
3290
  const safeId = params.agentId.replace(/[^a-zA-Z0-9_-]/g, "-");
3269
3291
  const branch = params.branch ?? `kith/${safeId}`;
3270
3292
  const baseRef = params.baseRef ?? "HEAD";
3271
- const worktreePath = path9.join(this.coordDb.coordDir, "worktrees", safeId);
3293
+ const worktreePath = path10.join(this.coordDb.coordDir, "worktrees", safeId);
3272
3294
  if (!this.fencing.getAgent(params.agentId)) throw new Error(`Unknown agent: ${params.agentId}`);
3273
3295
  if (!safeId || safeId === "." || safeId === "..") throw new Error("Invalid agent ID");
3274
3296
  if (fs11.existsSync(worktreePath)) {
3275
3297
  throw new Error(`Worktree already exists: ${worktreePath}. Commit or preserve its work before removing it.`);
3276
3298
  }
3277
- fs11.mkdirSync(path9.dirname(worktreePath), { recursive: true });
3299
+ fs11.mkdirSync(path10.dirname(worktreePath), { recursive: true });
3278
3300
  execFileSync6("git", ["worktree", "add", "-b", branch, worktreePath, baseRef], {
3279
3301
  cwd: this.coordDb.repoRoot,
3280
3302
  stdio: "ignore"
@@ -3282,16 +3304,16 @@ var CoWWorktreeManager = class _CoWWorktreeManager {
3282
3304
  const hydratedDirs = [];
3283
3305
  let hydrationMethod = "none";
3284
3306
  for (const dirName of _CoWWorktreeManager.HYDRATABLE_DIRS) {
3285
- const srcDir = path9.join(this.coordDb.repoRoot, dirName);
3286
- const destDir = path9.join(worktreePath, dirName);
3307
+ const srcDir = path10.join(this.coordDb.repoRoot, dirName);
3308
+ const destDir = path10.join(worktreePath, dirName);
3287
3309
  if (!fs11.existsSync(srcDir) || fs11.existsSync(destDir)) continue;
3288
3310
  const method = this.cloneDirectoryCoW(srcDir, destDir);
3289
3311
  hydrationMethod = method;
3290
3312
  hydratedDirs.push(dirName);
3291
3313
  }
3292
3314
  for (const envName of _CoWWorktreeManager.ENV_FILES) {
3293
- const srcEnv = path9.join(this.coordDb.repoRoot, envName);
3294
- const destEnv = path9.join(worktreePath, envName);
3315
+ const srcEnv = path10.join(this.coordDb.repoRoot, envName);
3316
+ const destEnv = path10.join(worktreePath, envName);
3295
3317
  if (fs11.existsSync(srcEnv) && !fs11.existsSync(destEnv)) {
3296
3318
  fs11.copyFileSync(srcEnv, destEnv);
3297
3319
  }
@@ -3342,7 +3364,7 @@ var CoWWorktreeManager = class _CoWWorktreeManager {
3342
3364
  removeWorktree(agentId) {
3343
3365
  this.fencing.assertSession(agentId);
3344
3366
  const safeId = agentId.replace(/[^a-zA-Z0-9_-]/g, "-");
3345
- const worktreePath = path9.join(this.coordDb.coordDir, "worktrees", safeId);
3367
+ const worktreePath = path10.join(this.coordDb.coordDir, "worktrees", safeId);
3346
3368
  if (!fs11.existsSync(worktreePath)) return false;
3347
3369
  const registered = this.fencing.getAgent(agentId)?.worktree_path;
3348
3370
  if (!registered || physicalPath(registered) !== physicalPath(worktreePath) || this.fencing.listAgents(true).some((agent) => agent.id !== agentId && agent.worktree_path && physicalPath(agent.worktree_path) === physicalPath(worktreePath))) {
@@ -3433,7 +3455,7 @@ var KithEngine = class {
3433
3455
  const file = LockEngine.normalizeFile(targetRoot, params.filePath);
3434
3456
  const access = this.locks.checkFileWriteAccess({ agentId: params.agentId, filePath: file });
3435
3457
  if (!access.allowed) throw new Error(access.reason);
3436
- const content = params.content ?? (fs12.existsSync(path10.join(targetRoot, file)) ? fs12.readFileSync(path10.join(targetRoot, file), "utf8") : "");
3458
+ const content = params.content ?? (fs12.existsSync(path11.join(targetRoot, file)) ? fs12.readFileSync(path11.join(targetRoot, file), "utf8") : "");
3437
3459
  const contractReport = this.depGraph.recordFileModification(
3438
3460
  params.agentId,
3439
3461
  file,
@@ -3573,7 +3595,7 @@ ${lines.join("\n")}` : "";
3573
3595
  // src/core/observation.ts
3574
3596
  import fs13 from "fs";
3575
3597
  import os from "os";
3576
- import path11 from "path";
3598
+ import path12 from "path";
3577
3599
  import { createRequire as createRequire2 } from "module";
3578
3600
  var { DatabaseSync: DatabaseSync2 } = createRequire2(import.meta.url)("node:sqlite");
3579
3601
  function readCoordinationState(directory = process.cwd()) {
@@ -3601,16 +3623,16 @@ function readCoordinationState(directory = process.cwd()) {
3601
3623
  };
3602
3624
  let scratch, db;
3603
3625
  try {
3604
- const file = path11.join(layout.coordDir, "state.db");
3626
+ const file = path12.join(layout.coordDir, "state.db");
3605
3627
  if (physicalPath(layout.coordDir) !== layout.coordDir || physicalPath(file) !== file) throw new Error("Coordination state must not be a symlink");
3606
- for (let current = fs13.realpathSync.native(directory); isWithin(layout.checkoutRoot, current); current = path11.dirname(current)) {
3607
- const local = path11.join(current, ".coord");
3608
- if (local !== layout.coordDir && fs13.existsSync(path11.join(local, "state.db"))) throw new Error(`Legacy independent state at ${local}; preserve it before consolidation`);
3628
+ for (let current = fs13.realpathSync.native(directory); isWithin(layout.checkoutRoot, current); current = path12.dirname(current)) {
3629
+ const local = path12.join(current, ".coord");
3630
+ if (local !== layout.coordDir && fs13.existsSync(path12.join(local, "state.db"))) throw new Error(`Legacy independent state at ${local}; preserve it before consolidation`);
3609
3631
  if (current === layout.checkoutRoot) break;
3610
3632
  }
3611
3633
  if (!fs13.existsSync(file)) {
3612
- if (["setup.lock", "merge-train.lock"].some((name) => fs13.existsSync(path11.join(layout.coordDir, name))) || ["-wal", "-shm", "-journal"].some((s) => fs13.existsSync(file + s)) || ["worktrees", "quarantine", "snapshots", "shadow-index", "setup"].some((name) => {
3613
- const p = path11.join(layout.coordDir, name);
3634
+ if (["setup.lock", "merge-train.lock"].some((name) => fs13.existsSync(path12.join(layout.coordDir, name))) || ["-wal", "-shm", "-journal"].some((s) => fs13.existsSync(file + s)) || ["worktrees", "quarantine", "snapshots", "shadow-index", "setup"].some((name) => {
3635
+ const p = path12.join(layout.coordDir, name);
3614
3636
  return fs13.existsSync(p) && (!fs13.lstatSync(p).isDirectory() || fs13.readdirSync(p).length > 0);
3615
3637
  })) throw new Error("Missing database with retained sidecars; preserve the recovery set");
3616
3638
  return empty;
@@ -3623,14 +3645,14 @@ function readCoordinationState(directory = process.cwd()) {
3623
3645
  return [s.dev, s.ino, s.size, s.mtimeNs, s.ctimeNs].join(":");
3624
3646
  };
3625
3647
  const before = files.map(identity);
3626
- scratch = fs13.mkdtempSync(path11.join(os.tmpdir(), "kith-observe-"));
3648
+ scratch = fs13.mkdtempSync(path12.join(os.tmpdir(), "kith-observe-"));
3627
3649
  for (let i = 0; i < files.length; i++) if (before[i]) {
3628
- const target = path11.join(scratch, path11.basename(files[i]));
3650
+ const target = path12.join(scratch, path12.basename(files[i]));
3629
3651
  fs13.copyFileSync(files[i], target);
3630
3652
  fs13.chmodSync(target, 384);
3631
3653
  }
3632
3654
  if (JSON.stringify(before) !== JSON.stringify(files.map(identity))) throw new Error("Coordination state changed during snapshot; stop writers or retry");
3633
- db = new DatabaseSync2(path11.join(scratch, "state.db"), { readOnly: true });
3655
+ db = new DatabaseSync2(path12.join(scratch, "state.db"), { readOnly: true });
3634
3656
  db.exec("PRAGMA query_only=ON; PRAGMA busy_timeout=1000; BEGIN");
3635
3657
  if (CoordDatabase.inspectSchema(db) !== 2) throw new Error("Offline observation requires schema 2; preserve state and upgrade with clients stopped");
3636
3658
  const all = (sql) => db.prepare(sql).all();
@@ -3662,19 +3684,19 @@ function readCoordinationState(directory = process.cwd()) {
3662
3684
  }
3663
3685
  }
3664
3686
  function recoveryReport(directory = process.cwd()) {
3665
- const state = readCoordinationState(directory), retainedRoot = path11.join(state.repository.coordDir, "worktrees");
3687
+ const state = readCoordinationState(directory), retainedRoot = path12.join(state.repository.coordDir, "worktrees");
3666
3688
  const retained = physicalPath(retainedRoot) === retainedRoot && fs13.existsSync(retainedRoot) && fs13.lstatSync(retainedRoot).isDirectory() && !fs13.lstatSync(retainedRoot).isSymbolicLink() ? fs13.readdirSync(retainedRoot, { withFileTypes: true }).map((entry) => ({
3667
- path: path11.join(retainedRoot, entry.name),
3689
+ path: path12.join(retainedRoot, entry.name),
3668
3690
  symlink: entry.isSymbolicLink(),
3669
- agentIds: state.agents.filter((a) => a.worktree_path === path11.join(retainedRoot, entry.name)).map((a) => a.id)
3691
+ agentIds: state.agents.filter((a) => a.worktree_path === path12.join(retainedRoot, entry.name)).map((a) => a.id)
3670
3692
  })) : [];
3671
3693
  return {
3672
3694
  ...state,
3673
3695
  retainedWorktrees: retained,
3674
3696
  expiredClaims: state.locks.filter((lock) => Number(lock.expires_at) <= state.observedAt),
3675
3697
  unfinishedIntegrations: state.mergeQueue.filter((entry) => entry.status !== "merged"),
3676
- setupLock: { path: path11.join(state.repository.coordDir, "setup.lock"), present: fs13.existsSync(path11.join(state.repository.coordDir, "setup.lock")) },
3677
- integrationLock: { path: path11.join(state.repository.coordDir, "merge-train.lock"), present: fs13.existsSync(path11.join(state.repository.coordDir, "merge-train.lock")) }
3698
+ setupLock: { path: path12.join(state.repository.coordDir, "setup.lock"), present: fs13.existsSync(path12.join(state.repository.coordDir, "setup.lock")) },
3699
+ integrationLock: { path: path12.join(state.repository.coordDir, "merge-train.lock"), present: fs13.existsSync(path12.join(state.repository.coordDir, "merge-train.lock")) }
3678
3700
  };
3679
3701
  }
3680
3702
  export {