failproofai 1.0.5-beta.2 → 1.0.6-beta.0
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 +6 -6
- 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 +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.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 +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.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 +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- 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.nft.json +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.nft.json +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/logout/route.js.nft.json +1 -1
- 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.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.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 +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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.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 +14 -14
- 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.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 +1 -1
- 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.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 +3 -3
- 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]__0sds_16._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +3 -3
- 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/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/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]__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]__1xu5t8b._.js → [root-of-the-server]__1i26dsw._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0sz_5na._.js → [root-of-the-server]__1ouobn4._.js} +1 -1
- 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/_0-yi74u._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1mo4bp9._.js → _09hs6ms._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1olen4g._.js → _0lbwa8m._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0wso5d3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1-3unkg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +3 -3
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_components_project-list_tsx_1ke6anr._.js +1 -1
- 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/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1hl81y4._.js +3 -0
- 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/middleware-build-manifest.js +6 -6
- 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 +19 -19
- package/.next/standalone/.next/static/chunks/{0pdmk3xha1zih.js → 03n_qjih7-w6q.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0h95tsad4_-gr.js → 06jxjnw8bfczc.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0671mdu5plc8o.js → 0vanab2g65op_.js} +1 -1
- package/.next/standalone/.next/static/chunks/17pjz_h45j10c.js +1 -0
- package/.next/standalone/.next/static/chunks/1ekh-hj_iom84.js +1 -0
- package/.next/standalone/.next/static/chunks/{2pnvnpg-kdbyt.js → 1so_3s0q4c4j1.js} +1 -1
- package/.next/standalone/.next/static/chunks/1xacnwjkbekn7.js +1 -0
- package/.next/standalone/.next/static/chunks/2hg_bp8z_vm2g.js +1 -0
- package/.next/standalone/.next/static/chunks/{1qwk_vp6dsat_.js → 2u0kol_boahtv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3gfi5ixrc6v1z.js → 3_vesf83-0903.js} +1 -1
- package/.next/standalone/.next/static/chunks/3d3hzhcnns6ww.js +1 -0
- package/.next/standalone/.next/static/chunks/{315zh293r-n7v.js → 3i8rg2fv9nd6e.js} +1 -1
- package/.next/standalone/.next/static/chunks/3iasus89n8mg1.js +6 -0
- package/.next/standalone/.next/static/chunks/{04cawl3i7gqi5.js → 44_cj1_i_aa2z.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-44avr18ueo9ob.js → turbopack-38nzuvp95_2rv.js} +1 -1
- package/.next/standalone/fp-cloud-cli/uv.lock +3 -3
- package/.next/standalone/hermes-plugin/README.md +133 -0
- package/.next/standalone/hermes-plugin/__init__.py +248 -0
- package/.next/standalone/hermes-plugin/client.py +134 -0
- package/.next/standalone/hermes-plugin/ledger.py +187 -0
- package/.next/standalone/hermes-plugin/plugin.yaml +37 -0
- 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/app-page-turbo-experimental.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/app-render/create-component-styles-and-scripts.js +2 -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 +14 -0
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/disk-lru-cache.external.js +7 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/use-cache/use-cache-wrapper.js +6 -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 +10 -10
- package/.next/standalone/node_modules/react/cjs/react.development.js +92 -46
- package/.next/standalone/node_modules/react/cjs/react.production.js +112 -89
- package/.next/standalone/node_modules/react/package.json +1 -1
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1165 -790
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1171 -791
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +1294 -862
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +1305 -868
- package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +1315 -896
- package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +24 -5
- package/.next/standalone/node_modules/react-dom/package.json +4 -4
- package/.next/standalone/package.json +13 -12
- package/.next/standalone/sdk/python/uv.lock +18 -18
- package/.next/standalone/server.js +1 -1
- package/README.md +29 -13
- package/bin/failproofai.mjs +3 -1
- package/dist/cli.mjs +420 -178
- package/dist/worker.mjs +17 -3
- package/hermes-plugin/README.md +133 -0
- package/hermes-plugin/__init__.py +248 -0
- package/hermes-plugin/client.py +134 -0
- package/hermes-plugin/ledger.py +187 -0
- package/hermes-plugin/plugin.yaml +37 -0
- package/package.json +13 -12
- package/scripts/translate-docs/validate-translation.ts +48 -0
- package/src/hooks/handler.ts +29 -1
- package/src/hooks/integrations.ts +264 -73
- package/src/hooks/manager.ts +17 -5
- package/src/hooks/types.ts +8 -8
- package/src/hooks/worker-server.ts +1 -0
- package/.next/standalone/.next/server/chunks/[externals]__157pf8j._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +0 -3
- package/.next/standalone/.next/static/chunks/0cn62s92buqr1.js +0 -6
- package/.next/standalone/.next/static/chunks/1oey07bwm1ayr.js +0 -1
- package/.next/standalone/.next/static/chunks/3aoj_ov89ym_h.js +0 -1
- package/.next/standalone/.next/static/chunks/3k3g_4k13t9za.js +0 -1
- package/.next/standalone/.next/static/chunks/3l36d73kj18zw.js +0 -1
- package/.next/standalone/.next/static/chunks/40lpt2o2b283e.js +0 -1
- /package/.next/standalone/.next/static/{GDwSJ81wNHeh1pQdxpvk_ → -7OT2NwGPxAu-skEL_Xee}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{GDwSJ81wNHeh1pQdxpvk_ → -7OT2NwGPxAu-skEL_Xee}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{GDwSJ81wNHeh1pQdxpvk_ → -7OT2NwGPxAu-skEL_Xee}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/1c6aptmytnu6q.js","static/chunks/2yuzx8716maf0.js","static/chunks/
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,{otherChunks:["static/chunks/1c6aptmytnu6q.js","static/chunks/2yuzx8716maf0.js","static/chunks/3i8rg2fv9nd6e.js","static/chunks/06jxjnw8bfczc.js"],runtimeModuleIds:[94553]}]),(()=>{let e;if(!Array.isArray(globalThis.TURBOPACK))return;var t,r=function(){if(null!=self.TURBOPACK_ASSET_SUFFIX)return self.TURBOPACK_ASSET_SUFFIX;let e=document?.currentScript?.getAttribute?.("src")??"",t=e.indexOf("?");return t>=0?e.slice(t):""}(),n=((t=n||{})[t.Runtime=0]="Runtime",t[t.Parent=1]="Parent",t[t.Update=2]="Update",t);let o=new WeakMap;function l(e,t){this.m=e,this.e=t}let i=l.prototype,u=Object.prototype.hasOwnProperty,s="u">typeof Symbol&&Symbol.toStringTag;function c(e,t,r){u.call(e,t)||Object.defineProperty(e,t,r)}function a(e,t){let r=e[t];return r||(r=f(t),e[t]=r),r}function f(e){return{exports:{},error:void 0,id:e,namespaceObject:void 0}}function p(e,t,r){c(e,"__esModule",{value:!0}),s&&c(e,s,{value:"Module"});let n=0;for(;n<t.length;){let r=t[n++],o=t[n++];if("number"==typeof o)if(0===o)c(e,r,{value:t[n++],enumerable:!0,writable:!1});else throw Error(`unexpected tag: ${o}`);else"function"==typeof t[n]?c(e,r,{get:o,set:t[n++],enumerable:!0}):c(e,r,{get:o,enumerable:!0})}r||Object.seal(e)}function h(e,t){(null!=t?a(this.c,t):this.m).exports=e}i.s=function(e,t,r){let n,o;null!=t?o=(n=a(this.c,t)).exports:(n=this.m,o=this.e),n.namespaceObject=o,p(o,e,r)},i.j=function(e,t){let r,n;null!=t?n=(r=a(this.c,t)).exports:(r=this.m,n=this.e);let l=function(e,t){let r=o.get(e);if(!r){o.set(e,r=[]);let n=e=>{if("default"!==e){for(let t of r)if(u.call(t,e))return t}};e.exports=e.namespaceObject=new Proxy(t,{get(e,t){if(u.call(e,t)||"default"===t||"__esModule"===t)return Reflect.get(e,t);let r=n(t);return r&&Reflect.get(r,t)},set:()=>!1,defineProperty:()=>!1,deleteProperty:()=>!1,has:(e,t)=>!!Reflect.has(e,t)||"default"!==t&&"__esModule"!==t&&void 0!==n(t),ownKeys(e){let t=Reflect.ownKeys(e);for(let e of r)for(let r of Reflect.ownKeys(e))"default"===r||t.includes(r)||t.push(r);return t},getOwnPropertyDescriptor(e,t){let r=Reflect.getOwnPropertyDescriptor(e,t);if(r||"default"===t||"__esModule"===t)return r;let o=n(t);if(o)return{enumerable:!0,configurable:!0,get:()=>Reflect.get(o,t)}}})}return r}(r,n);"object"==typeof e&&null!==e&&l.push(e)},i.v=h,i.n=function(e,t){let r;(r=null!=t?a(this.c,t):this.m).exports=r.namespaceObject=e};let d=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,m=[null,d({}),d([]),d(d)];function y(e,t,r){let n=[],o=-1;for(let t=e;("object"==typeof t||"function"==typeof t)&&!m.includes(t);t=d(t))for(let r of Object.getOwnPropertyNames(t))n.push(r,function(e,t){return()=>e[t]}(e,r)),-1===o&&"default"===r&&(o=n.length-1);return r&&o>=0||(o>=0?n.splice(o,1,0,e):n.push("default",0,e)),p(t,n),t}function g(e){let t=K(e,this.m);if(t.namespaceObject)return t.namespaceObject;let r=t.exports;return t.namespaceObject=y(r,"function"==typeof r?function(...e){return r.apply(this,e)}:Object.create(null),r&&r.__esModule)}function b(e){let t=e.indexOf("#");-1!==t&&(e=e.substring(0,t));let r=e.indexOf("?");return -1!==r&&(e=e.substring(0,r)),e}i.i=g,i.A=function(e){return this.r(e)(g.bind(this))},i.t="function"==typeof require?require:function(){throw Error("Unexpected use of runtime require")},i.r=function(e){return K(e,this.m).exports},i.f=function(e){function t(t){if(t=b(t),u.call(e,t))return e[t].module();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r}return t.keys=()=>Object.keys(e),t.resolve=t=>{if(t=b(t),u.call(e,t))return e[t].id();let r=Error(`Cannot find module '${t}'`);throw r.code="MODULE_NOT_FOUND",r},t.import=async e=>await t(e),t};let O=function(e){let t=new URL(e,"x:/"),r={};for(let e in t)r[e]=t[e];for(let t in r.href=e,r.pathname=e.replace(/[?#].*/,""),r.origin=r.protocol="",r.toString=r.toJSON=(...t)=>e,r)Object.defineProperty(this,t,{enumerable:!0,configurable:!0,value:r[t]})};function w(e,t){throw Error(`Invariant: ${t(e)}`)}O.prototype=URL.prototype,i.U=O,i.z=function(e){throw Error("dynamic usage of require is not supported")},i.g=globalThis;let R=l.prototype,k="string"==typeof TURBOPACK_CHUNK_BASE_PATH?TURBOPACK_CHUNK_BASE_PATH:"/_next/",U=new Map;i.M=U;let v=new Map,_=new Map,P=new Map;async function j(e,t,r){let n;if("string"==typeof r)return function(e,t,r){return A(e,t,r)}(e,t,E(r));let o=r.included||[],l=o.map(e=>!!U.has(e)||v.get(e));if(l.length>0&&l.every(e=>e))return void await Promise.all(l);for(let l of(n=A(e,t,E(r.path)),o))v.has(l)||v.set(l,n);await n}R.l=function(e){return j(n.Parent,this.m.id,e)};let C=Promise.resolve(void 0),$=new WeakMap;function A(t,r,o){let l=e.loadChunkCached(t,o),i=$.get(l);if(void 0===i){let e=$.set.bind($,l,C);i=l.then(e).catch(e=>{let l;switch(t){case n.Runtime:l=`as a runtime dependency of chunk ${r}`;break;case n.Parent:l=`from module ${r}`;break;case n.Update:l="from an HMR update";break;default:w(t,e=>`Unknown source type: ${e}`)}let i=Error(`Failed to load chunk ${o} ${l}${e?`: ${e}`:""}`,e?{cause:e}:void 0);throw i.name="ChunkLoadError",i}),$.set(l,i)}return i}R.L=function(e){var t,r;return t=n.Parent,r=this.m.id,A(t,r,e)};R.R=function(e){let t=this.r(e);return t?.default??t},R.P=function(e){return`/ROOT/${e??""}`},R.F=function(e){return e?`file:///ROOT/${e.split("/").map(encodeURIComponent).join("/")}`:"file:///ROOT/"},R.q=function(e,t){h.call(this,`${e}${r}`,t)};let T=/[^A-Za-z0-9\-_.!~*'()/]/;function E(e,t=k){let n=T.test(e)?e.split("/").map(encodeURIComponent).join("/"):e;return`${t}${n}${r}`}function S(e,t){let r,n=e.indexOf("?");if(-1!==n)r=n;else{let t=e.indexOf("#");r=-1!==t?t:e.length}return r>=t.length&&e.startsWith(t,r-t.length)}R.b=k,R.X=r,R.h=E;function x(e){return S(e,".css")}let M={};i.c=M;let K=(e,t)=>{let r=M[e];if(r){if(r.error)throw r.error;return r}return N(e,n.Parent,t.id)};function N(e,t,r){let n=U.get(e);if("function"!=typeof n)throw Error(function(e,t,r){let n;switch(t){case 0:n=`as a runtime entry of chunk ${r}`;break;case 1:n=`because it was required from module ${r}`;break;case 2:n="because of an HMR update";break;default:w(t,e=>`Unknown source type: ${e}`)}return`Module ${e} was instantiated ${n}, but the module factory is not available.`}(e,t,r));let o=f(e),i=o.exports;M[e]=o;let u=new l(o,i);try{n(u,o,i)}catch(e){throw o.error=e,e}return o.namespaceObject&&o.exports!==o.namespaceObject&&y(o.exports,o.namespaceObject),o}function B(t){let r;if(!Array.isArray(t))return e.registerChunk(void 0,t);let n=function(e){if("string"==typeof e)return e;if(e)return{src:e.getAttribute("src")};if("u">typeof TURBOPACK_NEXT_CHUNK_URLS)return{src:TURBOPACK_NEXT_CHUNK_URLS.pop()};throw Error("chunk path empty but not in a worker")}(t[0]);return 2===t.length?r=t[1]:(r=void 0,!function(e,t){let r=1;for(;r<e.length;){let n,o=r+1;for(;o<e.length&&"function"!=typeof e[o];)o++;if(o===e.length)throw Error("malformed chunk format, expected a factory function");let l=e[o];for(let l=r;l<o;l++){let r=e[l],o=t.get(r);if(o){n=o;break}}let i=n??l,u=!1;for(let n=r;n<o;n++){let r=e[n];t.has(r)||(u||(i===l&&Object.defineProperty(l,"name",{value:"module evaluation"}),u=!0),t.set(r,i))}r=o+1}}(t,U)),e.registerChunk(n,r)}let L=new Map;function I(e){let t=L.get(e);if(!t){let r,n;t={resolved:!1,loadingStarted:!1,retryAttempts:0,promise:new Promise((e,t)=>{r=e,n=t}),resolve:()=>{t.resolved=!0,r()},reject:n},L.set(e,t)}return t}function q(e,t,r,n,o){!(null==n||n instanceof DOMException&&"NetworkError"===n.name)||r.retryAttempts>=1||L.get(t)!==r?(L.get(t)===r&&L.delete(t),r.reject(n)):(r.retryAttempts++,setTimeout(()=>{r.resolved||L.get(t)!==r||(o?o():(r.loadingStarted=!1,H(e,t)))},200+Math.floor(401*Math.random())))}function H(e,t){let r=I(t);if(r.loadingStarted)return r.promise;if(e===n.Runtime)return r.loadingStarted=!0,x(t)&&r.resolve(),r.promise;if("function"==typeof importScripts)if(x(t));else if(S(t,".js")){self.TURBOPACK_NEXT_CHUNK_URLS.push(t);try{importScripts(t)}catch(n){q(e,t,r,n)}}else throw Error(`can't infer type of chunk from URL ${t} in worker`);else{let n=decodeURI(t);if(x(t))if(document.querySelectorAll(`link[rel=stylesheet][href="${t}"],link[rel=stylesheet][href^="${t}?"],link[rel=stylesheet][href="${n}"],link[rel=stylesheet][href^="${n}?"]`).length>0)r.resolve();else{let n=()=>{let o=document.createElement("link");return o.rel="stylesheet",o.crossOrigin=null,o.href=t,o.onerror=()=>{let l=document.createComment("");o.replaceWith(l),q(e,t,r,void 0,()=>l.replaceWith(n()))},o.onload=()=>{r.resolve()},o};document.head.appendChild(n())}else if(S(t,".js")){let o=document.querySelectorAll(`script[src="${t}"],script[src^="${t}?"],script[src="${n}"],script[src^="${n}?"]`);if(o.length>0)for(let n of Array.from(o))n.addEventListener("error",()=>{n.remove(),q(e,t,r)},{once:!0});else{let n=document.createElement("script");n.crossOrigin=null,n.src=t,n.onerror=()=>{n.remove(),q(e,t,r)},document.head.appendChild(n)}}else throw Error(`can't infer type of chunk from URL ${t}`)}return r.loadingStarted=!0,r.promise}e={async registerChunk(e,t){let r;if(null!=e&&(r=function(e){if("string"==typeof e)return e;let t=decodeURIComponent(e.src.replace(/[?#].*$/,""));return t.startsWith(k)?t.slice(k.length):t}(e),I("string"==typeof e?E(e):e.src).resolve()),null!=t){for(let e of t.otherChunks)I(E("string"==typeof e?e:e.path));if(await Promise.all(t.otherChunks.map(e=>{var t;return t=r,j(n.Runtime,t,e)})),t.runtimeModuleIds.length>0)for(let e of t.runtimeModuleIds)!function(e,t){let r=M[t];if(r){if(r.error)throw r.error;return}N(t,n.Runtime,e)}(r,e)}},loadChunkCached:(e,t)=>H(e,t)};var F=globalThis.TURBOPACK;globalThis.TURBOPACK={push:B},F.forEach(B)})();
|
|
@@ -372,7 +372,7 @@ wheels = [
|
|
|
372
372
|
|
|
373
373
|
[[package]]
|
|
374
374
|
name = "posthog"
|
|
375
|
-
version = "7.
|
|
375
|
+
version = "7.53.0"
|
|
376
376
|
source = { registry = "https://pypi.org/simple" }
|
|
377
377
|
dependencies = [
|
|
378
378
|
{ name = "backoff" },
|
|
@@ -380,9 +380,9 @@ dependencies = [
|
|
|
380
380
|
{ name = "requests" },
|
|
381
381
|
{ name = "typing-extensions" },
|
|
382
382
|
]
|
|
383
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
383
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f2/c4/17a2a764a63e061f768a56d50c245e3012da536b5a15ed2521616b8869b7/posthog-7.53.0.tar.gz", hash = "sha256:38223a8769b9db7ab906d32f5b87c4cde534cc91b30f1dcfa4443577819e068c", size = 565313, upload-time = "2026-09-11T17:51:16.199Z" }
|
|
384
384
|
wheels = [
|
|
385
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
385
|
+
{ url = "https://files.pythonhosted.org/packages/03/9d/4af58450b986666c810dc1fbae04e41cdec62ab31ae1e0e51bb5a4b75f71/posthog-7.53.0-py3-none-any.whl", hash = "sha256:ca9176334cf0c7799fe5518f7b9dc02d3f2f709a859da4332f14b3586b9d4554", size = 662783, upload-time = "2026-09-11T17:51:14.275Z" },
|
|
386
386
|
]
|
|
387
387
|
|
|
388
388
|
[[package]]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# FailproofAI for Hermes
|
|
2
|
+
|
|
3
|
+
This directory is the native Hermes adapter shipped inside the `failproofai`
|
|
4
|
+
npm package. It keeps policy evaluation in FailproofAI's TypeScript worker and
|
|
5
|
+
translates structured verdicts into Hermes-native hook behavior.
|
|
6
|
+
|
|
7
|
+
Validated contract: Hermes 0.21.3 at
|
|
8
|
+
`4d55ca91656ac5f83e1506679b7f81e0238e5e16`. The plugin uses only documented
|
|
9
|
+
manifest v2 fields, `PluginContext` methods/state, and keyword hook payloads.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
failproofai policies --install --cli hermes --scope user
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
FailproofAI copies this directory to every discovered profile at
|
|
20
|
+
`<HERMES_HOME>/plugins/failproofai/` and adds `failproofai` to
|
|
21
|
+
`plugins.enabled` in that profile's `config.yaml`. Reinstall replaces only a
|
|
22
|
+
directory carrying `.failproofai-managed`; an unrelated plugin with the same
|
|
23
|
+
directory name is never overwritten.
|
|
24
|
+
|
|
25
|
+
Legacy FailproofAI shell hooks are removed during migration. Operator-owned
|
|
26
|
+
hooks and unrelated plugin settings are preserved. No dashboard deployment or
|
|
27
|
+
backtest is part of installation.
|
|
28
|
+
|
|
29
|
+
## Runtime path
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Hermes pre_tool_call
|
|
33
|
+
-> native Python plugin
|
|
34
|
+
-> owner-only failproofaid Unix socket
|
|
35
|
+
-> warm TypeScript policy worker
|
|
36
|
+
-> structured allow | deny | instruct verdict
|
|
37
|
+
-> Hermes-native return value
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
There is no cloud request and no new CLI process in the tool-call path.
|
|
41
|
+
|
|
42
|
+
## Decision behavior
|
|
43
|
+
|
|
44
|
+
- `allow`: return no directive; Hermes runs the tool.
|
|
45
|
+
- `deny`: always return `{"action":"block","message":"..."}`.
|
|
46
|
+
- `instruct`: persist delivery state, block the first attempt with a
|
|
47
|
+
model-visible `FAILPROOF INSTRUCTION`, keep the same API request blocked, then
|
|
48
|
+
allow a later API iteration for the same instruction scope.
|
|
49
|
+
|
|
50
|
+
The default instruction scope is profile + session + task + turn + policy
|
|
51
|
+
fingerprint + canonical tool. The persistent SQLite ledger lives below Hermes'
|
|
52
|
+
profile-scoped plugin data directory. Two distinct instruction interruptions
|
|
53
|
+
are allowed per turn by default; after that, advisory instructions fail open so
|
|
54
|
+
they cannot create an infinite retry loop. A real deny is never bypassed by the
|
|
55
|
+
ledger.
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
Settings live under `plugins.entries.failproofai.settings`:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
plugins:
|
|
63
|
+
enabled:
|
|
64
|
+
- failproofai
|
|
65
|
+
entries:
|
|
66
|
+
failproofai:
|
|
67
|
+
settings:
|
|
68
|
+
failure_mode: deny
|
|
69
|
+
connect_timeout_ms: 250
|
|
70
|
+
evaluation_timeout_ms: 12000
|
|
71
|
+
instruction_ttl_seconds: 3600
|
|
72
|
+
max_instruction_rounds: 2
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`failure_mode` controls evaluator/protocol failures only. `deny` is the safe
|
|
76
|
+
default. `allow` favors availability when the local daemon cannot return a
|
|
77
|
+
trusted verdict. Ledger failures always allow only the advisory `instruct`
|
|
78
|
+
decision, because otherwise corrupted retry state could block a turn forever.
|
|
79
|
+
|
|
80
|
+
## Local protocol
|
|
81
|
+
|
|
82
|
+
Requests and responses use the existing length-prefixed failproofaid Unix
|
|
83
|
+
socket protocol, version 1.
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"type": "policyEvaluation",
|
|
88
|
+
"protocolVersion": 1,
|
|
89
|
+
"integration": "hermes",
|
|
90
|
+
"event": "pre_tool_call",
|
|
91
|
+
"payload": {},
|
|
92
|
+
"cwd": "/workspace/project"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"type": "policyResult",
|
|
99
|
+
"protocolVersion": 1,
|
|
100
|
+
"decision": "instruct",
|
|
101
|
+
"policyNames": ["custom/approved-write-route"],
|
|
102
|
+
"reason": "Use the approved write route.",
|
|
103
|
+
"matchedPolicies": ["custom/approved-write-route"],
|
|
104
|
+
"durationMs": 3,
|
|
105
|
+
"toolName": "Write"
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Requests are limited to 1 MiB and responses to 16 MiB. Invalid JSON, unknown
|
|
110
|
+
message types, version mismatch, timeout, and socket failure are treated as
|
|
111
|
+
untrusted evaluation failures.
|
|
112
|
+
|
|
113
|
+
## Diagnostics and rollback
|
|
114
|
+
|
|
115
|
+
`failproofai config --status` reports each existing Hermes profile as healthy,
|
|
116
|
+
disabled, incomplete, or duplicated with a legacy shell hook. Hermes-side load
|
|
117
|
+
errors are available through:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
HERMES_PLUGINS_DEBUG=1 hermes plugins list
|
|
121
|
+
hermes plugins doctor ~/.hermes/plugins/failproofai --ci
|
|
122
|
+
hermes logs --level WARNING
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use a temporary `HERMES_HOME` for development and compatibility tests. Remove
|
|
126
|
+
the integration with:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
failproofai policies --uninstall --cli hermes --scope user
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Uninstall removes the config registration and only the plugin directory marked
|
|
133
|
+
as FailproofAI-managed.
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Native Hermes bridge for FailproofAI policy enforcement."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Mapping
|
|
9
|
+
|
|
10
|
+
from .client import PolicyVerdict, evaluate_policy
|
|
11
|
+
from .ledger import InstructionLedger, LedgerError
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_PROTOCOL_CONTEXT = (
|
|
16
|
+
"When a tool result starts with FAILPROOF INSTRUCTION, treat it as policy guidance. "
|
|
17
|
+
"Reconsider the attempted action before retrying. Do not evade or ignore the instruction; "
|
|
18
|
+
"change tools, arguments, targets, or side effects only when the instruction requires it. "
|
|
19
|
+
"If you cannot follow the instruction, stop and explain why."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _bounded_int(value: object, default: int, minimum: int, maximum: int) -> int:
|
|
24
|
+
try:
|
|
25
|
+
parsed = int(value)
|
|
26
|
+
except (TypeError, ValueError):
|
|
27
|
+
return default
|
|
28
|
+
return min(max(parsed, minimum), maximum)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _profile_name() -> str:
|
|
32
|
+
explicit = os.environ.get("HERMES_PROFILE", "").strip()
|
|
33
|
+
if explicit:
|
|
34
|
+
return explicit
|
|
35
|
+
configured_home = os.environ.get("HERMES_HOME", "").strip()
|
|
36
|
+
home = Path(configured_home).expanduser() if configured_home else Path.home() / ".hermes"
|
|
37
|
+
if home.parent.name == "profiles":
|
|
38
|
+
return home.name
|
|
39
|
+
return "default"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _string(value: object) -> str:
|
|
43
|
+
return value if isinstance(value, str) else ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FailproofAIPlugin:
|
|
47
|
+
def __init__(self, ctx: Any) -> None:
|
|
48
|
+
self.ctx = ctx
|
|
49
|
+
self.profile = _profile_name()
|
|
50
|
+
self.failure_mode = str(ctx.get_config("failure_mode", "deny")).strip().lower()
|
|
51
|
+
self.connect_timeout_ms = _bounded_int(
|
|
52
|
+
ctx.get_config("connect_timeout_ms", 250), 250, 25, 5_000
|
|
53
|
+
)
|
|
54
|
+
self.evaluation_timeout_ms = _bounded_int(
|
|
55
|
+
ctx.get_config("evaluation_timeout_ms", 12_000), 12_000, 100, 29_000
|
|
56
|
+
)
|
|
57
|
+
ttl_seconds = _bounded_int(
|
|
58
|
+
ctx.get_config("instruction_ttl_seconds", 3_600), 3_600, 60, 86_400
|
|
59
|
+
)
|
|
60
|
+
max_rounds = _bounded_int(
|
|
61
|
+
ctx.get_config("max_instruction_rounds", 2), 2, 0, 10
|
|
62
|
+
)
|
|
63
|
+
self.ledger = InstructionLedger(
|
|
64
|
+
Path(ctx.state.data_dir) / "instructions.db",
|
|
65
|
+
ttl_seconds=ttl_seconds,
|
|
66
|
+
max_rounds=max_rounds,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def _fallback(self, error: Exception) -> dict[str, str] | None:
|
|
70
|
+
logger.error("FailproofAI evaluator unavailable: %s", error)
|
|
71
|
+
if self.failure_mode == "allow":
|
|
72
|
+
return None
|
|
73
|
+
return {
|
|
74
|
+
"action": "block",
|
|
75
|
+
"message": (
|
|
76
|
+
"FailproofAI could not verify this tool call because the local policy "
|
|
77
|
+
"evaluator is unavailable. Check `failproofai status` before retrying."
|
|
78
|
+
),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def _evaluate(self, event: str, payload: Mapping[str, Any]) -> PolicyVerdict:
|
|
82
|
+
cwd = _string(payload.get("cwd")) or os.getcwd()
|
|
83
|
+
return evaluate_policy(
|
|
84
|
+
event=event,
|
|
85
|
+
payload=payload,
|
|
86
|
+
cwd=cwd,
|
|
87
|
+
connect_timeout_ms=self.connect_timeout_ms,
|
|
88
|
+
evaluation_timeout_ms=self.evaluation_timeout_ms,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def pre_tool_call(
|
|
92
|
+
self,
|
|
93
|
+
tool_name: str = "",
|
|
94
|
+
args: Mapping[str, Any] | None = None,
|
|
95
|
+
session_id: str = "",
|
|
96
|
+
task_id: str = "",
|
|
97
|
+
tool_call_id: str = "",
|
|
98
|
+
turn_id: str = "",
|
|
99
|
+
api_request_id: str = "",
|
|
100
|
+
**kwargs: Any,
|
|
101
|
+
) -> dict[str, str] | None:
|
|
102
|
+
payload = {
|
|
103
|
+
"hook_event_name": "pre_tool_call",
|
|
104
|
+
"tool_name": tool_name,
|
|
105
|
+
"tool_input": dict(args or {}),
|
|
106
|
+
"session_id": session_id,
|
|
107
|
+
"cwd": _string(kwargs.get("cwd")) or os.getcwd(),
|
|
108
|
+
"hermes": {
|
|
109
|
+
"profile": self.profile,
|
|
110
|
+
"task_id": task_id,
|
|
111
|
+
"tool_call_id": tool_call_id,
|
|
112
|
+
"turn_id": turn_id,
|
|
113
|
+
"api_request_id": api_request_id,
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
try:
|
|
117
|
+
verdict = self._evaluate("pre_tool_call", payload)
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
return self._fallback(exc)
|
|
120
|
+
|
|
121
|
+
if verdict.decision == "allow":
|
|
122
|
+
return None
|
|
123
|
+
if verdict.decision == "deny":
|
|
124
|
+
return {
|
|
125
|
+
"action": "block",
|
|
126
|
+
"message": verdict.reason or "Blocked by FailproofAI policy.",
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
reason = verdict.reason or "Reconsider this tool call before retrying."
|
|
130
|
+
try:
|
|
131
|
+
action = self.ledger.decide(
|
|
132
|
+
profile=self.profile,
|
|
133
|
+
session_id=session_id,
|
|
134
|
+
task_id=task_id,
|
|
135
|
+
turn_id=turn_id,
|
|
136
|
+
api_request_id=api_request_id,
|
|
137
|
+
policy_names=verdict.policy_names,
|
|
138
|
+
reason=reason,
|
|
139
|
+
scope_key=verdict.tool_name or tool_name or "unknown-tool",
|
|
140
|
+
)
|
|
141
|
+
except LedgerError as exc:
|
|
142
|
+
# An advisory instruction must never become an indefinite denial
|
|
143
|
+
# because its local retry state is unavailable.
|
|
144
|
+
logger.error("FailproofAI instruction state unavailable; allowing: %s", exc)
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
if not action.block:
|
|
148
|
+
if action.status in {"missing-correlation-id", "turn-cap"}:
|
|
149
|
+
logger.warning("FailproofAI instruction allowed in degraded state: %s", action.status)
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
policies = ", ".join(verdict.policy_names) or "unknown policy"
|
|
153
|
+
return {
|
|
154
|
+
"action": "block",
|
|
155
|
+
"message": (
|
|
156
|
+
f"FAILPROOF INSTRUCTION ({policies})\n\n{reason}\n\n"
|
|
157
|
+
"Reconsider the attempted action, then make a corrected tool call. "
|
|
158
|
+
"Do not route around this instruction with an equivalent ungoverned action."
|
|
159
|
+
),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def _observe(self, event: str, payload: Mapping[str, Any]) -> None:
|
|
163
|
+
try:
|
|
164
|
+
self._evaluate(event, payload)
|
|
165
|
+
except Exception as exc:
|
|
166
|
+
logger.warning("FailproofAI observation failed for %s: %s", event, exc)
|
|
167
|
+
|
|
168
|
+
def post_tool_call(
|
|
169
|
+
self,
|
|
170
|
+
tool_name: str = "",
|
|
171
|
+
args: Mapping[str, Any] | None = None,
|
|
172
|
+
result: Any = None,
|
|
173
|
+
session_id: str = "",
|
|
174
|
+
**kwargs: Any,
|
|
175
|
+
) -> None:
|
|
176
|
+
self._observe(
|
|
177
|
+
"post_tool_call",
|
|
178
|
+
{
|
|
179
|
+
"hook_event_name": "post_tool_call",
|
|
180
|
+
"tool_name": tool_name,
|
|
181
|
+
"tool_input": dict(args or {}),
|
|
182
|
+
"tool_response": result,
|
|
183
|
+
"session_id": session_id,
|
|
184
|
+
"cwd": _string(kwargs.get("cwd")) or os.getcwd(),
|
|
185
|
+
"hermes": {"profile": self.profile, **kwargs},
|
|
186
|
+
},
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def on_session_start(self, session_id: str = "", **kwargs: Any) -> None:
|
|
190
|
+
self._observe(
|
|
191
|
+
"on_session_start",
|
|
192
|
+
{
|
|
193
|
+
"hook_event_name": "on_session_start",
|
|
194
|
+
"session_id": session_id,
|
|
195
|
+
"cwd": _string(kwargs.get("cwd")) or os.getcwd(),
|
|
196
|
+
"hermes": {"profile": self.profile, **kwargs},
|
|
197
|
+
},
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def on_session_end(self, session_id: str = "", **kwargs: Any) -> None:
|
|
201
|
+
self._observe(
|
|
202
|
+
"on_session_end",
|
|
203
|
+
{
|
|
204
|
+
"hook_event_name": "on_session_end",
|
|
205
|
+
"session_id": session_id,
|
|
206
|
+
"cwd": _string(kwargs.get("cwd")) or os.getcwd(),
|
|
207
|
+
"hermes": {"profile": self.profile, **kwargs},
|
|
208
|
+
},
|
|
209
|
+
)
|
|
210
|
+
self._clear_session(session_id)
|
|
211
|
+
|
|
212
|
+
def subagent_stop(self, session_id: str = "", **kwargs: Any) -> None:
|
|
213
|
+
self._observe(
|
|
214
|
+
"subagent_stop",
|
|
215
|
+
{
|
|
216
|
+
"hook_event_name": "subagent_stop",
|
|
217
|
+
"session_id": session_id,
|
|
218
|
+
"cwd": _string(kwargs.get("cwd")) or os.getcwd(),
|
|
219
|
+
"hermes": {"profile": self.profile, **kwargs},
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def pre_llm_call(self, **kwargs: Any) -> dict[str, str]:
|
|
224
|
+
return {"context": _PROTOCOL_CONTEXT}
|
|
225
|
+
|
|
226
|
+
def on_session_reset(self, session_id: str = "", **kwargs: Any) -> None:
|
|
227
|
+
self._clear_session(session_id)
|
|
228
|
+
|
|
229
|
+
def on_session_finalize(self, session_id: str = "", **kwargs: Any) -> None:
|
|
230
|
+
self._clear_session(session_id)
|
|
231
|
+
|
|
232
|
+
def _clear_session(self, session_id: str) -> None:
|
|
233
|
+
try:
|
|
234
|
+
self.ledger.clear_session(session_id)
|
|
235
|
+
except LedgerError as exc:
|
|
236
|
+
logger.warning("FailproofAI instruction cleanup failed: %s", exc)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def register(ctx: Any) -> None:
|
|
240
|
+
plugin = FailproofAIPlugin(ctx)
|
|
241
|
+
ctx.register_hook("pre_tool_call", plugin.pre_tool_call)
|
|
242
|
+
ctx.register_hook("post_tool_call", plugin.post_tool_call)
|
|
243
|
+
ctx.register_hook("pre_llm_call", plugin.pre_llm_call)
|
|
244
|
+
ctx.register_hook("on_session_start", plugin.on_session_start)
|
|
245
|
+
ctx.register_hook("on_session_end", plugin.on_session_end)
|
|
246
|
+
ctx.register_hook("on_session_reset", plugin.on_session_reset)
|
|
247
|
+
ctx.register_hook("on_session_finalize", plugin.on_session_finalize)
|
|
248
|
+
ctx.register_hook("subagent_stop", plugin.subagent_stop)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Versioned Unix-socket client for failproofaid policy evaluation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import socket
|
|
8
|
+
import struct
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Mapping, Sequence
|
|
12
|
+
|
|
13
|
+
PROTOCOL_VERSION = 1
|
|
14
|
+
MAX_FRAME_BYTES = 16 * 1024 * 1024
|
|
15
|
+
MAX_REQUEST_BYTES = 1024 * 1024
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EvaluationError(RuntimeError):
|
|
19
|
+
"""The daemon could not return a trustworthy policy verdict."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class PolicyVerdict:
|
|
24
|
+
decision: str
|
|
25
|
+
policy_names: tuple[str, ...]
|
|
26
|
+
reason: str | None
|
|
27
|
+
matched_policies: tuple[str, ...]
|
|
28
|
+
duration_ms: int
|
|
29
|
+
tool_name: str | None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def daemon_socket_path() -> Path:
|
|
33
|
+
override = os.environ.get("FAILPROOFAI_DAEMON_SOCKET", "").strip()
|
|
34
|
+
if override:
|
|
35
|
+
return Path(override).expanduser()
|
|
36
|
+
root = os.environ.get("FAILPROOFAI_HOME", "").strip()
|
|
37
|
+
home = Path(root).expanduser() if root else Path.home() / ".failproofai"
|
|
38
|
+
return home / "run" / "failproofaid.sock"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _read_exact(sock: socket.socket, length: int) -> bytes:
|
|
42
|
+
chunks: list[bytes] = []
|
|
43
|
+
remaining = length
|
|
44
|
+
while remaining:
|
|
45
|
+
chunk = sock.recv(remaining)
|
|
46
|
+
if not chunk:
|
|
47
|
+
raise EvaluationError("failproofaid closed the connection before returning a verdict")
|
|
48
|
+
chunks.append(chunk)
|
|
49
|
+
remaining -= len(chunk)
|
|
50
|
+
return b"".join(chunks)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _string_list(value: object, field: str) -> tuple[str, ...]:
|
|
54
|
+
if not isinstance(value, Sequence) or isinstance(value, (str, bytes, bytearray)):
|
|
55
|
+
raise EvaluationError(f"failproofaid returned an invalid {field}")
|
|
56
|
+
if not all(isinstance(item, str) for item in value):
|
|
57
|
+
raise EvaluationError(f"failproofaid returned an invalid {field}")
|
|
58
|
+
return tuple(value)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def evaluate_policy(
|
|
62
|
+
*,
|
|
63
|
+
event: str,
|
|
64
|
+
payload: Mapping[str, Any],
|
|
65
|
+
cwd: str | None,
|
|
66
|
+
connect_timeout_ms: int = 250,
|
|
67
|
+
evaluation_timeout_ms: int = 12_000,
|
|
68
|
+
) -> PolicyVerdict:
|
|
69
|
+
request = {
|
|
70
|
+
"type": "policyEvaluation",
|
|
71
|
+
"protocolVersion": PROTOCOL_VERSION,
|
|
72
|
+
"integration": "hermes",
|
|
73
|
+
"event": event,
|
|
74
|
+
"payload": dict(payload),
|
|
75
|
+
"cwd": cwd,
|
|
76
|
+
}
|
|
77
|
+
try:
|
|
78
|
+
body = json.dumps(
|
|
79
|
+
request,
|
|
80
|
+
ensure_ascii=False,
|
|
81
|
+
separators=(",", ":"),
|
|
82
|
+
default=lambda value: repr(value),
|
|
83
|
+
).encode("utf-8")
|
|
84
|
+
except (TypeError, ValueError) as exc:
|
|
85
|
+
raise EvaluationError(f"could not encode policy request: {exc}") from exc
|
|
86
|
+
if len(body) > MAX_REQUEST_BYTES:
|
|
87
|
+
raise EvaluationError("policy request exceeds the 1 MiB limit")
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
|
91
|
+
sock.settimeout(max(connect_timeout_ms, 1) / 1000)
|
|
92
|
+
sock.connect(str(daemon_socket_path()))
|
|
93
|
+
sock.settimeout(max(evaluation_timeout_ms, 1) / 1000)
|
|
94
|
+
sock.sendall(struct.pack(">I", len(body)) + body)
|
|
95
|
+
declared_length = struct.unpack(">I", _read_exact(sock, 4))[0]
|
|
96
|
+
if declared_length > MAX_FRAME_BYTES:
|
|
97
|
+
raise EvaluationError("failproofaid response exceeds the 16 MiB limit")
|
|
98
|
+
response = json.loads(_read_exact(sock, declared_length).decode("utf-8"))
|
|
99
|
+
except EvaluationError:
|
|
100
|
+
raise
|
|
101
|
+
except (OSError, UnicodeError, ValueError, struct.error) as exc:
|
|
102
|
+
raise EvaluationError(f"failproofaid evaluation failed: {exc}") from exc
|
|
103
|
+
|
|
104
|
+
if not isinstance(response, dict):
|
|
105
|
+
raise EvaluationError("failproofaid returned a non-object response")
|
|
106
|
+
if response.get("protocolVersion") != PROTOCOL_VERSION:
|
|
107
|
+
raise EvaluationError("failproofaid protocol version mismatch")
|
|
108
|
+
if response.get("type") == "error":
|
|
109
|
+
message = response.get("message")
|
|
110
|
+
raise EvaluationError(str(message or "failproofaid could not evaluate the policy"))
|
|
111
|
+
if response.get("type") != "policyResult":
|
|
112
|
+
raise EvaluationError("failproofaid does not support native policy evaluation")
|
|
113
|
+
|
|
114
|
+
decision = response.get("decision")
|
|
115
|
+
if decision not in {"allow", "deny", "instruct"}:
|
|
116
|
+
raise EvaluationError("failproofaid returned an invalid policy decision")
|
|
117
|
+
reason = response.get("reason")
|
|
118
|
+
if reason is not None and not isinstance(reason, str):
|
|
119
|
+
raise EvaluationError("failproofaid returned an invalid policy reason")
|
|
120
|
+
duration_ms = response.get("durationMs", 0)
|
|
121
|
+
if not isinstance(duration_ms, int) or duration_ms < 0:
|
|
122
|
+
raise EvaluationError("failproofaid returned an invalid evaluation duration")
|
|
123
|
+
tool_name = response.get("toolName")
|
|
124
|
+
if tool_name is not None and not isinstance(tool_name, str):
|
|
125
|
+
raise EvaluationError("failproofaid returned an invalid tool name")
|
|
126
|
+
|
|
127
|
+
return PolicyVerdict(
|
|
128
|
+
decision=decision,
|
|
129
|
+
policy_names=_string_list(response.get("policyNames", []), "policyNames"),
|
|
130
|
+
reason=reason,
|
|
131
|
+
matched_policies=_string_list(response.get("matchedPolicies", []), "matchedPolicies"),
|
|
132
|
+
duration_ms=duration_ms,
|
|
133
|
+
tool_name=tool_name,
|
|
134
|
+
)
|