failproofai 1.0.0-beta.2 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
- package/.next/standalone/.next/build-manifest.json +6 -6
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +5 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +3 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +30 -18
- package/.next/standalone/.next/server/app/policies/page.js +5 -3
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +4 -2
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
- package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
- package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
- package/.next/standalone/.next/server/app/settings/page.js +18 -0
- package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
- package/.next/standalone/.next/server/chunks/[externals]__0h9_83k._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[externals]__1ifpkx-._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__00ni9za._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__0effm9m._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0ix8gm7._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ji11a-._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0ojwjxl._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__16le-kd._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1aq139v._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-wfhrp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1apb7k_._.js → [root-of-the-server]__0_1tiya._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__170799-._.js → [root-of-the-server]__0spkm68._.js} +11 -11
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t281d4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17itmx-._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01od0m6._.js → [root-of-the-server]__1n2vcv4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qapotl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tlvv9a._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0vo2qyt._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0xej-0e._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_10399ge._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_149k9oy._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1_hwp06._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ctse6u._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1e19atl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1mnhi_r._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ror7k9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1sdgprl._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +4 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
- package/.next/standalone/.next/server/middleware.js +2 -2
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +74 -20
- package/.next/standalone/.next/static/chunks/{03fmihek9n986.js → 06zgzyh3wv1ig.js} +1 -1
- package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
- package/.next/standalone/.next/static/chunks/{1hdhteui932bq.js → 0qmo2t50kpb30.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2sbelanzvxvv0.js → 113g8wtal6_c4.js} +22 -22
- package/.next/standalone/.next/static/chunks/16p68cybetr31.js +1 -0
- package/.next/standalone/.next/static/chunks/18wqn0_3mzylf.js +1 -0
- package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
- package/.next/standalone/.next/static/chunks/{0fi645si-uiiq.js → 1xq1m8h14tnlj.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3hp8ulqknybil.js → 1y3vwogqb88dx.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3vs4ay36tjds_.js → 1zmh-b0ulrqu7.js} +1 -1
- package/.next/standalone/.next/static/chunks/23kev26by1e4f.js +1 -0
- package/.next/standalone/.next/static/chunks/299lhw60e8gr6.js +1 -0
- package/.next/standalone/.next/static/chunks/{18rvdw5v72364.js → 2adbpdaxp5ygq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2j94uwkb4s3qr.js → 2kbqcw-6swmav.js} +1 -1
- package/.next/standalone/.next/static/chunks/2n9n5m0963pdg.js +6 -0
- package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
- package/.next/standalone/.next/static/chunks/{31feojxhjyjpl.js → 30nrrpwymht5m.js} +1 -1
- package/.next/standalone/.next/static/chunks/31coz11ia5vc4.js +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-1lsv92x7klwrn.js → turbopack-3diub4ctvn1n0.js} +1 -1
- package/.next/standalone/SECURITY.md +8 -2
- package/.next/standalone/app/actions/get-active-pauses.ts +16 -0
- package/.next/standalone/app/actions/get-hooks-config.ts +2 -2
- package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
- package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
- package/.next/standalone/app/api/audit/run/route.ts +28 -0
- package/.next/standalone/app/api/audit/status/route.ts +13 -1
- package/.next/standalone/app/api/auth/status/route.ts +1 -1
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +1 -3
- package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +1 -1
- package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -1
- package/.next/standalone/app/audit/_components/empty-state.tsx +0 -1
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +1 -1
- package/.next/standalone/app/audit/_components/quirks-section.tsx +0 -1
- package/.next/standalone/app/audit/_components/report-footer.tsx +0 -2
- package/.next/standalone/app/audit/_components/run-progress.tsx +1 -1
- package/.next/standalone/app/audit/_components/strengths-section.tsx +0 -1
- package/.next/standalone/app/audit/audit-styles.css +6 -149
- package/.next/standalone/app/components/pause-notices.tsx +111 -0
- package/.next/standalone/app/components/raw-log-viewer.tsx +1 -1
- package/.next/standalone/app/components/toast.tsx +1 -1
- package/.next/standalone/app/policies/hooks-client.tsx +84 -8
- package/.next/standalone/app/settings/page.tsx +31 -0
- package/.next/standalone/app/settings/settings-client.tsx +487 -0
- package/.next/standalone/components/navbar.tsx +2 -0
- package/.next/standalone/lib/auth/api-server-client.ts +2 -3
- package/.next/standalone/lib/auth/auth-store.ts +4 -5
- package/.next/standalone/lib/codex-sessions.ts +2 -1
- package/.next/standalone/lib/dashboard-host.ts +101 -0
- package/.next/standalone/lib/install-check.ts +22 -7
- package/.next/standalone/lib/pi-sessions.ts +99 -14
- package/.next/standalone/lib/telemetry-enabled.ts +54 -0
- package/.next/standalone/lib/telemetry-id.ts +52 -1
- package/.next/standalone/lib/telemetry.ts +8 -4
- package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
- package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
- package/.next/standalone/node_modules/react/package.json +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +23 -12
- package/.next/standalone/proxy.ts +111 -1
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai-worker.mjs +1 -1
- package/bin/failproofai.mjs +1041 -81
- package/bin/failproofaid-shim.mjs +4 -4
- package/dist/cli.mjs +18513 -10474
- package/dist/worker.mjs +1223 -299
- package/lib/auth/api-server-client.ts +2 -3
- package/lib/auth/auth-store.ts +4 -5
- package/lib/codex-sessions.ts +2 -1
- package/lib/dashboard-host.ts +101 -0
- package/lib/install-check.ts +22 -7
- package/lib/pi-sessions.ts +99 -14
- package/lib/telemetry-enabled.ts +54 -0
- package/lib/telemetry-id.ts +52 -1
- package/lib/telemetry.ts +8 -4
- package/package.json +23 -12
- package/scripts/build-daemon-packages.mjs +247 -0
- package/scripts/daemon-platforms.mjs +42 -0
- package/scripts/dev-hook.mjs +46 -5
- package/scripts/install-telemetry.mjs +8 -1
- package/scripts/launch.ts +25 -3
- package/scripts/parse-script-args.ts +27 -1
- package/scripts/prune-standalone.mjs +26 -0
- package/scripts/publish-aliases.mjs +62 -41
- package/scripts/repro-npm-install.sh +155 -0
- package/scripts/translate-docs/cache.ts +1 -1
- package/scripts/translate-docs/cli.ts +2 -2
- package/scripts/translate-docs/mintlify-nav.ts +1 -1
- package/scripts/translate-docs/readme-translator.ts +163 -4
- package/scripts/validate-mdx.ts +153 -13
- package/src/audit/audit-lock.ts +285 -0
- package/src/audit/audit-schedule.ts +111 -0
- package/src/audit/cache.ts +18 -5
- package/src/audit/cli-adapters/goose.ts +2 -1
- package/src/audit/cli-adapters/hermes.ts +28 -10
- package/src/audit/cli.ts +191 -35
- package/src/audit/dashboard-cache.ts +2 -3
- package/src/audit/report.ts +0 -1
- package/src/hooks/backfill-cli.ts +247 -0
- package/src/hooks/builtin-policies.ts +391 -3
- package/src/hooks/cloud-connection.ts +354 -0
- package/src/hooks/cloud-enrollment-cli.ts +439 -0
- package/src/hooks/cloud-enrollment.ts +284 -0
- package/src/hooks/cloud-introspect.ts +147 -0
- package/src/hooks/cloud-managed-policies.ts +206 -0
- package/src/hooks/collector-config.ts +218 -0
- package/src/hooks/configure-wizard.ts +1211 -233
- package/src/hooks/custom-hooks-loader.ts +243 -37
- package/src/hooks/daemon-client.ts +136 -33
- package/src/hooks/daemon-download.ts +264 -31
- package/src/hooks/daemon-service.ts +1124 -76
- package/src/hooks/first-run-gate.ts +50 -0
- package/src/hooks/flush-cli.ts +180 -0
- package/src/hooks/fp-config.ts +835 -0
- package/src/hooks/fp-home.ts +714 -0
- package/src/hooks/fp-reset.ts +1363 -0
- package/src/hooks/handler.ts +185 -6
- package/src/hooks/harness-cli.ts +386 -0
- package/src/hooks/hook-activity-store.ts +171 -24
- package/src/hooks/hook-logger.ts +2 -2
- package/src/hooks/hook-telemetry.ts +5 -1
- package/src/hooks/hooks-config.ts +4 -3
- package/src/hooks/integrations.ts +0 -1
- package/src/hooks/loader-utils.ts +172 -9
- package/src/hooks/manager.ts +41 -5
- package/src/hooks/migrations.ts +450 -0
- package/src/hooks/onboarding-attempt.ts +195 -0
- package/src/hooks/onboarding-lock.ts +118 -0
- package/src/hooks/policy-types.ts +28 -0
- package/src/hooks/session-pause-cli.ts +159 -0
- package/src/hooks/session-pause.ts +267 -0
- package/src/hooks/setup-state.ts +256 -0
- package/src/hooks/tui.ts +209 -9
- package/src/hooks/uninstall-cli.ts +418 -0
- package/src/hooks/worker-server.ts +134 -15
- package/.next/standalone/.next/server/chunks/[externals]__14odj07._.js +0 -3
- package/.next/standalone/.next/server/chunks/[externals]__1s61mel._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0longh-._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[externals]__1_g_b3t._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0nb0syg._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0uno39i._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1psjybo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0oy_-36.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
- package/.next/standalone/.next/static/chunks/0zbxssxh53n-3.js +0 -1
- package/.next/standalone/.next/static/chunks/2mazwb1qmur1p.js +0 -1
- package/.next/standalone/.next/static/chunks/2pujpnfx4pws6.css +0 -2
- package/.next/standalone/.next/static/chunks/33u59vf_8xpd-.js +0 -1
- package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
- package/.next/standalone/.next/static/chunks/3s7myk_co0gbh.js +0 -1
- package/.next/standalone/.next/static/chunks/40k1_v4os4cw6.js +0 -6
- package/.next/standalone/Cargo.lock +0 -428
- package/.next/standalone/Cargo.toml +0 -9
- package/.next/standalone/crates/PROTOCOL.md +0 -122
- package/.next/standalone/crates/failproofaid/Cargo.toml +0 -18
- package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
- package/.next/standalone/crates/failproofaid/src/main.rs +0 -83
- package/.next/standalone/crates/failproofaid/src/paths.rs +0 -172
- package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
- package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
- package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
- package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
- package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
- package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
- package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
- package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
- package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
- package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
- package/.next/standalone/integration-suite/Dockerfile +0 -47
- package/.next/standalone/integration-suite/README.md +0 -129
- package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
- package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
- package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
- package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
- package/.next/standalone/integration-suite/install-clis.sh +0 -136
- package/.next/standalone/integration-suite/probe-cli.sh +0 -248
- package/.next/standalone/integration-suite/report.js +0 -120
- package/.next/standalone/integration-suite/run.sh +0 -159
- package/.next/standalone/openclaw-plugin/index.js +0 -223
- package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
- package/.next/standalone/openclaw-plugin/package.json +0 -12
- package/.next/standalone/osv-scanner.toml +0 -17
- package/.next/standalone/pi-extension/index.ts +0 -524
- package/.next/standalone/pi-extension/package.json +0 -12
- package/.next/standalone/postcss.config.mjs +0 -8
- package/.next/standalone/readme-arch-hq.gif +0 -0
- package/.next/standalone/rust-toolchain.toml +0 -4
- package/src/auth/cli.ts +0 -371
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{NWAjs0tHFyzk97gRGG09r → VjEdIL6W5uBsvEzknXqV9}/_ssgManifest.js +0 -0
|
@@ -1,524 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* failproofai policy bridge for Pi (pi-coding-agent).
|
|
3
|
-
*
|
|
4
|
-
* This extension is loaded by Pi at startup and registered via
|
|
5
|
-
* `pi install <abs-path-to-this-dir> [-l]` (or by hand-authoring an entry in
|
|
6
|
-
* `<scope>/.pi/settings.json`). It subscribes to Pi's `tool_call`, `user_bash`,
|
|
7
|
-
* `input`, and `session_start` events and forwards them to the failproofai
|
|
8
|
-
* binary as `failproofai --hook <Event> --cli pi`. failproofai prints a
|
|
9
|
-
* decision JSON to stdout; this shim parses it and translates into Pi's
|
|
10
|
-
* `{ block: true, reason }` return shape so policy `deny` decisions cancel
|
|
11
|
-
* tool execution.
|
|
12
|
-
*
|
|
13
|
-
* Marker comment for failproofai's installer detection (do not remove):
|
|
14
|
-
* __failproofai_hook__: true
|
|
15
|
-
*
|
|
16
|
-
* Binary resolution. failproofai ships two entrypoints:
|
|
17
|
-
* • dist/cli.mjs — bundled, node-compatible (production npm install)
|
|
18
|
-
* • bin/failproofai.mjs — source, requires `bun` (dev / monorepo)
|
|
19
|
-
*
|
|
20
|
-
* dist/cli.mjs is preferred because spawning `node bin/failproofai.mjs`
|
|
21
|
-
* fails with ERR_IMPORT_ATTRIBUTE_MISSING (the source `import package.json`
|
|
22
|
-
* needs `with { type: "json" }` under node, which bun handles transparently
|
|
23
|
-
* but the build:cli step transpiles away in dist/cli.mjs). When dist/cli.mjs
|
|
24
|
-
* isn't present, fall back to running bin/failproofai.mjs with `bun`. Pi
|
|
25
|
-
* spawns extensions with an undefined cwd contract, so paths are resolved
|
|
26
|
-
* relative to this file via `import.meta.url`, NOT process.cwd().
|
|
27
|
-
*/
|
|
28
|
-
import { spawnSync } from "node:child_process";
|
|
29
|
-
import { resolve, dirname, join } from "node:path";
|
|
30
|
-
import { fileURLToPath } from "node:url";
|
|
31
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
32
|
-
import { homedir } from "node:os";
|
|
33
|
-
|
|
34
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
35
|
-
const DIST_BIN = resolve(HERE, "..", "dist", "cli.mjs");
|
|
36
|
-
const SRC_BIN = resolve(HERE, "..", "bin", "failproofai.mjs");
|
|
37
|
-
// Prefer the bundled dist/cli.mjs (node-compatible); fall back to source +
|
|
38
|
-
// bun for dev workflows where dist/ hasn't been built yet.
|
|
39
|
-
function resolveSpawn(): { cmd: string; args: string[] } {
|
|
40
|
-
if (process.env.FAILPROOFAI_BINARY_OVERRIDE) {
|
|
41
|
-
return { cmd: "node", args: [process.env.FAILPROOFAI_BINARY_OVERRIDE] };
|
|
42
|
-
}
|
|
43
|
-
if (existsSync(DIST_BIN)) {
|
|
44
|
-
return { cmd: "node", args: [DIST_BIN] };
|
|
45
|
-
}
|
|
46
|
-
return { cmd: "bun", args: [SRC_BIN] };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface PolicyDecision {
|
|
50
|
-
permission?: "allow" | "deny";
|
|
51
|
-
reason?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Spawn `failproofai --hook <eventName> --cli pi`, write the JSON payload to
|
|
56
|
-
* stdin, and parse the flat `{permission, reason}` JSON we expect failproofai
|
|
57
|
-
* to print on stdout. Fail-open on any subprocess / parse error.
|
|
58
|
-
*/
|
|
59
|
-
/** Optional stderr trace for debugging the shim. Enabled with
|
|
60
|
-
* FAILPROOFAI_PI_DEBUG=1; silent otherwise. */
|
|
61
|
-
function debug(msg: string): void {
|
|
62
|
-
if (process.env.FAILPROOFAI_PI_DEBUG === "1") {
|
|
63
|
-
process.stderr.write(`[failproofai-pi-shim] ${msg}\n`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function callPolicy(eventName: string, payload: unknown): { block: boolean; reason: string } {
|
|
68
|
-
const { cmd, args } = resolveSpawn();
|
|
69
|
-
debug(`callPolicy event=${eventName} cmd=${cmd}`);
|
|
70
|
-
try {
|
|
71
|
-
const result = spawnSync(
|
|
72
|
-
cmd,
|
|
73
|
-
[...args, "--hook", eventName, "--cli", "pi"],
|
|
74
|
-
{
|
|
75
|
-
input: JSON.stringify(payload),
|
|
76
|
-
encoding: "utf8",
|
|
77
|
-
timeout: 60_000,
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
if (result.status !== 0) return { block: false, reason: "" };
|
|
81
|
-
const stdout = (result.stdout || "").trim();
|
|
82
|
-
if (!stdout) return { block: false, reason: "" };
|
|
83
|
-
const parsed = JSON.parse(stdout) as PolicyDecision;
|
|
84
|
-
if (parsed.permission === "deny") {
|
|
85
|
-
debug(`DENY reason=${parsed.reason}`);
|
|
86
|
-
return { block: true, reason: parsed.reason ?? "Blocked by failproofai" };
|
|
87
|
-
}
|
|
88
|
-
} catch (err) {
|
|
89
|
-
debug(`EXCEPTION ${err instanceof Error ? err.message : String(err)}`);
|
|
90
|
-
// Fail-open: never block tool execution because of an infra failure.
|
|
91
|
-
}
|
|
92
|
-
return { block: false, reason: "" };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
interface PiToolCallEvent {
|
|
96
|
-
type?: string;
|
|
97
|
-
toolName?: string;
|
|
98
|
-
toolCallId?: string;
|
|
99
|
-
input?: Record<string, unknown>;
|
|
100
|
-
cwd?: string;
|
|
101
|
-
sessionId?: string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Pi emits tool names in lowercase (`bash`, `read`, `edit`, `write`, `glob`,
|
|
106
|
-
* `grep`). failproofai's builtin policies match on Claude PascalCase
|
|
107
|
-
* (`Bash`, `Read`, `Edit`, `Write`, `Glob`, `Grep`) via case-sensitive
|
|
108
|
-
* `Array.includes` in policy-registry.ts. Map between the two so existing
|
|
109
|
-
* tool-name match clauses fire on Pi sessions.
|
|
110
|
-
*
|
|
111
|
-
* Keys must stay in sync with PI_TOOL_MAP in src/hooks/types.ts (this shim is
|
|
112
|
-
* loaded in-process by Pi and must be self-contained — no imports from the
|
|
113
|
-
* failproofai package). Update both whenever Pi adds a tool ID we care about.
|
|
114
|
-
*
|
|
115
|
-
* Unknown tools (anything not in this map) pass through unchanged so custom
|
|
116
|
-
* policies that match on raw Pi tool IDs still work.
|
|
117
|
-
*/
|
|
118
|
-
const PI_TOOL_MAP: Record<string, string> = {
|
|
119
|
-
bash: "Bash",
|
|
120
|
-
read: "Read",
|
|
121
|
-
write: "Write",
|
|
122
|
-
edit: "Edit",
|
|
123
|
-
glob: "Glob",
|
|
124
|
-
grep: "Grep",
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
function canonicalizeToolName(piToolName: string | undefined): string | undefined {
|
|
128
|
-
if (!piToolName) return undefined;
|
|
129
|
-
return PI_TOOL_MAP[piToolName] ?? piToolName;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Per-tool input-key translation. Pi's Read / Write / Edit tools deliver
|
|
134
|
-
* `path` (not `file_path`); failproofai's `block-env-files` and
|
|
135
|
-
* `block-secrets-write` builtins only read `file_path`, so without this map
|
|
136
|
-
* they silently no-op on Pi. `block-read-outside-cwd` already has a `path`
|
|
137
|
-
* fallback so it works either way. Pi's Edit tool nests `edits[{oldText,
|
|
138
|
-
* newText}]` which doesn't translate flatly to Claude's `{old_string,
|
|
139
|
-
* new_string}` — we only map the top-level `path`; the nested array stays
|
|
140
|
-
* Pi-shape (no current builtin reads it).
|
|
141
|
-
*
|
|
142
|
-
* Keep in sync with PI_TOOL_INPUT_MAP in src/hooks/types.ts.
|
|
143
|
-
*/
|
|
144
|
-
const PI_TOOL_INPUT_MAP: Record<string, Record<string, string>> = {
|
|
145
|
-
Read: { path: "file_path" },
|
|
146
|
-
Write: { path: "file_path" },
|
|
147
|
-
Edit: { path: "file_path" },
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
function canonicalizeToolInput(
|
|
151
|
-
canonicalToolName: string | undefined,
|
|
152
|
-
args: Record<string, unknown> | undefined,
|
|
153
|
-
): Record<string, unknown> | undefined {
|
|
154
|
-
if (!args || typeof args !== "object" || !canonicalToolName) return args;
|
|
155
|
-
const map = PI_TOOL_INPUT_MAP[canonicalToolName];
|
|
156
|
-
if (!map) return args;
|
|
157
|
-
const out: Record<string, unknown> = {};
|
|
158
|
-
for (const k of Object.keys(args)) out[map[k] ?? k] = args[k];
|
|
159
|
-
return out;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Resolve the cwd for the policy payload. Pi events don't include cwd, so
|
|
163
|
-
* fall back to the extension's process.cwd() — which is where Pi was
|
|
164
|
-
* launched and where `.failproofai/` config lives. */
|
|
165
|
-
function resolveCwd(eventCwd: string | undefined): string {
|
|
166
|
-
return eventCwd ?? process.cwd();
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Pi (verified empirically against pi-coding-agent v0.71.1) does NOT
|
|
171
|
-
* populate `event.sessionId` on any of its events — `session_start`,
|
|
172
|
-
* `tool_call`, `user_bash`, `input`, `tool_result`, `agent_end`,
|
|
173
|
-
* `session_shutdown` all leave it undefined. Without help the shim can't
|
|
174
|
-
* tag activity records with a session id, so the dashboard renders
|
|
175
|
-
* `Session ID: —` for every Pi row.
|
|
176
|
-
*
|
|
177
|
-
* What Pi DOES do: at session start it creates a JSONL transcript at
|
|
178
|
-
* `~/.pi/agent/sessions/<encodedCwd>/<isoTimestamp>_<uuid>.jsonl` where
|
|
179
|
-
* the filename encodes the sessionId. We discover ours by scanning the
|
|
180
|
-
* encoded-cwd directory for the most-recently-modified matching file.
|
|
181
|
-
*
|
|
182
|
-
* Strategy: scan once and cache. Pi runs one session per process so the
|
|
183
|
-
* cache is per-process and lives for the session's lifetime. If Pi ever
|
|
184
|
-
* multiplexes, we'd need a keyed map.
|
|
185
|
-
*/
|
|
186
|
-
const PI_FILE_RE = /^[\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;
|
|
187
|
-
|
|
188
|
-
/** Encode a cwd into Pi's on-disk session-dir name. Pi strips the leading
|
|
189
|
-
* `/` before replacing remaining slashes with `-`, e.g.
|
|
190
|
-
* `/home/u/repo` → `--home-u-repo--`. */
|
|
191
|
-
function piEncodeCwd(cwd: string): string {
|
|
192
|
-
const inner = cwd.replace(/^\/+/, "").replace(/\//g, "-");
|
|
193
|
-
return `--${inner}--`;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Process start boundary — files older than this aren't from the current
|
|
197
|
-
* Pi session. Captured at module load so cold-start in a cwd with stale
|
|
198
|
-
* transcripts doesn't pin a previous session's UUID. We allow a small
|
|
199
|
-
* tolerance below `processStartMs` because mtime resolution and clock
|
|
200
|
-
* skew can put a "current" file's mtime a few hundred ms before module
|
|
201
|
-
* load on slow startup. */
|
|
202
|
-
const PROCESS_START_MS = Date.now();
|
|
203
|
-
const STALE_TOLERANCE_MS = 2_000;
|
|
204
|
-
|
|
205
|
-
/** Find the newest `<ts>_<uuid>.jsonl` file under `~/.pi/agent/sessions/<encodedCwd>/`
|
|
206
|
-
* whose mtime indicates it belongs to the CURRENT Pi process (≥ process
|
|
207
|
-
* start, with a small tolerance). Files older than that are stale
|
|
208
|
-
* transcripts from prior sessions in the same cwd — caching their UUID
|
|
209
|
-
* would cross-attribute every event of the new session.
|
|
210
|
-
* Returns undefined when the dir doesn't exist, has no matching file, or
|
|
211
|
-
* every matching file is stale. */
|
|
212
|
-
function discoverPiSessionId(cwd: string): string | undefined {
|
|
213
|
-
const root = process.env.PI_SESSIONS_DIR || join(homedir(), ".pi", "agent", "sessions");
|
|
214
|
-
const dir = join(root, piEncodeCwd(cwd));
|
|
215
|
-
let entries: string[];
|
|
216
|
-
try { entries = readdirSync(dir); } catch { return undefined; }
|
|
217
|
-
const boundary = PROCESS_START_MS - STALE_TOLERANCE_MS;
|
|
218
|
-
let best: { sessionId: string; mtime: number } | undefined;
|
|
219
|
-
for (const name of entries) {
|
|
220
|
-
const m = PI_FILE_RE.exec(name);
|
|
221
|
-
if (!m) continue;
|
|
222
|
-
let mtime: number;
|
|
223
|
-
try { mtime = statSync(join(dir, name)).mtimeMs; } catch { continue; }
|
|
224
|
-
if (mtime < boundary) continue;
|
|
225
|
-
if (!best || mtime > best.mtime) best = { sessionId: m[1], mtime };
|
|
226
|
-
}
|
|
227
|
-
return best?.sessionId;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** sessionId cache, keyed by cwd. Per-cwd so a multi-cwd Pi (extension running
|
|
231
|
-
* across multiple workspace roots) can't cross-attribute. Cleared on
|
|
232
|
-
* session_shutdown reasons `new`/`resume`/`fork` (Pi reuses the process). */
|
|
233
|
-
const cachedSessionIdByCwd = new Map<string, string>();
|
|
234
|
-
|
|
235
|
-
/** Pending Stop-policy deny reason from agent_end, keyed by sessionId.
|
|
236
|
-
* Drained by before_agent_start on the next user turn in the same Pi
|
|
237
|
-
* process. Cleared on every session_shutdown.
|
|
238
|
-
*
|
|
239
|
-
* Why this exists: Pi's agent_end has no Result type — the agent loop
|
|
240
|
-
* has already exited when it fires, so a deny return cannot keep Pi
|
|
241
|
-
* running the way Claude's exit-2-from-Stop does. The closest analog
|
|
242
|
-
* is to capture the deny here and re-inject it as a MANDATORY ACTION
|
|
243
|
-
* system-prompt addition on the NEXT before_agent_start, which fires
|
|
244
|
-
* after the user submits a prompt but before the agent loop runs.
|
|
245
|
-
* Best-effort: bounded by the Pi process lifetime — same bound Claude
|
|
246
|
-
* has on exit-2-from-Stop (kill the agent and the gate is missed).
|
|
247
|
-
*
|
|
248
|
-
* Why per-session not per-cwd: a Pi process can host multiple sessions
|
|
249
|
-
* via /resume and /fork; per-cwd would cross-attribute a stale block
|
|
250
|
-
* from a prior session into a fresh one. */
|
|
251
|
-
const pendingStopBlockBySession = new Map<string, string>();
|
|
252
|
-
function resolveSessionId(eventSessionId: string | undefined, cwd: string): string | undefined {
|
|
253
|
-
if (eventSessionId) {
|
|
254
|
-
cachedSessionIdByCwd.set(cwd, eventSessionId);
|
|
255
|
-
return eventSessionId;
|
|
256
|
-
}
|
|
257
|
-
const cached = cachedSessionIdByCwd.get(cwd);
|
|
258
|
-
if (cached) return cached;
|
|
259
|
-
// Pi v0.71.1 never sets sessionId — discover from disk.
|
|
260
|
-
const discovered = discoverPiSessionId(cwd);
|
|
261
|
-
if (discovered) cachedSessionIdByCwd.set(cwd, discovered);
|
|
262
|
-
return discovered;
|
|
263
|
-
}
|
|
264
|
-
/** Clear the cached sessionId for a cwd. Called on session_shutdown reasons
|
|
265
|
-
* that indicate a new session is starting in the same process (`new`,
|
|
266
|
-
* `resume`, `fork`). Without this, the next session would inherit the prior
|
|
267
|
-
* sessionId until disk discovery refreshed it. */
|
|
268
|
-
function resetSessionIdCache(cwd: string): void {
|
|
269
|
-
cachedSessionIdByCwd.delete(cwd);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
interface PiUserBashEvent {
|
|
273
|
-
type?: string;
|
|
274
|
-
command?: string;
|
|
275
|
-
cwd?: string;
|
|
276
|
-
sessionId?: string;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
interface PiInputEvent {
|
|
280
|
-
type?: string;
|
|
281
|
-
text?: string;
|
|
282
|
-
source?: string;
|
|
283
|
-
cwd?: string;
|
|
284
|
-
sessionId?: string;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
interface PiSessionStartEvent {
|
|
288
|
-
type?: string;
|
|
289
|
-
reason?: string;
|
|
290
|
-
cwd?: string;
|
|
291
|
-
sessionId?: string;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
interface PiSessionShutdownEvent {
|
|
295
|
-
type?: string;
|
|
296
|
-
/** "quit" | "reload" | "new" | "resume" | "fork" per pi-coding-agent v0.72.1 */
|
|
297
|
-
reason?: string;
|
|
298
|
-
targetSessionFile?: string;
|
|
299
|
-
cwd?: string;
|
|
300
|
-
sessionId?: string;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
interface PiToolResultEvent {
|
|
304
|
-
type?: string;
|
|
305
|
-
toolCallId?: string;
|
|
306
|
-
toolName?: string;
|
|
307
|
-
input?: Record<string, unknown>;
|
|
308
|
-
/** TextContent | ImageContent — opaque to us; forwarded as-is. */
|
|
309
|
-
content?: unknown[];
|
|
310
|
-
isError?: boolean;
|
|
311
|
-
cwd?: string;
|
|
312
|
-
sessionId?: string;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
interface PiAgentEndEvent {
|
|
316
|
-
type?: string;
|
|
317
|
-
/** AgentMessage[] — opaque; not forwarded (Stop policies don't need it). */
|
|
318
|
-
messages?: unknown[];
|
|
319
|
-
cwd?: string;
|
|
320
|
-
sessionId?: string;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/** Pi v0.73.x before_agent_start event payload. Fires once per turn,
|
|
324
|
-
* after the user submits a prompt but before the agent loop runs. */
|
|
325
|
-
interface PiBeforeAgentStartEvent {
|
|
326
|
-
type?: string;
|
|
327
|
-
prompt?: string;
|
|
328
|
-
/** The fully assembled system prompt for this turn — we append to it. */
|
|
329
|
-
systemPrompt?: string;
|
|
330
|
-
cwd?: string;
|
|
331
|
-
sessionId?: string;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
interface PiExtensionApi {
|
|
335
|
-
on(event: string, handler: (event: unknown) => unknown): void;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
export default function failproofaiBridge(pi: PiExtensionApi) {
|
|
339
|
-
// tool_call → PreToolUse. Block tool execution when failproofai denies.
|
|
340
|
-
pi.on("tool_call", (event: unknown): unknown => {
|
|
341
|
-
const e = event as PiToolCallEvent;
|
|
342
|
-
const canonicalTool = canonicalizeToolName(e.toolName);
|
|
343
|
-
const decision = callPolicy("tool_call", {
|
|
344
|
-
tool_name: canonicalTool,
|
|
345
|
-
tool_input: canonicalizeToolInput(canonicalTool, e.input),
|
|
346
|
-
session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
|
|
347
|
-
cwd: resolveCwd(e.cwd),
|
|
348
|
-
hook_event_name: "PreToolUse",
|
|
349
|
-
});
|
|
350
|
-
// `ToolCallEventResult` is `{block?, reason?}` (extensions/types.d.ts:766).
|
|
351
|
-
// agent-loop.ts branches on `beforeResult?.block`, so this is the shape that
|
|
352
|
-
// stops the tool. Do NOT put the user_bash full-replacement shape here — it
|
|
353
|
-
// has no `block` field, so the call would sail straight through.
|
|
354
|
-
if (decision.block) return { block: true, reason: decision.reason };
|
|
355
|
-
return undefined;
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
// user_bash → PreToolUse with synthesized toolName=Bash.
|
|
359
|
-
pi.on("user_bash", (event: unknown): unknown => {
|
|
360
|
-
const e = event as PiUserBashEvent;
|
|
361
|
-
const decision = callPolicy("user_bash", {
|
|
362
|
-
tool_name: "Bash",
|
|
363
|
-
tool_input: { command: e.command },
|
|
364
|
-
session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
|
|
365
|
-
cwd: resolveCwd(e.cwd),
|
|
366
|
-
hook_event_name: "PreToolUse",
|
|
367
|
-
});
|
|
368
|
-
// `UserBashEventResult` is `{operations?, result?: BashResult}` — there is
|
|
369
|
-
// no `block` field, so the `{block:true}` we used to return matched nothing
|
|
370
|
-
// and the command ran anyway. The supported way to stop it is a FULL
|
|
371
|
-
// REPLACEMENT result: the extension declares it handled execution, so Pi
|
|
372
|
-
// uses this instead of running the command. Verified against the installed
|
|
373
|
-
// pi-coding-agent d.ts (extensions/types.d.ts:772, core/bash-executor.d.ts:15).
|
|
374
|
-
if (decision.block) {
|
|
375
|
-
return {
|
|
376
|
-
result: {
|
|
377
|
-
output: decision.reason ?? "Blocked by failproofai",
|
|
378
|
-
exitCode: 1,
|
|
379
|
-
cancelled: false,
|
|
380
|
-
truncated: false,
|
|
381
|
-
},
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
return undefined;
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// input → UserPromptSubmit.
|
|
388
|
-
//
|
|
389
|
-
// `InputEventResult` is `{action:"continue"|"transform"|"handled"}`
|
|
390
|
-
// (extensions/types.d.ts:629) — there is NO `block` field, so the
|
|
391
|
-
// `{block:true}` we used to return matched neither branch and every prompt
|
|
392
|
-
// was submitted regardless of the verdict. `{action:"handled"}` is the only
|
|
393
|
-
// stop: it tells Pi an extension consumed the input, so nothing is sent to
|
|
394
|
-
// the model.
|
|
395
|
-
//
|
|
396
|
-
// Known UX limitation: `handled` drops the prompt silently — Pi shows the
|
|
397
|
-
// user nothing. We log the reason to stderr so it is at least recoverable
|
|
398
|
-
// from the session output. Pi has no first-class channel for explaining a
|
|
399
|
-
// refusal at this event; if one appears, prefer it over the silent drop.
|
|
400
|
-
pi.on("input", (event: unknown): unknown => {
|
|
401
|
-
const e = event as PiInputEvent;
|
|
402
|
-
const decision = callPolicy("input", {
|
|
403
|
-
prompt: e.text,
|
|
404
|
-
session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
|
|
405
|
-
cwd: resolveCwd(e.cwd),
|
|
406
|
-
hook_event_name: "UserPromptSubmit",
|
|
407
|
-
});
|
|
408
|
-
if (decision.block) {
|
|
409
|
-
console.error(`[failproofai] prompt blocked: ${decision.reason ?? "blocked by policy"}`);
|
|
410
|
-
return { action: "handled" };
|
|
411
|
-
}
|
|
412
|
-
return { action: "continue" };
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// session_start → SessionStart. Observe-only; we still forward so the
|
|
416
|
-
// activity feed records the session and any UserPromptSubmit policies that
|
|
417
|
-
// need session_id continuity see the metadata.
|
|
418
|
-
pi.on("session_start", (event: unknown): unknown => {
|
|
419
|
-
const e = event as PiSessionStartEvent;
|
|
420
|
-
callPolicy("session_start", {
|
|
421
|
-
session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
|
|
422
|
-
cwd: resolveCwd(e.cwd),
|
|
423
|
-
reason: e.reason,
|
|
424
|
-
hook_event_name: "SessionStart",
|
|
425
|
-
});
|
|
426
|
-
return undefined;
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// tool_result → PostToolUse. Observation-only on Pi: ToolResultEventResult
|
|
430
|
-
// exposes {content, details, isError} for mutation but no `block`. We
|
|
431
|
-
// forward to the failproofai binary so PostToolUse builtins (sanitize-jwt,
|
|
432
|
-
// sanitize-api-keys, sanitize-connection-strings, sanitize-private-key-
|
|
433
|
-
// content, sanitize-bearer-tokens) run and get their decisions logged to
|
|
434
|
-
// the activity store + stderr — but Pi keeps the original tool result.
|
|
435
|
-
pi.on("tool_result", (event: unknown): unknown => {
|
|
436
|
-
const e = event as PiToolResultEvent;
|
|
437
|
-
const canonicalTool = canonicalizeToolName(e.toolName);
|
|
438
|
-
callPolicy("tool_result", {
|
|
439
|
-
tool_name: canonicalTool,
|
|
440
|
-
tool_input: canonicalizeToolInput(canonicalTool, e.input) ?? {},
|
|
441
|
-
tool_response: { content: e.content, isError: e.isError },
|
|
442
|
-
session_id: resolveSessionId(e.sessionId, resolveCwd(e.cwd)),
|
|
443
|
-
cwd: resolveCwd(e.cwd),
|
|
444
|
-
hook_event_name: "PostToolUse",
|
|
445
|
-
});
|
|
446
|
-
return undefined;
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
// agent_end → Stop. Pi cannot veto agent_end (the agent loop has already
|
|
450
|
-
// exited when this fires — see the AgentEndEvent typedef in pi-coding-agent
|
|
451
|
-
// which has NO Result type). Instead we capture any deny reason and stash
|
|
452
|
-
// it keyed by sessionId for the next before_agent_start handler to drain.
|
|
453
|
-
// The 5 require-*-before-stop builtins thus enforce by gating the NEXT
|
|
454
|
-
// user turn's system prompt rather than by retrying the same loop. If the
|
|
455
|
-
// user kills Pi between turns, the gate is missed — same bound Claude has.
|
|
456
|
-
pi.on("agent_end", (event: unknown): unknown => {
|
|
457
|
-
const e = event as PiAgentEndEvent;
|
|
458
|
-
const cwd = resolveCwd(e.cwd);
|
|
459
|
-
const sessionId = resolveSessionId(e.sessionId, cwd);
|
|
460
|
-
const decision = callPolicy("agent_end", {
|
|
461
|
-
session_id: sessionId,
|
|
462
|
-
cwd,
|
|
463
|
-
hook_event_name: "Stop",
|
|
464
|
-
});
|
|
465
|
-
if (decision.block && decision.reason && sessionId) {
|
|
466
|
-
pendingStopBlockBySession.set(sessionId, decision.reason);
|
|
467
|
-
debug(`agent_end deny stored for session=${sessionId}`);
|
|
468
|
-
}
|
|
469
|
-
return undefined;
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// before_agent_start → drain any pending Stop-policy deny captured at
|
|
473
|
-
// agent_end. This is Pi's only first-class channel to influence the next
|
|
474
|
-
// turn before the LLM call: the result type accepts a `systemPrompt`
|
|
475
|
-
// replacement (chained across extensions) and an optional injected
|
|
476
|
-
// CustomMessage. We only return systemPrompt — sufficient for the LLM to
|
|
477
|
-
// see the MANDATORY ACTION directive immediately, and avoids polluting the
|
|
478
|
-
// visible conversation history with framework chrome. The reason text
|
|
479
|
-
// already carries the policy-attributed MANDATORY ACTION wording from
|
|
480
|
-
// policy-evaluator's Pi-Stop branch.
|
|
481
|
-
pi.on("before_agent_start", (event: unknown): unknown => {
|
|
482
|
-
const e = event as PiBeforeAgentStartEvent;
|
|
483
|
-
const cwd = resolveCwd(e.cwd);
|
|
484
|
-
const sessionId = resolveSessionId(e.sessionId, cwd);
|
|
485
|
-
if (!sessionId) return undefined;
|
|
486
|
-
const pending = pendingStopBlockBySession.get(sessionId);
|
|
487
|
-
if (!pending) return undefined;
|
|
488
|
-
pendingStopBlockBySession.delete(sessionId);
|
|
489
|
-
debug(`before_agent_start drains stop-block for session=${sessionId}`);
|
|
490
|
-
const base = e.systemPrompt ?? "";
|
|
491
|
-
return { systemPrompt: `${base}\n\n${pending}` };
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// session_shutdown → SessionEnd. Observation-only; emits a SessionEnd
|
|
495
|
-
// record so per-session telemetry has a clean close. Reset the per-cwd
|
|
496
|
-
// sessionId cache for shutdown reasons that mean "Pi is starting a new
|
|
497
|
-
// session in the same process" — without the reset, the next session's
|
|
498
|
-
// events would inherit the prior session's id until disk discovery
|
|
499
|
-
// refreshed it.
|
|
500
|
-
pi.on("session_shutdown", (event: unknown): unknown => {
|
|
501
|
-
const e = event as PiSessionShutdownEvent;
|
|
502
|
-
const cwd = resolveCwd(e.cwd);
|
|
503
|
-
callPolicy("session_shutdown", {
|
|
504
|
-
session_id: resolveSessionId(e.sessionId, cwd),
|
|
505
|
-
cwd,
|
|
506
|
-
reason: e.reason,
|
|
507
|
-
hook_event_name: "SessionEnd",
|
|
508
|
-
});
|
|
509
|
-
// Capture sessionId BEFORE the cache reset so we delete the pending
|
|
510
|
-
// entry under the just-ending session's id. After resetSessionIdCache,
|
|
511
|
-
// a subsequent resolveSessionId would re-discover from disk and could
|
|
512
|
-
// bind to a different (stale) file — wrong key for the cleanup below.
|
|
513
|
-
const sessionId = resolveSessionId(e.sessionId, cwd);
|
|
514
|
-
if (e.reason === "new" || e.reason === "resume" || e.reason === "fork") {
|
|
515
|
-
resetSessionIdCache(cwd);
|
|
516
|
-
}
|
|
517
|
-
// Drop any pending Stop-policy deny for this session on every shutdown
|
|
518
|
-
// reason — `quit` ends the session for good (don't leak the entry into
|
|
519
|
-
// GC); `new`/`resume`/`fork` start a different session in the same
|
|
520
|
-
// process and must not inherit the prior session's gate.
|
|
521
|
-
if (sessionId) pendingStopBlockBySession.delete(sessionId);
|
|
522
|
-
return undefined;
|
|
523
|
-
});
|
|
524
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@failproofai/pi-extension",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"description": "failproofai policy bridge for Pi (pi-coding-agent)",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./index.ts",
|
|
7
|
-
"private": true,
|
|
8
|
-
"keywords": [
|
|
9
|
-
"pi-extension",
|
|
10
|
-
"failproofai"
|
|
11
|
-
]
|
|
12
|
-
}
|
|
Binary file
|