failproofai 1.0.2-beta.8 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +4 -4
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  22. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  24. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
  27. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
  29. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
  31. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
  33. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  36. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  38. package/.next/standalone/.next/server/app/index.html +1 -1
  39. package/.next/standalone/.next/server/app/index.rsc +14 -14
  40. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
  41. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
  42. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  43. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  44. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  47. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  48. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  51. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  54. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  55. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  58. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  61. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/chunks/[externals]__0-9ti7z._.js +3 -0
  64. package/.next/standalone/.next/server/chunks/{[externals]__1lt47k4._.js → [externals]__0sg9-3p._.js} +1 -1
  65. package/.next/standalone/.next/server/chunks/[externals]__0u3wrvq._.js +3 -0
  66. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
  67. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
  68. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zlqo2t._.js +3 -0
  70. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1anxb5k._.js +3 -0
  71. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
  73. package/.next/standalone/.next/server/chunks/_09dz7xv._.js +27 -0
  74. package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
  75. package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
  76. package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
  77. package/.next/standalone/.next/server/chunks/{_19uwam-._.js → _1-akg6w._.js} +2 -2
  78. package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
  79. package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -7
  80. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
  81. package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
  82. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +3 -0
  84. package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +10 -0
  85. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
  87. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
  88. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
  89. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
  90. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
  91. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_lxhg._.js +5 -0
  92. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
  94. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
  95. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
  96. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1yh7hlv._.js +3 -0
  97. package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/_0ehudfr._.js +23 -0
  99. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1n2ywfz._.js → _1es2j7i._.js} +4 -4
  100. package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
  101. package/.next/standalone/.next/server/chunks/ssr/{_1h33neq._.js → _1un1pug._.js} +1 -1
  102. package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +9 -9
  104. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  107. package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
  108. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +10 -0
  109. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
  110. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  111. package/.next/standalone/.next/server/middleware.js +1 -1
  112. package/.next/standalone/.next/server/pages/404.html +1 -1
  113. package/.next/standalone/.next/server/pages/500.html +1 -1
  114. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/server-reference-manifest.json +19 -19
  116. package/.next/standalone/.next/static/chunks/0nkpwjk01ei3t.js +1 -0
  117. package/.next/standalone/.next/static/chunks/{2iryw37qfqy5y.js → 0ofdelz65_517.js} +1 -1
  118. package/.next/standalone/.next/static/chunks/{34j7nmidlcuni.js → 12uhnpwgsmcev.js} +1 -1
  119. package/.next/standalone/.next/static/chunks/{0i3a6wf-f_yky.js → 1_1eb_149xyt9.js} +1 -1
  120. package/.next/standalone/.next/static/chunks/{3927pu4ss_l4k.js → 1tx9pnxyjrkoj.js} +1 -1
  121. package/.next/standalone/.next/static/chunks/{1n04wzsaes73g.js → 1x0jrl3a4_i2m.js} +1 -1
  122. package/.next/standalone/.next/static/chunks/{3r0udrtpn78-e.js → 1zpv6omeln7al.js} +1 -1
  123. package/.next/standalone/.next/static/chunks/{416zumre3fvfw.js → 20hbg-r3io_ip.js} +1 -1
  124. package/.next/standalone/.next/static/chunks/{11a_m8yksthtf.js → 32bhi32ym8yrz.js} +2 -2
  125. package/.next/standalone/.next/static/chunks/{0u2ojcw25c_np.js → 3fr78ugeb4tjc.js} +1 -1
  126. package/.next/standalone/app/actions/get-hooks-config.ts +8 -1
  127. package/.next/standalone/app/actions/pack-actions.ts +12 -1
  128. package/.next/standalone/app/policies/hooks-client.tsx +41 -4
  129. package/.next/standalone/fp-cloud-cli/tests/test_pagination.py +204 -0
  130. package/.next/standalone/package.json +9 -9
  131. package/.next/standalone/server.js +1 -1
  132. package/README.md +58 -13
  133. package/bin/failproofai.mjs +10 -1
  134. package/dist/cli.mjs +22429 -21292
  135. package/dist/worker.mjs +1247 -540
  136. package/package.json +9 -9
  137. package/src/audit/index.ts +24 -4
  138. package/src/hooks/builtin-policies.ts +933 -8
  139. package/src/hooks/custom-hooks-loader.ts +98 -8
  140. package/src/hooks/handler.ts +26 -14
  141. package/src/hooks/manager.ts +36 -2
  142. package/src/hooks/pack-cli.ts +415 -42
  143. package/src/hooks/pack-failclosed.ts +100 -4
  144. package/src/hooks/pack-manifest.ts +29 -0
  145. package/src/hooks/pack-param-key.ts +36 -0
  146. package/src/hooks/pack-store.ts +101 -9
  147. package/src/hooks/policy-catalog.ts +9 -1
  148. package/src/hooks/policy-evaluator.ts +77 -1
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +0 -3
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +0 -3
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tg4pu6._.js +0 -3
  156. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
  157. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
  158. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mgrjnx._.js +0 -10
  159. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-wzs7f._.js +0 -3
  160. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1wlvpe5._.js +0 -5
  161. package/.next/standalone/.next/server/chunks/ssr/_0l7zv8b._.js +0 -23
  162. package/.next/standalone/.next/static/chunks/1ch5l9t2p16y4.js +0 -1
  163. /package/.next/standalone/.next/static/{NgD5HvuyPxwgrpeIiafJL → 5-xDMxcnrcXDFPQiy3yhg}/_buildManifest.js +0 -0
  164. /package/.next/standalone/.next/static/{NgD5HvuyPxwgrpeIiafJL → 5-xDMxcnrcXDFPQiy3yhg}/_clientMiddlewareManifest.js +0 -0
  165. /package/.next/standalone/.next/static/{NgD5HvuyPxwgrpeIiafJL → 5-xDMxcnrcXDFPQiy3yhg}/_ssgManifest.js +0 -0
