@primitive.ai/prim 0.1.0-alpha.91 → 0.1.0-alpha.93

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.
Files changed (40) hide show
  1. package/README.md +34 -2
  2. package/dist/{chunk-67VNLLBN.js → chunk-2GP5UFV7.js} +1 -0
  3. package/dist/{chunk-VW3FDYRT.js → chunk-36TWVNBE.js} +6 -6
  4. package/dist/{chunk-HU4PW6I6.js → chunk-C3VAA5CG.js} +1 -1
  5. package/dist/{chunk-FOLKHXLJ.js → chunk-DCJN6LZH.js} +37 -5
  6. package/dist/{chunk-M6RIJVRE.js → chunk-DSWT6BCC.js} +78 -4
  7. package/dist/{chunk-FBAYCS3X.js → chunk-DXJQR4QI.js} +150 -38
  8. package/dist/{chunk-PCVGBE5B.js → chunk-G2VMA2QO.js} +2 -1
  9. package/dist/{chunk-NAHHFSLH.js → chunk-ICM2XCXS.js} +2 -2
  10. package/dist/{chunk-OAAP2MY6.js → chunk-K7D2EYBS.js} +2 -2
  11. package/dist/{chunk-5AFRMOPX.js → chunk-OH3EZXYX.js} +1 -1
  12. package/dist/{chunk-KFXF2V5G.js → chunk-OTHKG76J.js} +1 -1
  13. package/dist/chunk-OYHYCK4K.js +13 -0
  14. package/dist/{chunk-FKXFUUWJ.js → chunk-TYAHC25F.js} +1 -1
  15. package/dist/{chunk-5GYQ4J3D.js → chunk-UKUAAAXE.js} +2 -2
  16. package/dist/{chunk-EWZMDD44.js → chunk-X64HTHUF.js} +22 -3
  17. package/dist/{chunk-WX52U7Z2.js → chunk-ZU563PQI.js} +58 -2
  18. package/dist/daemon/server.js +382 -124
  19. package/dist/hook-runtime/daemon/server.js +382 -124
  20. package/dist/hook-runtime/hooks/post-commit.js +57 -39
  21. package/dist/hook-runtime/hooks/post-rewrite.js +58 -40
  22. package/dist/hook-runtime/hooks/post-tool-use.js +111 -78
  23. package/dist/hook-runtime/hooks/pre-tool-use.js +48 -26
  24. package/dist/hook-runtime/hooks/prim-hook.js +157 -117
  25. package/dist/hook-runtime/hooks/session-end.js +1 -1
  26. package/dist/hook-runtime/hooks/session-start.js +38 -4
  27. package/dist/hook-runtime/index.js +1293 -526
  28. package/dist/hook-runtime/statusline-main.js +23 -1
  29. package/dist/hooks/post-commit.js +11 -8
  30. package/dist/hooks/post-rewrite.js +11 -8
  31. package/dist/hooks/post-tool-use.js +13 -15
  32. package/dist/hooks/pre-commit.js +4 -4
  33. package/dist/hooks/pre-tool-use.js +10 -12
  34. package/dist/hooks/prim-hook.js +17 -14
  35. package/dist/hooks/session-end.js +4 -4
  36. package/dist/hooks/session-start.js +12 -36
  37. package/dist/index.js +867 -291
  38. package/dist/statusline-main.js +23 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-BTNNEH2W.js +0 -79
