failproofai 1.0.4-beta.1 → 1.0.4-beta.4
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/build-manifest.json +5 -5
- package/.next/standalone/.next/prerender-manifest.json +5 -5
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
- 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.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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- 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 +2 -2
- 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.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/_not-found/__PAGE__.segment.rsc +14 -14
- 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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route_client-reference-manifest.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/audit/status/route_client-reference-manifest.js +1 -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-request/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route_client-reference-manifest.js +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/api/download/[project]/[session]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +37 -4
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- 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 +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -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 +2 -2
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +1 -1
- 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 +2 -2
- 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 +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +1 -1
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1zndo1i._.js → [externals]__0v1hz15._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__0_gzk8q._.js → [externals]__1nb206a._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cuho4x._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q0qzx1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adacul._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0otft92._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +14 -21
- package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00-s7h8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0wvusu0._.js → [root-of-the-server]__0n_lxhg._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__16gamwq._.js → [root-of-the-server]__0yl9wb5._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__190won8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.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]__1sc84bb._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zl_r0t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1byck5v._.js → _0guxnh-._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0wso5d3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +61 -168
- package/.next/standalone/.next/server/chunks/ssr/{_08f0jd2._.js → _1if9ixi._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -69
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
- 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 +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
- 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 +54 -21
- package/.next/standalone/.next/static/chunks/04o5zfl7p5lhx.js +1 -0
- package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0jpd8dv2vk930.css} +1 -1
- package/.next/standalone/.next/static/chunks/{1po3lirgk2riw.js → 0po8n4tyotjf2.js} +1 -1
- package/.next/standalone/.next/static/chunks/0qmsunv04x4na.css +2 -0
- package/.next/standalone/.next/static/chunks/0qx7gav1c2_vu.js +1 -0
- package/.next/standalone/.next/static/chunks/17tfki9x5rd_7.js +1 -0
- package/.next/standalone/.next/static/chunks/{1_bxotmf9qngs.js → 1i-i0pxg9q_8y.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1_2jsatfuthrw.js → 2bxr2_2h4kaqq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{01hvwwrhvbzyh.js → 2zafgs90r_leq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{00gv0v91mycm-.js → 323cyb3a2s06y.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0qn9khv0oo9ex.js → 364cqbpeg1y5i.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2bvwsyu6x__ia.js → 38dqur43qthrl.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3_--3ibia6ba1.js → 3l36d73kj18zw.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
- package/.next/standalone/.next/static/chunks/{2bnf_4pgyk1rq.js → 40lpt2o2b283e.js} +1 -1
- package/.next/standalone/.next/static/chunks/42gwu2bsz4ske.js +6 -0
- package/.next/standalone/.next/static/chunks/{turbopack-18qf4jodtft6b.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
- package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
- package/.next/standalone/app/actions/get-leaks.ts +109 -0
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +140 -34
- package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
- package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
- package/.next/standalone/app/audit/_components/leak-section.tsx +192 -0
- package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
- package/.next/standalone/app/audit/audit-styles.css +132 -1
- package/.next/standalone/app/project/[name]/page.tsx +4 -24
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
- package/.next/standalone/lib/auth/api-server-client.ts +28 -0
- package/.next/standalone/lib/claude-sessions.ts +101 -27
- package/.next/standalone/lib/cli-registry.ts +1 -33
- package/.next/standalone/lib/download-session.ts +0 -37
- package/.next/standalone/lib/projects.ts +3 -27
- package/.next/standalone/lib/sqlite-reader.ts +78 -1
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
- package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/image-optimizer.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +11 -11
- package/.next/standalone/package.json +15 -14
- package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
- package/.next/standalone/sdk/python/README.md +8 -0
- package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
- package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
- package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
- package/.next/standalone/sdk/python/uv.lock +18 -18
- package/.next/standalone/server.js +1 -1
- package/README.md +19 -95
- package/bin/failproofai.mjs +8 -25
- package/dist/cli.mjs +20091 -25864
- package/dist/worker.mjs +4881 -6361
- package/lib/auth/api-server-client.ts +28 -0
- package/lib/claude-sessions.ts +101 -27
- package/lib/cli-registry.ts +1 -33
- package/lib/download-session.ts +0 -37
- package/lib/projects.ts +3 -27
- package/lib/sqlite-reader.ts +78 -1
- package/package.json +15 -14
- package/pi-extension/index.ts +61 -8
- package/scripts/changelog-open.py +115 -0
- package/src/audit/cache.ts +6 -0
- package/src/audit/cli-adapters/claude.ts +13 -0
- package/src/audit/cli-adapters/index.ts +0 -24
- package/src/audit/cli-login.ts +4 -1
- package/src/audit/cli.ts +164 -5
- package/src/audit/desktop-notify.ts +420 -0
- package/src/audit/harm-report.ts +101 -0
- package/src/audit/index.ts +284 -15
- package/src/audit/leak-fingerprint.ts +200 -0
- package/src/audit/leak-notice.ts +259 -0
- package/src/audit/leak-record.ts +232 -0
- package/src/audit/leak-scan.ts +268 -0
- package/src/audit/leak-store.ts +265 -0
- package/src/audit/macos-notifier.ts +310 -0
- package/src/audit/redact-example.ts +322 -15
- package/src/audit/report-harm.ts +12 -1
- package/src/audit/report.ts +6 -3
- package/src/audit/schedule-cli.ts +37 -0
- package/src/audit/scoring.ts +49 -0
- package/src/audit/types.ts +47 -0
- package/src/hooks/builtin-policies.ts +71 -8
- package/src/hooks/configure-wizard.ts +20 -0
- package/src/hooks/enforcement-capability.ts +0 -97
- package/src/hooks/fp-config.ts +71 -11
- package/src/hooks/fp-home.ts +27 -0
- package/src/hooks/handler.ts +84 -62
- package/src/hooks/harness-cli.ts +1 -3
- package/src/hooks/integrations.ts +63 -769
- package/src/hooks/normalize-cli-payload.ts +0 -126
- package/src/hooks/notice.ts +191 -0
- package/src/hooks/policy-evaluator.ts +6 -297
- package/src/hooks/resolve-transcript-path.ts +0 -10
- package/src/hooks/tool-name-canonicalize.ts +0 -64
- package/src/hooks/types.ts +2 -533
- package/src/hooks/uninstall-cli.ts +15 -0
- package/.next/standalone/.grok/hooks/failproofai.json +0 -172
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07hor7h._.js +0 -5
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0elsuli._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gnj_qa._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljuzm_._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w23bpg._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0x5c7sh._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yik9_w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1jg6s9m._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1s6qci9._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1xfm05z._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0tdy_6l._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_grok-projects_ts_1i2epl_._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_qwen-projects_ts_0wu_a8e._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0_4gcz8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0d--wmy._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qqrlf_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s11fx1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1bbk-si._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1c3ybxr._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1izi7zn._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__21bj7uw._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_00z5uuq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_cline-projects_ts_13_hbdd._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_grok-projects_ts_0rc-d0o._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_ori-projects_ts_04tofqo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_qwen-projects_ts_0w3i9o7._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
- package/.next/standalone/.next/static/chunks/0hzmxwb2pvk5p.js +0 -6
- package/.next/standalone/.next/static/chunks/0j884dwnwtyrt.js +0 -1
- package/.next/standalone/.next/static/chunks/0mr9dg3bnloa1.js +0 -1
- package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
- package/.next/standalone/.next/static/chunks/3gf1jg-5m-u1t.css +0 -2
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
- package/.next/standalone/.next/static/chunks/3ws7eljkq25wg.js +0 -69
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
- package/.next/standalone/.qwen/settings.json +0 -251
- package/.next/standalone/assets/logos/cline.png +0 -0
- package/.next/standalone/assets/logos/grok-dark.svg +0 -1
- package/.next/standalone/assets/logos/grok-light.svg +0 -1
- package/.next/standalone/assets/logos/ori-dark.svg +0 -1
- package/.next/standalone/assets/logos/ori-light.svg +0 -1
- package/.next/standalone/assets/logos/qwen.svg +0 -1
- package/.next/standalone/lib/cline-projects.ts +0 -155
- package/.next/standalone/lib/cline-sessions.ts +0 -268
- package/.next/standalone/lib/grok-projects.ts +0 -123
- package/.next/standalone/lib/grok-sessions.ts +0 -355
- package/.next/standalone/lib/ori-projects.ts +0 -141
- package/.next/standalone/lib/ori-sessions.ts +0 -221
- package/.next/standalone/lib/qwen-projects.ts +0 -106
- package/.next/standalone/lib/qwen-sessions.ts +0 -313
- package/lib/cline-projects.ts +0 -155
- package/lib/cline-sessions.ts +0 -268
- package/lib/grok-projects.ts +0 -123
- package/lib/grok-sessions.ts +0 -355
- package/lib/ori-projects.ts +0 -141
- package/lib/ori-sessions.ts +0 -221
- package/lib/qwen-projects.ts +0 -106
- package/lib/qwen-sessions.ts +0 -313
- package/src/audit/cli-adapters/cline.ts +0 -50
- package/src/audit/cli-adapters/grok.ts +0 -70
- package/src/audit/cli-adapters/ori.ts +0 -55
- package/src/audit/cli-adapters/qwen.ts +0 -71
- package/src/hooks/batch-expand.ts +0 -380
- package/src/hooks/batch-fanout.ts +0 -142
- package/src/hooks/risk-patterns.ts +0 -26
- /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{qJx3mbOQV-5ElrcRb4Sp7 → P6icI2V2PvVuePJrqjHCr}/_ssgManifest.js +0 -0
|
@@ -10,56 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { IntegrationType } from "./types";
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* True when `parsed` carries grok's camelCase envelope.
|
|
15
|
-
*
|
|
16
|
-
* This exists because grok EXECUTES OTHER CLIS' HOOK CONFIGS. Its discovery
|
|
17
|
-
* scans `~/.claude/settings.json`, `~/.claude/settings.local.json`,
|
|
18
|
-
* `<cwd>/.claude/settings.json` and the `.cursor` equivalents by default
|
|
19
|
-
* (`[compat.claude] hooks = true`) — and `<cwd>/.claude/settings.json` is
|
|
20
|
-
* exactly the file `policies --install --cli claude --scope project` writes. So
|
|
21
|
-
* on any machine with both tools, grok runs OUR hooks and hands them `--cli
|
|
22
|
-
* claude` on the command line while piping ITS OWN camelCase payload.
|
|
23
|
-
*
|
|
24
|
-
* Verified live against grok 1.0.3: the hook fired, `tool_name` and
|
|
25
|
-
* `tool_input` were both undefined, and every builtin that reads either —
|
|
26
|
-
* block-sudo, block-env-files, block-secrets-write, block-force-push — allowed.
|
|
27
|
-
* Installed, running, costing latency, enforcing nothing; strictly worse than
|
|
28
|
-
* no coverage, because the install reports success. Detecting the envelope lets
|
|
29
|
-
* handler.ts re-route the event onto grok's path (tool maps AND response
|
|
30
|
-
* shapes — grok ignores Claude's `hookSpecificOutput.permissionDecision`, also
|
|
31
|
-
* verified by A/B).
|
|
32
|
-
*
|
|
33
|
-
* The test is on the PAYLOAD, never on an env var: `GROK_HOOK_EVENT` is set by
|
|
34
|
-
* grok's runner but is still just an env var, and mistaking a real Claude event
|
|
35
|
-
* for a grok one would break Claude's own enforcement. `workspaceRoot` +
|
|
36
|
-
* `hookEventName` with no `hook_event_name` is a shape Claude never sends.
|
|
37
|
-
*/
|
|
38
|
-
export function isGrokEnvelope(parsed: Record<string, unknown>): boolean {
|
|
39
|
-
return (
|
|
40
|
-
typeof parsed.hookEventName === "string" &&
|
|
41
|
-
typeof parsed.workspaceRoot === "string" &&
|
|
42
|
-
parsed.hook_event_name === undefined
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* The CLI whose contract actually governs this event.
|
|
48
|
-
*
|
|
49
|
-
* Identical to the declared `--cli` for every integration except the grok
|
|
50
|
-
* cross-execution case above, where the flag says `claude` and the wire says
|
|
51
|
-
* grok. Returning "grok" there routes the rest of the pipeline — tool-name and
|
|
52
|
-
* tool-input canonicalization, and the response shape policy-evaluator emits —
|
|
53
|
-
* onto the contract that will actually be honored.
|
|
54
|
-
*/
|
|
55
|
-
export function resolveEffectiveCli(
|
|
56
|
-
cli: IntegrationType,
|
|
57
|
-
parsed: Record<string, unknown>,
|
|
58
|
-
): IntegrationType {
|
|
59
|
-
if (cli === "claude" && isGrokEnvelope(parsed)) return "grok";
|
|
60
|
-
return cli;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
13
|
export function normalizeCliPayload(cli: IntegrationType, parsed: Record<string, unknown>): void {
|
|
64
14
|
// Antigravity (agy) pipes a camelCase protojson payload; normalize the fields
|
|
65
15
|
// the handler downstream reads to canonical snake_case BEFORE any
|
|
@@ -97,33 +47,6 @@ export function normalizeCliPayload(cli: IntegrationType, parsed: Record<string,
|
|
|
97
47
|
}
|
|
98
48
|
}
|
|
99
49
|
|
|
100
|
-
// grok pipes a camelCase envelope (verified live against grok 1.0.3 with a
|
|
101
|
-
// recorder hook on all 14 events). Normalize the fields the handler reads to
|
|
102
|
-
// snake_case BEFORE any canonicalization runs. Note `workspaceRoot` → `cwd`:
|
|
103
|
-
// grok sends BOTH, they were identical in every capture, and `cwd` is what
|
|
104
|
-
// resolveCwd + block-read-outside-cwd need. `toolResult` is grok's PostToolUse
|
|
105
|
-
// output key — it is NOT Claude's `tool_response`, so a sanitize policy
|
|
106
|
-
// reading the documented Claude key would see nothing without this line.
|
|
107
|
-
// `hookEventName` is deliberately NOT mapped: its value is snake_case
|
|
108
|
-
// ("pre_tool_use") whereas the canonical set is PascalCase, and the `--hook`
|
|
109
|
-
// arg already carries the PascalCase name.
|
|
110
|
-
if (cli === "grok") {
|
|
111
|
-
if (typeof parsed.toolName === "string") parsed.tool_name = parsed.toolName;
|
|
112
|
-
if (parsed.toolInput !== undefined) parsed.tool_input = parsed.toolInput;
|
|
113
|
-
if (parsed.toolResult !== undefined && parsed.tool_response === undefined) {
|
|
114
|
-
parsed.tool_response = parsed.toolResult;
|
|
115
|
-
}
|
|
116
|
-
if (typeof parsed.sessionId === "string") parsed.session_id = parsed.sessionId;
|
|
117
|
-
if (typeof parsed.transcriptPath === "string") parsed.transcript_path = parsed.transcriptPath;
|
|
118
|
-
if (typeof parsed.workspaceRoot === "string" && parsed.cwd === undefined) {
|
|
119
|
-
parsed.cwd = parsed.workspaceRoot;
|
|
120
|
-
}
|
|
121
|
-
if (typeof parsed.permissionMode === "string") parsed.permission_mode = parsed.permissionMode;
|
|
122
|
-
if (typeof parsed.stopHookActive === "boolean") {
|
|
123
|
-
parsed.stop_hook_active = parsed.stopHookActive;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
50
|
// Goose pipes `event` / `working_dir` instead of Claude's `hook_event_name` /
|
|
128
51
|
// `cwd` (its `tool_name` / `tool_input` are already canonical field names).
|
|
129
52
|
// Normalize both so resolveCwd keeps its cwd (block-read-outside-cwd) and the
|
|
@@ -135,53 +58,4 @@ export function normalizeCliPayload(cli: IntegrationType, parsed: Record<string,
|
|
|
135
58
|
parsed.hook_event_name = parsed.event;
|
|
136
59
|
}
|
|
137
60
|
}
|
|
138
|
-
|
|
139
|
-
// Cline pipes its own payload verbatim — its hooks are event-NAMED scripts,
|
|
140
|
-
// not a generated shim that could pre-shape stdin — so without this every
|
|
141
|
-
// field the handler reads (tool_name, tool_input, session_id, cwd) is
|
|
142
|
-
// undefined and resolveCwd / block-read-outside-cwd see nothing. Verified live
|
|
143
|
-
// against cline v3.0.60 with a recorder hook on PreToolUse.
|
|
144
|
-
//
|
|
145
|
-
// `tool_call.input` is the source, NOT `preToolUse.parameters`: the latter
|
|
146
|
-
// JSON-STRINGIFIES every array value (`commands` arrives as the literal string
|
|
147
|
-
// `["echo hi"]`), so reading it would hand the batch expander a string and
|
|
148
|
-
// silently drop the entire fan-out. `preToolUse` is used only as a fallback
|
|
149
|
-
// for a payload carrying no `tool_call` at all.
|
|
150
|
-
//
|
|
151
|
-
// `hookName` is deliberately NOT mapped onto hook_event_name: its value is
|
|
152
|
-
// cline's INTERNAL name ("tool_call"), not the filename event, and the --hook
|
|
153
|
-
// argument already carries the canonical one. Same reasoning as grok's
|
|
154
|
-
// hookEventName.
|
|
155
|
-
if (cli === "cline") {
|
|
156
|
-
const tc = parsed.tool_call;
|
|
157
|
-
if (tc && typeof tc === "object" && !Array.isArray(tc)) {
|
|
158
|
-
const t = tc as Record<string, unknown>;
|
|
159
|
-
if (typeof t.name === "string") parsed.tool_name = t.name;
|
|
160
|
-
if (t.input !== undefined) parsed.tool_input = t.input;
|
|
161
|
-
}
|
|
162
|
-
const pre = parsed.preToolUse;
|
|
163
|
-
if (pre && typeof pre === "object" && !Array.isArray(pre)) {
|
|
164
|
-
const pp = pre as Record<string, unknown>;
|
|
165
|
-
if (parsed.tool_name === undefined && typeof pp.toolName === "string") {
|
|
166
|
-
parsed.tool_name = pp.toolName;
|
|
167
|
-
}
|
|
168
|
-
if (parsed.tool_input === undefined && pp.parameters !== undefined) {
|
|
169
|
-
parsed.tool_input = pp.parameters;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// taskId is the conversation ("conv_…"). sessionContext.rootSessionId and
|
|
173
|
-
// agent_id / parent_agent_id stay raw for custom policies correlating a
|
|
174
|
-
// subagent back to its root.
|
|
175
|
-
if (typeof parsed.taskId === "string") parsed.session_id = parsed.taskId;
|
|
176
|
-
const roots = parsed.workspaceRoots;
|
|
177
|
-
if (Array.isArray(roots) && typeof roots[0] === "string" && roots[0].length > 0) {
|
|
178
|
-
parsed.cwd = roots[0];
|
|
179
|
-
} else {
|
|
180
|
-
const wi = parsed.workspaceInfo;
|
|
181
|
-
if (wi && typeof wi === "object" && !Array.isArray(wi)) {
|
|
182
|
-
const rp = (wi as Record<string, unknown>).rootPath;
|
|
183
|
-
if (typeof rp === "string" && rp.length > 0) parsed.cwd = rp;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
61
|
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Putting a short message in front of a HUMAN, inside the agent they are using.
|
|
3
|
+
*
|
|
4
|
+
* A notice is not a verdict. `allow` / `deny` / `instruct` all answer "what
|
|
5
|
+
* happens to this tool call"; a notice answers nothing — it is one line of text
|
|
6
|
+
* for the person, delivered alongside whatever the verdict turned out to be.
|
|
7
|
+
* That is why it is a separate field rather than a fourth decision: shaping it
|
|
8
|
+
* into stdout beside a verdict ad hoc is how the copilot nested-vs-flat
|
|
9
|
+
* `additionalContext` bug happened.
|
|
10
|
+
*
|
|
11
|
+
* ## Why the channel differs per CLI, and why it is not `additionalContext`
|
|
12
|
+
*
|
|
13
|
+
* Measured across all 12 CLIs by live probe. The obvious channel —
|
|
14
|
+
* `hookSpecificOutput.additionalContext` — reaches the MODEL, not the user, and
|
|
15
|
+
* a model is a paraphraser: over eight real Claude turns it mentioned an
|
|
16
|
+
* informational notice 8/8, but an earlier notice that asked the agent to *act*
|
|
17
|
+
* was disclaimed as a suspected prompt injection 6/6 ("instructions arriving
|
|
18
|
+
* that way aren't something I'll act on unasked"). A security notice routed
|
|
19
|
+
* through something that may decline to repeat it is not a delivered notice.
|
|
20
|
+
*
|
|
21
|
+
* So each CLI gets the channel that was proven to paint text on the user's
|
|
22
|
+
* terminal, and the ones with no such channel get nothing rather than a
|
|
23
|
+
* pretend delivery:
|
|
24
|
+
*
|
|
25
|
+
* claude `{"systemMessage":…}` renders `Stop says: …`; model-invisible
|
|
26
|
+
* (a neutral probe answered `NONE`)
|
|
27
|
+
* codex `{"systemMessage":…}` renders `• Stop (completed) says: …`
|
|
28
|
+
* copilot exit 0 + stderr renders as a `!` session warning
|
|
29
|
+
* factory plain stdout renders under a `Hooks Stop` block
|
|
30
|
+
*
|
|
31
|
+
* opencode and pi have real channels too, but they live inside the plugin shims
|
|
32
|
+
* we ship rather than in a hook's stdout, so a private JSON field is consumed
|
|
33
|
+
* there and handed to `showToast` / `ui.notify`. OpenClaw, cursor, devin, goose,
|
|
34
|
+
* antigravity and hermes have no verified channel that reaches a human without
|
|
35
|
+
* an unsupported escape hatch — they get nothing here, and their users get the
|
|
36
|
+
* finding from `failproofai audit` and the scheduled desktop notification.
|
|
37
|
+
*/
|
|
38
|
+
import type { IntegrationType } from "./types";
|
|
39
|
+
|
|
40
|
+
/** How a notice is delivered on one CLI. */
|
|
41
|
+
type NoticeChannel = "systemMessage" | "stderr" | "stdout" | "shim" | "none";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Per-CLI channel, from live probes rather than documentation.
|
|
45
|
+
*
|
|
46
|
+
* A CLI absent from this map delivers nothing — deliberately. Guessing a
|
|
47
|
+
* channel produces the worst outcome available: output that looks delivered
|
|
48
|
+
* from our side and reaches nobody, so we would report a leak as "notified"
|
|
49
|
+
* that the user never saw.
|
|
50
|
+
*/
|
|
51
|
+
const NOTICE_CHANNEL: Partial<Record<IntegrationType, NoticeChannel>> = {
|
|
52
|
+
claude: "systemMessage",
|
|
53
|
+
codex: "systemMessage",
|
|
54
|
+
copilot: "stderr",
|
|
55
|
+
factory: "stdout",
|
|
56
|
+
opencode: "shim",
|
|
57
|
+
pi: "shim",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Claude truncates `systemMessage` at 4000 characters on the cloud-forwarding
|
|
62
|
+
* path, and a notice that gets cut mid-sentence reads as a broken tool. Ours is
|
|
63
|
+
* two lines by design; this is a backstop, not a budget.
|
|
64
|
+
*/
|
|
65
|
+
const MAX_NOTICE_CHARS = 900;
|
|
66
|
+
|
|
67
|
+
export interface ShapedNotice {
|
|
68
|
+
/** Merged into the outcome's stdout, or empty. */
|
|
69
|
+
stdout: string;
|
|
70
|
+
/** Merged into the outcome's stderr, or empty. */
|
|
71
|
+
stderr: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Render a notice for one CLI, merging it into any verdict JSON already bound
|
|
76
|
+
* for stdout.
|
|
77
|
+
*
|
|
78
|
+
* `existingStdout` matters: a Stop-gate deny already emits a JSON object on
|
|
79
|
+
* several CLIs, and emitting a second object would produce two JSON documents
|
|
80
|
+
* on one stream — which every host parses as a syntax error and drops, taking
|
|
81
|
+
* the deny with it. So the notice is merged INTO that object when there is one.
|
|
82
|
+
*/
|
|
83
|
+
export function shapeNotice(
|
|
84
|
+
cli: IntegrationType | undefined,
|
|
85
|
+
notice: string,
|
|
86
|
+
existingStdout = "",
|
|
87
|
+
): ShapedNotice {
|
|
88
|
+
const empty: ShapedNotice = { stdout: existingStdout, stderr: "" };
|
|
89
|
+
if (!notice.trim() || !cli) return empty;
|
|
90
|
+
|
|
91
|
+
const channel = NOTICE_CHANNEL[cli] ?? "none";
|
|
92
|
+
const text = notice.length > MAX_NOTICE_CHARS
|
|
93
|
+
? notice.slice(0, MAX_NOTICE_CHARS - 1) + "…"
|
|
94
|
+
: notice;
|
|
95
|
+
|
|
96
|
+
switch (channel) {
|
|
97
|
+
case "systemMessage": {
|
|
98
|
+
// Merge into the verdict object when one exists, so the stream stays a
|
|
99
|
+
// single JSON document.
|
|
100
|
+
if (existingStdout.trim().startsWith("{")) {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(existingStdout) as Record<string, unknown>;
|
|
103
|
+
parsed.systemMessage = text;
|
|
104
|
+
return { stdout: JSON.stringify(parsed), stderr: "" };
|
|
105
|
+
} catch {
|
|
106
|
+
// Not the JSON we thought it was — leave the verdict untouched
|
|
107
|
+
// rather than risk destroying it to add a courtesy message.
|
|
108
|
+
return empty;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Anything already on stdout that we could not merge into is left
|
|
112
|
+
// exactly as it is, and the notice is dropped instead. The alternative —
|
|
113
|
+
// emitting our own object over the top — DESTROYS it: measured with a
|
|
114
|
+
// JSON array on stdout, which does not start with `{`, so it fell past
|
|
115
|
+
// the merge and was replaced wholesale. A courtesy message is never worth
|
|
116
|
+
// more than whatever the host was already being told.
|
|
117
|
+
if (existingStdout.trim()) return empty;
|
|
118
|
+
return { stdout: JSON.stringify({ systemMessage: text }), stderr: "" };
|
|
119
|
+
}
|
|
120
|
+
case "stderr":
|
|
121
|
+
// Exit code is NOT touched: on copilot a non-zero exit from a tool event
|
|
122
|
+
// is a block, and a notice must never block anything.
|
|
123
|
+
return { stdout: existingStdout, stderr: text + "\n" };
|
|
124
|
+
case "stdout":
|
|
125
|
+
// factory renders plain text under its Hooks block. Only safe when no
|
|
126
|
+
// verdict JSON is already on the stream.
|
|
127
|
+
if (existingStdout.trim()) return empty;
|
|
128
|
+
return { stdout: text + "\n", stderr: "" };
|
|
129
|
+
case "shim": {
|
|
130
|
+
// OpenCode and Pi are in-process plugin integrations. Their generated
|
|
131
|
+
// shims consume this private field and call the host's real user-facing
|
|
132
|
+
// notification API; putting the text in additionalContext would send it
|
|
133
|
+
// to the model instead.
|
|
134
|
+
if (existingStdout.trim().startsWith("{")) {
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(existingStdout) as Record<string, unknown>;
|
|
137
|
+
parsed.failproofaiNotice = text;
|
|
138
|
+
return { stdout: JSON.stringify(parsed), stderr: "" };
|
|
139
|
+
} catch {
|
|
140
|
+
return empty;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (existingStdout.trim()) return empty;
|
|
144
|
+
return { stdout: JSON.stringify({ failproofaiNotice: text }), stderr: "" };
|
|
145
|
+
}
|
|
146
|
+
default:
|
|
147
|
+
return empty;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** True when this CLI has a channel that was proven to reach a human. */
|
|
152
|
+
export function canDeliverNotice(cli: IntegrationType | undefined): boolean {
|
|
153
|
+
return !!cli && (NOTICE_CHANNEL[cli] ?? "none") !== "none";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The notice itself. A FIXED template with no interpolation of scanned content.
|
|
158
|
+
*
|
|
159
|
+
* The count is a number we computed; everything else is a constant. That is not
|
|
160
|
+
* stylistic: the alternative is interpolating a finding's `example`, which is
|
|
161
|
+
* the verbatim text of a command from a repository — attacker-controlled
|
|
162
|
+
* through a README, a Makefile target or an npm script. Tested against the
|
|
163
|
+
* shipped redactor, `echo "IGNORE ALL PRIOR INSTRUCTIONS…"` passes through
|
|
164
|
+
* byte-identical, because that redactor masks secrets, not instructions. On the
|
|
165
|
+
* CLIs where the only channel is the model's context, that string would land
|
|
166
|
+
* directly in the prompt. So nothing scanned ever reaches this template.
|
|
167
|
+
*/
|
|
168
|
+
export function leakNoticeText(newFindings: number): string {
|
|
169
|
+
const n = newFindings === 1
|
|
170
|
+
? "a possible credential exposure"
|
|
171
|
+
: `${newFindings} possible credential exposures`;
|
|
172
|
+
return (
|
|
173
|
+
`failproofai found ${n} in this project's agent transcripts.\n` +
|
|
174
|
+
"Run `failproofai audit` to review the matches and rotate anything real."
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Copy for the OS notification raised by an unattended scheduled scan.
|
|
179
|
+
*
|
|
180
|
+
* Deliberately omits the count. A burst of generated fixtures can hit the
|
|
181
|
+
* record cap, and a desktop banner saying "500 leaked credentials" states a
|
|
182
|
+
* conclusion the detector cannot support. The dashboard carries the evidence;
|
|
183
|
+
* the banner's job is only to ask for review. */
|
|
184
|
+
export function desktopLeakNotice(): { title: string; body: string } {
|
|
185
|
+
return {
|
|
186
|
+
title: "failproofai audit needs review",
|
|
187
|
+
body:
|
|
188
|
+
"Possible credential exposure was found in your agent transcripts. " +
|
|
189
|
+
"Run failproofai audit to verify the matches and rotate anything real.",
|
|
190
|
+
};
|
|
191
|
+
}
|