package/dist/worker.mjs CHANGED
@@ -702,7 +702,10 @@ var init_policy_catalog = __esm(() => {
702
702
  displayTitle: "Tried to disable, pause or modify failproofai itself",
703
703
  impact: "An agent that can pause or remove enforcement can switch off every other policy.",
704
704
  description: "Block failproofai CLI commands, self-pause and uninstallation",
705
- match: { events: ["PreToolUse", "PermissionRequest"], toolNames: ["Bash"] },
705
+ match: {
706
+ events: ["PreToolUse", "PermissionRequest"],
707
+ toolNames: ["Bash", "Write", "Edit", "NotebookEdit"]
708
+ },
706
709
  defaultEnabled: true,
707
710
  alwaysOn: true,
708
711
  category: "Dangerous Commands"
@@ -1278,6 +1281,415 @@ function stripShellQuoting(command) {
1278
1281
  const ansiDecoded = joined.replace(/\$'((?:[^'\\]|\\.)*)'/g, (_, body) => decodeAnsiC(body));
1279
1282
  return ansiDecoded.replace(/\\(.)/g, "$1").replace(/['"]/g, "");
1280
1283
  }
1284
+ function gitSubcommand(args) {
1285
+ for (let i = 0;i < args.length; i++) {
1286
+ const arg = args[i];
1287
+ if (GIT_FLAGS_WITH_OPERANDS.has(arg)) {
1288
+ i++;
1289
+ continue;
1290
+ }
1291
+ if (arg.startsWith("-"))
1292
+ continue;
1293
+ return arg;
1294
+ }
1295
+ return;
1296
+ }
1297
+ function globCouldNameState(raw) {
1298
+ const stripped = raw.replace(/^[()]+|[()]+$/g, "");
1299
+ return globWordCouldNameState(raw) || stripped !== raw && globWordCouldNameState(stripped);
1300
+ }
1301
+ function globWordCouldNameState(token) {
1302
+ if (!GLOB_METACHAR_RE.test(token))
1303
+ return false;
1304
+ for (const word of expandBraces(token)) {
1305
+ if (wordNamesState(word))
1306
+ return true;
1307
+ }
1308
+ return false;
1309
+ }
1310
+ function expandBraces(token) {
1311
+ let words = [token];
1312
+ for (let round = 0;round < BRACE_EXPANSION_ROUNDS; round++) {
1313
+ const next = [];
1314
+ let expanded = false;
1315
+ for (const word of words) {
1316
+ const group = firstBraceGroup(word);
1317
+ if (!group) {
1318
+ next.push(word);
1319
+ continue;
1320
+ }
1321
+ expanded = true;
1322
+ for (const alternative of group.alternatives) {
1323
+ next.push(word.slice(0, group.start) + alternative + word.slice(group.end + 1));
1324
+ }
1325
+ }
1326
+ if (!expanded)
1327
+ return next;
1328
+ if (next.length > BRACE_EXPANSION_LIMIT)
1329
+ return [collapseBraces(token)];
1330
+ words = next;
1331
+ }
1332
+ return words.map(collapseBraces);
1333
+ }
1334
+ function collapseBraces(word) {
1335
+ let out = "";
1336
+ let depth = 0;
1337
+ for (const ch of word) {
1338
+ if (ch === "{") {
1339
+ if (depth === 0)
1340
+ out += "*";
1341
+ depth++;
1342
+ continue;
1343
+ }
1344
+ if (ch === "}") {
1345
+ if (depth > 0)
1346
+ depth--;
1347
+ continue;
1348
+ }
1349
+ if (depth === 0)
1350
+ out += ch;
1351
+ }
1352
+ return out;
1353
+ }
1354
+ function firstBraceGroup(word) {
1355
+ const start = word.indexOf("{");
1356
+ if (start === -1)
1357
+ return null;
1358
+ let depth = 0;
1359
+ const alternatives = [];
1360
+ let current = "";
1361
+ for (let i = start;i < word.length; i++) {
1362
+ const ch = word[i];
1363
+ if (ch === "{") {
1364
+ depth++;
1365
+ if (depth === 1)
1366
+ continue;
1367
+ } else if (ch === "}") {
1368
+ depth--;
1369
+ if (depth === 0) {
1370
+ alternatives.push(current);
1371
+ return { start, end: i, alternatives };
1372
+ }
1373
+ } else if (ch === "," && depth === 1) {
1374
+ alternatives.push(current);
1375
+ current = "";
1376
+ continue;
1377
+ }
1378
+ current += ch;
1379
+ }
1380
+ return null;
1381
+ }
1382
+ function wordNamesState(word) {
1383
+ if (FAILPROOFAI_STATE_PATH_RE.test(word))
1384
+ return true;
1385
+ const segments = word.split("/");
1386
+ for (let end = 1;end <= segments.length; end++) {
1387
+ if (globPrefixNamesState(segments.slice(0, end).join("/")))
1388
+ return true;
1389
+ }
1390
+ return false;
1391
+ }
1392
+ function compileGlobPattern(token) {
1393
+ let pattern = "";
1394
+ for (let i = 0;i < token.length; i++) {
1395
+ const extended = extglobAt(token, i);
1396
+ if (extended) {
1397
+ pattern += extended.source;
1398
+ i = extended.end;
1399
+ continue;
1400
+ }
1401
+ const ch = token[i];
1402
+ if (ch === "*")
1403
+ pattern += "[^/]*";
1404
+ else if (ch === "?")
1405
+ pattern += "[^/]";
1406
+ else if (ch === "[") {
1407
+ const bracket = bracketExpressionAt(token, i);
1408
+ if (!bracket) {
1409
+ pattern += "\\[";
1410
+ continue;
1411
+ }
1412
+ pattern += bracket.source;
1413
+ i = bracket.end;
1414
+ } else
1415
+ pattern += ch.replace(/[.+^${}()|\\\]]/g, "\\$&");
1416
+ }
1417
+ return pattern;
1418
+ }
1419
+ function extglobAt(token, start) {
1420
+ const operator = token[start];
1421
+ if (!"?*+@!".includes(operator) || token[start + 1] !== "(")
1422
+ return null;
1423
+ const alternatives = [];
1424
+ let current = "";
1425
+ let depth = 0;
1426
+ let i = start + 1;
1427
+ for (;i < token.length; i++) {
1428
+ const ch = token[i];
1429
+ if (ch === "(") {
1430
+ depth++;
1431
+ if (depth === 1)
1432
+ continue;
1433
+ } else if (ch === ")") {
1434
+ depth--;
1435
+ if (depth === 0) {
1436
+ alternatives.push(current);
1437
+ break;
1438
+ }
1439
+ } else if (ch === "|" && depth === 1) {
1440
+ alternatives.push(current);
1441
+ current = "";
1442
+ continue;
1443
+ }
1444
+ current += ch;
1445
+ }
1446
+ if (i >= token.length)
1447
+ return null;
1448
+ if (operator === "!")
1449
+ return { source: "[^/]*", end: i };
1450
+ const body = alternatives.map((alternative) => compileGlobPattern(alternative)).join("|");
1451
+ const quantifier = operator === "?" ? "?" : operator === "*" ? "*" : operator === "+" ? "+" : "";
1452
+ return { source: `(?:${body})${quantifier}`, end: i };
1453
+ }
1454
+ function bracketExpressionAt(token, start) {
1455
+ let i = start + 1;
1456
+ let negated = false;
1457
+ if (token[i] === "!" || token[i] === "^") {
1458
+ negated = true;
1459
+ i++;
1460
+ }
1461
+ let body = "";
1462
+ if (token[i] === "]") {
1463
+ body += "]";
1464
+ i++;
1465
+ }
1466
+ while (i < token.length && token[i] !== "]") {
1467
+ const inner = token[i] === "[" ? token[i + 1] : undefined;
1468
+ if (inner === ":" || inner === "." || inner === "=") {
1469
+ const close = token.indexOf(`${inner}]`, i + 2);
1470
+ if (close !== -1) {
1471
+ body += token.slice(i, close + 2);
1472
+ i = close + 2;
1473
+ continue;
1474
+ }
1475
+ }
1476
+ body += token[i];
1477
+ i++;
1478
+ }
1479
+ if (i >= token.length)
1480
+ return null;
1481
+ if (/\[[.=:]/.test(body))
1482
+ return { source: "[^/]", end: i };
1483
+ const escaped = body.replace(/[\\\]]/g, "\\$&");
1484
+ if (escaped.length === 0)
1485
+ return { source: "[^/]", end: i };
1486
+ return { source: `[${negated ? "^" : ""}${escaped}]`, end: i };
1487
+ }
1488
+ function globPrefixNamesState(token) {
1489
+ if (!GLOB_METACHAR_RE.test(token))
1490
+ return false;
1491
+ const pattern = "^" + compileGlobPattern(token);
1492
+ let re;
1493
+ try {
1494
+ re = new RegExp(pattern + "$");
1495
+ } catch {
1496
+ return false;
1497
+ }
1498
+ if (!STATE_GLOB_CANDIDATES.some((candidate) => re.test(candidate)))
1499
+ return false;
1500
+ return !GLOB_DECOYS.some((decoy) => re.test(decoy));
1501
+ }
1502
+ function substitutionBodies(command) {
1503
+ const bodies = [];
1504
+ for (let i = 0;i < command.length; i++) {
1505
+ if (command[i] === "`") {
1506
+ const close = command.indexOf("`", i + 1);
1507
+ if (close === -1)
1508
+ break;
1509
+ bodies.push(command.slice(i + 1, close));
1510
+ i = close;
1511
+ continue;
1512
+ }
1513
+ if (command[i] !== "$" || command[i + 1] !== "(")
1514
+ continue;
1515
+ let depth = 1;
1516
+ let j = i + 2;
1517
+ for (;j < command.length && depth > 0; j++) {
1518
+ if (command[j] === "(")
1519
+ depth++;
1520
+ else if (command[j] === ")")
1521
+ depth--;
1522
+ }
1523
+ if (depth === 0)
1524
+ bodies.push(command.slice(i + 2, j - 1));
1525
+ i = j - 1;
1526
+ }
1527
+ return bodies;
1528
+ }
1529
+ function bareToken(token) {
1530
+ return token.replace(/^[(){}'"]+|[(){}'"]+$/g, "");
1531
+ }
1532
+ function unquotedToken(token) {
1533
+ return token.replace(/^['"]+|['"]+$/g, "");
1534
+ }
1535
+ function commandBasename(token) {
1536
+ const bare = bareToken(token);
1537
+ return bare.slice(bare.lastIndexOf("/") + 1);
1538
+ }
1539
+ function commandWords(simpleCommand) {
1540
+ const tokens = simpleCommand.trim().split(/\s+/).map(bareToken).filter(Boolean);
1541
+ let i = 0;
1542
+ while (i < tokens.length) {
1543
+ const token = tokens[i];
1544
+ if (/^\d*(?:>{1,2}\|?|<{1,3})/.test(token)) {
1545
+ i += /^\d*(?:>{1,2}\|?|<{1,3})$/.test(token) ? 2 : 1;
1546
+ continue;
1547
+ }
1548
+ const skippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || RUNNER_OPERAND_RE.test(token) || SHELL_KEYWORD_PREFIXES.has(token) || COMMAND_PREFIX_TOKENS.has(commandBasename(token));
1549
+ if (!skippable)
1550
+ break;
1551
+ i++;
1552
+ }
1553
+ return tokens.slice(i);
1554
+ }
1555
+ function simpleCommandDestroys(words, namesState) {
1556
+ if (words.length === 0)
1557
+ return false;
1558
+ const head = commandBasename(words[0]);
1559
+ const args = words.slice(1);
1560
+ if (head === "find") {
1561
+ if (args.some((a) => a === "-delete"))
1562
+ return true;
1563
+ if (args.some((a) => FIND_WRITE_ACTIONS.has(a)))
1564
+ return true;
1565
+ return args.some((a, i) => FIND_EXEC_ACTIONS.has(a) && !SAFE_EXEC_COMMANDS.has(commandBasename(args[i + 1] ?? "")));
1566
+ }
1567
+ if (head === "sed") {
1568
+ if (args.some((a) => a.startsWith("-i") || a.startsWith("--in-place")))
1569
+ return true;
1570
+ return args.some((a, i) => /(?:^|[;}/\s])w$/.test(a) && namesState(args[i + 1] ?? "")) || args.some((a) => /(?:^|[;}/\s])w\s*\S*\.failproofai/.test(a));
1571
+ }
1572
+ if (head === "awk" || head === "gawk" || head === "mawk") {
1573
+ return args.some((a) => /system\s*\(|\|\s*["']|\|&/.test(a));
1574
+ }
1575
+ if (COPY_COMMANDS.has(head)) {
1576
+ if (args.includes("--remove-source-files") && args.some((a) => namesState(a)))
1577
+ return true;
1578
+ const targetAt = args.findIndex((a) => a === "-t" || a === "--target-directory");
1579
+ if (targetAt !== -1 && namesState(args[targetAt + 1] ?? ""))
1580
+ return true;
1581
+ if (args.some((a) => a.startsWith("--target-directory=") && namesState(a)))
1582
+ return true;
1583
+ const operands = args.filter((a) => !a.startsWith("-"));
1584
+ const destination = operands[operands.length - 1];
1585
+ return destination !== undefined && namesState(destination);
1586
+ }
1587
+ if (OPERAND_WRITE_COMMANDS.has(head)) {
1588
+ return args.some((a) => !a.startsWith("-") && namesState(a));
1589
+ }
1590
+ if (SECOND_OPERAND_WRITERS.has(head)) {
1591
+ const operands = args.filter((a) => !a.startsWith("-"));
1592
+ return operands.length > 1 && namesState(operands[operands.length - 1]);
1593
+ }
1594
+ if (head === "git") {
1595
+ const subcommand = gitSubcommand(args);
1596
+ return subcommand !== undefined && GIT_DESTRUCTIVE_SUBCOMMANDS.has(subcommand);
1597
+ }
1598
+ if (writesViaOutputFlag(head, args, namesState))
1599
+ return true;
1600
+ if (STATE_READ_COMMANDS.has(head) || STATE_MENTION_COMMANDS.has(head))
1601
+ return false;
1602
+ return true;
1603
+ }
1604
+ function writesViaOutputFlag(head, args, namesState) {
1605
+ const flags = OUTPUT_FLAG_COMMANDS[head];
1606
+ if (!flags)
1607
+ return false;
1608
+ for (let i = 0;i < args.length; i++) {
1609
+ const arg = args[i];
1610
+ const long = flags.find((f) => f.startsWith("--") && arg.startsWith(`${f}=`));
1611
+ if (long && namesState(arg))
1612
+ return true;
1613
+ const bundled = /^-[A-Za-z]*[oD]$/.test(arg) && flags.includes(`-${arg[arg.length - 1]}`);
1614
+ if ((flags.includes(arg) || bundled) && namesState(args[i + 1] ?? ""))
1615
+ return true;
1616
+ }
1617
+ return false;
1618
+ }
1619
+ function destroysFailproofaiState(command, depth = 0) {
1620
+ if (depth === 0) {
1621
+ const bodies = substitutionBodies(command);
1622
+ for (let i = 0;i < bodies.length && i < 64; i++) {
1623
+ if (destroysFailproofaiState(bodies[i], 1))
1624
+ return true;
1625
+ bodies.push(...substitutionBodies(bodies[i]));
1626
+ }
1627
+ }
1628
+ const assignments = [
1629
+ ...command.matchAll(/(?:^|[\s;&|(])(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;&|)]+)/g)
1630
+ ].map(([, name, value]) => [name, value.replace(/^["']|["']$/g, "")]);
1631
+ const stateVars = new Set;
1632
+ for (let pass = 0;pass < assignments.length + 1; pass++) {
1633
+ const before = stateVars.size;
1634
+ for (const [name, value] of assignments) {
1635
+ const holdsState = FAILPROOFAI_STATE_PATH_RE.test(value) || [...value.matchAll(VAR_REFERENCE_RE)].some(([, ref]) => stateVars.has(ref));
1636
+ if (holdsState)
1637
+ stateVars.add(name);
1638
+ }
1639
+ if (stateVars.size === before)
1640
+ break;
1641
+ }
1642
+ const namesState = (text) => {
1643
+ if (FAILPROOFAI_STATE_PATH_RE.test(text))
1644
+ return true;
1645
+ for (const [, name] of text.matchAll(VAR_REFERENCE_RE)) {
1646
+ if (stateVars.has(name))
1647
+ return true;
1648
+ }
1649
+ return text.split(/\s+/).some((token) => globCouldNameState(unquotedToken(token)));
1650
+ };
1651
+ let cwdInState = false;
1652
+ let parenDepth = 0;
1653
+ let cdParenDepth = null;
1654
+ let loopCarriesState = false;
1655
+ for (const pipeline of command.split(PIPELINE_SEPARATORS)) {
1656
+ if (!pipeline.trim())
1657
+ continue;
1658
+ const simpleCommands = pipeline.split("|");
1659
+ const reachesState = namesState(pipeline) || cwdInState || loopCarriesState;
1660
+ if (reachesState && LOOP_HEADER_RE.test(pipeline))
1661
+ loopCarriesState = true;
1662
+ if (/(?:^|\s)done(?:\s|$)/.test(pipeline))
1663
+ loopCarriesState = false;
1664
+ if (reachesState) {
1665
+ for (const [, target] of pipeline.matchAll(REDIRECT_TARGET_RE)) {
1666
+ if (namesState(target.replace(/^["']|["']$/g, "")))
1667
+ return true;
1668
+ }
1669
+ }
1670
+ for (const simple of simpleCommands) {
1671
+ parenDepth += (simple.match(/\(/g) ?? []).length;
1672
+ const words = commandWords(simple);
1673
+ const head = words.length > 0 ? commandBasename(words[0]) : "";
1674
+ if (head === "cd" || head === "pushd") {
1675
+ const operand = words.slice(1).find((w) => !w.startsWith("-"));
1676
+ cwdInState = operand !== undefined && namesState(operand);
1677
+ cdParenDepth = cwdInState ? parenDepth : null;
1678
+ } else if (head === "popd") {
1679
+ cwdInState = false;
1680
+ cdParenDepth = null;
1681
+ } else if (reachesState && words.length > 0 && simpleCommandDestroys(words, namesState)) {
1682
+ return true;
1683
+ }
1684
+ parenDepth -= (simple.match(/\)/g) ?? []).length;
1685
+ if (cdParenDepth !== null && parenDepth < cdParenDepth) {
1686
+ cwdInState = false;
1687
+ cdParenDepth = null;
1688
+ }
1689
+ }
1690
+ }
1691
+ return false;
1692
+ }
1281
1693
  function statGitHeadMtimeMs(cwd) {
1282
1694
  try {
1283
1695
  return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
@@ -1893,6 +2305,13 @@ function blockWorkOnMain(ctx) {
1893
2305
  return allow();
1894
2306
  }
1895
2307
  function blockFailproofaiCommands(ctx) {
2308
+ if (ctx.toolName === "Write" || ctx.toolName === "Edit" || ctx.toolName === "NotebookEdit") {
2309
+ const path = getFilePath(ctx) || (ctx.toolInput?.notebook_path ?? "");
2310
+ if (FAILPROOFAI_STATE_PATH_RE.test(path)) {
2311
+ return deny("Writing to failproofai's own state would switch enforcement off. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
2312
+ }
2313
+ return allow();
2314
+ }
1896
2315
  if (ctx.toolName !== "Bash")
1897
2316
  return allow();
1898
2317
  const cmd = getCommand(ctx);
@@ -1907,8 +2326,8 @@ function blockFailproofaiCommands(ctx) {
1907
2326
  if (FAILPROOFAI_UNINSTALL_RE.test(cmd) || FAILPROOFAI_UNINSTALL_RE.test(unescaped)) {
1908
2327
  return deny("Uninstalling failproofai is blocked");
1909
2328
  }
1910
- if (FAILPROOFAI_STATE_WRITE_RE.test(cmd) || FAILPROOFAI_STATE_WRITE_RE.test(unescaped)) {
1911
- return deny("Deleting or moving failproofai's own state would switch enforcement off. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
2329
+ if (destroysFailproofaiState(cmd) || destroysFailproofaiState(unescaped)) {
2330
+ return deny("Deleting, moving or overwriting failproofai's own state would switch enforcement off. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
1912
2331
  }
1913
2332
  return allow();
1914
2333
  }
@@ -2368,7 +2787,7 @@ function registerBuiltinPolicies(enabledNames) {
2368
2787
  }
2369
2788
  }
2370
2789
  }
2371
- var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, SELF_ENTRY_PATH_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_STATE_WRITE_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, POLICY_IMPLEMENTATIONS, BUILTIN_POLICIES;
2790
+ var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, SELF_ENTRY_PATH_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_STATE_PATH_RE, STATE_READ_COMMANDS, SHELL_KEYWORD_PREFIXES, LOOP_HEADER_RE, COPY_COMMANDS, OPERAND_WRITE_COMMANDS, SECOND_OPERAND_WRITERS, OUTPUT_FLAG_COMMANDS, FIND_WRITE_ACTIONS, SAFE_EXEC_COMMANDS, GIT_DESTRUCTIVE_SUBCOMMANDS, GIT_FLAGS_WITH_OPERANDS, STATE_MENTION_COMMANDS, FIND_EXEC_ACTIONS, PIPELINE_SEPARATORS, VAR_REFERENCE_RE, STATE_GLOB_CANDIDATES, GLOB_DECOYS, BRACE_EXPANSION_LIMIT = 4096, BRACE_EXPANSION_ROUNDS = 24, GLOB_METACHAR_RE, REDIRECT_TARGET_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, POLICY_IMPLEMENTATIONS, BUILTIN_POLICIES;
2372
2791
  var init_builtin_policies = __esm(() => {
2373
2792
  init_policy_catalog();
2374
2793
  init_hook_logger();
@@ -2464,7 +2883,175 @@ var init_builtin_policies = __esm(() => {
2464
2883
  SECRET_FILE_ID_RSA_RE = /id_rsa/;
2465
2884
  SECRET_FILE_CREDENTIALS_RE = /credentials/;
2466
2885
  GIT_COMMIT_MERGE_RE = /git\s+(commit|merge|rebase|cherry-pick)\b/;
2467
- FAILPROOFAI_STATE_WRITE_RE = /\b(?:rm|unlink|shred|mv|truncate)\b[^;&|]*\.failproofai(?:\/|\b)/;
2886
+ FAILPROOFAI_STATE_PATH_RE = /\.failproofai(?:\/|\b)/;
2887
+ STATE_READ_COMMANDS = new Set([
2888
+ "cat",
2889
+ "bat",
2890
+ "less",
2891
+ "more",
2892
+ "head",
2893
+ "tail",
2894
+ "nl",
2895
+ "strings",
2896
+ "xxd",
2897
+ "od",
2898
+ "jq",
2899
+ "yq",
2900
+ "diff",
2901
+ "cmp",
2902
+ "wc",
2903
+ "sort",
2904
+ "uniq",
2905
+ "column",
2906
+ "awk",
2907
+ "cut",
2908
+ "ls",
2909
+ "dir",
2910
+ "tree",
2911
+ "stat",
2912
+ "file",
2913
+ "du",
2914
+ "df",
2915
+ "readlink",
2916
+ "realpath",
2917
+ "basename",
2918
+ "dirname",
2919
+ "pwd",
2920
+ "test",
2921
+ "find",
2922
+ "grep",
2923
+ "egrep",
2924
+ "fgrep",
2925
+ "rg",
2926
+ "ag",
2927
+ "ack",
2928
+ "md5sum",
2929
+ "sha1sum",
2930
+ "sha256sum",
2931
+ "shasum",
2932
+ "cksum",
2933
+ "cd",
2934
+ "pushd",
2935
+ "popd",
2936
+ "cp",
2937
+ "rsync",
2938
+ "install",
2939
+ "tee",
2940
+ "sed",
2941
+ "true",
2942
+ "false",
2943
+ ":",
2944
+ "echo",
2945
+ "printf",
2946
+ "[",
2947
+ "[[",
2948
+ "]",
2949
+ "]]",
2950
+ "test",
2951
+ "read",
2952
+ "for",
2953
+ "select",
2954
+ "case",
2955
+ "done",
2956
+ "fi",
2957
+ "esac"
2958
+ ]);
2959
+ SHELL_KEYWORD_PREFIXES = new Set(["do", "then", "else", "elif", "if", "while", "until", "!"]);
2960
+ LOOP_HEADER_RE = /(?:^|[\s(])(?:while|until|for|select)\s/;
2961
+ COPY_COMMANDS = new Set(["cp", "rsync", "install"]);
2962
+ OPERAND_WRITE_COMMANDS = new Set(["tee"]);
2963
+ SECOND_OPERAND_WRITERS = new Set(["uniq", "xxd"]);
2964
+ OUTPUT_FLAG_COMMANDS = {
2965
+ sort: ["-o", "--output"],
2966
+ tree: ["-o", "--output"],
2967
+ curl: ["-o", "--output", "--output-dir", "-D", "--dump-header", "--trace", "--trace-ascii"]
2968
+ };
2969
+ FIND_WRITE_ACTIONS = new Set(["-fprint", "-fprint0", "-fprintf", "-fls"]);
2970
+ SAFE_EXEC_COMMANDS = new Set([
2971
+ "cat",
2972
+ "bat",
2973
+ "head",
2974
+ "tail",
2975
+ "nl",
2976
+ "strings",
2977
+ "xxd",
2978
+ "od",
2979
+ "jq",
2980
+ "yq",
2981
+ "wc",
2982
+ "ls",
2983
+ "stat",
2984
+ "file",
2985
+ "du",
2986
+ "readlink",
2987
+ "realpath",
2988
+ "basename",
2989
+ "dirname",
2990
+ "grep",
2991
+ "egrep",
2992
+ "fgrep",
2993
+ "rg",
2994
+ "md5sum",
2995
+ "sha1sum",
2996
+ "sha256sum",
2997
+ "shasum",
2998
+ "cksum",
2999
+ "echo",
3000
+ "printf",
3001
+ "true",
3002
+ ":"
3003
+ ]);
3004
+ GIT_DESTRUCTIVE_SUBCOMMANDS = new Set([
3005
+ "clean",
3006
+ "rm",
3007
+ "restore",
3008
+ "checkout",
3009
+ "reset",
3010
+ "stash"
3011
+ ]);
3012
+ GIT_FLAGS_WITH_OPERANDS = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--exec-path"]);
3013
+ STATE_MENTION_COMMANDS = new Set(["git", "gh", "glab", "echo", "printf", "curl", "code", "open"]);
3014
+ FIND_EXEC_ACTIONS = new Set(["-exec", "-execdir", "-ok", "-okdir"]);
3015
+ PIPELINE_SEPARATORS = /\|\||&&|[;\n\r&]+/;
3016
+ VAR_REFERENCE_RE = /\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?/g;
3017
+ STATE_GLOB_CANDIDATES = [
3018
+ ".failproofai",
3019
+ "~/.failproofai",
3020
+ "$HOME/.failproofai",
3021
+ "${HOME}/.failproofai",
3022
+ "/root/.failproofai",
3023
+ "/home/u/.failproofai",
3024
+ "/Users/u/.failproofai"
3025
+ ];
3026
+ GLOB_DECOYS = [
3027
+ ".config",
3028
+ ".cache",
3029
+ ".local",
3030
+ ".git",
3031
+ ".npm",
3032
+ ".ssh",
3033
+ ".bashrc",
3034
+ "node_modules",
3035
+ "dist",
3036
+ "build",
3037
+ "target",
3038
+ "coverage",
3039
+ "tmp",
3040
+ "src",
3041
+ "~/.config",
3042
+ "~/.cache",
3043
+ "$HOME/.config",
3044
+ "${HOME}/.cache",
3045
+ "/home/u/.config",
3046
+ "/Users/u/.config",
3047
+ "/root/.cache",
3048
+ "/tmp/build",
3049
+ "a",
3050
+ "foo.txt",
3051
+ "test-failures"
3052
+ ];
3053
+ GLOB_METACHAR_RE = /[*?[{]|[@+!]\(/;
3054
+ REDIRECT_TARGET_RE = /\d*>{1,2}\|?\s*("[^"]*"|'[^']*'|[^\s;&|<>]+)/g;
2468
3055
  FAILPROOFAI_UNINSTALL_RE = /(?:npm\s+(?:uninstall|remove|un|r)\s.*failproofai|bun\s+remove\s.*failproofai|yarn\s+global\s+remove\s+failproofai|pnpm\s+(?:remove|uninstall|un)\s.*failproofai)/;
2469
3056
  GIT_AMEND_RE = /\bgit\s+commit\b.*--amend\b/;
2470
3057
  GIT_STASH_DROP_RE = /\bgit\s+stash\s+(?:drop|clear)\b/;
@@ -2558,87 +3145,416 @@ var init_builtin_policies = __esm(() => {
2558
3145
  }));
2559
3146
  });
2560
3147
 
2561
- // package.json
2562
- var version = "1.0.2-beta.8";
2563
- var init_package = () => {};
2564
-
2565
- // src/posthog-key.ts
2566
- var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
2567
-
2568
- // src/hooks/fp-config.ts
2569
- import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
2570
- function detectLayout() {
2571
- const raw = readVersionFile();
2572
- if (raw) {
2573
- if (raw.layout === LAYOUT_VERSION)
2574
- return { kind: "current", version: raw };
2575
- return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
3148
+ // src/hooks/cloud-managed-policies.ts
3149
+ import { createHash } from "node:crypto";
3150
+ import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, rmSync } from "node:fs";
3151
+ import { isAbsolute, relative, resolve as resolve4 } from "node:path";
3152
+ function cloudManagedPolicyRoot() {
3153
+ return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
3154
+ }
3155
+ function parseManifest(value) {
3156
+ if (!value || typeof value !== "object")
3157
+ throw new Error("active manifest is not an object");
3158
+ const raw = value;
3159
+ if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
3160
+ throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
2576
3161
  }
2577
- if (existsSync3(configFile())) {
2578
- const layoutThreePositions = [
2579
- legacy.authJson(),
2580
- legacy.nextAudit(),
2581
- legacy.auditSchedule()
2582
- ];
2583
- if (layoutThreePositions.some((p) => existsSync3(p)))
2584
- return { kind: "stale", found: 3 };
2585
- return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
3162
+ const deployment = raw.deployment ?? raw.generation;
3163
+ if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
3164
+ throw new Error("active manifest deployment is invalid");
2586
3165
  }
2587
- if (existsSync3(legacy.configToml()))
2588
- return { kind: "stale", found: 2 };
2589
- const layoutOneMarkers = [
2590
- legacy.policyConfig(),
2591
- legacy.cacheDir(),
2592
- legacy.launcherMarker(),
2593
- legacy.lastVersion(),
2594
- legacy.cloudCredentials(),
2595
- legacy.ingestCredentials(),
2596
- legacy.auditDashboard()
2597
- ];
2598
- return layoutOneMarkers.some((p) => existsSync3(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
3166
+ if (!Array.isArray(raw.policies))
3167
+ throw new Error("active manifest policies is not an array");
3168
+ const policies = raw.policies.map((p) => {
3169
+ const entry = p;
3170
+ return { ...entry, version: entry.version ?? entry.revision };
3171
+ });
3172
+ return { schemaVersion: raw.schemaVersion, deployment, policies };
2599
3173
  }
2600
- function readLegacyTomlVersion(text) {
2601
- const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
2602
- if (!Number.isFinite(layout))
2603
- return null;
2604
- const str = (key) => new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m").exec(text)?.[1];
2605
- return { layout, cli: str("cli") ?? "", daemon: str("daemon") };
3174
+ function resolveManagedPath(root, candidate) {
3175
+ if (!candidate || isAbsolute(candidate))
3176
+ throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
3177
+ const absolute = resolve4(root, candidate);
3178
+ const lexicalRelative = relative(root, absolute);
3179
+ if (lexicalRelative.startsWith("..") || isAbsolute(lexicalRelative)) {
3180
+ throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
3181
+ }
3182
+ const realRoot = realpathSync(root);
3183
+ const realFile = realpathSync(absolute);
3184
+ const physicalRelative = relative(realRoot, realFile);
3185
+ if (physicalRelative.startsWith("..") || isAbsolute(physicalRelative)) {
3186
+ throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
3187
+ }
3188
+ return realFile;
2606
3189
  }
2607
- function readVersionFile() {
2608
- let text;
3190
+ function readActiveCloudManagedPolicies() {
3191
+ const root = cloudManagedPolicyRoot();
3192
+ const activePath = resolve4(root, "active.json");
3193
+ if (!existsSync3(activePath))
3194
+ return [];
3195
+ let raw;
2609
3196
  try {
2610
- text = readFileSync2(versionFile(), "utf8");
2611
- } catch {
2612
- return null;
3197
+ raw = JSON.parse(readFileSync2(activePath, "utf8"));
3198
+ } catch (err) {
3199
+ throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
2613
3200
  }
2614
- try {
2615
- const parsed = JSON.parse(text);
2616
- const layout = Number(parsed.layout);
2617
- if (!Number.isFinite(layout))
2618
- return null;
3201
+ const manifest = parseManifest(raw);
3202
+ const seen = new Set;
3203
+ return manifest.policies.map((policy) => {
3204
+ if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
3205
+ throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
3206
+ }
3207
+ if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
3208
+ throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
3209
+ }
3210
+ if (!SHA256_RE.test(policy.sha256)) {
3211
+ throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
3212
+ }
3213
+ if (seen.has(policy.id))
3214
+ throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
3215
+ seen.add(policy.id);
3216
+ const path = resolveManagedPath(root, policy.path);
3217
+ const bytes = readFileSync2(path);
3218
+ const actual = createHash("sha256").update(bytes).digest("hex");
3219
+ if (actual !== policy.sha256) {
3220
+ throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
3221
+ }
3222
+ if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
3223
+ throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
3224
+ }
2619
3225
  return {
2620
- layout,
2621
- cli: typeof parsed.cli === "string" ? parsed.cli : "",
2622
- daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined
3226
+ id: policy.id,
3227
+ version: policy.version,
3228
+ effect: policy.effect ?? "enforce",
3229
+ sha256: policy.sha256,
3230
+ path,
3231
+ deployment: manifest.deployment
2623
3232
  };
2624
- } catch {
2625
- return readLegacyTomlVersion(text);
2626
- }
2627
- }
2628
- function readIntervalDays(raw) {
2629
- if (typeof raw !== "number" || !Number.isFinite(raw))
2630
- return DEFAULT_AUDIT_INTERVAL_DAYS;
2631
- const days = Math.floor(raw);
2632
- if (days < MIN_AUDIT_INTERVAL_DAYS)
2633
- return DEFAULT_AUDIT_INTERVAL_DAYS;
2634
- return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
3233
+ });
2635
3234
  }
2636
- function readSources(raw) {
2637
- if (!raw || typeof raw !== "object" || Array.isArray(raw))
2638
- return;
2639
- const out = {};
2640
- for (const [name, table] of Object.entries(raw)) {
2641
- if (!table || typeof table !== "object" || Array.isArray(table))
3235
+ var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
3236
+ var init_cloud_managed_policies = __esm(() => {
3237
+ init_fp_home();
3238
+ ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
3239
+ SHA256_RE = /^[a-f0-9]{64}$/;
3240
+ POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
3241
+ });
3242
+
3243
+ // src/hooks/pack-manifest.ts
3244
+ import { createHash as createHash2 } from "node:crypto";
3245
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
3246
+ import { resolve as resolve5 } from "node:path";
3247
+ function packsRoot() {
3248
+ return process.env.FAILPROOFAI_PACK_DIR ?? packsDir();
3249
+ }
3250
+ function parsePackIdentity(value) {
3251
+ if (typeof value.id !== "string" || !PACK_ID_RE.test(value.id)) {
3252
+ throw new Error(`unsafe pack id ${JSON.stringify(value.id)}`);
3253
+ }
3254
+ if (typeof value.version !== "string" || !PACK_VERSION_RE.test(value.version)) {
3255
+ throw new Error(`invalid version for pack ${value.id}`);
3256
+ }
3257
+ if (value.effect !== undefined && value.effect !== "enforce" && value.effect !== "observe") {
3258
+ throw new Error(`unknown effect ${JSON.stringify(value.effect)} for pack ${value.id}`);
3259
+ }
3260
+ const commit = typeof value.commit === "string" && PACK_COMMIT_RE.test(value.commit.trim().toLowerCase()) ? value.commit.trim().toLowerCase() : undefined;
3261
+ return {
3262
+ id: value.id,
3263
+ version: value.version,
3264
+ effect: value.effect ?? "enforce",
3265
+ ...commit ? { commit } : {}
3266
+ };
3267
+ }
3268
+ function hasInstalledPacks() {
3269
+ try {
3270
+ const raw = JSON.parse(readFileSync3(installedFilePath(), "utf8"));
3271
+ return Array.isArray(raw.packs) && raw.packs.length > 0;
3272
+ } catch {
3273
+ return false;
3274
+ }
3275
+ }
3276
+ function installedFilePath() {
3277
+ return process.env.FAILPROOFAI_PACK_DIR ? resolve5(process.env.FAILPROOFAI_PACK_DIR, "installed.json") : packsInstalledFile();
3278
+ }
3279
+ function parsePackPolicy(packId, value, index) {
3280
+ const where = `${packId} policy #${index}`;
3281
+ if (!value || typeof value !== "object")
3282
+ throw new Error(`${where} is not an object`);
3283
+ const raw = value;
3284
+ const name = raw.name;
3285
+ if (typeof name !== "string" || !PACK_POLICY_NAME_RE.test(name)) {
3286
+ throw new Error(`${where} has an unsafe name ${JSON.stringify(name)}`);
3287
+ }
3288
+ if ("alwaysOn" in raw) {
3289
+ throw new Error(`${where} declares alwaysOn, which packs may not set`);
3290
+ }
3291
+ if (typeof raw.description !== "string" || raw.description.length === 0) {
3292
+ throw new Error(`${where} is missing a description`);
3293
+ }
3294
+ if (typeof raw.category !== "string" || raw.category.length === 0) {
3295
+ throw new Error(`${where} is missing a category`);
3296
+ }
3297
+ if (typeof raw.defaultEnabled !== "boolean") {
3298
+ throw new Error(`${where} is missing defaultEnabled`);
3299
+ }
3300
+ if (!raw.match || typeof raw.match !== "object") {
3301
+ throw new Error(`${where} is missing a match`);
3302
+ }
3303
+ const matchShape = raw.match;
3304
+ for (const key of ["events", "toolNames"]) {
3305
+ const value2 = matchShape[key];
3306
+ if (value2 === undefined)
3307
+ continue;
3308
+ if (!Array.isArray(value2) || value2.some((e) => typeof e !== "string" || e.length === 0)) {
3309
+ throw new Error(`${where} has a match.${key} that is not a list of names`);
3310
+ }
3311
+ }
3312
+ return raw;
3313
+ }
3314
+ function parsePack(root, value) {
3315
+ if (!value || typeof value !== "object")
3316
+ throw new Error("pack entry is not an object");
3317
+ const raw = value;
3318
+ const identity = parsePackIdentity(raw);
3319
+ if (typeof raw.source !== "string" || raw.source.length === 0) {
3320
+ throw new Error(`pack ${raw.id} has no source`);
3321
+ }
3322
+ if (typeof raw.sha256 !== "string" || !SHA256_RE2.test(raw.sha256)) {
3323
+ throw new Error(`invalid SHA-256 for pack ${raw.id}`);
3324
+ }
3325
+ const effect = identity.effect;
3326
+ const path = resolveManagedPath(root, raw.entry);
3327
+ const actual = createHash2("sha256").update(readFileSync3(path)).digest("hex");
3328
+ if (actual !== raw.sha256) {
3329
+ throw new Error(`pack ${raw.id} failed integrity verification: expected ${raw.sha256}, got ${actual}`);
3330
+ }
3331
+ if (!Array.isArray(raw.policies))
3332
+ throw new Error(`pack ${raw.id} policies is not an array`);
3333
+ const policies = raw.policies.map((p, i) => parsePackPolicy(raw.id, p, i));
3334
+ const names = new Set;
3335
+ for (const p of policies) {
3336
+ if (names.has(p.name))
3337
+ throw new Error(`pack ${raw.id} declares ${p.name} twice`);
3338
+ names.add(p.name);
3339
+ }
3340
+ let enabled = null;
3341
+ if (raw.enabled !== undefined) {
3342
+ if (!Array.isArray(raw.enabled) || raw.enabled.some((n) => typeof n !== "string")) {
3343
+ throw new Error(`pack ${raw.id} enabled is not an array of names`);
3344
+ }
3345
+ enabled = raw.enabled.filter((n) => names.has(n));
3346
+ }
3347
+ let clis = null;
3348
+ if (raw.clis !== undefined) {
3349
+ if (!Array.isArray(raw.clis) || raw.clis.some((c) => typeof c !== "string")) {
3350
+ throw new Error(`pack ${raw.id} clis is not an array of agent names`);
3351
+ }
3352
+ clis = raw.clis;
3353
+ }
3354
+ return {
3355
+ clis,
3356
+ id: identity.id,
3357
+ version: identity.version,
3358
+ source: raw.source,
3359
+ path,
3360
+ sha256: raw.sha256,
3361
+ effect,
3362
+ policies,
3363
+ enabled
3364
+ };
3365
+ }
3366
+ function readInstalledPacks() {
3367
+ const manifestPath = installedFilePath();
3368
+ if (!existsSync4(manifestPath))
3369
+ return { packs: [], errors: [] };
3370
+ let raw;
3371
+ try {
3372
+ raw = JSON.parse(readFileSync3(manifestPath, "utf8"));
3373
+ } catch (err) {
3374
+ return {
3375
+ packs: [],
3376
+ errors: [{ id: null, reason: `unreadable pack manifest: ${errText(err)}` }]
3377
+ };
3378
+ }
3379
+ if (!raw || typeof raw !== "object") {
3380
+ return { packs: [], errors: [{ id: null, reason: "pack manifest is not an object" }] };
3381
+ }
3382
+ const manifest = raw;
3383
+ if (!ACCEPTED_PACK_SCHEMA_VERSIONS.includes(manifest.schemaVersion)) {
3384
+ return {
3385
+ packs: [],
3386
+ errors: [
3387
+ {
3388
+ id: null,
3389
+ reason: `unsupported pack manifest schema ${String(manifest.schemaVersion)} ` + `(supported: ${ACCEPTED_PACK_SCHEMA_VERSIONS.join(", ")})`
3390
+ }
3391
+ ]
3392
+ };
3393
+ }
3394
+ if (!Array.isArray(manifest.packs)) {
3395
+ return { packs: [], errors: [{ id: null, reason: "pack manifest packs is not an array" }] };
3396
+ }
3397
+ const root = packsRoot();
3398
+ const packs = [];
3399
+ const errors = [];
3400
+ const seen = new Set;
3401
+ for (const entry of manifest.packs) {
3402
+ const declaredId = entry && typeof entry === "object" && typeof entry.id === "string" ? entry.id : null;
3403
+ try {
3404
+ const pack = parsePack(root, entry);
3405
+ if (seen.has(pack.id))
3406
+ throw new Error(`duplicate pack id ${pack.id}`);
3407
+ seen.add(pack.id);
3408
+ packs.push(pack);
3409
+ } catch (err) {
3410
+ const rec = entry;
3411
+ const declaredEffect = rec && (rec.effect === "observe" || rec.effect === "enforce") ? rec.effect : "enforce";
3412
+ errors.push({
3413
+ id: declaredId,
3414
+ reason: errText(err),
3415
+ effect: declaredEffect,
3416
+ ...Array.isArray(rec?.policies) ? { declared: safeDeclared(rec.policies) } : {},
3417
+ ...Array.isArray(rec?.clis) && rec.clis.every((c) => typeof c === "string") ? { clis: rec.clis } : {}
3418
+ });
3419
+ }
3420
+ }
3421
+ return { packs, errors };
3422
+ }
3423
+ function safeDeclared(raw) {
3424
+ const out = [];
3425
+ for (const value of raw) {
3426
+ if (!value || typeof value !== "object")
3427
+ continue;
3428
+ const p = value;
3429
+ if (typeof p.name !== "string" || !p.match || typeof p.match !== "object")
3430
+ continue;
3431
+ out.push(p);
3432
+ }
3433
+ return out;
3434
+ }
3435
+ function errText(err) {
3436
+ return err instanceof Error ? err.message : String(err);
3437
+ }
3438
+ var ACCEPTED_PACK_SCHEMA_VERSIONS, SHA256_RE2, PACK_ID_RE, PACK_VERSION_RE, PACK_COMMIT_RE, PACK_POLICY_NAME_RE;
3439
+ var init_pack_manifest = __esm(() => {
3440
+ init_fp_home();
3441
+ init_cloud_managed_policies();
3442
+ ACCEPTED_PACK_SCHEMA_VERSIONS = [1];
3443
+ SHA256_RE2 = /^[a-f0-9]{64}$/;
3444
+ PACK_ID_RE = /^[A-Za-z0-9._-]{1,64}\/[A-Za-z0-9._-]{1,64}$/;
3445
+ PACK_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
3446
+ PACK_COMMIT_RE = /^[0-9a-f]{7,40}$/;
3447
+ PACK_POLICY_NAME_RE = /^[A-Za-z0-9._-]{1,128}$/;
3448
+ });
3449
+
3450
+ // src/hooks/pack-store.ts
3451
+ var MAX_ARTIFACT_BYTES, CORE_SOURCE = "FailproofAI/policies", RETIRED_CORE_ALIASES;
3452
+ var init_pack_store = __esm(() => {
3453
+ init_pack_manifest();
3454
+ MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
3455
+ RETIRED_CORE_ALIASES = new Set(["core", "failproofai", "official"]);
3456
+ });
3457
+
3458
+ // src/hooks/pack-param-key.ts
3459
+ function packPolicyParamKey(packId, policyName) {
3460
+ return `pack/${packId}/${policyName}`;
3461
+ }
3462
+ function parsePackPolicyName(canonicalName) {
3463
+ if (!canonicalName.startsWith("pack/"))
3464
+ return null;
3465
+ const rest = canonicalName.slice("pack/".length);
3466
+ const slash = rest.lastIndexOf("/");
3467
+ if (slash <= 0)
3468
+ return null;
3469
+ const idWithVersion = rest.slice(0, slash);
3470
+ const at2 = idWithVersion.lastIndexOf("@");
3471
+ return {
3472
+ packId: at2 > 0 ? idWithVersion.slice(0, at2) : idWithVersion,
3473
+ name: rest.slice(slash + 1)
3474
+ };
3475
+ }
3476
+
3477
+ // package.json
3478
+ var version = "1.0.2";
3479
+ var init_package = () => {};
3480
+
3481
+ // src/posthog-key.ts
3482
+ var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
3483
+
3484
+ // src/hooks/fp-config.ts
3485
+ import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
3486
+ function detectLayout() {
3487
+ const raw = readVersionFile();
3488
+ if (raw) {
3489
+ if (raw.layout === LAYOUT_VERSION)
3490
+ return { kind: "current", version: raw };
3491
+ return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
3492
+ }
3493
+ if (existsSync5(configFile())) {
3494
+ const layoutThreePositions = [
3495
+ legacy.authJson(),
3496
+ legacy.nextAudit(),
3497
+ legacy.auditSchedule()
3498
+ ];
3499
+ if (layoutThreePositions.some((p) => existsSync5(p)))
3500
+ return { kind: "stale", found: 3 };
3501
+ return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
3502
+ }
3503
+ if (existsSync5(legacy.configToml()))
3504
+ return { kind: "stale", found: 2 };
3505
+ const layoutOneMarkers = [
3506
+ legacy.policyConfig(),
3507
+ legacy.cacheDir(),
3508
+ legacy.launcherMarker(),
3509
+ legacy.lastVersion(),
3510
+ legacy.cloudCredentials(),
3511
+ legacy.ingestCredentials(),
3512
+ legacy.auditDashboard()
3513
+ ];
3514
+ return layoutOneMarkers.some((p) => existsSync5(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
3515
+ }
3516
+ function readLegacyTomlVersion(text) {
3517
+ const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
3518
+ if (!Number.isFinite(layout))
3519
+ return null;
3520
+ const str = (key) => new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m").exec(text)?.[1];
3521
+ return { layout, cli: str("cli") ?? "", daemon: str("daemon") };
3522
+ }
3523
+ function readVersionFile() {
3524
+ let text;
3525
+ try {
3526
+ text = readFileSync4(versionFile(), "utf8");
3527
+ } catch {
3528
+ return null;
3529
+ }
3530
+ try {
3531
+ const parsed = JSON.parse(text);
3532
+ const layout = Number(parsed.layout);
3533
+ if (!Number.isFinite(layout))
3534
+ return null;
3535
+ return {
3536
+ layout,
3537
+ cli: typeof parsed.cli === "string" ? parsed.cli : "",
3538
+ daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined
3539
+ };
3540
+ } catch {
3541
+ return readLegacyTomlVersion(text);
3542
+ }
3543
+ }
3544
+ function readIntervalDays(raw) {
3545
+ if (typeof raw !== "number" || !Number.isFinite(raw))
3546
+ return DEFAULT_AUDIT_INTERVAL_DAYS;
3547
+ const days = Math.floor(raw);
3548
+ if (days < MIN_AUDIT_INTERVAL_DAYS)
3549
+ return DEFAULT_AUDIT_INTERVAL_DAYS;
3550
+ return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
3551
+ }
3552
+ function readSources(raw) {
3553
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
3554
+ return;
3555
+ const out = {};
3556
+ for (const [name, table] of Object.entries(raw)) {
3557
+ if (!table || typeof table !== "object" || Array.isArray(table))
2642
3558
  continue;
2643
3559
  const entries = table.extra_paths;
2644
3560
  if (!Array.isArray(entries))
@@ -2651,7 +3567,7 @@ function readSources(raw) {
2651
3567
  }
2652
3568
  function readConfig() {
2653
3569
  try {
2654
- const parsed = JSON.parse(readFileSync2(configFile(), "utf8"));
3570
+ const parsed = JSON.parse(readFileSync4(configFile(), "utf8"));
2655
3571
  return projectConfig(parsed);
2656
3572
  } catch {
2657
3573
  return structuredClone(DEFAULT_CONFIG);
@@ -2871,12 +3787,26 @@ function appendHint(baseReason, hint) {
2871
3787
  return normalizedHint;
2872
3788
  return `${base}. ${normalizedHint}`;
2873
3789
  }
3790
+ function readPackPolicyParams(policyParams, packId, policyName) {
3791
+ if (!policyParams)
3792
+ return;
3793
+ const qualified = policyParams[packPolicyParamKey(packId, policyName)];
3794
+ if (qualified)
3795
+ return qualified;
3796
+ if (packId.toLowerCase() === CORE_SOURCE.toLowerCase())
3797
+ return policyParams[policyName];
3798
+ return;
3799
+ }
2874
3800
  function getConfigParamsFor(config, canonicalName) {
2875
3801
  if (!config?.policyParams)
2876
3802
  return;
2877
3803
  const canonicalParams = config.policyParams[canonicalName];
2878
3804
  if (canonicalParams)
2879
3805
  return canonicalParams;
3806
+ const packPolicy = parsePackPolicyName(canonicalName);
3807
+ if (packPolicy) {
3808
+ return readPackPolicyParams(config.policyParams, packPolicy.packId, packPolicy.name);
3809
+ }
2880
3810
  const defaultPrefix = `${DEFAULT_POLICY_NAMESPACE}/`;
2881
3811
  if (!canonicalName.startsWith(defaultPrefix))
2882
3812
  return;
@@ -3598,6 +4528,7 @@ You MUST complete the above action(s) NOW. Do NOT ask the user for confirmation
3598
4528
  return { exitCode: 0, stdout: "", stderr: "", policyName: null, reason: null, decision: "allow" };
3599
4529
  }
3600
4530
  var init_policy_evaluator = __esm(() => {
4531
+ init_pack_store();
3601
4532
  init_hook_logger();
3602
4533
  init_hook_telemetry();
3603
4534
  init_telemetry_id();
@@ -3629,9 +4560,9 @@ var init_custom_hooks_registry = __esm(() => {
3629
4560
  // src/hooks/loader-utils.ts
3630
4561
  import { readFile as readFile2, writeFile as writeFile2, unlink, access, mkdir, lstat } from "fs/promises";
3631
4562
  import { tmpdir } from "os";
3632
- import { resolve as resolve4, dirname as dirname2, relative, join as join3 } from "path";
4563
+ import { resolve as resolve6, dirname as dirname2, relative as relative2, join as join3 } from "path";
3633
4564
  import { pathToFileURL } from "url";
3634
- import { createHash } from "crypto";
4565
+ import { createHash as createHash3 } from "crypto";
3635
4566
  function isTmpArtifact(name) {
3636
4567
  return TMP_ARTIFACT_RE.test(name);
3637
4568
  }
@@ -3642,7 +4573,7 @@ async function sweepStaleTmpArtifacts(dir, now = Date.now()) {
3642
4573
  for (const name of await readdir(dir)) {
3643
4574
  if (!isTmpArtifact(name))
3644
4575
  continue;
3645
- const path2 = resolve4(dir, name);
4576
+ const path2 = resolve6(dir, name);
3646
4577
  try {
3647
4578
  const info = await stat(path2);
3648
4579
  if (now - info.mtimeMs < TMP_SWEEP_MIN_AGE_MS)
@@ -3665,14 +4596,14 @@ async function fileExists(path2) {
3665
4596
  async function findDistIndex() {
3666
4597
  const distPath = process.env.FAILPROOFAI_DIST_PATH;
3667
4598
  if (distPath) {
3668
- const candidate = resolve4(distPath, "index.js");
4599
+ const candidate = resolve6(distPath, "index.js");
3669
4600
  if (await fileExists(candidate))
3670
4601
  return candidate;
3671
4602
  }
3672
4603
  const candidates = [
3673
- resolve4(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
3674
- resolve4(process.cwd(), "dist", "index.js"),
3675
- resolve4(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
4604
+ resolve6(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
4605
+ resolve6(process.cwd(), "dist", "index.js"),
4606
+ resolve6(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
3676
4607
  ];
3677
4608
  for (const c of candidates) {
3678
4609
  if (await fileExists(c))
@@ -3681,8 +4612,8 @@ async function findDistIndex() {
3681
4612
  return null;
3682
4613
  }
3683
4614
  async function resolveLocalImport(fromDir, specifier) {
3684
- const base = resolve4(fromDir, specifier);
3685
- const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve4(base, "index.js")];
4615
+ const base = resolve6(fromDir, specifier);
4616
+ const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve6(base, "index.js")];
3686
4617
  for (const c of candidates) {
3687
4618
  if (await fileExists(c))
3688
4619
  return c;
@@ -3745,7 +4676,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SU
3745
4676
  let code;
3746
4677
  if (entrySha256 && filePath === entryPath) {
3747
4678
  const buf = await readFile2(filePath);
3748
- const actual = createHash("sha256").update(buf).digest("hex");
4679
+ const actual = createHash3("sha256").update(buf).digest("hex");
3749
4680
  if (actual !== entrySha256) {
3750
4681
  throw new Error(`cloud-managed policy failed integrity re-verification at load: expected ${entrySha256}, got ${actual}`);
3751
4682
  }
@@ -3774,7 +4705,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SU
3774
4705
  if (!visited.has(resolved2) && !queue.includes(resolved2)) {
3775
4706
  queue.push(resolved2);
3776
4707
  }
3777
- let relPath = relative(dir, resolved2 + tmpSuffix).split("\\").join("/");
4708
+ let relPath = relative2(dir, resolved2 + tmpSuffix).split("\\").join("/");
3778
4709
  if (!relPath.startsWith("."))
3779
4710
  relPath = "./" + relPath;
3780
4711
  rewrites.set(specifier, relPath);
@@ -3810,24 +4741,24 @@ var init_loader_utils = __esm(() => {
3810
4741
  });
3811
4742
 
3812
4743
  // src/hooks/custom-hooks-loader.ts
3813
- import { resolve as resolve5, isAbsolute, basename } from "node:path";
4744
+ import { resolve as resolve7, isAbsolute as isAbsolute2, basename } from "node:path";
3814
4745
  import { randomUUID } from "crypto";
3815
- import { existsSync as existsSync4, readdirSync } from "node:fs";
4746
+ import { existsSync as existsSync6, readdirSync } from "node:fs";
3816
4747
  import { readFile as readFile3 } from "node:fs/promises";
3817
4748
  import { pathToFileURL as pathToFileURL2 } from "node:url";
3818
- import { createHash as createHash2 } from "node:crypto";
4749
+ import { createHash as createHash4 } from "node:crypto";
3819
4750
  function discoverPolicyFiles(dir) {
3820
- if (!existsSync4(dir))
4751
+ if (!existsSync6(dir))
3821
4752
  return [];
3822
4753
  try {
3823
4754
  const entries = readdirSync(dir, { withFileTypes: true });
3824
- return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve5(dir, e.name));
4755
+ return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve7(dir, e.name));
3825
4756
  } catch {
3826
4757
  return [];
3827
4758
  }
3828
4759
  }
3829
4760
  function findSkippedPolicyFiles(dir) {
3830
- if (!existsSync4(dir))
4761
+ if (!existsSync6(dir))
3831
4762
  return [];
3832
4763
  try {
3833
4764
  return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts") && !isTmpArtifact(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
@@ -3902,7 +4833,7 @@ async function loadSingleFile(absPath, opts) {
3902
4833
  }
3903
4834
  }
3904
4835
  async function fingerprintTemporaryTree(tmpFiles, tmpSuffix) {
3905
- const hash = createHash2("sha256");
4836
+ const hash = createHash4("sha256");
3906
4837
  for (const tmpFile of [...tmpFiles].sort()) {
3907
4838
  hash.update(tmpFile.replaceAll(tmpSuffix, TMP_SUFFIX));
3908
4839
  const contents = await readFile3(tmpFile, "utf8");
@@ -3946,7 +4877,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3946
4877
  const loadedPaths = new Set;
3947
4878
  const cloudManagedByPath = new Map;
3948
4879
  for (const policy of opts?.cloudManagedPolicies ?? []) {
3949
- const key = resolve5(policy.path);
4880
+ const key = resolve7(policy.path);
3950
4881
  const existing = cloudManagedByPath.get(key);
3951
4882
  if (!existing) {
3952
4883
  cloudManagedByPath.set(key, policy);
@@ -3957,32 +4888,46 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3957
4888
  cloudManagedByPath.set(key, policy);
3958
4889
  }
3959
4890
  }
4891
+ const unionSelection = (a, b) => a === null || b === null ? null : [...new Set([...a, ...b])];
4892
+ const unionCatalog = (winner, other) => [
4893
+ ...winner,
4894
+ ...other.filter((p) => !winner.some((w) => w.name === p.name))
4895
+ ];
3960
4896
  const packByPath = new Map;
4897
+ const packIdsByPath = new Map;
3961
4898
  for (const pack of opts?.packs ?? []) {
3962
- const key = resolve5(pack.path);
4899
+ const key = resolve7(pack.path);
4900
+ packIdsByPath.set(key, [...packIdsByPath.get(key) ?? [], pack.id]);
3963
4901
  const existing = packByPath.get(key);
3964
4902
  if (!existing) {
3965
4903
  packByPath.set(key, pack);
3966
4904
  continue;
3967
4905
  }
3968
- hookLogWarn(`packs ${existing.id} and ${pack.id} have identical source, so they share one artifact ` + `and load as one pack; enforcing it if either asks to enforce`);
3969
- if (existing.effect !== "enforce" && pack.effect === "enforce") {
3970
- packByPath.set(key, pack);
3971
- }
4906
+ hookLogWarn(`packs ${existing.id} and ${pack.id} have identical source, so they share one artifact ` + `and load as one pack; enforcing it if either asks to enforce, and taking ` + `the union of what each has enabled and of the agents each guards`);
4907
+ const winner = existing.effect !== "enforce" && pack.effect === "enforce" ? pack : existing;
4908
+ const other = winner === existing ? pack : existing;
4909
+ packByPath.set(key, {
4910
+ ...winner,
4911
+ policies: unionCatalog(winner.policies, other.policies),
4912
+ enabled: unionSelection(winner.enabled, other.enabled),
4913
+ clis: unionSelection(winner.clis, other.clis)
4914
+ });
3972
4915
  }
3973
4916
  for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
3974
- const absPath = resolve5(projectRoot, customPoliciesPath);
4917
+ const absPath = resolve7(projectRoot, customPoliciesPath);
3975
4918
  const cloudManaged = cloudManagedByPath.get(absPath);
3976
4919
  const pack = packByPath.get(absPath);
3977
- if (existsSync4(absPath)) {
4920
+ if (existsSync6(absPath)) {
3978
4921
  if (!loadedPaths.has(absPath)) {
3979
4922
  loadedPaths.add(absPath);
3980
4923
  const hooksBefore = getCustomHooks().length;
3981
4924
  const failure = await loadSingleFile(absPath, {
3982
4925
  verifyEntrySha: cloudManaged?.sha256 ?? pack?.sha256
3983
4926
  });
3984
- if (failure && pack)
3985
- packFailures.set(pack.id, failure);
4927
+ if (failure && pack) {
4928
+ for (const id of packIdsByPath.get(absPath) ?? [pack.id])
4929
+ packFailures.set(id, failure);
4930
+ }
3986
4931
  for (const hook of getCustomHooks().slice(hooksBefore)) {
3987
4932
  const tagged = hook;
3988
4933
  if (cloudManaged) {
@@ -3999,12 +4944,15 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
3999
4944
  }
4000
4945
  } else {
4001
4946
  hookLogWarn(`custom policy path not found: ${absPath}`);
4002
- if (pack)
4003
- packFailures.set(pack.id, { type: "path_missing", reason: `path missing: ${absPath}` });
4947
+ if (pack) {
4948
+ const missing = { type: "path_missing", reason: `path missing: ${absPath}` };
4949
+ for (const id of packIdsByPath.get(absPath) ?? [pack.id])
4950
+ packFailures.set(id, missing);
4951
+ }
4004
4952
  }
4005
4953
  }
4006
4954
  const hooksBeforeConvention = getCustomHooks().length;
4007
- const projectDir = resolve5(projectRoot, ".failproofai", "policies");
4955
+ const projectDir = resolve7(projectRoot, ".failproofai", "policies");
4008
4956
  const projectScope = projectDir === customPoliciesDir() ? "user" : "project";
4009
4957
  if (conventionEnabled) {
4010
4958
  sweepStaleTmpArtifacts(projectDir);
@@ -4072,7 +5020,13 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
4072
5020
  hook.__conventionScope = hookNameToScope.get(hook.name) ?? "project";
4073
5021
  }
4074
5022
  }
4075
- return { hooks: allHooks, conventionSources, packFailures };
5023
+ const packAliases = new Map;
5024
+ for (const [path2, ids] of packIdsByPath) {
5025
+ const winner = packByPath.get(path2);
5026
+ if (winner && ids.length > 1)
5027
+ packAliases.set(winner.id, ids);
5028
+ }
5029
+ return { hooks: allHooks, conventionSources, packFailures, packAliases };
4076
5030
  }
4077
5031
  var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE;
4078
5032
  var init_custom_hooks_loader = __esm(() => {
@@ -4090,19 +5044,19 @@ var init_custom_hooks_loader = __esm(() => {
4090
5044
 
4091
5045
  // src/hooks/hook-activity-store.ts
4092
5046
  import {
4093
- readFileSync as readFileSync3,
5047
+ readFileSync as readFileSync5,
4094
5048
  writeFileSync as writeFileSync3,
4095
5049
  appendFileSync as appendFileSync2,
4096
5050
  renameSync as renameSync2,
4097
5051
  readdirSync as readdirSync2,
4098
5052
  mkdirSync as mkdirSync4,
4099
- existsSync as existsSync5,
5053
+ existsSync as existsSync7,
4100
5054
  statSync as statSync5,
4101
5055
  unlinkSync
4102
5056
  } from "node:fs";
4103
5057
  import { join as join4 } from "node:path";
4104
5058
  function ensureDir() {
4105
- if (!existsSync5(storeDirValue())) {
5059
+ if (!existsSync7(storeDirValue())) {
4106
5060
  mkdirSync4(storeDirValue(), { recursive: true });
4107
5061
  }
4108
5062
  }
@@ -4163,7 +5117,7 @@ function rotate(currentPath, countPath) {
4163
5117
  }
4164
5118
  function readCount(countPath) {
4165
5119
  try {
4166
- const n = parseInt(readFileSync3(countPath, "utf-8"), 10);
5120
+ const n = parseInt(readFileSync5(countPath, "utf-8"), 10);
4167
5121
  return isNaN(n) ? 0 : n;
4168
5122
  } catch {
4169
5123
  return 0;
@@ -4176,7 +5130,7 @@ function writeCount(countPath, n) {
4176
5130
  }
4177
5131
  function readStoredStats() {
4178
5132
  try {
4179
- return JSON.parse(readFileSync3(join4(storeDirValue(), STATS_FILE), "utf-8"));
5133
+ return JSON.parse(readFileSync5(join4(storeDirValue(), STATS_FILE), "utf-8"));
4180
5134
  } catch {
4181
5135
  return { totalEvents: 0, denyCount: 0, policyMap: {} };
4182
5136
  }
@@ -4927,7 +5881,7 @@ var init_cursor_projects = __esm(() => {
4927
5881
  });
4928
5882
 
4929
5883
  // lib/sqlite-reader.ts
4930
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
5884
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
4931
5885
  import initSqlJs from "sql.js/dist/sql-asm.js";
4932
5886
  async function tryNodeSqlite(dbPath) {
4933
5887
  try {
@@ -4954,7 +5908,7 @@ function loadSqlJs() {
4954
5908
  async function trySqlJs(dbPath) {
4955
5909
  try {
4956
5910
  const SQL = await loadSqlJs();
4957
- const db = new SQL.Database(readFileSync4(dbPath));
5911
+ const db = new SQL.Database(readFileSync6(dbPath));
4958
5912
  return {
4959
5913
  query(sql, params) {
4960
5914
  const stmt = db.prepare(sql);
@@ -4978,7 +5932,7 @@ async function trySqlJs(dbPath) {
4978
5932
  }
4979
5933
  }
4980
5934
  async function openSqliteReadonly(dbPath) {
4981
- if (!existsSync6(dbPath))
5935
+ if (!existsSync8(dbPath))
4982
5936
  return null;
4983
5937
  return await tryNodeSqlite(dbPath) ?? await trySqlJs(dbPath);
4984
5938
  }
@@ -5325,11 +6279,11 @@ var init_pi_projects = __esm(() => {
5325
6279
  // lib/hermes-profiles.ts
5326
6280
  import { readdirSync as readdirSync3 } from "node:fs";
5327
6281
  import { homedir as homedir10 } from "node:os";
5328
- import { basename as basename2, dirname as dirname3, join as join11, resolve as resolve6 } from "node:path";
6282
+ import { basename as basename2, dirname as dirname3, join as join11, resolve as resolve8 } from "node:path";
5329
6283
  function hermesRoot() {
5330
6284
  const env = (process.env.HERMES_HOME || "").trim();
5331
6285
  if (env) {
5332
- const home = resolve6(env);
6286
+ const home = resolve8(env);
5333
6287
  const parent = dirname3(home);
5334
6288
  if (basename2(parent) === PROFILES_DIR)
5335
6289
  return dirname3(parent);
@@ -5913,7 +6867,7 @@ __export(exports_openclaw_projects, {
5913
6867
  openClawProjectPath: () => openClawProjectPath,
5914
6868
  parseOpenClawProjectName: () => parseOpenClawProjectName
5915
6869
  });
5916
- import { readFileSync as readFileSync5 } from "node:fs";
6870
+ import { readFileSync as readFileSync7 } from "node:fs";
5917
6871
  import { join as join14 } from "node:path";
5918
6872
  function str(v) {
5919
6873
  return typeof v === "string" && v.length > 0 ? v : undefined;
@@ -5923,7 +6877,7 @@ function readSessionsIndex(agentId) {
5923
6877
  const indexPath = join14(openclawHome(), "agents", agentId, "sessions", "sessions.json");
5924
6878
  let raw;
5925
6879
  try {
5926
- raw = JSON.parse(readFileSync5(indexPath, "utf-8"));
6880
+ raw = JSON.parse(readFileSync7(indexPath, "utf-8"));
5927
6881
  } catch {
5928
6882
  return out;
5929
6883
  }
@@ -7278,7 +8232,7 @@ var init_goose_projects = __esm(() => {
7278
8232
 
7279
8233
  // lib/projects.ts
7280
8234
  import { readdir as readdir5, stat as stat4 } from "node:fs/promises";
7281
- import { join as join20, resolve as resolve7, sep } from "node:path";
8235
+ import { join as join20, resolve as resolve9, sep } from "node:path";
7282
8236
  async function getMtime(path2, label) {
7283
8237
  try {
7284
8238
  return (await stat4(path2)).mtime;
@@ -7430,8 +8384,8 @@ function resolveProjectPath(projectName) {
7430
8384
  throw new RangeError("Empty project name");
7431
8385
  if (/^[/\\]/.test(projectName))
7432
8386
  throw new RangeError("Absolute project name");
7433
- const projectsPath = resolve7(getClaudeProjectsPath());
7434
- const candidate = resolve7(join20(projectsPath, projectName));
8387
+ const projectsPath = resolve9(getClaudeProjectsPath());
8388
+ const candidate = resolve9(join20(projectsPath, projectName));
7435
8389
  if (!candidate.startsWith(projectsPath + sep)) {
7436
8390
  throw new RangeError("Project path escapes root");
7437
8391
  }
@@ -7478,7 +8432,7 @@ var init_projects = __esm(() => {
7478
8432
 
7479
8433
  // lib/resolve-subagent-path.ts
7480
8434
  import { access as access2 } from "fs/promises";
7481
- import { join as join21, relative as relative2 } from "path";
8435
+ import { join as join21, relative as relative3 } from "path";
7482
8436
  async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId) {
7483
8437
  const fileName = `agent-${agentId}.jsonl`;
7484
8438
  const candidatePaths = [
@@ -7487,7 +8441,7 @@ async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId
7487
8441
  join21(projectsPath, projectName, sessionId, "subagents", fileName)
7488
8442
  ];
7489
8443
  for (const candidatePath of candidatePaths) {
7490
- if (relative2(projectsPath, candidatePath).startsWith("..")) {
8444
+ if (relative3(projectsPath, candidatePath).startsWith("..")) {
7491
8445
  continue;
7492
8446
  }
7493
8447
  try {
@@ -7737,15 +8691,15 @@ var init_log_entries = __esm(() => {
7737
8691
  });
7738
8692
 
7739
8693
  // lib/codex-sessions.ts
7740
- import { readFileSync as readFileSync6, readdirSync as readdirSync7, existsSync as existsSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, statSync as statSync9 } from "node:fs";
8694
+ import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, statSync as statSync9 } from "node:fs";
7741
8695
  import { readFile as readFile11 } from "node:fs/promises";
7742
8696
  import { dirname as dirname4, join as join23 } from "node:path";
7743
8697
  import { homedir as homedir16 } from "node:os";
7744
8698
  function readCache() {
7745
8699
  try {
7746
- if (!existsSync7(CACHE_PATH))
8700
+ if (!existsSync9(CACHE_PATH))
7747
8701
  return {};
7748
- return JSON.parse(readFileSync6(CACHE_PATH, "utf-8"));
8702
+ return JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
7749
8703
  } catch {
7750
8704
  return {};
7751
8705
  }
@@ -7771,7 +8725,7 @@ function dirSearch(dir, sessionId) {
7771
8725
  function findCodexTranscript(sessionId) {
7772
8726
  const cache = readCache();
7773
8727
  const cached = cache[sessionId];
7774
- if (cached && existsSync7(cached))
8728
+ if (cached && existsSync9(cached))
7775
8729
  return cached;
7776
8730
  const root = join23(homedir16(), ".codex", "sessions");
7777
8731
  const today = new Date;
@@ -8025,7 +8979,7 @@ var init_codex_sessions = __esm(() => {
8025
8979
  });
8026
8980
 
8027
8981
  // src/hooks/resolve-permission-mode.ts
8028
- import { readFileSync as readFileSync7 } from "node:fs";
8982
+ import { readFileSync as readFileSync9 } from "node:fs";
8029
8983
  function resolvePermissionMode(integration, parsed, sessionId) {
8030
8984
  if (integration === "claude") {
8031
8985
  return parsed.permission_mode ?? "default";
@@ -8040,7 +8994,7 @@ function resolveCodexMode(sessionId) {
8040
8994
  const path2 = findCodexTranscript(sessionId);
8041
8995
  if (!path2)
8042
8996
  return;
8043
- for (const line of readFileSync7(path2, "utf-8").split(`
8997
+ for (const line of readFileSync9(path2, "utf-8").split(`
8044
8998
  `)) {
8045
8999
  if (!line.includes("turn_context"))
8046
9000
  continue;
@@ -8065,9 +9019,9 @@ var init_resolve_permission_mode = __esm(() => {
8065
9019
  });
8066
9020
 
8067
9021
  // lib/copilot-sessions.ts
8068
- import { readFileSync as readFileSync8, readdirSync as readdirSync8, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
9022
+ import { readFileSync as readFileSync10, readdirSync as readdirSync8, existsSync as existsSync10, statSync as statSync10 } from "node:fs";
8069
9023
  import { readFile as readFile12 } from "node:fs/promises";
8070
- import { join as join24, resolve as resolve8, sep as sep2 } from "node:path";
9024
+ import { join as join24, resolve as resolve10, sep as sep2 } from "node:path";
8071
9025
  import { homedir as homedir17 } from "node:os";
8072
9026
  function getCopilotHome() {
8073
9027
  return process.env.COPILOT_HOME || join24(homedir17(), ".copilot");
@@ -8078,8 +9032,8 @@ function getCopilotSessionStateRoot2() {
8078
9032
  function getCopilotSessionDir(sessionId) {
8079
9033
  if (!sessionId)
8080
9034
  return null;
8081
- const root = resolve8(getCopilotSessionStateRoot2());
8082
- const candidate = resolve8(root, sessionId);
9035
+ const root = resolve10(getCopilotSessionStateRoot2());
9036
+ const candidate = resolve10(root, sessionId);
8083
9037
  if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
8084
9038
  return null;
8085
9039
  return candidate;
@@ -8094,17 +9048,17 @@ function findCopilotTranscript(sessionId) {
8094
9048
  const candidate = resolveSessionFile(sessionId, "events.jsonl");
8095
9049
  if (!candidate)
8096
9050
  return null;
8097
- return existsSync8(candidate) ? candidate : null;
9051
+ return existsSync10(candidate) ? candidate : null;
8098
9052
  }
8099
9053
  function findCopilotWorkspace(sessionId) {
8100
9054
  const candidate = resolveSessionFile(sessionId, "workspace.yaml");
8101
9055
  if (!candidate)
8102
9056
  return null;
8103
- return existsSync8(candidate) ? candidate : null;
9057
+ return existsSync10(candidate) ? candidate : null;
8104
9058
  }
8105
9059
  function readYamlScalar(path2, key) {
8106
9060
  try {
8107
- const text = readFileSync8(path2, "utf-8");
9061
+ const text = readFileSync10(path2, "utf-8");
8108
9062
  const re = new RegExp(`^${key}\\s*:\\s*(.+?)\\s*$`, "m");
8109
9063
  const m = text.match(re);
8110
9064
  if (!m)
@@ -8282,9 +9236,9 @@ var init_copilot_sessions = __esm(() => {
8282
9236
  });
8283
9237
 
8284
9238
  // lib/cursor-sessions.ts
8285
- import { readFileSync as readFileSync9, readdirSync as readdirSync9, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
9239
+ import { readFileSync as readFileSync11, readdirSync as readdirSync9, existsSync as existsSync11, statSync as statSync11 } from "node:fs";
8286
9240
  import { readFile as readFile13 } from "node:fs/promises";
8287
- import { basename as basename3, join as join25, resolve as resolve9, sep as sep3 } from "node:path";
9241
+ import { basename as basename3, join as join25, resolve as resolve11, sep as sep3 } from "node:path";
8288
9242
  import { homedir as homedir18 } from "node:os";
8289
9243
  function getCursorHome2() {
8290
9244
  return process.env.CURSOR_HOME || join25(homedir18(), ".cursor");
@@ -8292,8 +9246,8 @@ function getCursorHome2() {
8292
9246
  function getCursorSessionDir(sessionId) {
8293
9247
  if (!sessionId)
8294
9248
  return null;
8295
- const home = resolve9(getCursorHome2());
8296
- const projectsRoot = resolve9(home, NEW_PROJECTS_DIR2);
9249
+ const home = resolve11(getCursorHome2());
9250
+ const projectsRoot = resolve11(home, NEW_PROJECTS_DIR2);
8297
9251
  let projectEntries = [];
8298
9252
  try {
8299
9253
  projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
@@ -8301,19 +9255,19 @@ function getCursorSessionDir(sessionId) {
8301
9255
  for (const entry of projectEntries) {
8302
9256
  if (!entry.isDirectory())
8303
9257
  continue;
8304
- const candidate = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
8305
- const transcriptRoot = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
9258
+ const candidate = resolve11(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
9259
+ const transcriptRoot = resolve11(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
8306
9260
  if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
8307
9261
  continue;
8308
- if (existsSync9(candidate))
9262
+ if (existsSync11(candidate))
8309
9263
  return candidate;
8310
9264
  }
8311
9265
  for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
8312
- const root = resolve9(home, sub);
8313
- const candidate = resolve9(root, sessionId);
9266
+ const root = resolve11(home, sub);
9267
+ const candidate = resolve11(root, sessionId);
8314
9268
  if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
8315
9269
  continue;
8316
- if (existsSync9(candidate))
9270
+ if (existsSync11(candidate))
8317
9271
  return candidate;
8318
9272
  }
8319
9273
  return null;
@@ -8323,11 +9277,11 @@ function findCursorTranscript(sessionId) {
8323
9277
  if (!dir)
8324
9278
  return null;
8325
9279
  const newCandidate = join25(dir, `${basename3(dir)}.jsonl`);
8326
- if (existsSync9(newCandidate))
9280
+ if (existsSync11(newCandidate))
8327
9281
  return newCandidate;
8328
9282
  for (const name of LEGACY_TRANSCRIPT_FILE_CANDIDATES2) {
8329
9283
  const candidate = join25(dir, name);
8330
- if (existsSync9(candidate))
9284
+ if (existsSync11(candidate))
8331
9285
  return candidate;
8332
9286
  }
8333
9287
  return null;
@@ -8538,9 +9492,9 @@ var init_cursor_sessions = __esm(() => {
8538
9492
  });
8539
9493
 
8540
9494
  // lib/pi-sessions.ts
8541
- import { readFileSync as readFileSync10, readdirSync as readdirSync10, existsSync as existsSync10, statSync as statSync12 } from "node:fs";
9495
+ import { readFileSync as readFileSync12, readdirSync as readdirSync10, existsSync as existsSync12, statSync as statSync12 } from "node:fs";
8542
9496
  import { readFile as readFile14 } from "node:fs/promises";
8543
- import { join as join26, resolve as resolve10, sep as sep4 } from "node:path";
9497
+ import { join as join26, resolve as resolve12, sep as sep4 } from "node:path";
8544
9498
  import { homedir as homedir19 } from "node:os";
8545
9499
  function getPiSessionStateRoot() {
8546
9500
  return process.env.PI_SESSIONS_DIR || join26(homedir19(), ".pi", "agent", "sessions");
@@ -8551,7 +9505,7 @@ function isSafeSessionId(sessionId) {
8551
9505
  function findPiTranscript(sessionId) {
8552
9506
  if (!isSafeSessionId(sessionId))
8553
9507
  return null;
8554
- const root = resolve10(getPiSessionStateRoot());
9508
+ const root = resolve12(getPiSessionStateRoot());
8555
9509
  let cwdDirs;
8556
9510
  try {
8557
9511
  cwdDirs = readdirSync10(root);
@@ -8559,7 +9513,7 @@ function findPiTranscript(sessionId) {
8559
9513
  return null;
8560
9514
  }
8561
9515
  for (const cwdDir of cwdDirs) {
8562
- const cwdPath = resolve10(root, cwdDir);
9516
+ const cwdPath = resolve12(root, cwdDir);
8563
9517
  if (!cwdPath.startsWith(`${root}${sep4}`))
8564
9518
  continue;
8565
9519
  let files;
@@ -8572,10 +9526,10 @@ function findPiTranscript(sessionId) {
8572
9526
  const m = SESSION_FILE_RE2.exec(f);
8573
9527
  if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
8574
9528
  continue;
8575
- const candidate = resolve10(cwdPath, f);
9529
+ const candidate = resolve12(cwdPath, f);
8576
9530
  if (!candidate.startsWith(`${cwdPath}${sep4}`))
8577
9531
  continue;
8578
- if (existsSync10(candidate))
9532
+ if (existsSync12(candidate))
8579
9533
  return candidate;
8580
9534
  }
8581
9535
  }
@@ -8709,404 +9663,123 @@ async function parsePiLog(fileContent, source = "session") {
8709
9663
  entries.push({
8710
9664
  type: "system",
8711
9665
  ...baseEntry(rawCopy, timestamp, date, source),
8712
- raw: rawCopy
8713
- });
8714
- continue;
8715
- }
8716
- entries.push({
8717
- type: "system",
8718
- ...baseEntry(rawCopy, timestamp, date, source),
8719
- raw: rawCopy
8720
- });
8721
- }
8722
- if (entries.length > 500)
8723
- await new Promise((r) => setImmediate(r));
8724
- entries.sort((a, b) => a.timestampMs - b.timestampMs);
8725
- return { entries, rawLines, cwd };
8726
- }
8727
- async function getPiSessionLog(sessionId) {
8728
- const filePath = findPiTranscript(sessionId);
8729
- if (!filePath)
8730
- return null;
8731
- let fileContent;
8732
- try {
8733
- fileContent = await readFile14(filePath, "utf-8");
8734
- } catch {
8735
- return null;
8736
- }
8737
- let parsed;
8738
- try {
8739
- parsed = await parsePiLog(fileContent, "session");
8740
- } catch {
8741
- return null;
8742
- }
8743
- return {
8744
- entries: parsed.entries,
8745
- rawLines: parsed.rawLines,
8746
- cwd: parsed.cwd,
8747
- filePath
8748
- };
8749
- }
8750
- var UUID_RE, SESSION_FILE_RE2, getCachedPiSessionLog;
8751
- var init_pi_sessions = __esm(() => {
8752
- init_log_entries();
8753
- UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8754
- SESSION_FILE_RE2 = /^[\d-]+T[\d-]+Z_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
8755
- getCachedPiSessionLog = runtimeCache((sessionId) => getPiSessionLog(sessionId), 60, { maxSize: 50 });
8756
- });
8757
-
8758
- // src/hooks/resolve-transcript-path.ts
8759
- function resolveTranscriptPath(integration, parsed, sessionId) {
8760
- const stdinPath = typeof parsed.transcript_path === "string" ? parsed.transcript_path : undefined;
8761
- if (stdinPath)
8762
- return stdinPath;
8763
- if (typeof sessionId !== "string" || sessionId.length === 0)
8764
- return;
8765
- switch (integration) {
8766
- case "claude":
8767
- return;
8768
- case "codex":
8769
- return findCodexTranscript(sessionId) ?? undefined;
8770
- case "copilot":
8771
- return findCopilotTranscript(sessionId) ?? undefined;
8772
- case "cursor":
8773
- return findCursorTranscript(sessionId) ?? undefined;
8774
- case "pi":
8775
- return findPiTranscript(sessionId) ?? undefined;
8776
- case "factory":
8777
- return findFactoryTranscript(sessionId) ?? undefined;
8778
- case "antigravity":
8779
- return findAntigravityTranscript(sessionId) ?? undefined;
8780
- case "devin":
8781
- return `devin-db://${sessionId}`;
8782
- case "goose":
8783
- return `goose-db://${sessionId}`;
8784
- case "opencode":
8785
- return `opencode-db://${sessionId}`;
8786
- case "hermes":
8787
- return;
8788
- case "openclaw":
8789
- return;
8790
- default:
8791
- return;
8792
- }
8793
- }
8794
- var init_resolve_transcript_path = __esm(() => {
8795
- init_codex_sessions();
8796
- init_copilot_sessions();
8797
- init_cursor_sessions();
8798
- init_pi_sessions();
8799
- init_factory_sessions();
8800
- init_antigravity_sessions();
8801
- });
8802
- // src/hooks/cloud-managed-policies.ts
8803
- import { createHash as createHash3 } from "node:crypto";
8804
- import { existsSync as existsSync11, readFileSync as readFileSync11, realpathSync, rmSync } from "node:fs";
8805
- import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve11 } from "node:path";
8806
- function cloudManagedPolicyRoot() {
8807
- return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
8808
- }
8809
- function parseManifest(value) {
8810
- if (!value || typeof value !== "object")
8811
- throw new Error("active manifest is not an object");
8812
- const raw = value;
8813
- if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
8814
- throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
8815
- }
8816
- const deployment = raw.deployment ?? raw.generation;
8817
- if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
8818
- throw new Error("active manifest deployment is invalid");
8819
- }
8820
- if (!Array.isArray(raw.policies))
8821
- throw new Error("active manifest policies is not an array");
8822
- const policies = raw.policies.map((p) => {
8823
- const entry = p;
8824
- return { ...entry, version: entry.version ?? entry.revision };
8825
- });
8826
- return { schemaVersion: raw.schemaVersion, deployment, policies };
8827
- }
8828
- function resolveManagedPath(root, candidate) {
8829
- if (!candidate || isAbsolute2(candidate))
8830
- throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
8831
- const absolute = resolve11(root, candidate);
8832
- const lexicalRelative = relative3(root, absolute);
8833
- if (lexicalRelative.startsWith("..") || isAbsolute2(lexicalRelative)) {
8834
- throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
8835
- }
8836
- const realRoot = realpathSync(root);
8837
- const realFile = realpathSync(absolute);
8838
- const physicalRelative = relative3(realRoot, realFile);
8839
- if (physicalRelative.startsWith("..") || isAbsolute2(physicalRelative)) {
8840
- throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
8841
- }
8842
- return realFile;
8843
- }
8844
- function readActiveCloudManagedPolicies() {
8845
- const root = cloudManagedPolicyRoot();
8846
- const activePath = resolve11(root, "active.json");
8847
- if (!existsSync11(activePath))
8848
- return [];
8849
- let raw;
8850
- try {
8851
- raw = JSON.parse(readFileSync11(activePath, "utf8"));
8852
- } catch (err) {
8853
- throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
8854
- }
8855
- const manifest = parseManifest(raw);
8856
- const seen = new Set;
8857
- return manifest.policies.map((policy) => {
8858
- if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
8859
- throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
8860
- }
8861
- if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
8862
- throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
8863
- }
8864
- if (!SHA256_RE.test(policy.sha256)) {
8865
- throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
8866
- }
8867
- if (seen.has(policy.id))
8868
- throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
8869
- seen.add(policy.id);
8870
- const path2 = resolveManagedPath(root, policy.path);
8871
- const bytes = readFileSync11(path2);
8872
- const actual = createHash3("sha256").update(bytes).digest("hex");
8873
- if (actual !== policy.sha256) {
8874
- throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
8875
- }
8876
- if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
8877
- throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
8878
- }
8879
- return {
8880
- id: policy.id,
8881
- version: policy.version,
8882
- effect: policy.effect ?? "enforce",
8883
- sha256: policy.sha256,
8884
- path: path2,
8885
- deployment: manifest.deployment
8886
- };
8887
- });
8888
- }
8889
- var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
8890
- var init_cloud_managed_policies = __esm(() => {
8891
- init_fp_home();
8892
- ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
8893
- SHA256_RE = /^[a-f0-9]{64}$/;
8894
- POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
8895
- });
8896
-
8897
- // src/hooks/pack-manifest.ts
8898
- import { createHash as createHash4 } from "node:crypto";
8899
- import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
8900
- import { resolve as resolve12 } from "node:path";
8901
- function packsRoot() {
8902
- return process.env.FAILPROOFAI_PACK_DIR ?? packsDir();
8903
- }
8904
- function parsePackIdentity(value) {
8905
- if (typeof value.id !== "string" || !PACK_ID_RE.test(value.id)) {
8906
- throw new Error(`unsafe pack id ${JSON.stringify(value.id)}`);
8907
- }
8908
- if (typeof value.version !== "string" || !PACK_VERSION_RE.test(value.version)) {
8909
- throw new Error(`invalid version for pack ${value.id}`);
8910
- }
8911
- if (value.effect !== undefined && value.effect !== "enforce" && value.effect !== "observe") {
8912
- throw new Error(`unknown effect ${JSON.stringify(value.effect)} for pack ${value.id}`);
9666
+ raw: rawCopy
9667
+ });
9668
+ continue;
9669
+ }
9670
+ entries.push({
9671
+ type: "system",
9672
+ ...baseEntry(rawCopy, timestamp, date, source),
9673
+ raw: rawCopy
9674
+ });
8913
9675
  }
8914
- const commit = typeof value.commit === "string" && PACK_COMMIT_RE.test(value.commit.trim().toLowerCase()) ? value.commit.trim().toLowerCase() : undefined;
8915
- return {
8916
- id: value.id,
8917
- version: value.version,
8918
- effect: value.effect ?? "enforce",
8919
- ...commit ? { commit } : {}
8920
- };
9676
+ if (entries.length > 500)
9677
+ await new Promise((r) => setImmediate(r));
9678
+ entries.sort((a, b) => a.timestampMs - b.timestampMs);
9679
+ return { entries, rawLines, cwd };
8921
9680
  }
8922
- function hasInstalledPacks() {
9681
+ async function getPiSessionLog(sessionId) {
9682
+ const filePath = findPiTranscript(sessionId);
9683
+ if (!filePath)
9684
+ return null;
9685
+ let fileContent;
8923
9686
  try {
8924
- const raw = JSON.parse(readFileSync12(installedFilePath(), "utf8"));
8925
- return Array.isArray(raw.packs) && raw.packs.length > 0;
9687
+ fileContent = await readFile14(filePath, "utf-8");
8926
9688
  } catch {
8927
- return false;
8928
- }
8929
- }
8930
- function installedFilePath() {
8931
- return process.env.FAILPROOFAI_PACK_DIR ? resolve12(process.env.FAILPROOFAI_PACK_DIR, "installed.json") : packsInstalledFile();
8932
- }
8933
- function parsePackPolicy(packId, value, index) {
8934
- const where = `${packId} policy #${index}`;
8935
- if (!value || typeof value !== "object")
8936
- throw new Error(`${where} is not an object`);
8937
- const raw = value;
8938
- const name = raw.name;
8939
- if (typeof name !== "string" || !PACK_POLICY_NAME_RE.test(name)) {
8940
- throw new Error(`${where} has an unsafe name ${JSON.stringify(name)}`);
8941
- }
8942
- if ("alwaysOn" in raw) {
8943
- throw new Error(`${where} declares alwaysOn, which packs may not set`);
8944
- }
8945
- if (typeof raw.description !== "string" || raw.description.length === 0) {
8946
- throw new Error(`${where} is missing a description`);
8947
- }
8948
- if (typeof raw.category !== "string" || raw.category.length === 0) {
8949
- throw new Error(`${where} is missing a category`);
8950
- }
8951
- if (typeof raw.defaultEnabled !== "boolean") {
8952
- throw new Error(`${where} is missing defaultEnabled`);
8953
- }
8954
- if (!raw.match || typeof raw.match !== "object") {
8955
- throw new Error(`${where} is missing a match`);
8956
- }
8957
- return raw;
8958
- }
8959
- function parsePack(root, value) {
8960
- if (!value || typeof value !== "object")
8961
- throw new Error("pack entry is not an object");
8962
- const raw = value;
8963
- const identity = parsePackIdentity(raw);
8964
- if (typeof raw.source !== "string" || raw.source.length === 0) {
8965
- throw new Error(`pack ${raw.id} has no source`);
8966
- }
8967
- if (typeof raw.sha256 !== "string" || !SHA256_RE2.test(raw.sha256)) {
8968
- throw new Error(`invalid SHA-256 for pack ${raw.id}`);
8969
- }
8970
- const effect = identity.effect;
8971
- const path2 = resolveManagedPath(root, raw.entry);
8972
- const actual = createHash4("sha256").update(readFileSync12(path2)).digest("hex");
8973
- if (actual !== raw.sha256) {
8974
- throw new Error(`pack ${raw.id} failed integrity verification: expected ${raw.sha256}, got ${actual}`);
8975
- }
8976
- if (!Array.isArray(raw.policies))
8977
- throw new Error(`pack ${raw.id} policies is not an array`);
8978
- const policies = raw.policies.map((p, i) => parsePackPolicy(raw.id, p, i));
8979
- const names = new Set;
8980
- for (const p of policies) {
8981
- if (names.has(p.name))
8982
- throw new Error(`pack ${raw.id} declares ${p.name} twice`);
8983
- names.add(p.name);
8984
- }
8985
- let enabled = null;
8986
- if (raw.enabled !== undefined) {
8987
- if (!Array.isArray(raw.enabled) || raw.enabled.some((n) => typeof n !== "string")) {
8988
- throw new Error(`pack ${raw.id} enabled is not an array of names`);
8989
- }
8990
- enabled = raw.enabled.filter((n) => names.has(n));
9689
+ return null;
8991
9690
  }
8992
- let clis = null;
8993
- if (raw.clis !== undefined) {
8994
- if (!Array.isArray(raw.clis) || raw.clis.some((c) => typeof c !== "string")) {
8995
- throw new Error(`pack ${raw.id} clis is not an array of agent names`);
8996
- }
8997
- clis = raw.clis;
9691
+ let parsed;
9692
+ try {
9693
+ parsed = await parsePiLog(fileContent, "session");
9694
+ } catch {
9695
+ return null;
8998
9696
  }
8999
9697
  return {
9000
- clis,
9001
- id: identity.id,
9002
- version: identity.version,
9003
- source: raw.source,
9004
- path: path2,
9005
- sha256: raw.sha256,
9006
- effect,
9007
- policies,
9008
- enabled
9698
+ entries: parsed.entries,
9699
+ rawLines: parsed.rawLines,
9700
+ cwd: parsed.cwd,
9701
+ filePath
9009
9702
  };
9010
9703
  }
9011
- function readInstalledPacks() {
9012
- const manifestPath = installedFilePath();
9013
- if (!existsSync12(manifestPath))
9014
- return { packs: [], errors: [] };
9015
- let raw;
9016
- try {
9017
- raw = JSON.parse(readFileSync12(manifestPath, "utf8"));
9018
- } catch (err) {
9019
- return {
9020
- packs: [],
9021
- errors: [{ id: null, reason: `unreadable pack manifest: ${errText(err)}` }]
9022
- };
9023
- }
9024
- if (!raw || typeof raw !== "object") {
9025
- return { packs: [], errors: [{ id: null, reason: "pack manifest is not an object" }] };
9026
- }
9027
- const manifest = raw;
9028
- if (!ACCEPTED_PACK_SCHEMA_VERSIONS.includes(manifest.schemaVersion)) {
9029
- return {
9030
- packs: [],
9031
- errors: [
9032
- {
9033
- id: null,
9034
- reason: `unsupported pack manifest schema ${String(manifest.schemaVersion)} ` + `(supported: ${ACCEPTED_PACK_SCHEMA_VERSIONS.join(", ")})`
9035
- }
9036
- ]
9037
- };
9038
- }
9039
- if (!Array.isArray(manifest.packs)) {
9040
- return { packs: [], errors: [{ id: null, reason: "pack manifest packs is not an array" }] };
9041
- }
9042
- const root = packsRoot();
9043
- const packs = [];
9044
- const errors = [];
9045
- const seen = new Set;
9046
- for (const entry of manifest.packs) {
9047
- const declaredId = entry && typeof entry === "object" && typeof entry.id === "string" ? entry.id : null;
9048
- try {
9049
- const pack = parsePack(root, entry);
9050
- if (seen.has(pack.id))
9051
- throw new Error(`duplicate pack id ${pack.id}`);
9052
- seen.add(pack.id);
9053
- packs.push(pack);
9054
- } catch (err) {
9055
- const rec = entry;
9056
- const declaredEffect = rec && (rec.effect === "observe" || rec.effect === "enforce") ? rec.effect : "enforce";
9057
- errors.push({
9058
- id: declaredId,
9059
- reason: errText(err),
9060
- effect: declaredEffect,
9061
- ...Array.isArray(rec?.policies) ? { declared: safeDeclared(rec.policies) } : {}
9062
- });
9063
- }
9064
- }
9065
- return { packs, errors };
9066
- }
9067
- function safeDeclared(raw) {
9068
- const out = [];
9069
- for (const value of raw) {
9070
- if (!value || typeof value !== "object")
9071
- continue;
9072
- const p = value;
9073
- if (typeof p.name !== "string" || !p.match || typeof p.match !== "object")
9074
- continue;
9075
- out.push(p);
9704
+ var UUID_RE, SESSION_FILE_RE2, getCachedPiSessionLog;
9705
+ var init_pi_sessions = __esm(() => {
9706
+ init_log_entries();
9707
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9708
+ SESSION_FILE_RE2 = /^[\d-]+T[\d-]+Z_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
9709
+ getCachedPiSessionLog = runtimeCache((sessionId) => getPiSessionLog(sessionId), 60, { maxSize: 50 });
9710
+ });
9711
+
9712
+ // src/hooks/resolve-transcript-path.ts
9713
+ function resolveTranscriptPath(integration, parsed, sessionId) {
9714
+ const stdinPath = typeof parsed.transcript_path === "string" ? parsed.transcript_path : undefined;
9715
+ if (stdinPath)
9716
+ return stdinPath;
9717
+ if (typeof sessionId !== "string" || sessionId.length === 0)
9718
+ return;
9719
+ switch (integration) {
9720
+ case "claude":
9721
+ return;
9722
+ case "codex":
9723
+ return findCodexTranscript(sessionId) ?? undefined;
9724
+ case "copilot":
9725
+ return findCopilotTranscript(sessionId) ?? undefined;
9726
+ case "cursor":
9727
+ return findCursorTranscript(sessionId) ?? undefined;
9728
+ case "pi":
9729
+ return findPiTranscript(sessionId) ?? undefined;
9730
+ case "factory":
9731
+ return findFactoryTranscript(sessionId) ?? undefined;
9732
+ case "antigravity":
9733
+ return findAntigravityTranscript(sessionId) ?? undefined;
9734
+ case "devin":
9735
+ return `devin-db://${sessionId}`;
9736
+ case "goose":
9737
+ return `goose-db://${sessionId}`;
9738
+ case "opencode":
9739
+ return `opencode-db://${sessionId}`;
9740
+ case "hermes":
9741
+ return;
9742
+ case "openclaw":
9743
+ return;
9744
+ default:
9745
+ return;
9076
9746
  }
9077
- return out;
9078
- }
9079
- function errText(err) {
9080
- return err instanceof Error ? err.message : String(err);
9081
9747
  }
9082
- var ACCEPTED_PACK_SCHEMA_VERSIONS, SHA256_RE2, PACK_ID_RE, PACK_VERSION_RE, PACK_COMMIT_RE, PACK_POLICY_NAME_RE;
9083
- var init_pack_manifest = __esm(() => {
9084
- init_fp_home();
9085
- init_cloud_managed_policies();
9086
- ACCEPTED_PACK_SCHEMA_VERSIONS = [1];
9087
- SHA256_RE2 = /^[a-f0-9]{64}$/;
9088
- PACK_ID_RE = /^[A-Za-z0-9._-]{1,64}\/[A-Za-z0-9._-]{1,64}$/;
9089
- PACK_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
9090
- PACK_COMMIT_RE = /^[0-9a-f]{7,40}$/;
9091
- PACK_POLICY_NAME_RE = /^[A-Za-z0-9._-]{1,128}$/;
9748
+ var init_resolve_transcript_path = __esm(() => {
9749
+ init_codex_sessions();
9750
+ init_copilot_sessions();
9751
+ init_cursor_sessions();
9752
+ init_pi_sessions();
9753
+ init_factory_sessions();
9754
+ init_antigravity_sessions();
9092
9755
  });
9093
-
9094
9756
  // src/hooks/pack-failclosed.ts
9757
+ function declaredList(value) {
9758
+ if (value === undefined || value === null)
9759
+ return null;
9760
+ if (!Array.isArray(value))
9761
+ return null;
9762
+ if (value.some((entry) => typeof entry !== "string" || entry.length === 0))
9763
+ return null;
9764
+ return value;
9765
+ }
9095
9766
  function unionMatch(matchers) {
9096
9767
  const events = new Set;
9097
9768
  const toolNames = new Set;
9098
9769
  let anyEvent = false;
9099
9770
  let anyTool = false;
9100
9771
  for (const m of matchers) {
9101
- if (!m.events || m.events.length === 0)
9772
+ const declaredEvents = declaredList(m?.events);
9773
+ const declaredTools = declaredList(m?.toolNames);
9774
+ if (!declaredEvents || declaredEvents.length === 0)
9102
9775
  anyEvent = true;
9103
9776
  else
9104
- for (const e of m.events)
9777
+ for (const e of declaredEvents)
9105
9778
  events.add(e);
9106
- if (!m.toolNames || m.toolNames.length === 0)
9779
+ if (!declaredTools || declaredTools.length === 0)
9107
9780
  anyTool = true;
9108
9781
  else
9109
- for (const t of m.toolNames)
9782
+ for (const t of declaredTools)
9110
9783
  toolNames.add(t);
9111
9784
  }
9112
9785
  const scoped = {};
@@ -9116,11 +9789,20 @@ function unionMatch(matchers) {
9116
9789
  scoped.toolNames = [...toolNames];
9117
9790
  return scoped;
9118
9791
  }
9792
+ function outOfScope(clis, cli) {
9793
+ if (!cli)
9794
+ return false;
9795
+ if (!Array.isArray(clis) || clis.length === 0)
9796
+ return false;
9797
+ return !clis.every((c) => typeof c === "string") ? false : !clis.includes(cli);
9798
+ }
9119
9799
  function missingGuards(input) {
9120
9800
  const out = [];
9121
9801
  for (const err of input.errors) {
9122
9802
  if (err.effect === "observe")
9123
9803
  continue;
9804
+ if (outOfScope(err.clis, input.cli))
9805
+ continue;
9124
9806
  const declared = err.declared ?? [];
9125
9807
  out.push({
9126
9808
  packId: err.id ?? "(unnamed pack)",
@@ -9133,6 +9815,8 @@ function missingGuards(input) {
9133
9815
  for (const pack of input.packs) {
9134
9816
  if (pack.effect === "observe")
9135
9817
  continue;
9818
+ if (outOfScope(pack.clis, input.cli))
9819
+ continue;
9136
9820
  const loadFailure = input.failed.get(pack.id);
9137
9821
  if (loadFailure && PERMANENT_LOAD_FAILURES.has(loadFailure.type)) {
9138
9822
  const taken2 = pack.enabled ?? pack.policies.map((p) => p.name);
@@ -9165,6 +9849,24 @@ function missingGuards(input) {
9165
9849
  }
9166
9850
  return out.filter((g) => !(g.match.events && g.match.events.length === 0));
9167
9851
  }
9852
+ function guardsCover(guards, eventType, toolName) {
9853
+ return guards.some((g) => {
9854
+ const events = g.match.events;
9855
+ if (events && events.length > 0 && !events.includes(eventType)) {
9856
+ return false;
9857
+ }
9858
+ const tools = g.match.toolNames;
9859
+ if (tools && tools.length > 0 && (!toolName || !tools.includes(toolName)))
9860
+ return false;
9861
+ return true;
9862
+ });
9863
+ }
9864
+ function combinedGuardMatch(guards) {
9865
+ if (guards.length === 1)
9866
+ return guards[0].match;
9867
+ const axis = (key) => guards.every((g) => g.match[key]?.length) ? [...new Set(guards.flatMap((g) => g.match[key] ?? []))] : undefined;
9868
+ return { events: axis("events"), toolNames: axis("toolNames") };
9869
+ }
9168
9870
  function packFailureReason(guards) {
9169
9871
  const named = guards.map((g) => {
9170
9872
  const where = g.packVersion ? `${g.packId}@${g.packVersion}` : g.packId;
@@ -9468,9 +10170,11 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
9468
10170
  if (pack && enabledBuiltinNames.has(hook.name))
9469
10171
  continue;
9470
10172
  if (pack) {
9471
- const seen = registeredByPack.get(pack.id) ?? new Set;
9472
- seen.add(hook.name);
9473
- registeredByPack.set(pack.id, seen);
10173
+ for (const id of loadResult.packAliases?.get(pack.id) ?? [pack.id]) {
10174
+ const seen = registeredByPack.get(id) ?? new Set;
10175
+ seen.add(hook.name);
10176
+ registeredByPack.set(id, seen);
10177
+ }
9474
10178
  }
9475
10179
  const hookName = hook.name;
9476
10180
  const conventionScope = hook.__conventionScope;
@@ -9526,20 +10230,23 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
9526
10230
  packs: installedPacks,
9527
10231
  registered: registeredByPack,
9528
10232
  failed: loadResult.packFailures,
9529
- disabled: disabledCustomPolicies
10233
+ disabled: disabledCustomPolicies,
10234
+ cli
9530
10235
  });
9531
10236
  if (guards.length > 0) {
9532
10237
  const reason = packFailureReason(guards);
9533
- const match = guards.length === 1 ? guards[0].match : {
9534
- events: guards.every((g) => g.match.events) ? [...new Set(guards.flatMap((g) => g.match.events ?? []))] : undefined
9535
- };
10238
+ const match = combinedGuardMatch(guards);
9536
10239
  const name = "pack/failproofai-pack-unavailable";
9537
10240
  policyAttribution.set(name, {
9538
10241
  source: "pack",
9539
10242
  packId: guards[0].packId,
9540
10243
  ...guards[0].packVersion ? { packVersion: guards[0].packVersion } : {}
9541
10244
  });
9542
- registerPolicy(name, "A policy pack this machine enforces could not be loaded", async () => canonicalEventType === "UserPromptSubmit" ? { decision: "instruct", reason } : { decision: "deny", reason }, match, 1);
10245
+ registerPolicy(name, "A policy pack this machine enforces could not be loaded", async (ctx) => {
10246
+ if (!guardsCover(guards, canonicalEventType, ctx.toolName))
10247
+ return { decision: "allow" };
10248
+ return canonicalEventType === "UserPromptSubmit" ? { decision: "instruct", reason } : { decision: "deny", reason };
10249
+ }, match, 1);
9543
10250
  hookLogWarn(reason);
9544
10251
  }
9545
10252
  }