@@ -3035,7 +3035,7 @@ var require_commander = __commonJS({
3035
3035
  var require_polyfills = __commonJS({
3036
3036
  "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js"(exports, module) {
3037
3037
  "use strict";
3038
- var constants8 = __require("constants");
3038
+ var constants9 = __require("constants");
3039
3039
  var origCwd = process.cwd;
3040
3040
  var cwd = null;
3041
3041
  var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
@@ -3059,7 +3059,7 @@ var require_polyfills = __commonJS({
3059
3059
  var chdir;
3060
3060
  module.exports = patch;
3061
3061
  function patch(fs5) {
3062
- if (constants8.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
3062
+ if (constants9.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
3063
3063
  patchLchmod(fs5);
3064
3064
  }
3065
3065
  if (!fs5.lutimes) {
@@ -3161,7 +3161,7 @@ var require_polyfills = __commonJS({
3161
3161
  fs6.lchmod = function(path9, mode, callback) {
3162
3162
  fs6.open(
3163
3163
  path9,
3164
- constants8.O_WRONLY | constants8.O_SYMLINK,
3164
+ constants9.O_WRONLY | constants9.O_SYMLINK,
3165
3165
  mode,
3166
3166
  function(err, fd) {
3167
3167
  if (err) {
@@ -3177,7 +3177,7 @@ var require_polyfills = __commonJS({
3177
3177
  );
3178
3178
  };
3179
3179
  fs6.lchmodSync = function(path9, mode) {
3180
- var fd = fs6.openSync(path9, constants8.O_WRONLY | constants8.O_SYMLINK, mode);
3180
+ var fd = fs6.openSync(path9, constants9.O_WRONLY | constants9.O_SYMLINK, mode);
3181
3181
  var threw = true;
3182
3182
  var ret;
3183
3183
  try {
@@ -3197,9 +3197,9 @@ var require_polyfills = __commonJS({
3197
3197
  };
3198
3198
  }
3199
3199
  function patchLutimes(fs6) {
3200
- if (constants8.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
3200
+ if (constants9.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
3201
3201
  fs6.lutimes = function(path9, at, mt, cb) {
3202
- fs6.open(path9, constants8.O_SYMLINK, function(er, fd) {
3202
+ fs6.open(path9, constants9.O_SYMLINK, function(er, fd) {
3203
3203
  if (er) {
3204
3204
  if (cb) cb(er);
3205
3205
  return;
@@ -3212,7 +3212,7 @@ var require_polyfills = __commonJS({
3212
3212
  });
3213
3213
  };
3214
3214
  fs6.lutimesSync = function(path9, at, mt) {
3215
- var fd = fs6.openSync(path9, constants8.O_SYMLINK);
3215
+ var fd = fs6.openSync(path9, constants9.O_SYMLINK);
3216
3216
  var ret;
3217
3217
  var threw = true;
3218
3218
  try {
@@ -3495,14 +3495,14 @@ var require_graceful_fs = __commonJS({
3495
3495
  return close;
3496
3496
  })(fs5.close);
3497
3497
  fs5.closeSync = (function(fs$closeSync) {
3498
- function closeSync8(fd) {
3498
+ function closeSync9(fd) {
3499
3499
  fs$closeSync.apply(fs5, arguments);
3500
3500
  resetQueue();
3501
3501
  }
3502
- Object.defineProperty(closeSync8, previousSymbol, {
3502
+ Object.defineProperty(closeSync9, previousSymbol, {
3503
3503
  value: fs$closeSync
3504
3504
  });
3505
- return closeSync8;
3505
+ return closeSync9;
3506
3506
  })(fs5.closeSync);
3507
3507
  if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
3508
3508
  process.on("exit", function() {
@@ -4963,7 +4963,7 @@ var require_rc = __commonJS({
4963
4963
  "node_modules/.pnpm/rc@1.2.8/node_modules/rc/index.js"(exports, module) {
4964
4964
  "use strict";
4965
4965
  var cc = require_utils();
4966
- var join28 = __require("path").join;
4966
+ var join30 = __require("path").join;
4967
4967
  var deepExtend = require_deep_extend();
4968
4968
  var etc = "/etc";
4969
4969
  var win = process.platform === "win32";
@@ -4988,15 +4988,15 @@ var require_rc = __commonJS({
4988
4988
  }
4989
4989
  if (!win)
4990
4990
  [
4991
- join28(etc, name, "config"),
4992
- join28(etc, name + "rc")
4991
+ join30(etc, name, "config"),
4992
+ join30(etc, name + "rc")
4993
4993
  ].forEach(addConfigFile);
4994
4994
  if (home)
4995
4995
  [
4996
- join28(home, ".config", name, "config"),
4997
- join28(home, ".config", name),
4998
- join28(home, "." + name, "config"),
4999
- join28(home, "." + name + "rc")
4996
+ join30(home, ".config", name, "config"),
4997
+ join30(home, ".config", name),
4998
+ join30(home, "." + name, "config"),
4999
+ join30(home, "." + name + "rc")
5000
5000
  ].forEach(addConfigFile);
5001
5001
  addConfigFile(cc.find("." + name + "rc"));
5002
5002
  if (env4.config) addConfigFile(env4.config);
@@ -5014,7 +5014,7 @@ var require_rc = __commonJS({
5014
5014
  var require_polyfills2 = __commonJS({
5015
5015
  "node_modules/.pnpm/graceful-fs@4.2.10/node_modules/graceful-fs/polyfills.js"(exports, module) {
5016
5016
  "use strict";
5017
- var constants8 = __require("constants");
5017
+ var constants9 = __require("constants");
5018
5018
  var origCwd = process.cwd;
5019
5019
  var cwd = null;
5020
5020
  var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
@@ -5038,7 +5038,7 @@ var require_polyfills2 = __commonJS({
5038
5038
  var chdir;
5039
5039
  module.exports = patch;
5040
5040
  function patch(fs5) {
5041
- if (constants8.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
5041
+ if (constants9.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
5042
5042
  patchLchmod(fs5);
5043
5043
  }
5044
5044
  if (!fs5.lutimes) {
@@ -5140,7 +5140,7 @@ var require_polyfills2 = __commonJS({
5140
5140
  fs6.lchmod = function(path9, mode, callback) {
5141
5141
  fs6.open(
5142
5142
  path9,
5143
- constants8.O_WRONLY | constants8.O_SYMLINK,
5143
+ constants9.O_WRONLY | constants9.O_SYMLINK,
5144
5144
  mode,
5145
5145
  function(err, fd) {
5146
5146
  if (err) {
@@ -5156,7 +5156,7 @@ var require_polyfills2 = __commonJS({
5156
5156
  );
5157
5157
  };
5158
5158
  fs6.lchmodSync = function(path9, mode) {
5159
- var fd = fs6.openSync(path9, constants8.O_WRONLY | constants8.O_SYMLINK, mode);
5159
+ var fd = fs6.openSync(path9, constants9.O_WRONLY | constants9.O_SYMLINK, mode);
5160
5160
  var threw = true;
5161
5161
  var ret;
5162
5162
  try {
@@ -5176,9 +5176,9 @@ var require_polyfills2 = __commonJS({
5176
5176
  };
5177
5177
  }
5178
5178
  function patchLutimes(fs6) {
5179
- if (constants8.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
5179
+ if (constants9.hasOwnProperty("O_SYMLINK") && fs6.futimes) {
5180
5180
  fs6.lutimes = function(path9, at, mt, cb) {
5181
- fs6.open(path9, constants8.O_SYMLINK, function(er, fd) {
5181
+ fs6.open(path9, constants9.O_SYMLINK, function(er, fd) {
5182
5182
  if (er) {
5183
5183
  if (cb) cb(er);
5184
5184
  return;
@@ -5191,7 +5191,7 @@ var require_polyfills2 = __commonJS({
5191
5191
  });
5192
5192
  };
5193
5193
  fs6.lutimesSync = function(path9, at, mt) {
5194
- var fd = fs6.openSync(path9, constants8.O_SYMLINK);
5194
+ var fd = fs6.openSync(path9, constants9.O_SYMLINK);
5195
5195
  var ret;
5196
5196
  var threw = true;
5197
5197
  try {
@@ -5474,14 +5474,14 @@ var require_graceful_fs2 = __commonJS({
5474
5474
  return close;
5475
5475
  })(fs5.close);
5476
5476
  fs5.closeSync = (function(fs$closeSync) {
5477
- function closeSync8(fd) {
5477
+ function closeSync9(fd) {
5478
5478
  fs$closeSync.apply(fs5, arguments);
5479
5479
  resetQueue();
5480
5480
  }
5481
- Object.defineProperty(closeSync8, previousSymbol, {
5481
+ Object.defineProperty(closeSync9, previousSymbol, {
5482
5482
  value: fs$closeSync
5483
5483
  });
5484
- return closeSync8;
5484
+ return closeSync9;
5485
5485
  })(fs5.closeSync);
5486
5486
  if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
5487
5487
  process.on("exit", function() {
@@ -6340,11 +6340,11 @@ var require_util = __commonJS({
6340
6340
  if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
6341
6341
  return name2;
6342
6342
  }
6343
- const dirname19 = path9.dirname(name2);
6344
- if (dirname19 === name2) {
6343
+ const dirname21 = path9.dirname(name2);
6344
+ if (dirname21 === name2) {
6345
6345
  return original;
6346
6346
  }
6347
- return find(dirname19, original);
6347
+ return find(dirname21, original);
6348
6348
  } catch (error) {
6349
6349
  if (name2 === original) {
6350
6350
  if (error.code === "ENOENT") {
@@ -8313,7 +8313,7 @@ var require_semver2 = __commonJS({
8313
8313
  "node_modules/.pnpm/semver@7.7.4/node_modules/semver/index.js"(exports, module) {
8314
8314
  "use strict";
8315
8315
  var internalRe = require_re();
8316
- var constants8 = require_constants();
8316
+ var constants9 = require_constants();
8317
8317
  var SemVer = require_semver();
8318
8318
  var identifiers = require_identifiers();
8319
8319
  var parse2 = require_parse();
@@ -8395,8 +8395,8 @@ var require_semver2 = __commonJS({
8395
8395
  re: internalRe.re,
8396
8396
  src: internalRe.src,
8397
8397
  tokens: internalRe.t,
8398
- SEMVER_SPEC_VERSION: constants8.SEMVER_SPEC_VERSION,
8399
- RELEASE_TYPES: constants8.RELEASE_TYPES,
8398
+ SEMVER_SPEC_VERSION: constants9.SEMVER_SPEC_VERSION,
8399
+ RELEASE_TYPES: constants9.RELEASE_TYPES,
8400
8400
  compareIdentifiers: identifiers.compareIdentifiers,
8401
8401
  rcompareIdentifiers: identifiers.rcompareIdentifiers
8402
8402
  };
@@ -8896,8 +8896,8 @@ var require_ucs2length = __commonJS({
8896
8896
  });
8897
8897
 
8898
8898
  // src/index.ts
8899
- import { readFileSync as readFileSync22 } from "fs";
8900
- import { dirname as dirname18, resolve as resolve10 } from "path";
8899
+ import { readFileSync as readFileSync24 } from "fs";
8900
+ import { dirname as dirname20, resolve as resolve10 } from "path";
8901
8901
  import { fileURLToPath as fileURLToPath7 } from "url";
8902
8902
 
8903
8903
  // node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
@@ -9257,14 +9257,14 @@ var Temp = {
9257
9257
  }
9258
9258
  },
9259
9259
  truncate: (filePath) => {
9260
- const basename4 = path2.basename(filePath);
9261
- if (basename4.length <= LIMIT_BASENAME_LENGTH)
9260
+ const basename5 = path2.basename(filePath);
9261
+ if (basename5.length <= LIMIT_BASENAME_LENGTH)
9262
9262
  return filePath;
9263
- const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename4);
9263
+ const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename5);
9264
9264
  if (!truncable)
9265
9265
  return filePath;
9266
- const truncationLength = basename4.length - LIMIT_BASENAME_LENGTH;
9267
- return `${filePath.slice(0, -basename4.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`;
9266
+ const truncationLength = basename5.length - LIMIT_BASENAME_LENGTH;
9267
+ return `${filePath.slice(0, -basename5.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`;
9268
9268
  }
9269
9269
  };
9270
9270
  node_default(Temp.purgeSyncAll);
@@ -12398,13 +12398,13 @@ function sameIssuerUrl(value, field, issuer) {
12398
12398
  return parsed.toString();
12399
12399
  }
12400
12400
  function parseWorkosConnectConfiguration(value) {
12401
- const record2 = recordFrom(value, "Connect discovery");
12401
+ const record3 = recordFrom(value, "Connect discovery");
12402
12402
  const expectedKeys = ["protocol_version", "issuer", "client_id", "default_scopes"];
12403
- if (record2.protocol_version !== 1 || Object.keys(record2).length !== expectedKeys.length || expectedKeys.some((key) => !Object.hasOwn(record2, key)) || !Array.isArray(record2.default_scopes) || record2.default_scopes.length !== WORKOS_CONNECT_DEFAULT_SCOPES.length || record2.default_scopes.some((scope, index) => scope !== WORKOS_CONNECT_DEFAULT_SCOPES[index])) {
12403
+ if (record3.protocol_version !== 1 || Object.keys(record3).length !== expectedKeys.length || expectedKeys.some((key) => !Object.hasOwn(record3, key)) || !Array.isArray(record3.default_scopes) || record3.default_scopes.length !== WORKOS_CONNECT_DEFAULT_SCOPES.length || record3.default_scopes.some((scope, index) => scope !== WORKOS_CONNECT_DEFAULT_SCOPES[index])) {
12404
12404
  throw new WorkosConnectProtocolError("invalid_response", "Connect discovery is invalid");
12405
12405
  }
12406
- const issuer = canonicalIssuer(record2.issuer);
12407
- const clientId = exactString(record2.client_id, "client_id", MAX_CLIENT_ID_LENGTH);
12406
+ const issuer = canonicalIssuer(record3.issuer);
12407
+ const clientId = exactString(record3.client_id, "client_id", MAX_CLIENT_ID_LENGTH);
12408
12408
  if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
12409
12409
  throw new WorkosConnectProtocolError("invalid_response", "Connect discovery is invalid");
12410
12410
  }
@@ -12415,14 +12415,14 @@ function parseWorkosConnectConfiguration(value) {
12415
12415
  };
12416
12416
  }
12417
12417
  function parseWorkosConnectCredentialMetadata(value) {
12418
- const record2 = recordFrom(value, "credential metadata");
12419
- if (record2.version !== 1 || record2.family !== "workos_connect") {
12418
+ const record3 = recordFrom(value, "credential metadata");
12419
+ if (record3.version !== 1 || record3.family !== "workos_connect") {
12420
12420
  throw new WorkosConnectProtocolError("invalid_metadata", "credential metadata is invalid");
12421
12421
  }
12422
- const issuer = canonicalIssuer(record2.issuer);
12423
- const clientId = exactString(record2.clientId, "clientId", MAX_CLIENT_ID_LENGTH);
12424
- const accessTokenHash = exactString(record2.accessTokenHash, "accessTokenHash", 64);
12425
- const refreshTokenHash = exactString(record2.refreshTokenHash, "refreshTokenHash", 64);
12422
+ const issuer = canonicalIssuer(record3.issuer);
12423
+ const clientId = exactString(record3.clientId, "clientId", MAX_CLIENT_ID_LENGTH);
12424
+ const accessTokenHash = exactString(record3.accessTokenHash, "accessTokenHash", 64);
12425
+ const refreshTokenHash = exactString(record3.refreshTokenHash, "refreshTokenHash", 64);
12426
12426
  if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
12427
12427
  throw new WorkosConnectProtocolError("invalid_metadata", "credential clientId is invalid");
12428
12428
  }
@@ -12526,37 +12526,37 @@ async function requestWorkosDeviceAuthorization(configuration, fetchImpl = fetch
12526
12526
  `Device authorization returned HTTP ${response.status}`
12527
12527
  );
12528
12528
  }
12529
- const record2 = recordFrom(value, "device authorization");
12529
+ const record3 = recordFrom(value, "device authorization");
12530
12530
  const verificationUri = sameIssuerUrl(
12531
- record2.verification_uri,
12531
+ record3.verification_uri,
12532
12532
  "verification_uri",
12533
12533
  configuration.issuer
12534
12534
  );
12535
12535
  return {
12536
- deviceCode: exactString(record2.device_code, "device_code", MAX_DEVICE_CODE_LENGTH),
12537
- userCode: exactString(record2.user_code, "user_code", MAX_USER_CODE_LENGTH),
12536
+ deviceCode: exactString(record3.device_code, "device_code", MAX_DEVICE_CODE_LENGTH),
12537
+ userCode: exactString(record3.user_code, "user_code", MAX_USER_CODE_LENGTH),
12538
12538
  verificationUri,
12539
- verificationUriComplete: record2.verification_uri_complete === void 0 ? void 0 : sameIssuerUrl(
12540
- record2.verification_uri_complete,
12539
+ verificationUriComplete: record3.verification_uri_complete === void 0 ? void 0 : sameIssuerUrl(
12540
+ record3.verification_uri_complete,
12541
12541
  "verification_uri_complete",
12542
12542
  configuration.issuer
12543
12543
  ),
12544
- expiresIn: safeInteger(record2.expires_in, "expires_in", 1, MAX_DEVICE_LIFETIME_SECONDS),
12545
- interval: safeInteger(record2.interval ?? 5, "interval", 1, MAX_INTERVAL_SECONDS)
12544
+ expiresIn: safeInteger(record3.expires_in, "expires_in", 1, MAX_DEVICE_LIFETIME_SECONDS),
12545
+ interval: safeInteger(record3.interval ?? 5, "interval", 1, MAX_INTERVAL_SECONDS)
12546
12546
  };
12547
12547
  }
12548
12548
  function parseWorkosConnectTokens(value, options = {}) {
12549
- const record2 = recordFrom(value, "token response");
12549
+ const record3 = recordFrom(value, "token response");
12550
12550
  const accessToken = exactString(
12551
- record2.access_token,
12551
+ record3.access_token,
12552
12552
  "access_token",
12553
12553
  WORKOS_CONNECT_RESPONSE_MAX_BYTES
12554
12554
  );
12555
- const refreshToken2 = record2.refresh_token === void 0 && options.fallbackRefreshToken !== void 0 ? options.fallbackRefreshToken : exactString(record2.refresh_token, "refresh_token", WORKOS_CONNECT_RESPONSE_MAX_BYTES);
12555
+ const refreshToken2 = record3.refresh_token === void 0 && options.fallbackRefreshToken !== void 0 ? options.fallbackRefreshToken : exactString(record3.refresh_token, "refresh_token", WORKOS_CONNECT_RESPONSE_MAX_BYTES);
12556
12556
  return {
12557
12557
  accessToken,
12558
12558
  refreshToken: refreshToken2,
12559
- expiresIn: record2.expires_in === void 0 ? void 0 : safeInteger(record2.expires_in, "expires_in", 1, MAX_TOKEN_LIFETIME_SECONDS)
12559
+ expiresIn: record3.expires_in === void 0 ? void 0 : safeInteger(record3.expires_in, "expires_in", 1, MAX_TOKEN_LIFETIME_SECONDS)
12560
12560
  };
12561
12561
  }
12562
12562
  function providerError(value) {
@@ -12678,15 +12678,15 @@ function parseLegacyBrokerCredentialMetadata(value) {
12678
12678
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
12679
12679
  throw new Error("credential family is invalid");
12680
12680
  }
12681
- const record2 = value;
12682
- if (record2.version !== 1 || record2.family !== "legacy_broker" || typeof record2.accessTokenHash !== "string" || typeof record2.refreshTokenHash !== "string" || !/^[a-f0-9]{64}$/u.test(record2.accessTokenHash) || !/^[a-f0-9]{64}$/u.test(record2.refreshTokenHash)) {
12681
+ const record3 = value;
12682
+ if (record3.version !== 1 || record3.family !== "legacy_broker" || typeof record3.accessTokenHash !== "string" || typeof record3.refreshTokenHash !== "string" || !/^[a-f0-9]{64}$/u.test(record3.accessTokenHash) || !/^[a-f0-9]{64}$/u.test(record3.refreshTokenHash)) {
12683
12683
  throw new Error("credential family is invalid");
12684
12684
  }
12685
12685
  return {
12686
12686
  version: 1,
12687
12687
  family: "legacy_broker",
12688
- accessTokenHash: record2.accessTokenHash,
12689
- refreshTokenHash: record2.refreshTokenHash
12688
+ accessTokenHash: record3.accessTokenHash,
12689
+ refreshTokenHash: record3.refreshTokenHash
12690
12690
  };
12691
12691
  }
12692
12692
  function readBoundedCredentialJson(path9) {
@@ -12721,9 +12721,9 @@ function readCredentialFamily(path9) {
12721
12721
  }
12722
12722
  function isCredentialMigrationSentinel(value) {
12723
12723
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
12724
- const record2 = value;
12725
- const keys = Object.keys(record2).sort();
12726
- return keys.length === 2 && keys[0] === "state" && keys[1] === "version" && record2.version === CREDENTIAL_MIGRATION_VERSION && record2.state === CREDENTIAL_MIGRATION_STATE;
12724
+ const record3 = value;
12725
+ const keys = Object.keys(record3).sort();
12726
+ return keys.length === 2 && keys[0] === "state" && keys[1] === "version" && record3.version === CREDENTIAL_MIGRATION_VERSION && record3.state === CREDENTIAL_MIGRATION_STATE;
12727
12727
  }
12728
12728
  function readCredentialMigrationState(path9 = CREDENTIAL_MIGRATION_PATH) {
12729
12729
  const parsed = readBoundedCredentialJson(path9);
@@ -13837,10 +13837,10 @@ async function performTokenRefresh(options = {}) {
13837
13837
  }
13838
13838
  return void 0;
13839
13839
  }
13840
- const record2 = typeof data === "object" && data !== null && !Array.isArray(data) ? data : void 0;
13841
- accessToken = typeof record2?.access_token === "string" ? record2.access_token.trim() : "";
13842
- replacementRefreshToken = typeof record2?.refresh_token === "string" ? record2.refresh_token.trim() : "";
13843
- expiresIn = typeof record2?.expires_in === "number" && Number.isFinite(record2.expires_in) ? record2.expires_in : void 0;
13840
+ const record3 = typeof data === "object" && data !== null && !Array.isArray(data) ? data : void 0;
13841
+ accessToken = typeof record3?.access_token === "string" ? record3.access_token.trim() : "";
13842
+ replacementRefreshToken = typeof record3?.refresh_token === "string" ? record3.refresh_token.trim() : "";
13843
+ expiresIn = typeof record3?.expires_in === "number" && Number.isFinite(record3.expires_in) ? record3.expires_in : void 0;
13844
13844
  if (!(accessToken && replacementRefreshToken)) {
13845
13845
  if (readTrimmed2(REFRESH_TOKEN_PATH) === currentGeneration) {
13846
13846
  writeTerminalFingerprint(currentGeneration);
@@ -31856,19 +31856,19 @@ var defaultDependencies = {
31856
31856
  function isSafeVersion(value) {
31857
31857
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
31858
31858
  }
31859
- function optionalStringArray(record2, key) {
31860
- const value = record2[key];
31859
+ function optionalStringArray(record3, key) {
31860
+ const value = record3[key];
31861
31861
  if (value === void 0) return void 0;
31862
31862
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) return void 0;
31863
31863
  return [...value];
31864
31864
  }
31865
- function optionalSafeInteger(record2, key) {
31866
- const value = record2[key];
31865
+ function optionalSafeInteger(record3, key) {
31866
+ const value = record3[key];
31867
31867
  if (value === void 0) return void 0;
31868
31868
  return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
31869
31869
  }
31870
- function optionalUserScopeMembers(record2) {
31871
- const value = record2.users;
31870
+ function optionalUserScopeMembers(record3) {
31871
+ const value = record3.users;
31872
31872
  if (value === void 0) return void 0;
31873
31873
  if (!Array.isArray(value)) return void 0;
31874
31874
  const members = [];
@@ -31897,27 +31897,27 @@ function optionalUserScopeMembers(record2) {
31897
31897
  }
31898
31898
  function policyFromUnknown(value) {
31899
31899
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
31900
- const record2 = value;
31901
- if (!isSafeVersion(record2.updatedAt)) return void 0;
31900
+ const record3 = value;
31901
+ if (!isSafeVersion(record3.updatedAt)) return void 0;
31902
31902
  const fields = {
31903
- repositories: optionalStringArray(record2, "repositories"),
31904
- directories: optionalStringArray(record2, "directories"),
31905
- globs: optionalStringArray(record2, "globs"),
31906
- branches: optionalStringArray(record2, "branches"),
31907
- effectiveFrom: optionalSafeInteger(record2, "effectiveFrom"),
31908
- effectiveUntil: optionalSafeInteger(record2, "effectiveUntil"),
31909
- users: optionalUserScopeMembers(record2)
31903
+ repositories: optionalStringArray(record3, "repositories"),
31904
+ directories: optionalStringArray(record3, "directories"),
31905
+ globs: optionalStringArray(record3, "globs"),
31906
+ branches: optionalStringArray(record3, "branches"),
31907
+ effectiveFrom: optionalSafeInteger(record3, "effectiveFrom"),
31908
+ effectiveUntil: optionalSafeInteger(record3, "effectiveUntil"),
31909
+ users: optionalUserScopeMembers(record3)
31910
31910
  };
31911
31911
  if (Object.entries(fields).some(
31912
- ([key, parsed]) => record2[key] !== void 0 && parsed === void 0
31912
+ ([key, parsed]) => record3[key] !== void 0 && parsed === void 0
31913
31913
  )) {
31914
31914
  return void 0;
31915
31915
  }
31916
- if (record2.effectiveFrom !== void 0 && fields.effectiveFrom === void 0 || record2.effectiveUntil !== void 0 && fields.effectiveUntil === void 0 || fields.effectiveFrom !== void 0 && fields.effectiveUntil !== void 0 && fields.effectiveFrom >= fields.effectiveUntil) {
31916
+ if (record3.effectiveFrom !== void 0 && fields.effectiveFrom === void 0 || record3.effectiveUntil !== void 0 && fields.effectiveUntil === void 0 || fields.effectiveFrom !== void 0 && fields.effectiveUntil !== void 0 && fields.effectiveFrom >= fields.effectiveUntil) {
31917
31917
  return void 0;
31918
31918
  }
31919
31919
  return {
31920
- updatedAt: record2.updatedAt,
31920
+ updatedAt: record3.updatedAt,
31921
31921
  ...fields.repositories === void 0 ? {} : { repositories: fields.repositories },
31922
31922
  ...fields.directories === void 0 ? {} : { directories: fields.directories },
31923
31923
  ...fields.globs === void 0 ? {} : { globs: fields.globs },
@@ -33697,18 +33697,18 @@ function parseAuthBrokerConfig(value) {
33697
33697
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
33698
33698
  throw new Error("broker config must be an object");
33699
33699
  }
33700
- const record2 = value;
33701
- if (typeof record2.authorization_server !== "string" || !record2.authorization_server.trim()) {
33700
+ const record3 = value;
33701
+ if (typeof record3.authorization_server !== "string" || !record3.authorization_server.trim()) {
33702
33702
  throw new Error("authorization_server is required");
33703
33703
  }
33704
- if (typeof record2.client_id !== "string" || !record2.client_id.trim()) {
33704
+ if (typeof record3.client_id !== "string" || !record3.client_id.trim()) {
33705
33705
  throw new Error("client_id is required");
33706
33706
  }
33707
- if (!Array.isArray(record2.default_scopes) || !record2.default_scopes.every((scope) => typeof scope === "string" && scope.length > 0)) {
33707
+ if (!Array.isArray(record3.default_scopes) || !record3.default_scopes.every((scope) => typeof scope === "string" && scope.length > 0)) {
33708
33708
  throw new Error("default_scopes must be a string array");
33709
33709
  }
33710
- const authorizationServer = parseHttpUrl(record2.authorization_server, "authorization_server");
33711
- const rawEndpoint = record2.authorization_endpoint === void 0 ? DEFAULT_WORKOS_AUTHORIZE_URL : record2.authorization_endpoint;
33710
+ const authorizationServer = parseHttpUrl(record3.authorization_server, "authorization_server");
33711
+ const rawEndpoint = record3.authorization_endpoint === void 0 ? DEFAULT_WORKOS_AUTHORIZE_URL : record3.authorization_endpoint;
33712
33712
  if (typeof rawEndpoint !== "string" || !rawEndpoint.trim()) {
33713
33713
  throw new Error("authorization_endpoint must be a URL");
33714
33714
  }
@@ -33719,8 +33719,8 @@ function parseAuthBrokerConfig(value) {
33719
33719
  return {
33720
33720
  authorization_server: authorizationServer.toString(),
33721
33721
  authorization_endpoint: authorizationEndpoint.toString(),
33722
- client_id: record2.client_id.trim(),
33723
- default_scopes: [...record2.default_scopes]
33722
+ client_id: record3.client_id.trim(),
33723
+ default_scopes: [...record3.default_scopes]
33724
33724
  };
33725
33725
  }
33726
33726
  async function fetchAuthBrokerConfig(siteUrl, fetchImpl = fetch) {
@@ -34963,7 +34963,7 @@ import {
34963
34963
  utimesSync,
34964
34964
  writeFileSync as writeFileSync6
34965
34965
  } from "fs";
34966
- import { dirname as dirname8, isAbsolute as isAbsolute5, join as join10, resolve as resolve5 } from "path";
34966
+ import { delimiter, dirname as dirname8, isAbsolute as isAbsolute5, join as join10, resolve as resolve5 } from "path";
34967
34967
  var HOOK_RUNTIME_SCHEMA_VERSION = 1;
34968
34968
  var DIRECTORY_MODE2 = 448;
34969
34969
  var DATA_FILE_MODE = 384;
@@ -35313,11 +35313,28 @@ function writeRelease(sourceDir, releaseDir, manifest, releasesDir) {
35313
35313
  rmSync4(stagingDir, { recursive: true, force: true });
35314
35314
  }
35315
35315
  }
35316
+ function stableNodePath(execPath = process.execPath, searchPath = process.env.PATH ?? "") {
35317
+ let target;
35318
+ try {
35319
+ target = realpathSync2(execPath);
35320
+ } catch {
35321
+ return execPath;
35322
+ }
35323
+ for (const directory of searchPath.split(delimiter)) {
35324
+ if (!isAbsolute5(directory)) continue;
35325
+ const candidate = join10(directory, "node");
35326
+ try {
35327
+ if (realpathSync2(candidate) === target) return candidate;
35328
+ } catch {
35329
+ }
35330
+ }
35331
+ return execPath;
35332
+ }
35316
35333
  function stageHookRuntime(options = {}) {
35317
35334
  const root = packageRoot();
35318
35335
  const sourceDir = options.sourceDir ?? (root ? join10(root, "dist", "hook-runtime") : null);
35319
35336
  const version = options.version ?? packageVersion();
35320
- const nodePath = resolve5(options.nodePath ?? process.execPath);
35337
+ const nodePath = resolve5(options.nodePath ?? stableNodePath());
35321
35338
  if (!sourceDir || !version || compareSemver(version, version) !== 0 || !isAbsolute5(nodePath) || /[\r\n]/u.test(nodePath)) {
35322
35339
  throw new Error("cannot stage hook runtime: package identity is unavailable");
35323
35340
  }
@@ -36369,7 +36386,7 @@ function registerCursorCommands(program3) {
36369
36386
 
36370
36387
  // src/commands/daemon.ts
36371
36388
  import { spawn as spawn3 } from "child_process";
36372
- import { closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync3, readFileSync as readFileSync13, unlinkSync as unlinkSync3 } from "fs";
36389
+ import { closeSync as closeSync4, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync13, unlinkSync as unlinkSync3 } from "fs";
36373
36390
  import { join as join16 } from "path";
36374
36391
 
36375
36392
  // src/daemon/latest-bootstrap.ts
@@ -36441,12 +36458,12 @@ async function runLatestDaemonBootstrap(ensureCurrent, options = {}) {
36441
36458
  import { spawnSync } from "child_process";
36442
36459
  import { createHash as createHash8, randomBytes as randomBytes5 } from "crypto";
36443
36460
  import {
36444
- constants as constants4,
36461
+ constants as constants5,
36445
36462
  accessSync as accessSync2,
36446
36463
  chmodSync as chmodSync4,
36447
36464
  copyFileSync as copyFileSync2,
36448
36465
  existsSync as existsSync11,
36449
- lstatSync as lstatSync5,
36466
+ lstatSync as lstatSync6,
36450
36467
  mkdirSync as mkdirSync8,
36451
36468
  mkdtempSync as mkdtempSync2,
36452
36469
  readFileSync as readFileSync12,
@@ -36455,16 +36472,53 @@ import {
36455
36472
  realpathSync as realpathSync3,
36456
36473
  renameSync as renameSync4,
36457
36474
  rmSync as rmSync5,
36475
+ statSync as statSync3,
36458
36476
  symlinkSync,
36459
36477
  writeFileSync as writeFileSync7
36460
36478
  } from "fs";
36461
36479
  import { homedir as homedir8 } from "os";
36462
36480
  import { basename as basename3, dirname as dirname10, isAbsolute as isAbsolute7, join as join15, resolve as resolve6 } from "path";
36463
36481
 
36482
+ // src/lib/bounded-file.ts
36483
+ import { constants as constants4, closeSync as closeSync3, fstatSync, lstatSync as lstatSync5, openSync as openSync3, readSync } from "fs";
36484
+ function readBoundedRegularFile(path9, maxBytes) {
36485
+ let fd;
36486
+ try {
36487
+ const linked = lstatSync5(path9);
36488
+ if (!linked.isFile() || linked.size > maxBytes) return void 0;
36489
+ fd = openSync3(path9, constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
36490
+ const opened = fstatSync(fd);
36491
+ if (!opened.isFile() || opened.dev !== linked.dev || opened.ino !== linked.ino || opened.size > maxBytes) {
36492
+ return void 0;
36493
+ }
36494
+ const buffer = Buffer.allocUnsafe(opened.size + 1);
36495
+ let length = 0;
36496
+ while (length < buffer.length) {
36497
+ const count = readSync(fd, buffer, length, buffer.length - length, length);
36498
+ if (count === 0) break;
36499
+ length += count;
36500
+ }
36501
+ if (length > opened.size) return void 0;
36502
+ return { text: buffer.subarray(0, length).toString("utf8"), ownerUid: opened.uid };
36503
+ } catch {
36504
+ return void 0;
36505
+ } finally {
36506
+ if (fd !== void 0) {
36507
+ try {
36508
+ closeSync3(fd);
36509
+ } catch {
36510
+ }
36511
+ }
36512
+ }
36513
+ }
36514
+
36464
36515
  // src/daemon/env-binding.ts
36465
36516
  function normalizeApiUrl(url) {
36466
36517
  return url.trim().replace(/\/+$/, "");
36467
36518
  }
36519
+ function apiUrlsMatch(a, b) {
36520
+ return normalizeApiUrl(a) === normalizeApiUrl(b);
36521
+ }
36468
36522
 
36469
36523
  // src/daemon/launchd.ts
36470
36524
  var LAUNCHD_LABEL = "ai.getprimitive.prim-daemon";
@@ -36483,6 +36537,11 @@ var SERVICE_NOT_FOUND = 113;
36483
36537
  var TRANSITION_IN_PROGRESS = 5;
36484
36538
  var OUTPUT_LIMIT = 4096;
36485
36539
  var DAEMON_DISABLED_CONTENT = "disabled by `prim daemon stop`\n";
36540
+ var LAUNCHER_MAX_BYTES = 64 * 1024;
36541
+ var PLIST_MAX_BYTES = 64 * 1024;
36542
+ var DRIFT_HEAL_MARKER = "daemon-drift-heal.json";
36543
+ var DRIFT_HEAL_MARKER_TEMPORARY_RE = /^daemon-drift-heal\.json\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp$/u;
36544
+ var DRIFT_HEAL_CLAIM_OWNER = "owner.json";
36486
36545
  var LaunchctlError = class extends Error {
36487
36546
  status;
36488
36547
  timedOut;
@@ -36522,6 +36581,8 @@ function daemonControlPaths(options = {}) {
36522
36581
  return {
36523
36582
  launcher: join15(configDir, `prim-daemon-launcher-v${LAUNCHER_SCHEMA_VERSION}`),
36524
36583
  disabledMarker: join15(configDir, "daemon.disabled"),
36584
+ driftHealMarker: join15(configDir, DRIFT_HEAL_MARKER),
36585
+ driftHealClaim: join15(configDir, "daemon-drift-heal.lock"),
36525
36586
  lifecycleLockDir: join15(configDir, "daemon.lifecycle.lock"),
36526
36587
  legacyDisabledMarker: legacy.disabledMarker,
36527
36588
  legacyLifecycleLockDir: legacy.lifecycleLockDir
@@ -36582,7 +36643,7 @@ function readOwnedRuntimeManifest(releaseDir) {
36582
36643
  }
36583
36644
  const manifest = parsed;
36584
36645
  if (manifest.schemaVersion !== RUNTIME_SCHEMA_VERSION || compareSemver(manifest.version, manifest.version) !== 0 || !isAbsolute7(manifest.nodePath) || /[\r\n]/u.test(manifest.nodePath) || manifest.daemonFile !== "prim-daemon-server" || !/^[0-9a-f]{64}$/u.test(manifest.daemonSha256) || new Date(manifest.stagedAt).toISOString() !== manifest.stagedAt || raw !== `${JSON.stringify(manifest, null, 2)}
36585
- ` || !lstatSync5(daemonPath).isFile() || sha256File(daemonPath) !== manifest.daemonSha256) {
36646
+ ` || !lstatSync6(daemonPath).isFile() || sha256File(daemonPath) !== manifest.daemonSha256) {
36586
36647
  return null;
36587
36648
  }
36588
36649
  const entries = readdirSync3(releaseDir).sort();
@@ -36591,8 +36652,17 @@ function readOwnedRuntimeManifest(releaseDir) {
36591
36652
  return null;
36592
36653
  }
36593
36654
  }
36655
+ function sameNode(recorded, desired) {
36656
+ if (recorded === desired) return true;
36657
+ try {
36658
+ const target = realpathSync3(desired);
36659
+ return recorded !== target && realpathSync3(recorded) === target;
36660
+ } catch {
36661
+ return false;
36662
+ }
36663
+ }
36594
36664
  function sameRuntime(current, desired, daemonPath) {
36595
- if (!current || current.version !== desired.version || current.nodePath !== desired.nodePath || current.daemonSha256 !== desired.daemonSha256) {
36665
+ if (!current || current.version !== desired.version || !sameNode(current.nodePath, desired.nodePath) || current.daemonSha256 !== desired.daemonSha256) {
36596
36666
  return false;
36597
36667
  }
36598
36668
  try {
@@ -36620,7 +36690,7 @@ function stageRuntime(options = {}) {
36620
36690
  if (!daemonSource || !existsSync11(daemonSource)) {
36621
36691
  throw new Error("cannot stage runtime: prim-daemon-server bundle is unavailable");
36622
36692
  }
36623
- const nodePath = resolve6(options.nodePath ?? process.execPath);
36693
+ const nodePath = resolve6(options.nodePath ?? stableNodePath());
36624
36694
  const version = options.version ?? findPackageVersion(daemonSource);
36625
36695
  const desired = {
36626
36696
  schemaVersion: RUNTIME_SCHEMA_VERSION,
@@ -36719,13 +36789,30 @@ function assertRegularFile(path9, label) {
36719
36789
  }
36720
36790
  function lstatIfPresent3(path9) {
36721
36791
  try {
36722
- return lstatSync5(path9);
36792
+ return lstatSync6(path9);
36723
36793
  } catch (error) {
36724
36794
  const code = error.code;
36725
36795
  if (code === "ENOENT" || code === "ENOTDIR") return void 0;
36726
36796
  throw error;
36727
36797
  }
36728
36798
  }
36799
+ function driftHealMarkerTemporaries(configDir) {
36800
+ try {
36801
+ return readdirSync3(configDir).filter((entry) => DRIFT_HEAL_MARKER_TEMPORARY_RE.test(entry)).map((entry) => join15(configDir, entry));
36802
+ } catch (error) {
36803
+ const code = error.code;
36804
+ if (code === "ENOENT" || code === "ENOTDIR") return [];
36805
+ throw error;
36806
+ }
36807
+ }
36808
+ function assertDriftHealClaim(path9) {
36809
+ const claim = lstatIfPresent3(path9);
36810
+ if (!claim) return;
36811
+ if (!claim.isDirectory() || readdirSync3(path9).some((entry) => entry !== DRIFT_HEAL_CLAIM_OWNER)) {
36812
+ throw new Error(`refusing to remove unrecognized daemon drift-heal claim at ${path9}`);
36813
+ }
36814
+ assertRegularFile(join15(path9, DRIFT_HEAL_CLAIM_OWNER), "daemon drift-heal claim owner");
36815
+ }
36729
36816
  function assertExactFile(path9, expected, label) {
36730
36817
  if (!lstatIfPresent3(path9)) return;
36731
36818
  assertRegularFile(path9, label);
@@ -36764,17 +36851,16 @@ function assertOwnedDaemonRuntime(options) {
36764
36851
  );
36765
36852
  }
36766
36853
  assertRegularFile(logPath, "daemon log");
36854
+ assertRegularFile(control.driftHealMarker, "daemon drift-heal marker");
36855
+ for (const temporary of driftHealMarkerTemporaries(configDir)) {
36856
+ assertRegularFile(temporary, "daemon drift-heal marker temporary");
36857
+ }
36858
+ assertDriftHealClaim(control.driftHealClaim);
36767
36859
  assertRegularFile(join15(configDir, "daemon-health.json"), "daemon health snapshot");
36768
36860
  assertRegularFile(join15(configDir, "client_instance_id"), "daemon client instance id");
36769
36861
  if (service && lstatIfPresent3(service.plistPath)) {
36770
36862
  assertRegularFile(service.plistPath, "launchd property list");
36771
- const expected = generateLaunchAgentPlist({
36772
- launcherPath: control.launcher,
36773
- logPath,
36774
- workingDirectory: options.homeDir ?? homedir8(),
36775
- label: options.label
36776
- });
36777
- if (readFileSync12(service.plistPath, "utf8") !== expected) {
36863
+ if (readFileSync12(service.plistPath, "utf8") !== ownedLaunchAgentPlist(options, logPath)) {
36778
36864
  throw new Error(
36779
36865
  `refusing to remove unrecognized launchd property list at ${service.plistPath}`
36780
36866
  );
@@ -36851,6 +36937,9 @@ async function removeDaemonRuntime(options = {}) {
36851
36937
  control.launcher,
36852
36938
  control.disabledMarker,
36853
36939
  control.legacyDisabledMarker,
36940
+ control.driftHealMarker,
36941
+ ...driftHealMarkerTemporaries(configDir),
36942
+ control.driftHealClaim,
36854
36943
  ...service ? [service.plistPath] : [],
36855
36944
  logPath,
36856
36945
  join15(configDir, "daemon-health.json"),
@@ -36862,7 +36951,7 @@ async function removeDaemonRuntime(options = {}) {
36862
36951
  return withDaemonLifecycleLock(async () => {
36863
36952
  assertOwnedDaemonRuntime(options);
36864
36953
  for (const path9 of ownedArtifacts.filter((candidate) => candidate !== runtime.runtimeDir)) {
36865
- rmSync5(path9, { force: true });
36954
+ rmSync5(path9, { force: true, recursive: path9 === control.driftHealClaim });
36866
36955
  }
36867
36956
  if (lstatIfPresent3(runtime.runtimeDir)) {
36868
36957
  const quarantined = `${runtime.runtimeDir}.uninstall-${String(process.pid)}-${randomBytes5(8).toString("hex")}`;
@@ -36896,9 +36985,22 @@ ${config.configDir ? `export PRIM_CONFIG_DIR=${shellQuote2(config.configDir)}
36896
36985
  `
36897
36986
  };
36898
36987
  }
36988
+ function ownedLaunchAgentPlist(options, logPath) {
36989
+ return generateLaunchAgentPlist({
36990
+ launcherPath: daemonControlPaths(options).launcher,
36991
+ logPath,
36992
+ workingDirectory: options.homeDir ?? homedir8(),
36993
+ label: options.label
36994
+ });
36995
+ }
36899
36996
  function readDaemonLauncher(path9) {
36997
+ return readDaemonLauncherFile(path9)?.config ?? null;
36998
+ }
36999
+ function readDaemonLauncherFile(path9) {
36900
37000
  try {
36901
- const content = readFileSync12(path9, "utf8");
37001
+ const file = readBoundedRegularFile(path9, LAUNCHER_MAX_BYTES);
37002
+ if (!file) return null;
37003
+ const content = file.text;
36902
37004
  const encoded = /^# prim-daemon-launcher: ([A-Za-z0-9_-]+)$/mu.exec(content)?.[1];
36903
37005
  if (!encoded) return null;
36904
37006
  const value = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
@@ -36913,7 +37015,7 @@ function readDaemonLauncher(path9) {
36913
37015
  ...typeof value.configDir === "string" ? { configDir: value.configDir } : {}
36914
37016
  };
36915
37017
  const expected = generateDaemonLauncher(config);
36916
- return expected.revision === value.revision && expected.content === content ? config : null;
37018
+ return expected.revision === value.revision && expected.content === content ? { config, ownerUid: file.ownerUid } : null;
36917
37019
  } catch {
36918
37020
  return null;
36919
37021
  }
@@ -36923,7 +37025,7 @@ function readUsableDaemonConfig(path9) {
36923
37025
  if (!config) return null;
36924
37026
  const manifest = readRuntimeManifest(join15(dirname10(config.daemonPath), "manifest.json"));
36925
37027
  try {
36926
- accessSync2(config.nodePath, constants4.X_OK);
37028
+ accessSync2(config.nodePath, constants5.X_OK);
36927
37029
  return manifest?.version === config.runtimeVersion && manifest.nodePath === config.nodePath && manifest.daemonSha256 === sha256File(config.daemonPath) ? config : null;
36928
37030
  } catch {
36929
37031
  return null;
@@ -37267,6 +37369,68 @@ function setDaemonExplicitlyDisabled(disabled, options = {}) {
37267
37369
  }
37268
37370
  atomicWrite3(paths.disabledMarker, content, RUNTIME_FILE_MODE);
37269
37371
  }
37372
+ function launcherRunnable(config) {
37373
+ try {
37374
+ accessSync2(config.nodePath, constants5.X_OK);
37375
+ return statSync3(config.daemonPath).isFile();
37376
+ } catch {
37377
+ return false;
37378
+ }
37379
+ }
37380
+ function selectedDaemonLauncher(options = {}) {
37381
+ const file = readDaemonLauncherFile(daemonControlPaths(options).launcher);
37382
+ if (!file) return null;
37383
+ const { config, ownerUid } = file;
37384
+ return {
37385
+ runtimeVersion: config.runtimeVersion,
37386
+ ...config.apiUrl ? { apiUrl: config.apiUrl } : {},
37387
+ ownerUid,
37388
+ runnable: launcherRunnable(config)
37389
+ };
37390
+ }
37391
+ var XML_ENTITIES = {
37392
+ "&amp;": "&",
37393
+ "&lt;": "<",
37394
+ "&gt;": ">",
37395
+ "&quot;": '"',
37396
+ "&apos;": "'"
37397
+ };
37398
+ var XML_ENTITY_RE = /&(?:amp|lt|gt|quot|apos);/gu;
37399
+ var PLIST_LABEL_KEY = "<key>Label</key>";
37400
+ var PLIST_PROGRAM_KEY = "<key>ProgramArguments</key>";
37401
+ var PLIST_LABEL_RE = /<key>Label<\/key>\s*<string>([^<]*)<\/string>/u;
37402
+ var PLIST_PROGRAM_RE = /<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]*)<\/string>/u;
37403
+ function xmlUnescape(value) {
37404
+ if (value.replace(XML_ENTITY_RE, "").includes("&")) return void 0;
37405
+ return value.replace(XML_ENTITY_RE, (entity) => XML_ENTITIES[entity] ?? entity);
37406
+ }
37407
+ function launchAgentProgram(plist) {
37408
+ if (plist.split(PLIST_LABEL_KEY).length !== 2 || plist.split(PLIST_PROGRAM_KEY).length !== 2) {
37409
+ return void 0;
37410
+ }
37411
+ const label = PLIST_LABEL_RE.exec(plist)?.[1];
37412
+ const program3 = PLIST_PROGRAM_RE.exec(plist)?.[1];
37413
+ if (label === void 0 || program3 === void 0) return void 0;
37414
+ const decodedLabel = xmlUnescape(label);
37415
+ const decodedProgram = xmlUnescape(program3);
37416
+ return decodedLabel === void 0 || decodedProgram === void 0 ? void 0 : { label: decodedLabel, program: decodedProgram };
37417
+ }
37418
+ function launchAgentRunsConfigRoot(options = {}) {
37419
+ try {
37420
+ const service = launchdPaths(options);
37421
+ const plist = readBoundedRegularFile(service.plistPath, PLIST_MAX_BYTES);
37422
+ const agent = plist ? launchAgentProgram(plist.text) : void 0;
37423
+ return agent?.label === (options.label ?? LAUNCHD_LABEL) && agent.program === daemonControlPaths(options).launcher;
37424
+ } catch {
37425
+ return false;
37426
+ }
37427
+ }
37428
+ function daemonDriftHealMarkerPath(options = {}) {
37429
+ return daemonControlPaths(options).driftHealMarker;
37430
+ }
37431
+ function daemonDriftHealClaimPath(options = {}) {
37432
+ return daemonControlPaths(options).driftHealClaim;
37433
+ }
37270
37434
  async function ensureMacDaemon(options = {}) {
37271
37435
  return withDaemonLifecycleLock(() => ensureMacDaemonLocked(options), options);
37272
37436
  }
@@ -37454,6 +37618,54 @@ async function bootoutMacDaemonLocked(options) {
37454
37618
  return { wasLoaded, legacyStopped, service };
37455
37619
  }
37456
37620
 
37621
+ // src/lib/delivery-backlog.ts
37622
+ var MS_PER_SECOND = 1e3;
37623
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
37624
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
37625
+ var MS_PER_DAY = 24 * MS_PER_HOUR;
37626
+ function isNonNegativeInteger(value) {
37627
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
37628
+ }
37629
+ function isPositiveInteger(value) {
37630
+ return isNonNegativeInteger(value) && value > 0;
37631
+ }
37632
+ function deliveryBacklogState(ingestion) {
37633
+ if (ingestion?.healthy !== false) return void 0;
37634
+ const failures = ingestion.consecutiveFailures;
37635
+ const failedWithoutProgress = failures !== 0 && !(isPositiveInteger(failures) && isPositiveInteger(ingestion.lastFailedDrainAcknowledgedCount));
37636
+ if (failedWithoutProgress) return "failing";
37637
+ const retained = ingestion.lastRetainedBucketCount;
37638
+ if (!isNonNegativeInteger(retained)) return "failing";
37639
+ return retained > 0 ? "retained" : "draining";
37640
+ }
37641
+ function formatBacklogAge(ageMs) {
37642
+ if (ageMs >= MS_PER_DAY) return `${String(Math.floor(ageMs / MS_PER_DAY))}d`;
37643
+ if (ageMs >= MS_PER_HOUR) return `${String(Math.floor(ageMs / MS_PER_HOUR))}h`;
37644
+ if (ageMs >= MS_PER_MINUTE) return `${String(Math.floor(ageMs / MS_PER_MINUTE))}m`;
37645
+ return `${String(Math.max(0, Math.floor(ageMs / MS_PER_SECOND)))}s`;
37646
+ }
37647
+ function formatPendingBacklog(backlog, now = Date.now()) {
37648
+ const count = backlog.pendingCount;
37649
+ const moves = typeof count === "number" && count > 0 ? `${backlog.pendingSampled ? "at least " : ""}${String(count)} pending move${count === 1 ? "" : "s"}` : "an unknown number of pending moves";
37650
+ const age = backlog.oldestPendingAt === void 0 ? "" : ` (oldest ${backlog.pendingSampled ? "\u2265 " : ""}${formatBacklogAge(now - backlog.oldestPendingAt)})`;
37651
+ return `${moves}${age}`;
37652
+ }
37653
+ function deliveryBacklogSummary(ingestion, now = Date.now(), backlog = ingestion) {
37654
+ const state = deliveryBacklogState(ingestion);
37655
+ if (!(state && ingestion && backlog)) return void 0;
37656
+ const pending = formatPendingBacklog(backlog, now);
37657
+ if (state === "draining") return `draining ${pending} in the background`;
37658
+ if (state === "retained") {
37659
+ const buckets = ingestion.lastRetainedBucketCount ?? 0;
37660
+ const reasons = boundedHealthError(ingestion.lastRetainedReasons);
37661
+ return `delivery held back: ${String(buckets)} journal bucket${buckets === 1 ? "" : "s"} retained${reasons ? ` (${reasons})` : ""} \xB7 holding ${pending} \u2014 run \`prim doctor\``;
37662
+ }
37663
+ const failures = ingestion.consecutiveFailures;
37664
+ const count = isPositiveInteger(failures) ? ` (${String(failures)} consecutive failure${failures === 1 ? "" : "s"})` : "";
37665
+ const error = boundedHealthError(ingestion.lastError);
37666
+ return `delivery failing${count}${error ? `: ${error}` : ""} \xB7 retrying ${pending} in the background`;
37667
+ }
37668
+
37457
37669
  // src/lib/presence.ts
37458
37670
  var DECISION_ORIGIN = "https://app.getprimitive.ai";
37459
37671
  function validDecisionUrl(value) {
@@ -37528,7 +37740,8 @@ function formatStatusline(version, snapshot, resolveIngestionStatus, options = {
37528
37740
  if (snapshot.ingestion?.healthy === false) {
37529
37741
  const pending = snapshot.ingestion.pendingCount;
37530
37742
  const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
37531
- return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
37743
+ const delivery = deliveryBacklogState(snapshot.ingestion) === "draining" && snapshot.heartbeat?.healthy === true ? "draining" : "stalled";
37744
+ return `primitive ${version} (daemon: degraded \xB7 delivery: ${delivery}${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
37532
37745
  }
37533
37746
  if (snapshot.heartbeat?.healthy === false) {
37534
37747
  return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
@@ -37571,16 +37784,20 @@ var STATUS_PROBE_TIMEOUT_MS = 500;
37571
37784
  var READY_TIMEOUT_MS = 5e3;
37572
37785
  var READY_POLL_MS2 = 100;
37573
37786
  var READY_PROBE_TIMEOUT_MS2 = 250;
37574
- var HEALTHY_TIMEOUT_MS = 3e4;
37575
- var HEALTHY_POLL_MS = 250;
37787
+ var READY_SNAPSHOT_TIMEOUT_MS = 3e4;
37788
+ var READY_SNAPSHOT_POLL_MS = 250;
37576
37789
  var EXIT_OK3 = 0;
37577
37790
  var EXIT_NOT_RUNNING = 2;
37578
37791
  var EXIT_BOOTING = 3;
37579
- function formatDaemonLifecycleMessage(message, decisionIngestion) {
37580
- return `${message} \xB7 Decision ingestion ${decisionIngestion}`;
37792
+ var VERIFIED_PREFIX = "[prim] \u2713 ";
37793
+ function formatDaemonLifecycleMessage(message, decisionIngestion, snapshot, now = Date.now()) {
37794
+ const backlog = deliveryBacklogSummary(snapshot?.ingestion, now);
37795
+ const state = deliveryBacklogState(snapshot?.ingestion);
37796
+ const verdict = (state === "failing" || state === "retained") && message.startsWith(VERIFIED_PREFIX) ? `[prim] \u26A0 ${message.slice(VERIFIED_PREFIX.length)}` : message;
37797
+ return `${verdict} \xB7 Decision ingestion ${decisionIngestion}${backlog ? ` \xB7 ${backlog}` : ""}`;
37581
37798
  }
37582
- function formatCurrentDaemonLifecycleMessage(message) {
37583
- return formatDaemonLifecycleMessage(message, decisionIngestionStatus(process.cwd()));
37799
+ function formatCurrentDaemonLifecycleMessage(message, snapshot) {
37800
+ return formatDaemonLifecycleMessage(message, decisionIngestionStatus(process.cwd()), snapshot);
37584
37801
  }
37585
37802
  function daemonDegradedReason(snapshot) {
37586
37803
  if (!snapshot || snapshot.healthy !== false) return void 0;
@@ -37631,7 +37848,7 @@ function openDaemonLog(configDir = CONFIG_DIR) {
37631
37848
  if (!existsSync12(configDir)) {
37632
37849
  mkdirSync9(configDir, { recursive: true, mode: CONFIG_DIR_MODE2 });
37633
37850
  }
37634
- return openSync3(join16(configDir, "daemon.log"), "a", LOG_FILE_MODE);
37851
+ return openSync4(join16(configDir, "daemon.log"), "a", LOG_FILE_MODE);
37635
37852
  }
37636
37853
  async function waitForReady() {
37637
37854
  const deadline = Date.now() + READY_TIMEOUT_MS;
@@ -37643,17 +37860,23 @@ async function waitForReady() {
37643
37860
  }
37644
37861
  return daemonIsLive(READY_PROBE_TIMEOUT_MS2);
37645
37862
  }
37646
- async function waitForHealthySnapshot(expectedVersion) {
37647
- const deadline = Date.now() + HEALTHY_TIMEOUT_MS;
37863
+ function requestStatusSnapshot() {
37864
+ return daemonRequest(
37865
+ "status_snapshot",
37866
+ {},
37867
+ { timeoutMs: STATUS_PROBE_TIMEOUT_MS }
37868
+ );
37869
+ }
37870
+ async function waitForReadySnapshot(expectedVersion, options = {}) {
37871
+ const request2 = options.requestSnapshot ?? requestStatusSnapshot;
37872
+ const wait = options.sleep ?? sleep;
37873
+ const nowMs = options.nowMs ?? Date.now;
37874
+ const deadline = nowMs() + READY_SNAPSHOT_TIMEOUT_MS;
37648
37875
  let snapshot = null;
37649
- while (Date.now() < deadline) {
37650
- snapshot = await daemonRequest(
37651
- "status_snapshot",
37652
- {},
37653
- { timeoutMs: STATUS_PROBE_TIMEOUT_MS }
37654
- );
37655
- if (snapshot?.healthy === true && snapshot.version === expectedVersion) return snapshot;
37656
- await sleep(HEALTHY_POLL_MS);
37876
+ while (nowMs() < deadline) {
37877
+ snapshot = await request2();
37878
+ if (daemonStartIsReady(true, snapshot, expectedVersion)) return snapshot;
37879
+ await wait(READY_SNAPSHOT_POLL_MS);
37657
37880
  }
37658
37881
  return snapshot;
37659
37882
  }
@@ -37666,11 +37889,20 @@ async function verifiedPid(existing) {
37666
37889
  );
37667
37890
  return snapshot?.pid === existing.pid ? snapshot : null;
37668
37891
  }
37669
- function daemonStartIsHealthy(serviceReady, snapshot, expectedVersion) {
37670
- return serviceReady && snapshot?.healthy === true && expectedVersion !== void 0 && snapshot.version === expectedVersion;
37892
+ function daemonStartIsReady(serviceReady, snapshot, expectedVersion) {
37893
+ if (!serviceReady || !snapshot || expectedVersion === void 0) return false;
37894
+ if (snapshot.version !== expectedVersion || snapshot.needsReauth === true) return false;
37895
+ if (snapshot.healthy === true) return true;
37896
+ return snapshot.heartbeat?.healthy === true && snapshot.ingestion?.healthy === false;
37671
37897
  }
37672
- function daemonStartHealthFields(healthy, snapshot) {
37673
- if (healthy) return {};
37898
+ function daemonBacklogFields(snapshot) {
37899
+ const state = deliveryBacklogState(snapshot?.ingestion);
37900
+ if (!state) return {};
37901
+ if (state === "draining") return { draining: true, ingestion: snapshot?.ingestion };
37902
+ return state === "retained" ? { draining: false, deliveryRetained: true, ingestion: snapshot?.ingestion } : { draining: false, deliveryFailing: true, ingestion: snapshot?.ingestion };
37903
+ }
37904
+ function daemonStartHealthFields(ready, snapshot) {
37905
+ if (ready) return daemonBacklogFields(snapshot);
37674
37906
  return {
37675
37907
  state: "degraded",
37676
37908
  needsReauth: snapshot?.needsReauth === true,
@@ -37678,16 +37910,28 @@ function daemonStartHealthFields(healthy, snapshot) {
37678
37910
  ingestion: snapshot?.ingestion ?? null
37679
37911
  };
37680
37912
  }
37913
+ function daemonStartFailureReason(snapshot, expectedVersion) {
37914
+ if (snapshot && !snapshot.needsReauth && expectedVersion !== void 0 && snapshot.version !== expectedVersion) {
37915
+ return `daemon reports ${snapshot.version ? `v${snapshot.version}` : "no version"}, expected v${expectedVersion}`;
37916
+ }
37917
+ return daemonDegradedReason(snapshot);
37918
+ }
37681
37919
  async function detachedDaemonStart(opts) {
37682
37920
  const existing = readPidfile();
37683
37921
  if (existing?.alive) {
37684
37922
  const snapshot = await verifiedPid(existing);
37685
37923
  if (snapshot) {
37686
37924
  process.stderr.write(
37687
- `${formatCurrentDaemonLifecycleMessage(`[prim] daemon already running (pid=${existing.pid})`)}
37925
+ `${formatCurrentDaemonLifecycleMessage(`[prim] daemon already running (pid=${existing.pid})`, snapshot)}
37688
37926
  `
37689
37927
  );
37690
- console.log(JSON.stringify({ started: false, pid: existing.pid }, null, 2));
37928
+ console.log(
37929
+ JSON.stringify(
37930
+ { started: false, pid: existing.pid, ...daemonBacklogFields(snapshot) },
37931
+ null,
37932
+ 2
37933
+ )
37934
+ );
37691
37935
  return;
37692
37936
  }
37693
37937
  process.stderr.write(
@@ -37736,16 +37980,19 @@ async function detachedDaemonStart(opts) {
37736
37980
  });
37737
37981
  child.unref();
37738
37982
  if (logFd !== void 0) {
37739
- closeSync3(logFd);
37983
+ closeSync4(logFd);
37740
37984
  }
37741
37985
  const live = await waitForReady();
37742
37986
  if (live) {
37743
37987
  const after = readPidfile();
37988
+ const snapshot = await requestStatusSnapshot();
37744
37989
  process.stderr.write(
37745
- `${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon started (pid=${after?.pid ?? "?"}, socket=${SOCK_PATH2})`)}
37990
+ `${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon started (pid=${after?.pid ?? "?"}, socket=${SOCK_PATH2})`, snapshot)}
37746
37991
  `
37747
37992
  );
37748
- console.log(JSON.stringify({ started: true, pid: after?.pid }, null, 2));
37993
+ console.log(
37994
+ JSON.stringify({ started: true, pid: after?.pid, ...daemonBacklogFields(snapshot) }, null, 2)
37995
+ );
37749
37996
  return;
37750
37997
  }
37751
37998
  process.stderr.write(
@@ -37868,19 +38115,25 @@ async function macDaemonStart(forceRestart = false) {
37868
38115
  const result = await ensureMacDaemon({ explicitlyStarted: true, forceRestart });
37869
38116
  const serviceReady = result.state === "running";
37870
38117
  const expectedVersion = result.runtime?.manifest.version;
37871
- const snapshot = serviceReady && expectedVersion ? await waitForHealthySnapshot(expectedVersion) : null;
37872
- const healthy = daemonStartIsHealthy(serviceReady, snapshot, expectedVersion);
37873
- if (healthy) {
38118
+ const snapshot = serviceReady && expectedVersion ? await waitForReadySnapshot(expectedVersion) : null;
38119
+ const ready = daemonStartIsReady(serviceReady, snapshot, expectedVersion);
38120
+ if (ready) {
37874
38121
  const verb = result.action === "none" ? "already running" : "started";
37875
38122
  const servicePid = result.service.loaded ? result.service.pid : void 0;
37876
38123
  process.stderr.write(
37877
- `${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ${verb} under launchd (pid=${snapshot?.pid ?? servicePid ?? "?"})`)}
38124
+ `${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ${verb} under launchd (pid=${snapshot?.pid ?? servicePid ?? "?"})`, snapshot)}
37878
38125
  `
37879
38126
  );
38127
+ } else if (!(serviceReady && expectedVersion)) {
38128
+ process.stderr.write(
38129
+ `[prim] \u2717 launchd did not converge on the desired daemon (${result.action}) (see ${LOG_PATH})
38130
+ `
38131
+ );
38132
+ if (!process.exitCode) process.exitCode = EXIT_NOT_RUNNING;
37880
38133
  } else {
37881
- const reason = daemonDegradedReason(snapshot);
38134
+ const reason = daemonStartFailureReason(snapshot, expectedVersion);
37882
38135
  process.stderr.write(
37883
- `[prim] \u2717 launchd daemon did not reach healthy heartbeat + ingestion state${reason ? ` \xB7 ${reason}` : ""} (see ${LOG_PATH})
38136
+ `[prim] \u2717 launchd daemon did not reach a healthy heartbeat on the expected version${reason ? ` \xB7 ${reason}` : ""} (see ${LOG_PATH})
37884
38137
  `
37885
38138
  );
37886
38139
  if (!process.exitCode) process.exitCode = EXIT_NOT_RUNNING;
@@ -37888,14 +38141,20 @@ async function macDaemonStart(forceRestart = false) {
37888
38141
  console.log(
37889
38142
  JSON.stringify(
37890
38143
  {
37891
- started: healthy,
38144
+ started: ready,
37892
38145
  supervised: true,
37893
38146
  action: result.action,
37894
38147
  pid: snapshot?.pid ?? (result.service.loaded ? result.service.pid : void 0),
37895
38148
  loaded: result.service.loaded,
37896
38149
  responding: result.responding,
37897
- healthy,
37898
- ...daemonStartHealthFields(healthy, snapshot),
38150
+ // `healthy` keeps meaning "start succeeded" (always equal to `started`
38151
+ // and the exit code) so existing consumers stay correct. It is not the
38152
+ // daemon's own health: `daemon status --json` stays degraded while a
38153
+ // backlog drains, is held back, or fails to deliver, which start
38154
+ // reports additively via `draining`, `deliveryRetained`,
38155
+ // `deliveryFailing`, and `ingestion`.
38156
+ healthy: ready,
38157
+ ...daemonStartHealthFields(ready, snapshot),
37899
38158
  version: snapshot?.version,
37900
38159
  expectedVersion
37901
38160
  },
@@ -38199,11 +38458,12 @@ async function daemonEnsure() {
38199
38458
  return { disabled };
38200
38459
  }
38201
38460
  const result = await ensureMacDaemon();
38461
+ const snapshot = result.state === "running" ? await requestStatusSnapshot() : null;
38202
38462
  if (result.state === "disabled") {
38203
38463
  process.stderr.write("[prim] daemon remains explicitly disabled\n");
38204
38464
  } else if (result.state === "running") {
38205
38465
  process.stderr.write(
38206
- `${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ensured under launchd (${result.action})`)}
38466
+ `${formatCurrentDaemonLifecycleMessage(`[prim] \u2713 daemon ensured under launchd (${result.action})`, snapshot)}
38207
38467
  `
38208
38468
  );
38209
38469
  } else {
@@ -38217,7 +38477,8 @@ async function daemonEnsure() {
38217
38477
  ensured: result.state === "running",
38218
38478
  disabled: result.state === "disabled",
38219
38479
  supervised: true,
38220
- action: result.action
38480
+ action: result.action,
38481
+ ...daemonBacklogFields(snapshot)
38221
38482
  },
38222
38483
  null,
38223
38484
  2
@@ -39273,11 +39534,11 @@ function asRecord(value, path9) {
39273
39534
  }
39274
39535
  return value;
39275
39536
  }
39276
- function assertKeys(record2, path9, required, optional = []) {
39537
+ function assertKeys(record3, path9, required, optional = []) {
39277
39538
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
39278
- const missing = required.find((key) => !Object.hasOwn(record2, key));
39539
+ const missing = required.find((key) => !Object.hasOwn(record3, key));
39279
39540
  if (missing !== void 0) contractError(path9, `is missing ${missing}`);
39280
- const extra = Object.keys(record2).find((key) => !allowed.has(key));
39541
+ const extra = Object.keys(record3).find((key) => !allowed.has(key));
39281
39542
  if (extra !== void 0) contractError(path9, `contains unexpected field ${extra}`);
39282
39543
  }
39283
39544
  function asString(value, path9, options) {
@@ -39307,97 +39568,97 @@ function asSha(value, path9) {
39307
39568
  if (!SHA_RE.test(sha)) contractError(path9, "must be a canonical lowercase Git commit SHA");
39308
39569
  return sha;
39309
39570
  }
39310
- function optionalString(record2, key, path9, max) {
39311
- if (!Object.hasOwn(record2, key)) return void 0;
39312
- return asString(record2[key], `${path9}.${key}`, { max, nonempty: true });
39571
+ function optionalString(record3, key, path9, max) {
39572
+ if (!Object.hasOwn(record3, key)) return void 0;
39573
+ return asString(record3[key], `${path9}.${key}`, { max, nonempty: true });
39313
39574
  }
39314
39575
  function parseProposal(value, path9) {
39315
- const record2 = asRecord(value, path9);
39576
+ const record3 = asRecord(value, path9);
39316
39577
  assertKeys(
39317
- record2,
39578
+ record3,
39318
39579
  path9,
39319
39580
  ["_id", "repoFullName", "deadCommitSha", "status", "signals", "lastEvaluatedAt"],
39320
39581
  ["proposedSha", "proposedTier", "nextEvaluateAt"]
39321
39582
  );
39322
- const repoFullName = asString(record2.repoFullName, `${path9}.repoFullName`, {
39583
+ const repoFullName = asString(record3.repoFullName, `${path9}.repoFullName`, {
39323
39584
  max: 200,
39324
39585
  nonempty: true
39325
39586
  });
39326
39587
  if (!REPO_FULL_NAME_RE.test(repoFullName)) {
39327
39588
  contractError(`${path9}.repoFullName`, "must be canonical owner/repository syntax");
39328
39589
  }
39329
- const status = record2.status;
39590
+ const status = record3.status;
39330
39591
  if (status !== "proposed" && status !== "confirmed" && status !== "verification_failed") {
39331
39592
  contractError(`${path9}.status`, "is not a review-visible proposal status");
39332
39593
  }
39333
- if (!Array.isArray(record2.signals) || record2.signals.length > MAX_SIGNALS) {
39594
+ if (!Array.isArray(record3.signals) || record3.signals.length > MAX_SIGNALS) {
39334
39595
  contractError(`${path9}.signals`, "must be a bounded array");
39335
39596
  }
39336
- const signals = record2.signals.map(
39597
+ const signals = record3.signals.map(
39337
39598
  (signal, index) => asString(signal, `${path9}.signals[${String(index)}]`, {
39338
39599
  max: MAX_SIGNAL_LENGTH,
39339
39600
  nonempty: true
39340
39601
  })
39341
39602
  );
39342
- const proposedSha = Object.hasOwn(record2, "proposedSha") ? asSha(record2.proposedSha, `${path9}.proposedSha`) : void 0;
39603
+ const proposedSha = Object.hasOwn(record3, "proposedSha") ? asSha(record3.proposedSha, `${path9}.proposedSha`) : void 0;
39343
39604
  let proposedTier;
39344
- if (Object.hasOwn(record2, "proposedTier")) {
39345
- if (record2.proposedTier !== "exact_patch_body" && record2.proposedTier !== "exact_file_set") {
39605
+ if (Object.hasOwn(record3, "proposedTier")) {
39606
+ if (record3.proposedTier !== "exact_patch_body" && record3.proposedTier !== "exact_file_set") {
39346
39607
  contractError(`${path9}.proposedTier`, "is not a supported match tier");
39347
39608
  }
39348
- proposedTier = record2.proposedTier;
39609
+ proposedTier = record3.proposedTier;
39349
39610
  }
39350
- const nextEvaluateAt = Object.hasOwn(record2, "nextEvaluateAt") ? asTimestamp(record2.nextEvaluateAt, `${path9}.nextEvaluateAt`) : void 0;
39611
+ const nextEvaluateAt = Object.hasOwn(record3, "nextEvaluateAt") ? asTimestamp(record3.nextEvaluateAt, `${path9}.nextEvaluateAt`) : void 0;
39351
39612
  return {
39352
- _id: asString(record2._id, `${path9}._id`, { max: 256, nonempty: true }),
39613
+ _id: asString(record3._id, `${path9}._id`, { max: 256, nonempty: true }),
39353
39614
  repoFullName,
39354
- deadCommitSha: asSha(record2.deadCommitSha, `${path9}.deadCommitSha`),
39615
+ deadCommitSha: asSha(record3.deadCommitSha, `${path9}.deadCommitSha`),
39355
39616
  status,
39356
39617
  proposedSha,
39357
39618
  proposedTier,
39358
39619
  signals,
39359
- lastEvaluatedAt: asTimestamp(record2.lastEvaluatedAt, `${path9}.lastEvaluatedAt`),
39620
+ lastEvaluatedAt: asTimestamp(record3.lastEvaluatedAt, `${path9}.lastEvaluatedAt`),
39360
39621
  nextEvaluateAt
39361
39622
  };
39362
39623
  }
39363
39624
  function parseDecision(value, path9) {
39364
- const record2 = asRecord(value, path9);
39365
- assertKeys(record2, path9, ["_id", "intent", "status", "intentTruncated"], ["shortId"]);
39366
- const status = record2.status;
39625
+ const record3 = asRecord(value, path9);
39626
+ assertKeys(record3, path9, ["_id", "intent", "status", "intentTruncated"], ["shortId"]);
39627
+ const status = record3.status;
39367
39628
  if (status !== "provisional" && status !== "abandoned") {
39368
39629
  contractError(`${path9}.status`, "is not a repairable decision status");
39369
39630
  }
39370
39631
  return {
39371
- _id: asString(record2._id, `${path9}._id`, { max: 256, nonempty: true }),
39372
- shortId: optionalString(record2, "shortId", path9, 128),
39373
- intent: asString(record2.intent, `${path9}.intent`, { max: MAX_INTENT_LENGTH }),
39632
+ _id: asString(record3._id, `${path9}._id`, { max: 256, nonempty: true }),
39633
+ shortId: optionalString(record3, "shortId", path9, 128),
39634
+ intent: asString(record3.intent, `${path9}.intent`, { max: MAX_INTENT_LENGTH }),
39374
39635
  status,
39375
- intentTruncated: asBoolean(record2.intentTruncated, `${path9}.intentTruncated`)
39636
+ intentTruncated: asBoolean(record3.intentTruncated, `${path9}.intentTruncated`)
39376
39637
  };
39377
39638
  }
39378
39639
  function parseListItem(value, path9) {
39379
- const record2 = asRecord(value, path9);
39380
- assertKeys(record2, path9, [
39640
+ const record3 = asRecord(value, path9);
39641
+ assertKeys(record3, path9, [
39381
39642
  "proposal",
39382
39643
  "decisions",
39383
39644
  "affectedDecisionCount",
39384
39645
  "decisionsTruncated",
39385
39646
  "reviewToken"
39386
39647
  ]);
39387
- if (!Array.isArray(record2.decisions) || record2.decisions.length > MAX_REPAIR_DECISIONS_FOR_CONFIRM) {
39648
+ if (!Array.isArray(record3.decisions) || record3.decisions.length > MAX_REPAIR_DECISIONS_FOR_CONFIRM) {
39388
39649
  contractError(`${path9}.decisions`, "must be a bounded array");
39389
39650
  }
39390
- const decisions = record2.decisions.map(
39651
+ const decisions = record3.decisions.map(
39391
39652
  (decision, index) => parseDecision(decision, `${path9}.decisions[${String(index)}]`)
39392
39653
  );
39393
- const proposal = parseProposal(record2.proposal, `${path9}.proposal`);
39394
- const decisionsTruncated = asBoolean(record2.decisionsTruncated, `${path9}.decisionsTruncated`);
39395
- const affectedDecisionCount = record2.affectedDecisionCount === null ? null : asCount(record2.affectedDecisionCount, `${path9}.affectedDecisionCount`);
39654
+ const proposal = parseProposal(record3.proposal, `${path9}.proposal`);
39655
+ const decisionsTruncated = asBoolean(record3.decisionsTruncated, `${path9}.decisionsTruncated`);
39656
+ const affectedDecisionCount = record3.affectedDecisionCount === null ? null : asCount(record3.affectedDecisionCount, `${path9}.affectedDecisionCount`);
39396
39657
  let reviewToken;
39397
- if (record2.reviewToken === null) {
39658
+ if (record3.reviewToken === null) {
39398
39659
  reviewToken = null;
39399
39660
  } else {
39400
- reviewToken = asString(record2.reviewToken, `${path9}.reviewToken`, {
39661
+ reviewToken = asString(record3.reviewToken, `${path9}.reviewToken`, {
39401
39662
  max: 64,
39402
39663
  nonempty: true
39403
39664
  });
@@ -39430,19 +39691,19 @@ function parseListItem(value, path9) {
39430
39691
  }
39431
39692
  function parseListPage(value, pageNumber) {
39432
39693
  const path9 = `page ${String(pageNumber)}`;
39433
- const record2 = asRecord(value, path9);
39434
- assertKeys(record2, path9, ["repairs", "isDone", "nextCursor", "truncated"]);
39435
- if (!Array.isArray(record2.repairs) || record2.repairs.length > MAX_REPAIRS_PER_PAGE) {
39694
+ const record3 = asRecord(value, path9);
39695
+ assertKeys(record3, path9, ["repairs", "isDone", "nextCursor", "truncated"]);
39696
+ if (!Array.isArray(record3.repairs) || record3.repairs.length > MAX_REPAIRS_PER_PAGE) {
39436
39697
  contractError(`${path9}.repairs`, "must be a bounded array");
39437
39698
  }
39438
- const isDone = asBoolean(record2.isDone, `${path9}.isDone`);
39439
- const truncated = asBoolean(record2.truncated, `${path9}.truncated`);
39699
+ const isDone = asBoolean(record3.isDone, `${path9}.isDone`);
39700
+ const truncated = asBoolean(record3.truncated, `${path9}.truncated`);
39440
39701
  if (truncated === isDone) contractError(path9, "has inconsistent isDone/truncated flags");
39441
39702
  let nextCursor;
39442
- if (record2.nextCursor === null) {
39703
+ if (record3.nextCursor === null) {
39443
39704
  nextCursor = null;
39444
39705
  } else {
39445
- nextCursor = asString(record2.nextCursor, `${path9}.nextCursor`, {
39706
+ nextCursor = asString(record3.nextCursor, `${path9}.nextCursor`, {
39446
39707
  max: MAX_CURSOR_LENGTH,
39447
39708
  nonempty: true
39448
39709
  });
@@ -39450,7 +39711,7 @@ function parseListPage(value, pageNumber) {
39450
39711
  if (isDone && nextCursor !== null) contractError(path9, "is done but includes a next cursor");
39451
39712
  if (!isDone && nextCursor === null) contractError(path9, "is incomplete without a next cursor");
39452
39713
  return {
39453
- repairs: record2.repairs.map(
39714
+ repairs: record3.repairs.map(
39454
39715
  (item, index) => parseListItem(item, `${path9}.repairs[${String(index)}]`)
39455
39716
  ),
39456
39717
  isDone,
@@ -39528,23 +39789,23 @@ function normalizedResolutionInput(proposalId, expectedProposedSha) {
39528
39789
  return { proposalId, expectedProposedSha: expectedProposedSha.toLowerCase() };
39529
39790
  }
39530
39791
  function parseResolutionOutcome(value, action) {
39531
- const record2 = asRecord(value, "resolution response");
39792
+ const record3 = asRecord(value, "resolution response");
39532
39793
  const allowed = action === "confirm" ? ["confirmed", "already_applied", "disabled", "no_op", "stale"] : ["rejected", "already_applied", "no_op", "stale"];
39533
- if (record2.status === "backoff") {
39534
- assertKeys(record2, "resolution response", ["status", "retryAt"]);
39794
+ if (record3.status === "backoff") {
39795
+ assertKeys(record3, "resolution response", ["status", "retryAt"]);
39535
39796
  if (action !== "confirm") {
39536
39797
  contractError("resolution response.status", "backoff is impossible for reject");
39537
39798
  }
39538
39799
  return {
39539
39800
  status: "backoff",
39540
- retryAt: asTimestamp(record2.retryAt, "resolution response.retryAt")
39801
+ retryAt: asTimestamp(record3.retryAt, "resolution response.retryAt")
39541
39802
  };
39542
39803
  }
39543
- assertKeys(record2, "resolution response", ["status"]);
39544
- if (typeof record2.status !== "string" || !allowed.includes(record2.status)) {
39804
+ assertKeys(record3, "resolution response", ["status"]);
39805
+ if (typeof record3.status !== "string" || !allowed.includes(record3.status)) {
39545
39806
  contractError("resolution response.status", "is not a supported outcome");
39546
39807
  }
39547
- return { status: record2.status };
39808
+ return { status: record3.status };
39548
39809
  }
39549
39810
  function normalizedReviewToken(value) {
39550
39811
  if (value === void 0 || !REVIEW_TOKEN_INPUT_RE.test(value)) {
@@ -39555,18 +39816,18 @@ function normalizedReviewToken(value) {
39555
39816
  return value.toLowerCase();
39556
39817
  }
39557
39818
  function parseConflictOutcome(value, action) {
39558
- const record2 = asRecord(value, "resolution conflict response");
39559
- assertKeys(record2, "resolution conflict response", ["status"]);
39819
+ const record3 = asRecord(value, "resolution conflict response");
39820
+ assertKeys(record3, "resolution conflict response", ["status"]);
39560
39821
  if (action !== "confirm") {
39561
39822
  contractError(
39562
39823
  "resolution conflict response.status",
39563
39824
  "a review conflict is impossible for reject"
39564
39825
  );
39565
39826
  }
39566
- if (record2.status !== "review_too_large" && record2.status !== "stale_review") {
39827
+ if (record3.status !== "review_too_large" && record3.status !== "stale_review") {
39567
39828
  contractError("resolution conflict response.status", "is not a supported 409 outcome");
39568
39829
  }
39569
- return { status: record2.status };
39830
+ return { status: record3.status };
39570
39831
  }
39571
39832
  async function resolveRepair(proposalId, expectedProposedSha, action, expectedReviewToken, deps = defaultDeps6) {
39572
39833
  const normalized = normalizedResolutionInput(proposalId, expectedProposedSha);
@@ -40708,7 +40969,7 @@ function registerDecisionsCommands(program3) {
40708
40969
  }
40709
40970
 
40710
40971
  // src/commands/doctor.ts
40711
- import { existsSync as existsSync15 } from "fs";
40972
+ import { existsSync as existsSync16 } from "fs";
40712
40973
 
40713
40974
  // src/decisions/feedback.ts
40714
40975
  var FEEDBACK_PROTOCOL_VERSION = 2;
@@ -40757,28 +41018,35 @@ function parseOrganizationBinding(value) {
40757
41018
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
40758
41019
  return { state: "server_contract_unavailable" };
40759
41020
  }
40760
- const record2 = value;
40761
- if (record2.authenticated !== true || record2.organizationBindingVersion !== ORGANIZATION_BINDING_VERSION || !isCaptureAuthorityKind(record2.captureAuthorityKind)) {
41021
+ const record3 = value;
41022
+ if (record3.authenticated !== true || record3.organizationBindingVersion !== ORGANIZATION_BINDING_VERSION || !isCaptureAuthorityKind(record3.captureAuthorityKind)) {
40762
41023
  return { state: "server_contract_unavailable" };
40763
41024
  }
40764
- if (record2.organizationId === null && record2.workosOrganizationId === null) {
41025
+ if (record3.organizationId === null && record3.workosOrganizationId === null) {
40765
41026
  return { state: "no_current_organization" };
40766
41027
  }
40767
- if (safeOrganizationId(record2.organizationId) && record2.workosOrganizationId === null) {
41028
+ if (safeOrganizationId(record3.organizationId) && record3.workosOrganizationId === null) {
40768
41029
  return { state: "organization_identity_unreconciled" };
40769
41030
  }
40770
- if (!safeOrganizationId(record2.organizationId) || !safeOrganizationId(record2.workosOrganizationId)) {
41031
+ if (!safeOrganizationId(record3.organizationId) || !safeOrganizationId(record3.workosOrganizationId)) {
40771
41032
  return { state: "server_contract_unavailable" };
40772
41033
  }
40773
41034
  return {
40774
41035
  state: "current",
40775
41036
  binding: {
40776
- captureAuthorityKind: record2.captureAuthorityKind,
40777
- organizationId: record2.organizationId,
40778
- workosOrganizationId: record2.workosOrganizationId
41037
+ captureAuthorityKind: record3.captureAuthorityKind,
41038
+ organizationId: record3.organizationId,
41039
+ workosOrganizationId: record3.workosOrganizationId
40779
41040
  }
40780
41041
  };
40781
41042
  }
41043
+ function summarizeRetainedBuckets(retained) {
41044
+ const counts = /* @__PURE__ */ new Map();
41045
+ for (const item of retained) {
41046
+ counts.set(item.reason, (counts.get(item.reason) ?? 0) + 1);
41047
+ }
41048
+ return [...counts].sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${String(count)}`).join(", ");
41049
+ }
40782
41050
  function classifyJournalBuckets(buckets, binding) {
40783
41051
  const deliverableBuckets = /* @__PURE__ */ new Set();
40784
41052
  const retainedBuckets = [];
@@ -40852,18 +41120,109 @@ async function inspectJournalDelivery(buckets, options = {}) {
40852
41120
  // src/journal.ts
40853
41121
  import {
40854
41122
  appendFileSync,
40855
- closeSync as closeSync4,
40856
- existsSync as existsSync13,
40857
- fstatSync,
41123
+ closeSync as closeSync5,
41124
+ existsSync as existsSync14,
41125
+ fstatSync as fstatSync2,
40858
41126
  mkdirSync as mkdirSync10,
40859
- openSync as openSync4,
41127
+ openSync as openSync5,
40860
41128
  opendirSync,
41129
+ readFileSync as readFileSync15,
41130
+ readSync as readSync2,
41131
+ readdirSync as readdirSync5
41132
+ } from "fs";
41133
+ import { dirname as dirname12, join as join18 } from "path";
41134
+
41135
+ // src/drain-progress.ts
41136
+ import {
41137
+ chmodSync as chmodSync5,
41138
+ existsSync as existsSync13,
40861
41139
  readFileSync as readFileSync14,
40862
- readSync,
40863
- readdirSync as readdirSync4
41140
+ readdirSync as readdirSync4,
41141
+ rmSync as rmSync6,
41142
+ rmdirSync
40864
41143
  } from "fs";
40865
- import { dirname as dirname11, join as join17 } from "path";
40866
- var JOURNAL_DIR = join17(primConfigDirectory(), "moves");
41144
+ import { basename as basename4, dirname as dirname11, join as join17 } from "path";
41145
+ var DIRECTORY_MODE3 = 448;
41146
+ var FILE_MODE2 = 384;
41147
+ var DRAIN_PROGRESS_DIRNAME = "drain-progress";
41148
+ var CHECKPOINT_NAME = /^(.+)\.json(?:\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.tmp)?$/;
41149
+ function rotationIdentity(stat) {
41150
+ return { size: stat.size, ino: stat.ino, dev: stat.dev };
41151
+ }
41152
+ function drainProgressDirectory(flushingPath) {
41153
+ return join17(dirname11(flushingPath), DRAIN_PROGRESS_DIRNAME);
41154
+ }
41155
+ function drainProgressPath(flushingPath) {
41156
+ return join17(drainProgressDirectory(flushingPath), `${basename4(flushingPath)}.json`);
41157
+ }
41158
+ function isByteCount(value) {
41159
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
41160
+ }
41161
+ function drainResumeOffset(flushingPath, rotation) {
41162
+ let parsed;
41163
+ try {
41164
+ parsed = JSON.parse(readFileSync14(drainProgressPath(flushingPath), "utf8"));
41165
+ } catch {
41166
+ return 0;
41167
+ }
41168
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
41169
+ return 0;
41170
+ }
41171
+ const { v, offset, size, ino, dev } = parsed;
41172
+ if (v !== 2 || ino !== rotation.ino || dev !== rotation.dev || size !== rotation.size || !isByteCount(offset) || offset > rotation.size) {
41173
+ return 0;
41174
+ }
41175
+ return offset;
41176
+ }
41177
+ function writeDrainCheckpoint(flushingPath, checkpoint) {
41178
+ const directory = drainProgressDirectory(flushingPath);
41179
+ ensureDurableDirectory(directory, DIRECTORY_MODE3);
41180
+ chmodSync5(directory, DIRECTORY_MODE3);
41181
+ atomicWriteFile(drainProgressPath(flushingPath), `${JSON.stringify(checkpoint)}
41182
+ `, {
41183
+ mode: FILE_MODE2
41184
+ });
41185
+ }
41186
+ function removeEmptyDirectory(directory) {
41187
+ try {
41188
+ rmdirSync(directory);
41189
+ } catch (error) {
41190
+ const code = error.code;
41191
+ if (code !== "ENOENT" && code !== "ENOTEMPTY" && code !== "EEXIST") {
41192
+ throw error;
41193
+ }
41194
+ }
41195
+ }
41196
+ function removeDrainCheckpoint(flushingPath) {
41197
+ rmSync6(drainProgressPath(flushingPath), { force: true });
41198
+ removeEmptyDirectory(drainProgressDirectory(flushingPath));
41199
+ }
41200
+ function sweepOrphanedDrainCheckpoints(bucketDirectory) {
41201
+ const directory = join17(bucketDirectory, DRAIN_PROGRESS_DIRNAME);
41202
+ let names;
41203
+ try {
41204
+ names = readdirSync4(directory);
41205
+ } catch {
41206
+ return;
41207
+ }
41208
+ for (const name of names) {
41209
+ const rotation = CHECKPOINT_NAME.exec(name)?.[1];
41210
+ if (rotation === void 0 || existsSync13(join17(bucketDirectory, rotation))) {
41211
+ continue;
41212
+ }
41213
+ try {
41214
+ rmSync6(join17(directory, name), { force: true });
41215
+ } catch {
41216
+ }
41217
+ }
41218
+ try {
41219
+ removeEmptyDirectory(directory);
41220
+ } catch {
41221
+ }
41222
+ }
41223
+
41224
+ // src/journal.ts
41225
+ var JOURNAL_DIR = join18(primConfigDirectory(), "moves");
40867
41226
  var JOURNAL_BASENAME = "journal.ndjson";
40868
41227
  var JOURNAL_STATS_SAMPLE_BYTES = 64 * 1024;
40869
41228
  var PENDING_STATS_MAX_FILES = 128;
@@ -40875,7 +41234,7 @@ function envSlug(apiUrl) {
40875
41234
  return slug;
40876
41235
  }
40877
41236
  function currentEnvDir() {
40878
- return join17(JOURNAL_DIR, envSlug(getSiteUrl()));
41237
+ return join18(JOURNAL_DIR, envSlug(getSiteUrl()));
40879
41238
  }
40880
41239
  function parseMoves(content) {
40881
41240
  const moves = [];
@@ -40891,23 +41250,23 @@ function parseMoves(content) {
40891
41250
  return moves;
40892
41251
  }
40893
41252
  function readMovesFromPath(path9) {
40894
- if (!existsSync13(path9)) {
41253
+ if (!existsSync14(path9)) {
40895
41254
  return [];
40896
41255
  }
40897
- return parseMoves(readFileSync14(path9, "utf-8"));
41256
+ return parseMoves(readFileSync15(path9, "utf-8"));
40898
41257
  }
40899
41258
  function listBuckets() {
40900
41259
  const out = [];
40901
41260
  const envDir = currentEnvDir();
40902
- if (!existsSync13(envDir)) {
41261
+ if (!existsSync14(envDir)) {
40903
41262
  return out;
40904
41263
  }
40905
- for (const entry of readdirSync4(envDir, { withFileTypes: true })) {
41264
+ for (const entry of readdirSync5(envDir, { withFileTypes: true })) {
40906
41265
  if (!entry.isDirectory()) {
40907
41266
  continue;
40908
41267
  }
40909
- const path9 = join17(envDir, entry.name, JOURNAL_BASENAME);
40910
- if (existsSync13(path9)) {
41268
+ const path9 = join18(envDir, entry.name, JOURNAL_BASENAME);
41269
+ if (existsSync14(path9)) {
40911
41270
  out.push({ bucket: entry.name, path: path9 });
40912
41271
  }
40913
41272
  }
@@ -40924,22 +41283,23 @@ function oldestCapturedAt(moves) {
40924
41283
  }
40925
41284
  return oldest;
40926
41285
  }
40927
- function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
40928
- const fd = openSync4(path9, "r");
41286
+ function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES, options = {}) {
41287
+ const fd = openSync5(path9, "r");
40929
41288
  try {
40930
- const initial = fstatSync(fd);
40931
- const target = Math.min(initial.size, Math.max(0, maxBytes));
41289
+ const initial = fstatSync2(fd);
41290
+ const start = options.fromDrainCheckpoint ? drainResumeOffset(path9, rotationIdentity(initial)) : 0;
41291
+ const target = Math.min(initial.size - start, Math.max(0, maxBytes));
40932
41292
  const buffer = Buffer.allocUnsafe(target);
40933
41293
  let sampledBytes = 0;
40934
41294
  while (sampledBytes < target) {
40935
- const count = readSync(fd, buffer, sampledBytes, target - sampledBytes, sampledBytes);
41295
+ const count = readSync2(fd, buffer, sampledBytes, target - sampledBytes, start + sampledBytes);
40936
41296
  if (count === 0) {
40937
41297
  break;
40938
41298
  }
40939
41299
  sampledBytes += count;
40940
41300
  }
40941
- const stat = fstatSync(fd);
40942
- const sampled = stat.size > sampledBytes;
41301
+ const stat = fstatSync2(fd);
41302
+ const sampled = stat.size - start > sampledBytes;
40943
41303
  let content = buffer.subarray(0, sampledBytes).toString("utf-8");
40944
41304
  if (sampled) {
40945
41305
  const lastNewline = content.lastIndexOf("\n");
@@ -40949,6 +41309,7 @@ function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
40949
41309
  const moves = parseMoves(content);
40950
41310
  return {
40951
41311
  sizeBytes: stat.size,
41312
+ pendingBytes: Math.max(0, stat.size - start),
40952
41313
  mtimeMs: stat.mtimeMs,
40953
41314
  lineCount: sampled && stat.size > 0 ? Math.max(1, lines.length) : lines.length,
40954
41315
  oldestCapturedAt: oldestCapturedAt(moves),
@@ -40956,7 +41317,7 @@ function sampleJournalFile(path9, maxBytes = JOURNAL_STATS_SAMPLE_BYTES) {
40956
41317
  sampledBytes
40957
41318
  };
40958
41319
  } finally {
40959
- closeSync4(fd);
41320
+ closeSync5(fd);
40960
41321
  }
40961
41322
  }
40962
41323
  function parseFlushingPid(name) {
@@ -40967,7 +41328,7 @@ function parseFlushingPid(name) {
40967
41328
  return Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
40968
41329
  }
40969
41330
  function listFlushingInDir(dir, bucket, options = {}) {
40970
- if (!existsSync13(dir)) {
41331
+ if (!existsSync14(dir)) {
40971
41332
  return [];
40972
41333
  }
40973
41334
  const out = [];
@@ -40981,10 +41342,10 @@ function listFlushingInDir(dir, bucket, options = {}) {
40981
41342
  entry = entries.readSync();
40982
41343
  continue;
40983
41344
  }
40984
- const path9 = join17(dir, entry.name);
41345
+ const path9 = join18(dir, entry.name);
40985
41346
  let sample;
40986
41347
  try {
40987
- sample = sampleJournalFile(path9, remainingBytes);
41348
+ sample = sampleJournalFile(path9, remainingBytes, { fromDrainCheckpoint: true });
40988
41349
  } catch (err) {
40989
41350
  if (err.code === "ENOENT") {
40990
41351
  entry = entries.readSync();
@@ -41008,18 +41369,18 @@ function listFlushingInDir(dir, bucket, options = {}) {
41008
41369
  }
41009
41370
  function listFlushing(options = {}) {
41010
41371
  const envDir = currentEnvDir();
41011
- if (!existsSync13(envDir)) {
41372
+ if (!existsSync14(envDir)) {
41012
41373
  return [];
41013
41374
  }
41014
41375
  const out = [];
41015
41376
  let remainingBytes = options.sampleBytes ?? JOURNAL_STATS_SAMPLE_BYTES;
41016
41377
  const maxFiles = options.maxFiles ?? Number.POSITIVE_INFINITY;
41017
- for (const entry of readdirSync4(envDir, { withFileTypes: true })) {
41378
+ for (const entry of readdirSync5(envDir, { withFileTypes: true })) {
41018
41379
  if (out.length >= maxFiles) {
41019
41380
  break;
41020
41381
  }
41021
41382
  if (entry.isDirectory()) {
41022
- const found = listFlushingInDir(join17(envDir, entry.name), entry.name, {
41383
+ const found = listFlushingInDir(join18(envDir, entry.name), entry.name, {
41023
41384
  sampleBytes: remainingBytes,
41024
41385
  maxFiles: maxFiles - out.length
41025
41386
  });
@@ -41032,6 +41393,17 @@ function listFlushing(options = {}) {
41032
41393
  }
41033
41394
  return out;
41034
41395
  }
41396
+ function sweepOrphanedDrainProgress() {
41397
+ const envDir = currentEnvDir();
41398
+ if (!existsSync14(envDir)) {
41399
+ return;
41400
+ }
41401
+ for (const entry of readdirSync5(envDir, { withFileTypes: true })) {
41402
+ if (entry.isDirectory()) {
41403
+ sweepOrphanedDrainCheckpoints(join18(envDir, entry.name));
41404
+ }
41405
+ }
41406
+ }
41035
41407
  function bucketStats(options = {}) {
41036
41408
  const out = [];
41037
41409
  let remainingBytes = options.sampleBytes ?? JOURNAL_STATS_SAMPLE_BYTES;
@@ -41077,9 +41449,9 @@ function pendingJournalStats() {
41077
41449
  }
41078
41450
 
41079
41451
  // src/commands/hermes-install.ts
41080
- import { existsSync as existsSync14, readFileSync as readFileSync15, rmSync as rmSync6, statSync as statSync3 } from "fs";
41452
+ import { existsSync as existsSync15, readFileSync as readFileSync16, rmSync as rmSync7, statSync as statSync4 } from "fs";
41081
41453
  import { homedir as homedir9 } from "os";
41082
- import { join as join18 } from "path";
41454
+ import { join as join19 } from "path";
41083
41455
 
41084
41456
  // node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
41085
41457
  var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias");
@@ -47358,13 +47730,13 @@ var JSON_INDENT3 = 2;
47358
47730
  var CONFIG_MODE = 384;
47359
47731
  var PRIM_BINS4 = [CAPTURE_BIN4, GATE_BIN4, POST_TOOL_USE_BIN3, SESSION_START_BIN4, SESSION_END_BIN3];
47360
47732
  function hermesHome() {
47361
- return process.env.HERMES_HOME ?? join18(homedir9(), ".hermes");
47733
+ return process.env.HERMES_HOME ?? join19(homedir9(), ".hermes");
47362
47734
  }
47363
47735
  function configPath() {
47364
- return join18(hermesHome(), "config.yaml");
47736
+ return join19(hermesHome(), "config.yaml");
47365
47737
  }
47366
47738
  function legacyShimPath() {
47367
- return join18(hermesHome(), "agent-hooks", "prim-shim.sh");
47739
+ return join19(hermesHome(), "agent-hooks", "prim-shim.sh");
47368
47740
  }
47369
47741
  function commandFor2(bin) {
47370
47742
  return stableHookCommand(bin, HERMES_ARGS);
@@ -47478,8 +47850,8 @@ function hookRuntimeResolutions3(hooks) {
47478
47850
  }
47479
47851
  function inspectHookRuntimeResolutions4() {
47480
47852
  const path9 = configPath();
47481
- if (!existsSync14(path9)) return [];
47482
- return hookRuntimeResolutions3(readHooks(parseHermesDocument(readFileSync15(path9, "utf8"))));
47853
+ if (!existsSync15(path9)) return [];
47854
+ return hookRuntimeResolutions3(readHooks(parseHermesDocument(readFileSync16(path9, "utf8"))));
47483
47855
  }
47484
47856
  function entryMatchesRegistration3(owned, registration) {
47485
47857
  const expected = entryFor(registration);
@@ -47748,11 +48120,11 @@ function assertMergeValid(before, after, expectedHooks) {
47748
48120
  }
47749
48121
  }
47750
48122
  function readConfigSnapshot(path9) {
47751
- if (!existsSync14(path9)) return { exists: false, mode: CONFIG_MODE, raw: "" };
48123
+ if (!existsSync15(path9)) return { exists: false, mode: CONFIG_MODE, raw: "" };
47752
48124
  return {
47753
48125
  exists: true,
47754
- mode: statSync3(path9).mode & 511,
47755
- raw: readFileSync15(path9, "utf8")
48126
+ mode: statSync4(path9).mode & 511,
48127
+ raw: readFileSync16(path9, "utf8")
47756
48128
  };
47757
48129
  }
47758
48130
  var CONCURRENT_CONFIG_MESSAGE = "Hermes config changed during update; concurrent changes were preserved, retry the command";
@@ -47761,7 +48133,7 @@ function writeHermesConfigSnapshot(path9, before, after, expectedHooks) {
47761
48133
  ensureParent: true,
47762
48134
  mode: before.mode,
47763
48135
  validate(temporaryPath) {
47764
- const flushed = readFileSync15(temporaryPath, "utf8");
48136
+ const flushed = readFileSync16(temporaryPath, "utf8");
47765
48137
  if (!mergePreservesHermesSemantics(before.raw, flushed, expectedHooks)) {
47766
48138
  throw new Error(INVALID_MERGE_MESSAGE);
47767
48139
  }
@@ -47773,7 +48145,7 @@ function writeHermesConfigSnapshot(path9, before, after, expectedHooks) {
47773
48145
  });
47774
48146
  }
47775
48147
  function removeLegacyShim() {
47776
- rmSync6(legacyShimPath(), { force: true });
48148
+ rmSync7(legacyShimPath(), { force: true });
47777
48149
  }
47778
48150
  function autoAcceptOf(raw) {
47779
48151
  if (raw.trim().length === 0) {
@@ -47814,7 +48186,7 @@ function performInstall4(opts) {
47814
48186
  }
47815
48187
  function performUninstall4() {
47816
48188
  const path9 = configPath();
47817
- if (!existsSync14(path9)) {
48189
+ if (!existsSync15(path9)) {
47818
48190
  removeLegacyShim();
47819
48191
  return { path: path9, gate: false, capture: false, autoAccept: false, changed: false };
47820
48192
  }
@@ -47839,7 +48211,7 @@ function performUninstall4() {
47839
48211
  }
47840
48212
  function performStatus4() {
47841
48213
  const path9 = configPath();
47842
- const raw = existsSync14(path9) ? readFileSync15(path9, "utf8") : "";
48214
+ const raw = existsSync15(path9) ? readFileSync16(path9, "utf8") : "";
47843
48215
  const hooks = raw ? readHooks(parseHermesDocument(raw)) : {};
47844
48216
  return {
47845
48217
  path: path9,
@@ -47893,7 +48265,6 @@ function registerHermesCommands(program3) {
47893
48265
  // src/commands/doctor.ts
47894
48266
  var DAEMON_PROBE_TIMEOUT_MS2 = 500;
47895
48267
  var CONNECTIVITY_TIMEOUT_MS = 3e3;
47896
- var MS_PER_SECOND = 1e3;
47897
48268
  var STALE_PENDING_MS = 3e4;
47898
48269
  var EXIT_UNHEALTHY = 1;
47899
48270
  function classifyDoctor(checks) {
@@ -47929,7 +48300,7 @@ function checkAuth() {
47929
48300
  return classifyAuthCredential(
47930
48301
  credential,
47931
48302
  storedCredential ? getTokenExpiresAt() : void 0,
47932
- storedCredential && existsSync15(REFRESH_TOKEN_PATH)
48303
+ storedCredential && existsSync16(REFRESH_TOKEN_PATH)
47933
48304
  );
47934
48305
  }
47935
48306
  function classifyDaemonHealth(snapshot, options = {}) {
@@ -48015,8 +48386,19 @@ function classifyDaemonHealth(snapshot, options = {}) {
48015
48386
  };
48016
48387
  }
48017
48388
  if (!snapshot.ingestion?.healthy) {
48018
- const pending = snapshot.ingestion?.pendingCount ?? 0;
48019
- const pendingLabel = snapshot.ingestion?.pendingSampled ? `at least ${String(pending)}` : String(pending);
48389
+ if (options.backlogExpected && deliveryBacklogState(snapshot.ingestion) === "draining") {
48390
+ const ingestionStatus2 = options.ingestionStatus ?? decisionIngestionStatus(process.cwd());
48391
+ const live = options.backlog;
48392
+ const progress = live && live.pendingCount === 0 && live.pendingSampled !== true ? "backlog drained; daemon health refreshes on its next sweep" : deliveryBacklogSummary(snapshot.ingestion, options.now, live);
48393
+ return {
48394
+ name: "daemon",
48395
+ status: "warn",
48396
+ detail: `supervised and live${snapshot.version ? ` \xB7 v${snapshot.version}` : ""} \xB7 Decision ingestion ${ingestionStatus2} \xB7 ${progress}`
48397
+ };
48398
+ }
48399
+ const counts = options.backlog ?? snapshot.ingestion;
48400
+ const pending = counts?.pendingCount ?? 0;
48401
+ const pendingLabel = counts?.pendingSampled ? `at least ${String(pending)}` : String(pending);
48020
48402
  return {
48021
48403
  name: "daemon",
48022
48404
  status: "fail",
@@ -48033,17 +48415,23 @@ function classifyDaemonHealth(snapshot, options = {}) {
48033
48415
  detail: `supervised and healthy${snapshot.version ? ` \xB7 v${snapshot.version}` : ""} \xB7 Decision ingestion ${ingestionStatus}`
48034
48416
  };
48035
48417
  }
48036
- async function checkDaemon() {
48418
+ async function probeDelivery() {
48419
+ const stats = pendingJournalStats();
48420
+ const now = Date.now();
48421
+ const disabled = daemonExplicitlyDisabled();
48422
+ const expectedVersion = packageVersion();
48037
48423
  let service;
48038
48424
  if (process.platform === "darwin") {
48039
48425
  try {
48040
48426
  service = getLaunchdService();
48041
48427
  } catch (error) {
48042
- const detail = boundedHealthError(error instanceof Error ? error.message : String(error));
48043
48428
  return {
48044
- name: "daemon",
48045
- status: "fail",
48046
- detail: `launchd status unavailable${detail ? `: ${detail}` : ""}`
48429
+ snapshot: null,
48430
+ launchdError: error instanceof Error ? error.message : String(error),
48431
+ disabled,
48432
+ expectedVersion,
48433
+ stats,
48434
+ now
48047
48435
  };
48048
48436
  }
48049
48437
  }
@@ -48052,53 +48440,74 @@ async function checkDaemon() {
48052
48440
  { callerEnv: getSiteUrl() },
48053
48441
  { timeoutMs: DAEMON_PROBE_TIMEOUT_MS2 }
48054
48442
  );
48055
- return classifyDaemonHealth(snapshot, {
48056
- disabled: daemonExplicitlyDisabled(),
48057
- service,
48058
- expectedVersion: packageVersion()
48443
+ return { snapshot, service, disabled, expectedVersion, stats, now };
48444
+ }
48445
+ function daemonCheckVouchesForDrain(check) {
48446
+ return check.name === "daemon" && check.status !== "fail";
48447
+ }
48448
+ function classifyDelivery(probe, options = {}) {
48449
+ const { stats, now } = probe;
48450
+ const launchdDetail = probe.launchdError === void 0 ? void 0 : boundedHealthError(probe.launchdError);
48451
+ const daemon = probe.launchdError === void 0 ? classifyDaemonHealth(probe.snapshot, {
48452
+ disabled: probe.disabled,
48453
+ service: probe.service,
48454
+ ingestionStatus: probe.ingestionStatus,
48455
+ expectedVersion: probe.expectedVersion,
48456
+ backlogExpected: options.backlogExpected,
48457
+ backlog: {
48458
+ pendingCount: stats.pendingCount,
48459
+ pendingSampled: stats.sampled,
48460
+ oldestPendingAt: stats.oldestPendingAt
48461
+ },
48462
+ now
48463
+ }) : {
48464
+ name: "daemon",
48465
+ status: "fail",
48466
+ detail: `launchd status unavailable${launchdDetail ? `: ${launchdDetail}` : ""}`
48467
+ };
48468
+ const journal = classifyJournal(stats, now, {
48469
+ backlogExpected: options.backlogExpected === true && daemonCheckVouchesForDrain(daemon)
48059
48470
  });
48471
+ return [daemon, journal, classifyStranded(stats)];
48060
48472
  }
48061
- function checkJournal() {
48062
- const stats = pendingJournalStats();
48473
+ function classifyJournal(stats, now, options = {}) {
48063
48474
  const pending = stats.pendingCount;
48064
48475
  const pendingLabel = stats.sampled ? `at least ${String(pending)}` : String(pending);
48476
+ const backlog = (detail) => options.backlogExpected ? {
48477
+ name: "journal",
48478
+ status: "warn",
48479
+ detail: `${formatPendingBacklog(
48480
+ {
48481
+ pendingCount: pending,
48482
+ pendingSampled: stats.sampled,
48483
+ oldestPendingAt: stats.oldestPendingAt
48484
+ },
48485
+ now
48486
+ )} \u2014 draining in the background`
48487
+ } : { name: "journal", status: "fail", detail };
48065
48488
  if (pending === 0) {
48066
48489
  if (stats.sampled) {
48067
- return {
48068
- name: "journal",
48069
- status: "fail",
48070
- detail: "bounded journal sample could not prove the queue is empty"
48071
- };
48490
+ return backlog("bounded journal sample could not prove the queue is empty");
48072
48491
  }
48073
48492
  return { name: "journal", status: "ok", detail: "no pending moves" };
48074
48493
  }
48075
48494
  if (stats.oldestPendingAt === void 0) {
48076
- return {
48077
- name: "journal",
48078
- status: "fail",
48079
- detail: `${pendingLabel} pending with no readable capture timestamp`
48080
- };
48495
+ const detail = `${pendingLabel} pending with no readable capture timestamp`;
48496
+ return stats.sampled ? backlog(detail) : { name: "journal", status: "fail", detail };
48081
48497
  }
48082
- const oldestMs = Date.now() - stats.oldestPendingAt;
48498
+ const oldestMs = now - stats.oldestPendingAt;
48083
48499
  const oldestS = Math.round(oldestMs / MS_PER_SECOND);
48084
48500
  if (oldestMs > STALE_PENDING_MS) {
48085
- return {
48086
- name: "journal",
48087
- status: "fail",
48088
- detail: `${pendingLabel} pending, oldest observed ${String(oldestS)}s \u2014 30s delivery SLA missed`
48089
- };
48501
+ return backlog(
48502
+ `${pendingLabel} pending, oldest observed ${String(oldestS)}s \u2014 30s delivery SLA missed`
48503
+ );
48090
48504
  }
48091
48505
  if (stats.sampled) {
48092
- return {
48093
- name: "journal",
48094
- status: "fail",
48095
- detail: `${pendingLabel} pending in bounded sample; 30s delivery SLA cannot be proven`
48096
- };
48506
+ return backlog(`${pendingLabel} pending in bounded sample; 30s delivery SLA cannot be proven`);
48097
48507
  }
48098
48508
  return { name: "journal", status: "ok", detail: `${String(pending)} pending, draining` };
48099
48509
  }
48100
- function checkStranded() {
48101
- const stats = pendingJournalStats();
48510
+ function classifyStranded(stats) {
48102
48511
  if (stats.strandedFileCount === 0 && !stats.strandedSampled) {
48103
48512
  return { name: "stranded", status: "ok", detail: "none" };
48104
48513
  }
@@ -48124,15 +48533,10 @@ function classifyJournalOrganization(bucketCount, retainedBuckets) {
48124
48533
  detail: "all pending buckets match the active credential"
48125
48534
  };
48126
48535
  }
48127
- const reasonCounts = /* @__PURE__ */ new Map();
48128
- for (const item of retainedBuckets) {
48129
- reasonCounts.set(item.reason, (reasonCounts.get(item.reason) ?? 0) + 1);
48130
- }
48131
- const reasons = [...reasonCounts].sort(([left], [right]) => left.localeCompare(right)).map(([reason, count]) => `${reason}:${String(count)}`).join(", ");
48132
48536
  return {
48133
48537
  name: "journal-org",
48134
48538
  status: "fail",
48135
- detail: `${String(retainedBuckets.length)} bucket(s) retained (${reasons})`
48539
+ detail: `${String(retainedBuckets.length)} bucket(s) retained (${summarizeRetainedBuckets(retainedBuckets)})`
48136
48540
  };
48137
48541
  }
48138
48542
  async function checkJournalOrganization() {
@@ -48543,30 +48947,37 @@ function refreshOwnedGlobalHooksForHealth() {
48543
48947
  } catch {
48544
48948
  }
48545
48949
  }
48546
- async function collectChecks() {
48950
+ async function independentChecks() {
48547
48951
  refreshOwnedGlobalHooksForHealth();
48548
48952
  const backend = await checkBackend();
48549
- return [
48550
- checkAuth(),
48551
- await checkDaemon(),
48552
- checkJournal(),
48553
- checkStranded(),
48554
- await checkJournalOrganization(),
48555
- checkFeedbackHooks(),
48556
- ...checkAgentHooks(),
48557
- checkHookRuntime(),
48558
- await checkRepositoryBinding(),
48559
- checkManagedHook("post-commit", inspectEffectivePostCommitHook),
48560
- checkManagedHook("post-rewrite", inspectEffectivePostRewriteHook),
48561
- ...backend,
48562
- await checkFeedbackCapability()
48563
- ];
48953
+ return {
48954
+ before: [checkAuth()],
48955
+ after: [
48956
+ await checkJournalOrganization(),
48957
+ checkFeedbackHooks(),
48958
+ ...checkAgentHooks(),
48959
+ checkHookRuntime(),
48960
+ await checkRepositoryBinding(),
48961
+ checkManagedHook("post-commit", inspectEffectivePostCommitHook),
48962
+ checkManagedHook("post-rewrite", inspectEffectivePostRewriteHook),
48963
+ ...backend,
48964
+ await checkFeedbackCapability()
48965
+ ]
48966
+ };
48967
+ }
48968
+ var DEFAULT_DOCTOR_PROBES = {
48969
+ delivery: probeDelivery,
48970
+ independent: independentChecks
48971
+ };
48972
+ async function collectChecks(options, probes) {
48973
+ const { before, after } = await probes.independent();
48974
+ return [...before, ...classifyDelivery(await probes.delivery(), options), ...after];
48564
48975
  }
48565
48976
  function icon(status) {
48566
48977
  return status === "ok" ? "\u2713" : status === "warn" ? "\u26A0" : "\u2717";
48567
48978
  }
48568
- async function runDoctor() {
48569
- const checks = await collectChecks();
48979
+ async function runDoctor(options, probes) {
48980
+ const checks = await collectChecks(options, probes);
48570
48981
  const { json, exitCode } = classifyDoctor(checks);
48571
48982
  const headline = json.status === "ok" ? "\u2713 healthy" : json.status === "warn" ? "\u26A0 degraded" : "\u2717 unhealthy";
48572
48983
  process.stderr.write(`[prim] doctor: ${headline}
@@ -48580,101 +48991,104 @@ async function runDoctor() {
48580
48991
  process.exitCode = exitCode;
48581
48992
  }
48582
48993
  }
48583
- function registerDoctorCommands(program3) {
48994
+ function registerDoctorCommands(program3, dependencies = {}) {
48584
48995
  program3.command("doctor").description(
48585
48996
  "Check capture and feedback health end to end (auth, supervisor, delivery, server)"
48586
- ).action(async () => {
48587
- await runDoctor();
48997
+ ).addOption(new Option("--expect-backlog").hideHelp()).action(async (opts) => {
48998
+ await runDoctor(
48999
+ { backlogExpected: opts.expectBacklog === true },
49000
+ dependencies.probes ?? DEFAULT_DOCTOR_PROBES
49001
+ );
48588
49002
  });
48589
49003
  }
48590
49004
 
48591
49005
  // src/commands/moves.ts
48592
- import { existsSync as existsSync17, mkdirSync as mkdirSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync9 } from "fs";
48593
- import { join as join21 } from "path";
49006
+ import { existsSync as existsSync18, mkdirSync as mkdirSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync9 } from "fs";
49007
+ import { join as join22 } from "path";
48594
49008
 
48595
49009
  // src/flusher.ts
48596
- import { createReadStream, renameSync as renameSync6, unlinkSync as unlinkSync4 } from "fs";
48597
- import { dirname as dirname13, join as join20 } from "path";
49010
+ import { createReadStream, renameSync as renameSync6, statSync as statSync5, unlinkSync as unlinkSync4 } from "fs";
49011
+ import { dirname as dirname14, join as join21 } from "path";
48598
49012
 
48599
49013
  // src/dead-letter.ts
48600
49014
  import { createHash as createHash9, randomBytes as randomBytes6 } from "crypto";
48601
49015
  import {
48602
- constants as constants5,
48603
- chmodSync as chmodSync5,
48604
- closeSync as closeSync5,
48605
- existsSync as existsSync16,
49016
+ constants as constants6,
49017
+ chmodSync as chmodSync6,
49018
+ closeSync as closeSync6,
49019
+ existsSync as existsSync17,
48606
49020
  fchmodSync as fchmodSync3,
48607
49021
  fsyncSync as fsyncSync3,
48608
49022
  mkdirSync as mkdirSync11,
48609
- openSync as openSync5,
49023
+ openSync as openSync6,
48610
49024
  renameSync as renameSync5,
48611
- rmSync as rmSync7,
49025
+ rmSync as rmSync8,
48612
49026
  writeFileSync as writeFileSync8
48613
49027
  } from "fs";
48614
- import { dirname as dirname12, join as join19 } from "path";
48615
- var DIRECTORY_MODE3 = 448;
48616
- var FILE_MODE2 = 384;
49028
+ import { dirname as dirname13, join as join20 } from "path";
49029
+ var DIRECTORY_MODE4 = 448;
49030
+ var FILE_MODE3 = 384;
48617
49031
  var DEAD_LETTER_DIRNAME = "dead-letter";
48618
49032
  function deadLetterDirectoryForRotation(flushingPath) {
48619
- return join19(dirname12(flushingPath), DEAD_LETTER_DIRNAME);
49033
+ return join20(dirname13(flushingPath), DEAD_LETTER_DIRNAME);
48620
49034
  }
48621
49035
  function deadLetterPathForRawLine(flushingPath, rawLine) {
48622
49036
  const quarantineId = createHash9("sha256").update(rawLine).digest("hex");
48623
- return join19(deadLetterDirectoryForRotation(flushingPath), `raw-${quarantineId}.json`);
49037
+ return join20(deadLetterDirectoryForRotation(flushingPath), `raw-${quarantineId}.json`);
48624
49038
  }
48625
49039
  function ensureDeadLetterDirectory(flushingPath, persistDirectory) {
48626
49040
  const directory = deadLetterDirectoryForRotation(flushingPath);
48627
- mkdirSync11(directory, { recursive: true, mode: DIRECTORY_MODE3 });
48628
- chmodSync5(directory, DIRECTORY_MODE3);
48629
- persistDirectory(dirname12(directory));
49041
+ mkdirSync11(directory, { recursive: true, mode: DIRECTORY_MODE4 });
49042
+ chmodSync6(directory, DIRECTORY_MODE4);
49043
+ persistDirectory(dirname13(directory));
48630
49044
  return directory;
48631
49045
  }
48632
- function persistDeadLetter(directory, path9, quarantineId, record2, persistDirectory) {
48633
- if (existsSync16(path9)) {
49046
+ function persistDeadLetter(directory, path9, quarantineId, record3, persistDirectory) {
49047
+ if (existsSync17(path9)) {
48634
49048
  persistDirectory(directory);
48635
49049
  return;
48636
49050
  }
48637
- const temporaryPath = join19(
49051
+ const temporaryPath = join20(
48638
49052
  directory,
48639
49053
  `.${quarantineId}.${String(process.pid)}.${randomBytes6(8).toString("hex")}.tmp`
48640
49054
  );
48641
- const flags = constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY | constants5.O_NOFOLLOW;
49055
+ const flags = constants6.O_CREAT | constants6.O_EXCL | constants6.O_WRONLY | constants6.O_NOFOLLOW;
48642
49056
  try {
48643
- const fd = openSync5(temporaryPath, flags, FILE_MODE2);
49057
+ const fd = openSync6(temporaryPath, flags, FILE_MODE3);
48644
49058
  try {
48645
- fchmodSync3(fd, FILE_MODE2);
48646
- writeFileSync8(fd, `${JSON.stringify(record2)}
49059
+ fchmodSync3(fd, FILE_MODE3);
49060
+ writeFileSync8(fd, `${JSON.stringify(record3)}
48647
49061
  `, "utf8");
48648
49062
  fsyncSync3(fd);
48649
49063
  } finally {
48650
- closeSync5(fd);
49064
+ closeSync6(fd);
48651
49065
  }
48652
49066
  renameSync5(temporaryPath, path9);
48653
49067
  persistDirectory(directory);
48654
49068
  } finally {
48655
- rmSync7(temporaryPath, { force: true });
49069
+ rmSync8(temporaryPath, { force: true });
48656
49070
  }
48657
49071
  }
48658
49072
  function quarantineMove(flushingPath, move, reason, now = Date.now, options = {}) {
48659
49073
  const persistDirectory = options.syncDirectory ?? syncDirectory;
48660
49074
  const directory = ensureDeadLetterDirectory(flushingPath, persistDirectory);
48661
49075
  const serializedMove = JSON.stringify(move);
48662
- const record2 = {
49076
+ const record3 = {
48663
49077
  version: 1,
48664
49078
  quarantineId: createHash9("sha256").update(serializedMove).digest("hex"),
48665
49079
  quarantinedAt: now(),
48666
49080
  reason,
48667
49081
  move
48668
49082
  };
48669
- const path9 = join19(directory, `${record2.quarantineId}.json`);
48670
- persistDeadLetter(directory, path9, record2.quarantineId, record2, persistDirectory);
48671
- return record2;
49083
+ const path9 = join20(directory, `${record3.quarantineId}.json`);
49084
+ persistDeadLetter(directory, path9, record3.quarantineId, record3, persistDirectory);
49085
+ return record3;
48672
49086
  }
48673
49087
  function quarantineRawLine(flushingPath, rawLine, now = Date.now, options = {}) {
48674
49088
  const persistDirectory = options.syncDirectory ?? syncDirectory;
48675
49089
  const directory = ensureDeadLetterDirectory(flushingPath, persistDirectory);
48676
49090
  const bytes = Buffer.from(rawLine);
48677
- const record2 = {
49091
+ const record3 = {
48678
49092
  version: 1,
48679
49093
  recordKind: "raw_line_v1",
48680
49094
  quarantineId: createHash9("sha256").update(bytes).digest("hex"),
@@ -48687,11 +49101,11 @@ function quarantineRawLine(flushingPath, rawLine, now = Date.now, options = {})
48687
49101
  persistDeadLetter(
48688
49102
  directory,
48689
49103
  deadLetterPathForRawLine(flushingPath, bytes),
48690
- record2.quarantineId,
48691
- record2,
49104
+ record3.quarantineId,
49105
+ record3,
48692
49106
  persistDirectory
48693
49107
  );
48694
- return record2;
49108
+ return record3;
48695
49109
  }
48696
49110
 
48697
49111
  // src/ingest-response.ts
@@ -48725,9 +49139,13 @@ function requireDurableIngestAcknowledgement(value, expected, cwd = process.cwd(
48725
49139
 
48726
49140
  // src/flusher.ts
48727
49141
  var BATCH_SIZE = 500;
49142
+ var BATCH_MAX_BYTES = 1048576;
48728
49143
  var HTTP_TIMEOUT_MS = 1e4;
48729
49144
  var OPPORTUNISTIC_FLUSH_AFTER_MS = 6e4;
48730
49145
  var ORPHAN_QUARANTINE_MS = 6e4;
49146
+ function exceedsBatchBytes(count, bytes, nextBytes, maxBytes) {
49147
+ return count > 0 && bytes + nextBytes > maxBytes;
49148
+ }
48731
49149
  function addDrainCounts(left, right) {
48732
49150
  return {
48733
49151
  flushed: left.flushed + right.flushed,
@@ -48738,8 +49156,8 @@ function collectionScopeRoots(batch) {
48738
49156
  return batch.flatMap((move) => {
48739
49157
  const env4 = move.env;
48740
49158
  if (typeof env4 !== "object" || env4 === null || Array.isArray(env4)) return [];
48741
- const record2 = env4;
48742
- const root = typeof record2.gitRoot === "string" ? record2.gitRoot : record2.cwd;
49159
+ const record3 = env4;
49160
+ const root = typeof record3.gitRoot === "string" ? record3.gitRoot : record3.cwd;
48743
49161
  return typeof root === "string" && root.length > 0 ? [root] : [];
48744
49162
  });
48745
49163
  }
@@ -48747,8 +49165,8 @@ function isVersionedInvalidMoveDisposition(body) {
48747
49165
  if (typeof body !== "object" || body === null || Array.isArray(body)) {
48748
49166
  return false;
48749
49167
  }
48750
- const record2 = body;
48751
- return Object.keys(record2).length === 2 && record2.error === "invalid_move" && record2.errorVersion === 1;
49168
+ const record3 = body;
49169
+ return Object.keys(record3).length === 2 && record3.error === "invalid_move" && record3.errorVersion === 1;
48752
49170
  }
48753
49171
  function deadLetterReason(error) {
48754
49172
  if (error instanceof InvalidJournalEnvelopeError) {
@@ -48771,23 +49189,31 @@ function deadLetterReason(error) {
48771
49189
  }
48772
49190
  return void 0;
48773
49191
  }
48774
- async function* rawJournalLines(input) {
49192
+ function mayBeBatchSizeFailure(error) {
49193
+ if (error instanceof HttpError) {
49194
+ return error.status >= 500 && error.status !== 503;
49195
+ }
49196
+ return error instanceof Error && error.name === "TimeoutError";
49197
+ }
49198
+ async function* rawJournalLines(input, offset) {
48775
49199
  let pending = [];
48776
49200
  let pendingBytes = 0;
49201
+ let end = offset;
48777
49202
  for await (const value of input) {
48778
49203
  const chunk2 = Buffer.isBuffer(value) ? value : Buffer.from(value);
48779
49204
  let start = 0;
48780
49205
  let newline = chunk2.indexOf(10, start);
48781
49206
  while (newline !== -1) {
48782
49207
  const tail = chunk2.subarray(start, newline + 1);
48783
- if (pending.length === 0) {
48784
- yield tail;
48785
- } else {
49208
+ let line = tail;
49209
+ if (pending.length > 0) {
48786
49210
  pending.push(tail);
48787
- yield Buffer.concat(pending, pendingBytes + tail.length);
49211
+ line = Buffer.concat(pending, pendingBytes + tail.length);
48788
49212
  pending = [];
48789
49213
  pendingBytes = 0;
48790
49214
  }
49215
+ end += line.length;
49216
+ yield { bytes: line, end };
48791
49217
  start = newline + 1;
48792
49218
  newline = chunk2.indexOf(10, start);
48793
49219
  }
@@ -48798,21 +49224,46 @@ async function* rawJournalLines(input) {
48798
49224
  }
48799
49225
  }
48800
49226
  if (pendingBytes > 0) {
48801
- yield pending.length === 1 ? pending[0] : Buffer.concat(pending, pendingBytes);
49227
+ const bytes = pending.length === 1 ? pending[0] : Buffer.concat(pending, pendingBytes);
49228
+ yield { bytes, end: end + pendingBytes };
48802
49229
  }
48803
49230
  }
49231
+ var drainProgressWarned = false;
48804
49232
  async function drainFlushingPath(flushingPath, client, binding, options = {}) {
49233
+ const rotation = rotationIdentity(statSync5(flushingPath));
49234
+ const resumeAt = drainResumeOffset(flushingPath, rotation);
49235
+ const recordProgress = (end) => {
49236
+ if (end === void 0 || end >= rotation.size) {
49237
+ return;
49238
+ }
49239
+ try {
49240
+ writeDrainCheckpoint(flushingPath, { v: 2, offset: end, ...rotation });
49241
+ drainProgressWarned = false;
49242
+ } catch (error) {
49243
+ if (!drainProgressWarned) {
49244
+ drainProgressWarned = true;
49245
+ process.stderr.write(
49246
+ `[prim] could not record drain progress: ${error instanceof Error ? error.message : String(error)}
49247
+ `
49248
+ );
49249
+ }
49250
+ }
49251
+ };
49252
+ const reportProgress = (delta) => {
49253
+ options.onProgress?.(delta);
49254
+ return delta;
49255
+ };
48805
49256
  const postBatch = async (batch2) => {
49257
+ const moves = batch2.map((entry) => entry.move);
48806
49258
  try {
48807
- const request2 = buildOrganizationBoundMoveRequest(batch2, binding);
49259
+ const request2 = buildOrganizationBoundMoveRequest(moves, binding);
48808
49260
  const response = await client.post("/api/cli/moves/ingest", request2, {
48809
49261
  signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
48810
49262
  });
48811
- requireDurableIngestAcknowledgement(response, batch2.length, collectionScopeRoots(batch2));
48812
- return { flushed: batch2.length, quarantined: 0 };
49263
+ requireDurableIngestAcknowledgement(response, moves.length, collectionScopeRoots(moves));
48813
49264
  } catch (error) {
48814
49265
  const reason = deadLetterReason(error);
48815
- if (!reason) {
49266
+ if (!(reason || mayBeBatchSizeFailure(error))) {
48816
49267
  throw error;
48817
49268
  }
48818
49269
  if (batch2.length > 1) {
@@ -48821,33 +49272,46 @@ async function drainFlushingPath(flushingPath, client, binding, options = {}) {
48821
49272
  const right = await postBatch(batch2.slice(midpoint));
48822
49273
  return addDrainCounts(left, right);
48823
49274
  }
48824
- const [move] = batch2;
49275
+ if (!reason) {
49276
+ throw error;
49277
+ }
49278
+ const [entry] = batch2;
48825
49279
  const quarantined = quarantineMove(
48826
49280
  flushingPath,
48827
- move,
49281
+ entry.move,
48828
49282
  reason,
48829
49283
  Date.now,
48830
49284
  options.deadLetterPersistence
48831
49285
  );
49286
+ recordProgress(entry.end);
48832
49287
  process.stderr.write(
48833
49288
  `[prim] quarantined rejected move ${quarantined.quarantineId.slice(0, 12)} (${reason})
48834
49289
  `
48835
49290
  );
48836
- return { flushed: 0, quarantined: 1 };
49291
+ return reportProgress({ flushed: 0, quarantined: 1 });
48837
49292
  }
49293
+ recordProgress(batch2.at(-1)?.end);
49294
+ return reportProgress({ flushed: batch2.length, quarantined: 0 });
48838
49295
  };
48839
- const input = createReadStream(flushingPath);
49296
+ const input = createReadStream(flushingPath, { start: resumeAt });
48840
49297
  const decoder = new TextDecoder("utf-8", { fatal: true });
48841
49298
  let batch = [];
49299
+ let batchBytes = 0;
48842
49300
  let counts = { flushed: 0, quarantined: 0 };
49301
+ const postPending = async () => {
49302
+ counts = addDrainCounts(counts, await postBatch(batch));
49303
+ batch = [];
49304
+ batchBytes = 0;
49305
+ };
48843
49306
  try {
48844
- for await (const rawLine of rawJournalLines(input)) {
49307
+ for await (const { bytes: rawLine, end } of rawJournalLines(input, resumeAt)) {
48845
49308
  if (rawLine.length === 1 && rawLine[0] === 10 || rawLine.length === 2 && rawLine[0] === 13 && rawLine[1] === 10) {
48846
49309
  continue;
48847
49310
  }
49311
+ let move;
48848
49312
  try {
48849
49313
  const line = decoder.decode(rawLine);
48850
- batch.push(JSON.parse(line));
49314
+ move = JSON.parse(line);
48851
49315
  } catch {
48852
49316
  const quarantined = quarantineRawLine(
48853
49317
  flushingPath,
@@ -48859,24 +49323,35 @@ async function drainFlushingPath(flushingPath, client, binding, options = {}) {
48859
49323
  `[prim] quarantined invalid journal line ${quarantined.quarantineId.slice(0, 12)} (invalid_move)
48860
49324
  `
48861
49325
  );
48862
- counts.quarantined += 1;
49326
+ counts = addDrainCounts(counts, reportProgress({ flushed: 0, quarantined: 1 }));
49327
+ if (batch.length === 0) {
49328
+ recordProgress(end);
49329
+ }
48863
49330
  continue;
48864
49331
  }
48865
- if (batch.length === BATCH_SIZE) {
48866
- counts = addDrainCounts(counts, await postBatch(batch));
48867
- batch = [];
49332
+ if (exceedsBatchBytes(batch.length, batchBytes, rawLine.length, BATCH_MAX_BYTES)) {
49333
+ await postPending();
49334
+ }
49335
+ batch.push({ move, end });
49336
+ batchBytes += rawLine.length;
49337
+ if (batch.length >= BATCH_SIZE) {
49338
+ await postPending();
48868
49339
  }
48869
49340
  }
48870
49341
  if (batch.length > 0) {
48871
- counts = addDrainCounts(counts, await postBatch(batch));
49342
+ await postPending();
48872
49343
  }
48873
49344
  } finally {
48874
49345
  input.destroy();
48875
49346
  }
48876
49347
  unlinkSync4(flushingPath);
49348
+ try {
49349
+ removeDrainCheckpoint(flushingPath);
49350
+ } catch {
49351
+ }
48877
49352
  return counts;
48878
49353
  }
48879
- async function drainPath(path9, client, binding) {
49354
+ async function drainPath(path9, client, binding, onProgress) {
48880
49355
  const tmpPath = `${path9}.flushing.${String(Date.now())}.${String(process.pid)}`;
48881
49356
  try {
48882
49357
  renameSync6(path9, tmpPath);
@@ -48886,7 +49361,19 @@ async function drainPath(path9, client, binding) {
48886
49361
  }
48887
49362
  throw err;
48888
49363
  }
48889
- return drainFlushingPath(tmpPath, client, binding);
49364
+ return drainFlushingPath(tmpPath, client, binding, { onProgress });
49365
+ }
49366
+ async function settleDrain(drain) {
49367
+ let progress = { flushed: 0, quarantined: 0 };
49368
+ try {
49369
+ return {
49370
+ counts: await drain((delta) => {
49371
+ progress = addDrainCounts(progress, delta);
49372
+ })
49373
+ };
49374
+ } catch (error) {
49375
+ return { counts: progress, failure: { error } };
49376
+ }
48890
49377
  }
48891
49378
  function selectRecoverable(files, now, opts = {}) {
48892
49379
  const quarantineMs = opts.quarantineMs ?? ORPHAN_QUARANTINE_MS;
@@ -48903,7 +49390,8 @@ async function recoverOrphans(candidates, options) {
48903
49390
  flushed: 0,
48904
49391
  quarantined: 0,
48905
49392
  errors: [],
48906
- failedBuckets: /* @__PURE__ */ new Set()
49393
+ failedBuckets: /* @__PURE__ */ new Set(),
49394
+ failedDrainFlushed: 0
48907
49395
  };
48908
49396
  const recoverable = selectRecoverable(candidates, options.now ?? Date.now(), {
48909
49397
  ownerPid: options.ownerPid ?? process.pid,
@@ -48914,28 +49402,41 @@ async function recoverOrphans(candidates, options) {
48914
49402
  if (summary.failedBuckets.has(file.bucket)) {
48915
49403
  continue;
48916
49404
  }
48917
- try {
48918
- const counts = await drain(file.path);
48919
- summary.flushed += counts.flushed;
48920
- summary.quarantined += counts.quarantined;
48921
- } catch (err) {
48922
- summary.errors.push(err);
49405
+ const { counts, failure: failure4 } = await settleDrain((onProgress) => drain(file.path, onProgress));
49406
+ summary.flushed += counts.flushed;
49407
+ summary.quarantined += counts.quarantined;
49408
+ if (failure4) {
49409
+ summary.failedDrainFlushed += counts.flushed;
49410
+ summary.errors.push(failure4.error);
48923
49411
  summary.failedBuckets.add(file.bucket);
48924
49412
  }
48925
49413
  }
48926
49414
  return summary;
48927
49415
  }
48928
49416
  var FlushError = class extends Error {
49417
+ /** Moves the whole sweep acknowledged, across every drain it ran. */
48929
49418
  flushed;
48930
49419
  quarantined;
48931
- constructor(cause, flushed, quarantined) {
49420
+ /** Buckets the failed sweep held back without sending, as on success. */
49421
+ retained;
49422
+ /**
49423
+ * Of `flushed`, the Moves acknowledged by the drains that failed, before
49424
+ * they failed. Only this shows the failure itself is advancing: `flushed`
49425
+ * also counts other buckets' drains, so a bucket whose drain fails every
49426
+ * time would read as progress while a sibling bucket kept delivering.
49427
+ */
49428
+ failedDrainFlushed;
49429
+ constructor(cause, flushed, quarantined, retained = [], failedDrainFlushed = 0) {
48932
49430
  super(cause instanceof Error ? cause.message : String(cause), { cause });
48933
49431
  this.name = "FlushError";
48934
49432
  this.flushed = flushed;
48935
49433
  this.quarantined = quarantined;
49434
+ this.retained = retained;
49435
+ this.failedDrainFlushed = failedDrainFlushed;
48936
49436
  }
48937
49437
  };
48938
49438
  async function flushOnce() {
49439
+ sweepOrphanedDrainProgress();
48939
49440
  const orphanCandidates = listFlushing({ sampleBytes: 0 });
48940
49441
  const liveBuckets = listBuckets();
48941
49442
  if (orphanCandidates.length === 0 && liveBuckets.length === 0) {
@@ -48952,10 +49453,11 @@ async function flushOnce() {
48952
49453
  const binding = inspection.binding;
48953
49454
  const recovered = await recoverOrphans(
48954
49455
  orphanCandidates.filter((file) => inspection.deliverableBuckets.has(file.bucket)),
48955
- { drain: (path9) => drainFlushingPath(path9, client, binding) }
49456
+ { drain: (path9, onProgress) => drainFlushingPath(path9, client, binding, { onProgress }) }
48956
49457
  );
48957
49458
  let total = recovered.flushed;
48958
49459
  let quarantined = recovered.quarantined;
49460
+ let failedDrainFlushed = recovered.failedDrainFlushed;
48959
49461
  const errors = recovered.errors;
48960
49462
  for (const { bucket, path: path9 } of liveBuckets) {
48961
49463
  if (!inspection.deliverableBuckets.has(bucket)) {
@@ -48964,21 +49466,29 @@ async function flushOnce() {
48964
49466
  if (recovered.failedBuckets.has(bucket)) {
48965
49467
  continue;
48966
49468
  }
48967
- try {
48968
- const counts = await drainPath(path9, client, binding);
48969
- total += counts.flushed;
48970
- quarantined += counts.quarantined;
48971
- } catch (err) {
48972
- errors.push(err);
49469
+ const { counts, failure: failure4 } = await settleDrain(
49470
+ (onProgress) => drainPath(path9, client, binding, onProgress)
49471
+ );
49472
+ total += counts.flushed;
49473
+ quarantined += counts.quarantined;
49474
+ if (failure4) {
49475
+ failedDrainFlushed += counts.flushed;
49476
+ errors.push(failure4.error);
48973
49477
  }
48974
49478
  }
48975
49479
  if (errors.length > 0) {
48976
- throw new FlushError(errors[0], total, quarantined);
49480
+ throw new FlushError(
49481
+ errors[0],
49482
+ total,
49483
+ quarantined,
49484
+ inspection.retainedBuckets,
49485
+ failedDrainFlushed
49486
+ );
48977
49487
  }
48978
49488
  return inspection.retainedBuckets.length > 0 ? { flushed: total, quarantined, retained: inspection.retainedBuckets } : { flushed: total, quarantined };
48979
49489
  }
48980
49490
  var flushInFlight;
48981
- var FLUSH_LOCK_PATH = join20(dirname13(JOURNAL_DIR), ".flush.lock");
49491
+ var FLUSH_LOCK_PATH = join21(dirname14(JOURNAL_DIR), ".flush.lock");
48982
49492
  var FLUSH_LOCK_TIMEOUT_MS = 250;
48983
49493
  function isFlushLockContended(error) {
48984
49494
  return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
@@ -49003,18 +49513,16 @@ function flush() {
49003
49513
  return attempt;
49004
49514
  }
49005
49515
  function shouldFlushPending(stats, now, thresholdMs = OPPORTUNISTIC_FLUSH_AFTER_MS) {
49006
- if (stats.sampled) {
49516
+ if (stats.sampled || stats.strandedFileCount > 0 && stats.strandedCount === 0) {
49007
49517
  return true;
49008
49518
  }
49009
49519
  return stats.pendingCount > 0 && (stats.oldestPendingAt === void 0 || now - stats.oldestPendingAt > thresholdMs);
49010
49520
  }
49011
- async function flushIfNeeded() {
49521
+ function journalNeedsFlush(now = Date.now()) {
49012
49522
  try {
49013
- const stats = pendingJournalStats();
49014
- if (shouldFlushPending(stats, Date.now())) {
49015
- await flush();
49016
- }
49523
+ return shouldFlushPending(pendingJournalStats(), now);
49017
49524
  } catch {
49525
+ return false;
49018
49526
  }
49019
49527
  }
49020
49528
 
@@ -49027,7 +49535,7 @@ var EVENT_COL_WIDTH = 20;
49027
49535
  var BUCKET_COL_WIDTH = 20;
49028
49536
  var TAIL_BUCKET_COL_WIDTH = 12;
49029
49537
  var DIR_MODE = 448;
49030
- var FILE_MODE3 = 384;
49538
+ var FILE_MODE4 = 384;
49031
49539
  var WORKSPACE_FILE = ".prim/workspace.json";
49032
49540
  function registerMovesCommands(program3) {
49033
49541
  const moves = program3.command("moves").description("Decision Event Pipeline \u2014 local journal");
@@ -49073,7 +49581,7 @@ function registerMovesCommands(program3) {
49073
49581
  }
49074
49582
  if (stranded.length > 0) {
49075
49583
  const moveCount = stranded.reduce((n, f) => n + f.lineCount, 0);
49076
- const byteCount = stranded.reduce((n, f) => n + f.sizeBytes, 0);
49584
+ const byteCount = stranded.reduce((n, f) => n + f.pendingBytes, 0);
49077
49585
  const sampled = stranded.some((file) => file.sampled);
49078
49586
  const qualifier = sampled ? "at least " : "";
49079
49587
  console.log(
@@ -49084,7 +49592,7 @@ function registerMovesCommands(program3) {
49084
49592
  const owner = f.pid === void 0 ? "no pid" : `pid ${String(f.pid)}`;
49085
49593
  const count = `${String(f.lineCount)}${f.sampled ? "+" : ""}`;
49086
49594
  console.log(
49087
- ` ${f.bucket.padEnd(BUCKET_COL_WIDTH)} ${count.padStart(5)} stranded, ${String(f.sizeBytes).padStart(8)} bytes, ${String(ageS)}s ago (${owner})`
49595
+ ` ${f.bucket.padEnd(BUCKET_COL_WIDTH)} ${count.padStart(5)} stranded, ${String(f.pendingBytes).padStart(8)} bytes, ${String(ageS)}s ago (${owner})`
49088
49596
  );
49089
49597
  }
49090
49598
  }
@@ -49112,19 +49620,19 @@ function registerMovesCommands(program3) {
49112
49620
  }
49113
49621
  });
49114
49622
  moves.command("bind").description("Pin the current directory to an org via .prim/workspace.json").requiredOption("--orgId <orgId>", "Convex organization id").action((opts) => {
49115
- const dir = join21(process.cwd(), ".prim");
49116
- if (!existsSync17(dir)) {
49623
+ const dir = join22(process.cwd(), ".prim");
49624
+ if (!existsSync18(dir)) {
49117
49625
  mkdirSync12(dir, { recursive: true, mode: DIR_MODE });
49118
49626
  }
49119
- const file = join21(process.cwd(), WORKSPACE_FILE);
49627
+ const file = join22(process.cwd(), WORKSPACE_FILE);
49120
49628
  writeFileSync9(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
49121
- mode: FILE_MODE3
49629
+ mode: FILE_MODE4
49122
49630
  });
49123
49631
  console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
49124
49632
  });
49125
49633
  moves.command("drop").description("Remove the .prim/workspace.json binding from the cwd").action(() => {
49126
- const file = join21(process.cwd(), WORKSPACE_FILE);
49127
- if (!existsSync17(file)) {
49634
+ const file = join22(process.cwd(), WORKSPACE_FILE);
49635
+ if (!existsSync18(file)) {
49128
49636
  console.log("[prim] no workspace binding in cwd");
49129
49637
  return;
49130
49638
  }
@@ -49215,31 +49723,31 @@ function registerReconcileCommands(program3) {
49215
49723
 
49216
49724
  // src/commands/session.ts
49217
49725
  import {
49218
- existsSync as existsSync19,
49726
+ existsSync as existsSync20,
49219
49727
  mkdirSync as mkdirSync13,
49220
- readFileSync as readFileSync17,
49221
- readdirSync as readdirSync5,
49728
+ readFileSync as readFileSync18,
49729
+ readdirSync as readdirSync6,
49222
49730
  unlinkSync as unlinkSync6,
49223
49731
  writeFileSync as writeFileSync10
49224
49732
  } from "fs";
49225
- import { join as join23 } from "path";
49733
+ import { join as join24 } from "path";
49226
49734
 
49227
49735
  // src/binding.ts
49228
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
49229
- import { dirname as dirname14, join as join22 } from "path";
49736
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
49737
+ import { dirname as dirname15, join as join23 } from "path";
49230
49738
  var PRIM_CONFIG_DIR = primConfigDirectory();
49231
- var SESSIONS_DIR = join22(PRIM_CONFIG_DIR, "sessions");
49739
+ var SESSIONS_DIR = join23(PRIM_CONFIG_DIR, "sessions");
49232
49740
 
49233
49741
  // src/commands/session.ts
49234
49742
  var DIR_MODE2 = 448;
49235
- var FILE_MODE4 = 384;
49743
+ var FILE_MODE5 = 384;
49236
49744
  function ensureDir() {
49237
- if (!existsSync19(SESSIONS_DIR)) {
49745
+ if (!existsSync20(SESSIONS_DIR)) {
49238
49746
  mkdirSync13(SESSIONS_DIR, { recursive: true, mode: DIR_MODE2 });
49239
49747
  }
49240
49748
  }
49241
49749
  function markerPath(sessionId) {
49242
- return join23(SESSIONS_DIR, `${sessionId}.json`);
49750
+ return join24(SESSIONS_DIR, `${sessionId}.json`);
49243
49751
  }
49244
49752
  function registerSessionCommands(program3) {
49245
49753
  const session = program3.command("session").description("Decision Event Pipeline \u2014 session binding markers");
@@ -49250,16 +49758,16 @@ function registerSessionCommands(program3) {
49250
49758
  startedAt: Date.now()
49251
49759
  };
49252
49760
  writeFileSync10(markerPath(sessionId), JSON.stringify(marker, null, 2), {
49253
- mode: FILE_MODE4
49761
+ mode: FILE_MODE5
49254
49762
  });
49255
49763
  console.log(`[prim] session ${sessionId} bound to org ${opts.orgId}`);
49256
49764
  });
49257
49765
  session.command("list").description("List active session markers").action(() => {
49258
- if (!existsSync19(SESSIONS_DIR)) {
49766
+ if (!existsSync20(SESSIONS_DIR)) {
49259
49767
  console.log("[prim] no session markers");
49260
49768
  return;
49261
49769
  }
49262
- const files = readdirSync5(SESSIONS_DIR).filter((f) => f.endsWith(".json"));
49770
+ const files = readdirSync6(SESSIONS_DIR).filter((f) => f.endsWith(".json"));
49263
49771
  if (files.length === 0) {
49264
49772
  console.log("[prim] no session markers");
49265
49773
  return;
@@ -49267,7 +49775,7 @@ function registerSessionCommands(program3) {
49267
49775
  for (const f of files) {
49268
49776
  const sessionId = f.replace(/\.json$/, "");
49269
49777
  try {
49270
- const m = JSON.parse(readFileSync17(join23(SESSIONS_DIR, f), "utf-8"));
49778
+ const m = JSON.parse(readFileSync18(join24(SESSIONS_DIR, f), "utf-8"));
49271
49779
  console.log(`${sessionId} org=${m.orgId}`);
49272
49780
  } catch {
49273
49781
  }
@@ -49275,7 +49783,7 @@ function registerSessionCommands(program3) {
49275
49783
  });
49276
49784
  session.command("drop <sessionId>").description("Remove a session marker").action((sessionId) => {
49277
49785
  const p = markerPath(sessionId);
49278
- if (!existsSync19(p)) {
49786
+ if (!existsSync20(p)) {
49279
49787
  console.log(`[prim] no marker for session ${sessionId}`);
49280
49788
  return;
49281
49789
  }
@@ -49286,10 +49794,24 @@ function registerSessionCommands(program3) {
49286
49794
 
49287
49795
  // src/commands/setup.ts
49288
49796
  import { spawnSync as spawnSync2 } from "child_process";
49289
- import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
49290
- import { join as join24 } from "path";
49797
+ import { existsSync as existsSync21, readFileSync as readFileSync19 } from "fs";
49798
+ import { join as join25 } from "path";
49291
49799
  var EXIT_INCOMPLETE = 1;
49292
49800
  var EXIT_USAGE3 = 2;
49801
+ var SETUP_ORCHESTRATOR_ENV = "PRIM_SETUP_ORCHESTRATOR";
49802
+ var SETUP_DAEMON_DRAINS_ENV = "PRIM_SETUP_DAEMON_DRAINS";
49803
+ function setupStepSpawnOptions(capture, env4 = process.env, options = {}) {
49804
+ return {
49805
+ env: {
49806
+ ...env4,
49807
+ [SETUP_ORCHESTRATOR_ENV]: "1",
49808
+ // Unset rather than inherited when this setup starts no daemon.
49809
+ [SETUP_DAEMON_DRAINS_ENV]: options.startsDaemon === true ? "1" : void 0
49810
+ },
49811
+ stdio: capture ? ["inherit", "pipe", "ignore"] : "inherit",
49812
+ encoding: "utf-8"
49813
+ };
49814
+ }
49293
49815
  var SESSION_LABELS = {
49294
49816
  claude: "Claude Code integration",
49295
49817
  codex: "Codex integration",
@@ -49340,7 +49862,7 @@ function planSetupSteps(opts) {
49340
49862
  steps.push({
49341
49863
  key: "health",
49342
49864
  label: "Capture health",
49343
- args: ["doctor"],
49865
+ args: ["doctor", "--expect-backlog"],
49344
49866
  required: true
49345
49867
  });
49346
49868
  }
@@ -49369,8 +49891,8 @@ function detectProjectConflicts(agent, run) {
49369
49891
  }
49370
49892
  try {
49371
49893
  const root = gitToplevel();
49372
- const preCommit = root && join24(root, ".git", "hooks", "pre-commit");
49373
- if (preCommit && existsSync20(preCommit) && readFileSync18(preCommit, "utf-8").includes("prim-pre-commit")) {
49894
+ const preCommit = root && join25(root, ".git", "hooks", "pre-commit");
49895
+ if (preCommit && existsSync21(preCommit) && readFileSync19(preCommit, "utf-8").includes("prim-pre-commit")) {
49374
49896
  conflicts.push(CONFLICT_HOOKS);
49375
49897
  }
49376
49898
  } catch {
@@ -49440,10 +49962,11 @@ function registerSetupCommand(program3, dependencies = {}) {
49440
49962
  const self = process.argv[1];
49441
49963
  const globals = program3.optsWithGlobals();
49442
49964
  const run = dependencies.run ?? ((args, capture = false) => {
49443
- const r = spawnSync2(process.execPath, [self, ...args], {
49444
- stdio: capture ? ["inherit", "pipe", "ignore"] : "inherit",
49445
- encoding: "utf-8"
49446
- });
49965
+ const r = spawnSync2(
49966
+ process.execPath,
49967
+ [self, ...args],
49968
+ setupStepSpawnOptions(capture, process.env, { startsDaemon: opts.daemon })
49969
+ );
49447
49970
  return { code: r.status ?? 1, stdout: capture ? r.stdout ?? "" : "" };
49448
49971
  });
49449
49972
  const results = {};
@@ -49536,20 +50059,20 @@ function registerSetupCommand(program3, dependencies = {}) {
49536
50059
  // src/commands/skill.ts
49537
50060
  import { createHash as createHash11 } from "crypto";
49538
50061
  import {
49539
- constants as constants7,
49540
- closeSync as closeSync7,
49541
- existsSync as existsSync22,
49542
- fstatSync as fstatSync3,
49543
- lstatSync as lstatSync7,
49544
- openSync as openSync7,
49545
- readFileSync as readFileSync20,
49546
- readSync as readSync3,
49547
- readdirSync as readdirSync7,
49548
- rmdirSync as rmdirSync2,
50062
+ constants as constants8,
50063
+ closeSync as closeSync8,
50064
+ existsSync as existsSync23,
50065
+ fstatSync as fstatSync4,
50066
+ lstatSync as lstatSync8,
50067
+ openSync as openSync8,
50068
+ readFileSync as readFileSync21,
50069
+ readSync as readSync4,
50070
+ readdirSync as readdirSync8,
50071
+ rmdirSync as rmdirSync3,
49549
50072
  unlinkSync as unlinkSync7
49550
50073
  } from "fs";
49551
50074
  import { homedir as homedir11 } from "os";
49552
- import { dirname as dirname16, join as join27, resolve as resolve8 } from "path";
50075
+ import { dirname as dirname17, join as join28, resolve as resolve8 } from "path";
49553
50076
  import { fileURLToPath as fileURLToPath5 } from "url";
49554
50077
 
49555
50078
  // node_modules/.pnpm/diff@5.2.2/node_modules/diff/lib/index.mjs
@@ -49719,7 +50242,7 @@ Diff.prototype = {
49719
50242
  tokenize: function tokenize(value) {
49720
50243
  return value.split("");
49721
50244
  },
49722
- join: function join25(chars) {
50245
+ join: function join26(chars) {
49723
50246
  return chars.join("");
49724
50247
  }
49725
50248
  };
@@ -50057,30 +50580,30 @@ function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
50057
50580
  // src/commands/claude-plugin.ts
50058
50581
  import { createHash as createHash10 } from "crypto";
50059
50582
  import {
50060
- constants as constants6,
50061
- closeSync as closeSync6,
50062
- existsSync as existsSync21,
50063
- fstatSync as fstatSync2,
50064
- lstatSync as lstatSync6,
50583
+ constants as constants7,
50584
+ closeSync as closeSync7,
50585
+ existsSync as existsSync22,
50586
+ fstatSync as fstatSync3,
50587
+ lstatSync as lstatSync7,
50065
50588
  mkdirSync as mkdirSync14,
50066
- openSync as openSync6,
50067
- readFileSync as readFileSync19,
50068
- readSync as readSync2,
50069
- readdirSync as readdirSync6,
50589
+ openSync as openSync7,
50590
+ readFileSync as readFileSync20,
50591
+ readSync as readSync3,
50592
+ readdirSync as readdirSync7,
50070
50593
  realpathSync as realpathSync4,
50071
- rmSync as rmSync8,
50072
- rmdirSync
50594
+ rmSync as rmSync9,
50595
+ rmdirSync as rmdirSync2
50073
50596
  } from "fs";
50074
50597
  import { homedir as homedir10 } from "os";
50075
- import { dirname as dirname15, isAbsolute as isAbsolute8, join as join26, relative as relative2, resolve as resolve7, sep as sep2 } from "path";
50598
+ import { dirname as dirname16, isAbsolute as isAbsolute8, join as join27, relative as relative2, resolve as resolve7, sep as sep2 } from "path";
50076
50599
  import { fileURLToPath as fileURLToPath4 } from "url";
50077
- var __dirname4 = dirname15(fileURLToPath4(import.meta.url));
50600
+ var __dirname4 = dirname16(fileURLToPath4(import.meta.url));
50078
50601
  var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
50079
50602
  var MAX_MANIFEST_BYTES2 = 64 * 1024;
50080
50603
  var MAX_SKILL_BYTES = 1024 * 1024;
50081
50604
  function pluginDirFor(cwd, scope) {
50082
- const base = scope === "user" ? join26(homedir10(), ".claude") : join26(gitToplevel(cwd) ?? cwd, ".claude");
50083
- return join26(base, "skills", "prim");
50605
+ const base = scope === "user" ? join27(homedir10(), ".claude") : join27(gitToplevel(cwd) ?? cwd, ".claude");
50606
+ return join27(base, "skills", "prim");
50084
50607
  }
50085
50608
  function resolvePluginDir(cwd, scope) {
50086
50609
  if (scope && scope !== "user" && scope !== "project") {
@@ -50091,10 +50614,10 @@ function resolvePluginDir(cwd, scope) {
50091
50614
  }
50092
50615
  function packageVersion2() {
50093
50616
  let dir = __dirname4;
50094
- while (dir !== dirname15(dir)) {
50617
+ while (dir !== dirname16(dir)) {
50095
50618
  const p = resolve7(dir, "package.json");
50096
- if (existsSync21(p)) return JSON.parse(readFileSync19(p, "utf-8")).version;
50097
- dir = dirname15(dir);
50619
+ if (existsSync22(p)) return JSON.parse(readFileSync20(p, "utf-8")).version;
50620
+ dir = dirname16(dir);
50098
50621
  }
50099
50622
  return "0.0.0";
50100
50623
  }
@@ -50108,12 +50631,12 @@ function renderClaudePlugin() {
50108
50631
  return { manifest: pluginManifest(version), skill: loadSkill(), version };
50109
50632
  }
50110
50633
  function removeDirIfEmpty(dir) {
50111
- if (existsSync21(dir) && readdirSync6(dir).length === 0) rmdirSync(dir);
50634
+ if (existsSync22(dir) && readdirSync7(dir).length === 0) rmdirSync2(dir);
50112
50635
  }
50113
50636
  function pluginPaths(dir) {
50114
50637
  return {
50115
- manifestPath: join26(dir, ".claude-plugin", "plugin.json"),
50116
- skillPath: join26(dir, "SKILL.md")
50638
+ manifestPath: join27(dir, ".claude-plugin", "plugin.json"),
50639
+ skillPath: join27(dir, "SKILL.md")
50117
50640
  };
50118
50641
  }
50119
50642
  function installClaudePlugin(cwd, opts) {
@@ -50121,8 +50644,8 @@ function installClaudePlugin(cwd, opts) {
50121
50644
  if (dir === null) return 1;
50122
50645
  const { manifestPath, skillPath } = pluginPaths(dir);
50123
50646
  const { manifest, skill } = renderClaudePlugin();
50124
- const manifestCurrent = existsSync21(manifestPath) ? readFileSync19(manifestPath, "utf-8") : null;
50125
- const skillCurrent = existsSync21(skillPath) ? readFileSync19(skillPath, "utf-8") : null;
50647
+ const manifestCurrent = existsSync22(manifestPath) ? readFileSync20(manifestPath, "utf-8") : null;
50648
+ const skillCurrent = existsSync22(skillPath) ? readFileSync20(skillPath, "utf-8") : null;
50126
50649
  if (manifestCurrent === manifest && skillCurrent === skill) {
50127
50650
  console.log("No changes \u2014 prim skill plugin already up to date.");
50128
50651
  return 0;
@@ -50131,7 +50654,7 @@ function installClaudePlugin(cwd, opts) {
50131
50654
  console.log(`Would write plugin to ${dir} (.claude-plugin/plugin.json + SKILL.md)`);
50132
50655
  return 0;
50133
50656
  }
50134
- mkdirSync14(join26(dir, ".claude-plugin"), { recursive: true });
50657
+ mkdirSync14(join27(dir, ".claude-plugin"), { recursive: true });
50135
50658
  atomicWrite4(manifestPath, manifest);
50136
50659
  atomicWrite4(skillPath, skill);
50137
50660
  console.log(`Installed prim skill plugin at ${dir}`);
@@ -50145,13 +50668,13 @@ function uninstallClaudePlugin(cwd, opts) {
50145
50668
  const dir = resolvePluginDir(cwd, opts.scope);
50146
50669
  if (dir === null) return 1;
50147
50670
  const { manifestPath, skillPath } = pluginPaths(dir);
50148
- if (!existsSync21(manifestPath) && !existsSync21(skillPath)) {
50671
+ if (!existsSync22(manifestPath) && !existsSync22(skillPath)) {
50149
50672
  console.log(`prim skill plugin not present at ${dir}`);
50150
50673
  return 0;
50151
50674
  }
50152
- rmSync8(manifestPath, { force: true });
50153
- rmSync8(skillPath, { force: true });
50154
- removeDirIfEmpty(join26(dir, ".claude-plugin"));
50675
+ rmSync9(manifestPath, { force: true });
50676
+ rmSync9(skillPath, { force: true });
50677
+ removeDirIfEmpty(join27(dir, ".claude-plugin"));
50155
50678
  removeDirIfEmpty(dir);
50156
50679
  console.log(`Removed prim skill plugin from ${dir}`);
50157
50680
  return 0;
@@ -50160,7 +50683,7 @@ function statusClaudePlugin(cwd, opts) {
50160
50683
  const dir = resolvePluginDir(cwd, opts.scope);
50161
50684
  if (dir === null) return 1;
50162
50685
  const { skillPath } = pluginPaths(dir);
50163
- const installed = existsSync21(skillPath);
50686
+ const installed = existsSync22(skillPath);
50164
50687
  if (opts.json) {
50165
50688
  printJson({ installed, target: dir });
50166
50689
  return installed ? 0 : 1;
@@ -50174,7 +50697,7 @@ function statusClaudePlugin(cwd, opts) {
50174
50697
  }
50175
50698
 
50176
50699
  // src/commands/skill.ts
50177
- var __dirname5 = dirname16(fileURLToPath5(import.meta.url));
50700
+ var __dirname5 = dirname17(fileURLToPath5(import.meta.url));
50178
50701
  var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
50179
50702
  var SKILL_END = "<!-- END PRIM SKILL v1 -->";
50180
50703
  var CODEX_GUIDANCE_LIMIT_BYTES = 1024 * 1024;
@@ -50202,25 +50725,25 @@ function sha2563(content) {
50202
50725
  }
50203
50726
  function cursorSkillDirectory(cwd, scope) {
50204
50727
  const base = scope === "user" ? cursorConfigDirectory() : gitToplevel(cwd) ?? cwd;
50205
- return join27(base, scope === "user" ? "skills" : ".cursor/skills", "prim");
50728
+ return join28(base, scope === "user" ? "skills" : ".cursor/skills", "prim");
50206
50729
  }
50207
50730
  function cursorSkillPaths(cwd, scope) {
50208
50731
  const directory = cursorSkillDirectory(cwd, scope);
50209
50732
  return {
50210
50733
  directory,
50211
- skill: join27(directory, "SKILL.md"),
50212
- manifest: join27(directory, CURSOR_SKILL_MANIFEST)
50734
+ skill: join28(directory, "SKILL.md"),
50735
+ manifest: join28(directory, CURSOR_SKILL_MANIFEST)
50213
50736
  };
50214
50737
  }
50215
50738
  function readCursorSkillManifest(path9) {
50216
50739
  try {
50217
- const value = JSON.parse(readFileSync20(path9, "utf8"));
50740
+ const value = JSON.parse(readFileSync21(path9, "utf8"));
50218
50741
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
50219
- const record2 = value;
50220
- if (record2.version !== 1 || record2.owner !== CURSOR_SKILL_OWNER || typeof record2.contentSha256 !== "string" || !/^[0-9a-f]{64}$/u.test(record2.contentSha256)) {
50742
+ const record3 = value;
50743
+ if (record3.version !== 1 || record3.owner !== CURSOR_SKILL_OWNER || typeof record3.contentSha256 !== "string" || !/^[0-9a-f]{64}$/u.test(record3.contentSha256)) {
50221
50744
  return;
50222
50745
  }
50223
- return record2;
50746
+ return record3;
50224
50747
  } catch {
50225
50748
  return;
50226
50749
  }
@@ -50228,7 +50751,7 @@ function readCursorSkillManifest(path9) {
50228
50751
  function cursorSkillStatus(cwd, scope) {
50229
50752
  const paths = cursorSkillPaths(cwd, scope);
50230
50753
  const manifest = readCursorSkillManifest(paths.manifest);
50231
- const content = existsSync22(paths.skill) ? readFileSync20(paths.skill, "utf8") : void 0;
50754
+ const content = existsSync23(paths.skill) ? readFileSync21(paths.skill, "utf8") : void 0;
50232
50755
  const owned = manifest !== void 0;
50233
50756
  const installed = owned && content !== void 0 && sha2563(content) === manifest.contentSha256;
50234
50757
  return {
@@ -50246,8 +50769,8 @@ function validCursorSkillScope(scope) {
50246
50769
  function installCursorSkill(cwd, opts) {
50247
50770
  const paths = cursorSkillPaths(cwd, opts.scope);
50248
50771
  const status = cursorSkillStatus(cwd, opts.scope);
50249
- const manifestExists = existsSync22(paths.manifest);
50250
- const skillExists = existsSync22(paths.skill);
50772
+ const manifestExists = existsSync23(paths.manifest);
50773
+ const skillExists = existsSync23(paths.skill);
50251
50774
  if (manifestExists && !status.owned || !manifestExists && skillExists || status.edited) {
50252
50775
  console.error(`Refusing to overwrite a foreign or edited Cursor skill at ${paths.skill}`);
50253
50776
  return 1;
@@ -50258,12 +50781,12 @@ function installCursorSkill(cwd, opts) {
50258
50781
  owner: CURSOR_SKILL_OWNER,
50259
50782
  contentSha256: sha2563(content)
50260
50783
  };
50261
- if (status.installed && readFileSync20(paths.skill, "utf8") === content) {
50784
+ if (status.installed && readFileSync21(paths.skill, "utf8") === content) {
50262
50785
  console.log(`No changes \u2014 Cursor skill already up to date at ${paths.skill}`);
50263
50786
  return 0;
50264
50787
  }
50265
50788
  if (opts.dryRun) {
50266
- const existing = skillExists ? readFileSync20(paths.skill, "utf8") : "";
50789
+ const existing = skillExists ? readFileSync21(paths.skill, "utf8") : "";
50267
50790
  process.stdout.write(createPatch(paths.skill, existing, content, "current", "proposed"));
50268
50791
  return 0;
50269
50792
  }
@@ -50284,34 +50807,34 @@ function uninstallCursorSkill(cwd, scope) {
50284
50807
  console.error(`Refusing to remove an edited Cursor skill at ${paths.skill}`);
50285
50808
  return 1;
50286
50809
  }
50287
- if (existsSync22(paths.skill)) unlinkSync7(paths.skill);
50288
- if (existsSync22(paths.manifest)) unlinkSync7(paths.manifest);
50810
+ if (existsSync23(paths.skill)) unlinkSync7(paths.skill);
50811
+ if (existsSync23(paths.manifest)) unlinkSync7(paths.manifest);
50289
50812
  try {
50290
- if (readdirSync7(paths.directory).length === 0) rmdirSync2(paths.directory);
50813
+ if (readdirSync8(paths.directory).length === 0) rmdirSync3(paths.directory);
50291
50814
  } catch {
50292
50815
  }
50293
50816
  console.log(`Removed Primitive-owned Cursor skill from ${paths.skill}`);
50294
50817
  return 0;
50295
50818
  }
50296
50819
  function userTargetFor(agent) {
50297
- if (agent === "claude") return join27(homedir11(), ".claude", "CLAUDE.md");
50298
- if (agent === "codex") return join27(homedir11(), ".codex", "AGENTS.md");
50820
+ if (agent === "claude") return join28(homedir11(), ".claude", "CLAUDE.md");
50821
+ if (agent === "codex") return join28(homedir11(), ".codex", "AGENTS.md");
50299
50822
  if (agent === "hermes") {
50300
- return join27(process.env.HERMES_HOME ?? join27(homedir11(), ".hermes"), ".hermes.md");
50823
+ return join28(process.env.HERMES_HOME ?? join28(homedir11(), ".hermes"), ".hermes.md");
50301
50824
  }
50302
50825
  return null;
50303
50826
  }
50304
50827
  function loadSkill() {
50305
50828
  let dir = __dirname5;
50306
- while (dir !== dirname16(dir)) {
50829
+ while (dir !== dirname17(dir)) {
50307
50830
  const p = resolve8(dir, "SKILL.md");
50308
- if (existsSync22(p)) return readFileSync20(p, "utf-8");
50309
- dir = dirname16(dir);
50831
+ if (existsSync23(p)) return readFileSync21(p, "utf-8");
50832
+ dir = dirname17(dir);
50310
50833
  }
50311
50834
  throw new Error("SKILL.md not found in package");
50312
50835
  }
50313
50836
  function detectTargets(cwd) {
50314
- return TARGET_CANDIDATES.filter((p) => existsSync22(resolve8(cwd, p)));
50837
+ return TARGET_CANDIDATES.filter((p) => existsSync23(resolve8(cwd, p)));
50315
50838
  }
50316
50839
  function detectNewline(content) {
50317
50840
  return content.includes("\r\n") ? "\r\n" : "\n";
@@ -50377,7 +50900,7 @@ function runInstall(cwd, opts) {
50377
50900
  }
50378
50901
  const target = resolveTarget(cwd, opts);
50379
50902
  if (target === null) return 1;
50380
- const existing = existsSync22(target) ? readFileSync20(target, "utf-8") : "";
50903
+ const existing = existsSync23(target) ? readFileSync21(target, "utf-8") : "";
50381
50904
  const eol = existing ? detectNewline(existing) : "\n";
50382
50905
  const block = composeBlock(loadSkill(), eol);
50383
50906
  const next = applyBlock(existing, block, eol);
@@ -50401,11 +50924,11 @@ function runUninstall(cwd, opts) {
50401
50924
  }
50402
50925
  const target = resolveTarget(cwd, opts);
50403
50926
  if (target === null) return 1;
50404
- if (!existsSync22(target)) {
50927
+ if (!existsSync23(target)) {
50405
50928
  console.log(`Skill block not present at ${target}`);
50406
50929
  return 0;
50407
50930
  }
50408
- const existing = readFileSync20(target, "utf-8");
50931
+ const existing = readFileSync21(target, "utf-8");
50409
50932
  const next = removeBlock(existing);
50410
50933
  if (next === null) {
50411
50934
  console.log(`Skill block not present at ${target}`);
@@ -50429,10 +50952,10 @@ function runStatus(cwd, opts) {
50429
50952
  }
50430
50953
  const target = resolveTarget(cwd, opts);
50431
50954
  if (target === null) return 1;
50432
- const fileExists = existsSync22(target);
50955
+ const fileExists = existsSync23(target);
50433
50956
  let installed = false;
50434
50957
  if (fileExists) {
50435
- const content = readFileSync20(target, "utf-8");
50958
+ const content = readFileSync21(target, "utf-8");
50436
50959
  installed = content.includes(SKILL_BEGIN) && content.includes(SKILL_END);
50437
50960
  }
50438
50961
  if (opts.json) {
@@ -50469,13 +50992,13 @@ function registerSkillCommands(program3) {
50469
50992
  }
50470
50993
 
50471
50994
  // src/commands/statusline.ts
50472
- import { readFileSync as readFileSync21 } from "fs";
50473
- import { dirname as dirname17, resolve as resolve9 } from "path";
50995
+ import { readFileSync as readFileSync22 } from "fs";
50996
+ import { dirname as dirname18, resolve as resolve9 } from "path";
50474
50997
  import { fileURLToPath as fileURLToPath6 } from "url";
50475
50998
  var STATUSLINE_TIMEOUT_MS = 200;
50476
50999
  function readPackageVersion() {
50477
51000
  try {
50478
- const here = dirname17(fileURLToPath6(import.meta.url));
51001
+ const here = dirname18(fileURLToPath6(import.meta.url));
50479
51002
  const candidates = [
50480
51003
  // Schema-v2 runtimes staged a manifest beside this standalone bundle.
50481
51004
  // Keep resolving it while those legacy launchers transition to schema v3.
@@ -50485,7 +51008,7 @@ function readPackageVersion() {
50485
51008
  ];
50486
51009
  for (const path9 of candidates) {
50487
51010
  try {
50488
- const pkg2 = JSON.parse(readFileSync21(path9, "utf-8"));
51011
+ const pkg2 = JSON.parse(readFileSync22(path9, "utf-8"));
50489
51012
  if (pkg2.version) {
50490
51013
  return pkg2.version;
50491
51014
  }
@@ -50899,28 +51422,272 @@ function registerWelcomeCommand(program3, deps = { getClient }) {
50899
51422
  });
50900
51423
  }
50901
51424
 
51425
+ // src/daemon/self-heal.ts
51426
+ import { spawn as spawn4 } from "child_process";
51427
+
51428
+ // src/lib/unattended.ts
51429
+ var UNATTENDED_ENV = "PRIM_UNATTENDED";
51430
+ function unattendedEnv(env4 = process.env) {
51431
+ return { ...env4, [UNATTENDED_ENV]: "1" };
51432
+ }
51433
+ function isUnattended(env4 = process.env) {
51434
+ return env4[UNATTENDED_ENV] === "1";
51435
+ }
51436
+
51437
+ // src/daemon/self-heal.ts
51438
+ function kickDaemonEnsure(options = {}) {
51439
+ const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
51440
+ if (!primEntry) {
51441
+ return false;
51442
+ }
51443
+ try {
51444
+ const args = [primEntry, "daemon", "ensure"];
51445
+ if (options.latestBootstrap ?? (options.platform ?? process.platform) === "darwin") {
51446
+ args.push("--latest-bootstrap");
51447
+ }
51448
+ const child = (options.spawnProcess ?? spawn4)(
51449
+ options.nodeEntry ?? process.execPath,
51450
+ args,
51451
+ // Unattended: neither SessionStart's repair nor a drift heal is a person
51452
+ // running prim, so the child (and its descendants) never heal in turn.
51453
+ { detached: true, stdio: "ignore", env: unattendedEnv() }
51454
+ );
51455
+ child.once("error", () => {
51456
+ });
51457
+ child.unref();
51458
+ return true;
51459
+ } catch {
51460
+ return false;
51461
+ }
51462
+ }
51463
+
51464
+ // src/daemon/drift-heal.ts
51465
+ var ATTEMPT_MARKER_MODE = 384;
51466
+ var ATTEMPT_MARKER_MAX_BYTES = 1024;
51467
+ var PATH_SEPARATORS = /[\\/]/u;
51468
+ var DAEMON_DRIFT_HEAL_ENV = "PRIM_DAEMON_DRIFT_HEAL";
51469
+ var DAEMON_DRIFT_HEAL_RETRY_MS = 60 * 60 * 1e3;
51470
+ function readAttempt(path9) {
51471
+ const file = readBoundedRegularFile(path9, ATTEMPT_MARKER_MAX_BYTES);
51472
+ if (!file) return null;
51473
+ try {
51474
+ const value = JSON.parse(file.text);
51475
+ if (typeof value !== "object" || value === null) return null;
51476
+ const attempt = value;
51477
+ return typeof attempt.attemptedAt === "number" && Number.isFinite(attempt.attemptedAt) && typeof attempt.fromVersion === "string" && typeof attempt.toVersion === "string" ? attempt : null;
51478
+ } catch {
51479
+ return null;
51480
+ }
51481
+ }
51482
+ function claimHeld() {
51483
+ throw new Error("another command holds the daemon drift-heal claim");
51484
+ }
51485
+ function claimAttempt(markerPath2, attempt) {
51486
+ const previous = readAttempt(markerPath2);
51487
+ const elapsed = previous ? attempt.attemptedAt - previous.attemptedAt : Number.POSITIVE_INFINITY;
51488
+ if (elapsed >= 0 && elapsed < DAEMON_DRIFT_HEAL_RETRY_MS) return false;
51489
+ atomicWriteFile(markerPath2, `${JSON.stringify(attempt)}
51490
+ `, { mode: ATTEMPT_MARKER_MODE });
51491
+ return true;
51492
+ }
51493
+ function isInstalledPackage(root) {
51494
+ return root?.split(PATH_SEPARATORS).includes("node_modules") === true;
51495
+ }
51496
+ function needsHeal(launcher, toVersion) {
51497
+ const order = compareSemver(launcher.runtimeVersion, toVersion);
51498
+ return order === -1 || order === 0 && !launcher.runnable;
51499
+ }
51500
+ function startDriftHeal(options) {
51501
+ if ((options.platform ?? process.platform) !== "darwin") return false;
51502
+ const env4 = options.env ?? process.env;
51503
+ if (env4[DAEMON_DRIFT_HEAL_ENV] === "0") return false;
51504
+ const root = options.packageRoot === void 0 ? packageRoot() : options.packageRoot;
51505
+ if (!isInstalledPackage(root)) return false;
51506
+ const euid = options.euid ?? process.geteuid?.();
51507
+ if (euid === void 0 || euid === 0) return false;
51508
+ const paths = { env: env4, homeDir: options.homeDir };
51509
+ if (daemonExplicitlyDisabled(paths)) return false;
51510
+ const launcher = (options.selectedLauncher ?? (() => selectedDaemonLauncher(paths)))();
51511
+ if (!launcher || launcher.ownerUid !== euid) return false;
51512
+ const toVersion = options.cliVersion === void 0 ? packageVersion() : options.cliVersion;
51513
+ if (!(toVersion && needsHeal(launcher, toVersion))) return false;
51514
+ if (!apiUrlsMatch(
51515
+ getSiteUrlForEnvironment(launcher.apiUrl),
51516
+ getSiteUrlForEnvironment(env4.PRIM_API_URL)
51517
+ )) {
51518
+ return false;
51519
+ }
51520
+ const runsConfigRoot = options.launchAgentRunsConfigRoot ?? (() => launchAgentRunsConfigRoot(paths));
51521
+ if (!runsConfigRoot()) return false;
51522
+ const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
51523
+ if (!primEntry) return false;
51524
+ const attempt = {
51525
+ attemptedAt: (options.nowMs ?? Date.now)(),
51526
+ fromVersion: launcher.runtimeVersion,
51527
+ toVersion
51528
+ };
51529
+ const claimed = withFileLockSync(
51530
+ daemonDriftHealClaimPath(paths),
51531
+ () => claimAttempt(daemonDriftHealMarkerPath(paths), attempt),
51532
+ { timeoutMs: 0, sleep: claimHeld }
51533
+ );
51534
+ if (!claimed) return false;
51535
+ return kickDaemonEnsure({
51536
+ primEntry,
51537
+ nodeEntry: options.nodeEntry,
51538
+ spawnProcess: options.spawnProcess,
51539
+ latestBootstrap: false
51540
+ });
51541
+ }
51542
+ function healDaemonDrift(options = {}) {
51543
+ try {
51544
+ return startDriftHeal(options);
51545
+ } catch {
51546
+ return false;
51547
+ }
51548
+ }
51549
+
51550
+ // src/lib/background-flush.ts
51551
+ import { spawn as spawn5 } from "child_process";
51552
+ import { readFileSync as readFileSync23 } from "fs";
51553
+
51554
+ // src/daemon/health.ts
51555
+ import { chmodSync as chmodSync7, existsSync as existsSync24, mkdirSync as mkdirSync15, renameSync as renameSync7, writeFileSync as writeFileSync11 } from "fs";
51556
+ import { dirname as dirname19, join as join29 } from "path";
51557
+ var HEARTBEAT_FRESH_MS = 9e4;
51558
+ var DAEMON_HEALTH_PATH = join29(primConfigDirectory(), "daemon-health.json");
51559
+
51560
+ // src/lib/background-flush.ts
51561
+ function record2(value) {
51562
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
51563
+ }
51564
+ function daemonOwnsDrain(options = {}) {
51565
+ try {
51566
+ const expectedVersion = options.expectedVersion === void 0 ? packageVersion() : options.expectedVersion;
51567
+ if (!expectedVersion) return false;
51568
+ const health = record2(
51569
+ JSON.parse(readFileSync23(options.healthPath ?? DAEMON_HEALTH_PATH, "utf8"))
51570
+ );
51571
+ const heartbeat = record2(health?.heartbeat);
51572
+ const ingestion = record2(health?.ingestion);
51573
+ if (!(health && heartbeat && ingestion)) return false;
51574
+ if (health.schemaVersion !== 1 || health.version !== expectedVersion) return false;
51575
+ const daemonSiteUrl = health.siteUrl;
51576
+ if (typeof daemonSiteUrl !== "string" || envSlug(daemonSiteUrl) !== envSlug(options.siteUrl ?? getSiteUrl())) {
51577
+ return false;
51578
+ }
51579
+ if (health.needsReauth === true) return false;
51580
+ const pid = health.pid;
51581
+ if (typeof pid !== "number" || !(options.isAlive ?? processIsAlive)(pid)) return false;
51582
+ const lastSuccessAt = heartbeat.lastSuccessAt;
51583
+ const now = options.now ?? Date.now();
51584
+ return heartbeat.consecutiveFailures === 0 && typeof lastSuccessAt === "number" && lastSuccessAt <= now && now - lastSuccessAt < HEARTBEAT_FRESH_MS && ingestion.consecutiveFailures === 0;
51585
+ } catch {
51586
+ return false;
51587
+ }
51588
+ }
51589
+ function startBackgroundFlush(options = {}) {
51590
+ try {
51591
+ if ((options.daemonOwnsDrain ?? daemonOwnsDrain)()) return false;
51592
+ if (!(options.needsFlush ?? journalNeedsFlush)()) return false;
51593
+ const primEntry = options.primEntry === void 0 ? binFile("prim") : options.primEntry;
51594
+ if (!primEntry) return false;
51595
+ const child = (options.spawnProcess ?? spawn5)(
51596
+ options.nodeEntry ?? process.execPath,
51597
+ [primEntry, "moves", "flush"],
51598
+ // Ignored stdio matters as much as detaching: a child holding inherited
51599
+ // pipes would keep a caller that captures them (spawnSync) waiting.
51600
+ // windowsHide keeps the detached child from opening a console window.
51601
+ // The child is unattended even when its parent is not: `doctor` or
51602
+ // `statusline` must not reach the daemon drift heal through its drain.
51603
+ { detached: true, stdio: "ignore", windowsHide: true, env: unattendedEnv() }
51604
+ );
51605
+ child.once("error", () => {
51606
+ });
51607
+ child.unref();
51608
+ return true;
51609
+ } catch {
51610
+ return false;
51611
+ }
51612
+ }
51613
+
50902
51614
  // src/lib/startup-background.ts
50903
51615
  var ROOT_OPTIONS = /* @__PURE__ */ new Set(["-y", "--yes", "--non-interactive"]);
51616
+ var HELP_OPTIONS = /* @__PURE__ */ new Set(["-h", "--help"]);
51617
+ var DAEMON_HEAL_COMMANDS = /* @__PURE__ */ new Map([
51618
+ // Credentials; `auth login` is how a person returns to a daemon held for re-auth.
51619
+ ["auth", /* @__PURE__ */ new Set(["api-keys", "login", "set-token", "status"])],
51620
+ // Reading and curating the decision graph: the everyday attended surface.
51621
+ [
51622
+ "decisions",
51623
+ /* @__PURE__ */ new Set([
51624
+ "cascade",
51625
+ "check",
51626
+ "confirm",
51627
+ "create",
51628
+ "demote",
51629
+ "link",
51630
+ "promote",
51631
+ "publish",
51632
+ "ratify",
51633
+ "recent",
51634
+ "repairs",
51635
+ "rescope",
51636
+ "restore",
51637
+ "show",
51638
+ "supersede",
51639
+ "unlink",
51640
+ "withdraw"
51641
+ ])
51642
+ ],
51643
+ ["reconcile", "any"],
51644
+ // Repository connection, activation, and journal plumbing a person steers.
51645
+ // Hook-side `moves flush` drains are unattended and never reach this table.
51646
+ ["github", /* @__PURE__ */ new Set(["connect"])],
51647
+ ["enable", "any"],
51648
+ ["moves", /* @__PURE__ */ new Set(["bind", "drop", "flush", "status", "tail"])],
51649
+ ["session", /* @__PURE__ */ new Set(["drop", "list", "start"])],
51650
+ // Installing or inspecting an integration, never removing one.
51651
+ ["claude", /* @__PURE__ */ new Set(["install", "preauth", "status"])],
51652
+ ["codex", /* @__PURE__ */ new Set(["install", "status"])],
51653
+ ["cursor", /* @__PURE__ */ new Set(["install", "status"])],
51654
+ ["hermes", /* @__PURE__ */ new Set(["install", "status"])],
51655
+ ["hooks", /* @__PURE__ */ new Set(["install"])],
51656
+ ["skill", /* @__PURE__ */ new Set(["install", "status"])],
51657
+ ["welcome", "any"]
51658
+ ]);
50904
51659
  function commandArguments(argv) {
50905
51660
  let index = 0;
50906
51661
  while (ROOT_OPTIONS.has(argv[index] ?? "")) index += 1;
50907
51662
  return argv.slice(index);
50908
51663
  }
51664
+ function mayHealDaemon(argv, env4) {
51665
+ if (isUnattended(env4) || env4[SETUP_ORCHESTRATOR_ENV] === "1") return false;
51666
+ const args = commandArguments(argv);
51667
+ if (args.some((arg) => HELP_OPTIONS.has(arg))) return false;
51668
+ const [command, subcommand] = args;
51669
+ const allowed = command === void 0 ? void 0 : DAEMON_HEAL_COMMANDS.get(command);
51670
+ if (allowed === void 0) return false;
51671
+ return allowed === "any" || subcommand !== void 0 && allowed.has(subcommand);
51672
+ }
50909
51673
  function runStartupBackgroundWork(argv, env4, work) {
50910
- const [command, subcommand] = commandArguments(argv);
51674
+ const args = commandArguments(argv);
51675
+ const [command, subcommand] = args;
50911
51676
  const uninstall = command === "uninstall" || env4[UNINSTALL_ORCHESTRATOR_ENV] === "1";
50912
51677
  if (uninstall) return;
50913
51678
  work.notify();
50914
- if (command !== "moves" || subcommand !== "flush") work.flush().catch(() => {
50915
- });
51679
+ const setupDaemonDrains = command === "setup" && !args.includes("--no-daemon") || env4[SETUP_DAEMON_DRAINS_ENV] === "1";
51680
+ if (!setupDaemonDrains && (command !== "moves" || subcommand !== "flush")) work.flush();
51681
+ if (mayHealDaemon(argv, env4)) work.healDaemon();
50916
51682
  }
50917
51683
 
50918
51684
  // src/index.ts
50919
- var __dirname6 = dirname18(fileURLToPath7(import.meta.url));
50920
- var pkg = JSON.parse(readFileSync22(resolve10(__dirname6, "../package.json"), "utf-8"));
51685
+ var __dirname6 = dirname20(fileURLToPath7(import.meta.url));
51686
+ var pkg = JSON.parse(readFileSync24(resolve10(__dirname6, "../package.json"), "utf-8"));
50921
51687
  runStartupBackgroundWork(process.argv.slice(2), process.env, {
50922
51688
  notify: () => updateNotifier({ pkg }).notify(),
50923
- flush: () => flushIfNeeded()
51689
+ flush: () => startBackgroundFlush(),
51690
+ healDaemon: () => healDaemonDrift()
50924
51691
  });
50925
51692
  var program2 = new Command();
50926
51693
  program2.name("prim").description("CLI for Primitive's decision graph").version(pkg.version).option("-y, --yes", "auto-confirm prompts").option(