failproofai 1.0.5 → 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 +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 +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 +2 -2
- 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 +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.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.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 +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 +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.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]__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]__15k-dqb._.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/{_1b78kcp._.js → _09hs6ms._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0guaevg._.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 +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 +19 -19
- package/.next/standalone/.next/static/chunks/{3w46euc60dx39.js → 03n_qjih7-w6q.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2zafgs90r_leq.js → 06jxjnw8bfczc.js} +1 -1
- package/.next/standalone/.next/static/chunks/{252xreaecy80h.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/{021ydrq6rhpsi.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/{3bleyy40s99f2.js → 2u0kol_boahtv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1vb33xa6j9y2q.js → 3_vesf83-0903.js} +1 -1
- package/.next/standalone/.next/static/chunks/3d3hzhcnns6ww.js +1 -0
- package/.next/standalone/.next/static/chunks/3iasus89n8mg1.js +6 -0
- package/.next/standalone/.next/static/chunks/{11s_v03ub6nss.js → 44_cj1_i_aa2z.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-3p73obe0xmrxm.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/0y8_1s45gbqmn.js +0 -6
- package/.next/standalone/.next/static/chunks/14b3ezufe5411.js +0 -1
- package/.next/standalone/.next/static/chunks/1oskk7pdo8aon.js +0 -1
- package/.next/standalone/.next/static/chunks/2xycd6mjg00fb.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/{uFpAs_vIXSo6qBzmkXPWD → -7OT2NwGPxAu-skEL_Xee}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{uFpAs_vIXSo6qBzmkXPWD → -7OT2NwGPxAu-skEL_Xee}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{uFpAs_vIXSo6qBzmkXPWD → -7OT2NwGPxAu-skEL_Xee}/_ssgManifest.js +0 -0
|
@@ -7,7 +7,17 @@
|
|
|
7
7
|
* is agent-agnostic — only install/uninstall plumbing varies.
|
|
8
8
|
*/
|
|
9
9
|
import { execSync } from "node:child_process";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
cpSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
lstatSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
renameSync,
|
|
18
|
+
rmSync,
|
|
19
|
+
unlinkSync,
|
|
20
|
+
} from "node:fs";
|
|
11
21
|
import { resolve, dirname } from "node:path";
|
|
12
22
|
import { fileURLToPath } from "node:url";
|
|
13
23
|
import { homedir } from "node:os";
|
|
@@ -135,6 +145,9 @@ export interface Integration {
|
|
|
135
145
|
/** Read the raw settings/hooks file (returns {} when missing). */
|
|
136
146
|
readSettings(settingsPath: string): Record<string, unknown>;
|
|
137
147
|
|
|
148
|
+
/** Install integration-owned files before enabling them in settings. */
|
|
149
|
+
prepareInstall?(settingsPath: string): void;
|
|
150
|
+
|
|
138
151
|
/** Write the settings/hooks file. */
|
|
139
152
|
writeSettings(settingsPath: string, settings: Record<string, unknown>): void;
|
|
140
153
|
|
|
@@ -1363,14 +1376,14 @@ function makePiProjectRelativeEntry(extPath: string): string {
|
|
|
1363
1376
|
// works for the local user.
|
|
1364
1377
|
return extResolved;
|
|
1365
1378
|
}
|
|
1366
|
-
// ── Hermes (hermes-agent) —
|
|
1379
|
+
// ── Hermes (hermes-agent) — native plugin (Pillar 1) ────────────────────────
|
|
1367
1380
|
//
|
|
1368
|
-
//
|
|
1369
|
-
//
|
|
1370
|
-
//
|
|
1371
|
-
//
|
|
1372
|
-
//
|
|
1373
|
-
//
|
|
1381
|
+
// Hermes loads trusted Python plugins from `<HERMES_HOME>/plugins/<name>/`.
|
|
1382
|
+
// The shipped plugin registers Hermes-native hooks and talks directly to the
|
|
1383
|
+
// local failproofaid socket, avoiding a fresh CLI process per event. Config is
|
|
1384
|
+
// YAML and profile-scoped; install copies the plugin into every profile and
|
|
1385
|
+
// adds `failproofai` to `plugins.enabled`. Legacy shell-hook entries are removed
|
|
1386
|
+
// during migration so one tool call is never evaluated twice.
|
|
1374
1387
|
|
|
1375
1388
|
/** One hook entry as stored under a `hooks:` event key in config.yaml. */
|
|
1376
1389
|
interface HermesHookEntry {
|
|
@@ -1379,6 +1392,200 @@ interface HermesHookEntry {
|
|
|
1379
1392
|
[key: string]: unknown;
|
|
1380
1393
|
}
|
|
1381
1394
|
|
|
1395
|
+
interface HermesPluginsConfig {
|
|
1396
|
+
enabled?: unknown[];
|
|
1397
|
+
disabled?: unknown[];
|
|
1398
|
+
entries?: Record<string, unknown>;
|
|
1399
|
+
[key: string]: unknown;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
const HERMES_PLUGIN_ID = "failproofai";
|
|
1403
|
+
const HERMES_PLUGIN_MARKER = ".failproofai-managed";
|
|
1404
|
+
const HERMES_PLUGIN_FILES = ["plugin.yaml", "__init__.py", "client.py", "ledger.py"] as const;
|
|
1405
|
+
|
|
1406
|
+
function getHermesPluginSourcePath(): string {
|
|
1407
|
+
const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
1408
|
+
if (fromEnv) return resolve(fromEnv, "hermes-plugin");
|
|
1409
|
+
return resolve(fileURLToPath(import.meta.url), "..", "..", "..", "hermes-plugin");
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
export function hermesPluginPathForSettings(settingsPath: string): string {
|
|
1413
|
+
return resolve(dirname(settingsPath), "plugins", HERMES_PLUGIN_ID);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
function hasManagedHermesPlugin(settingsPath: string): boolean {
|
|
1417
|
+
const pluginPath = hermesPluginPathForSettings(settingsPath);
|
|
1418
|
+
try {
|
|
1419
|
+
if (lstatSync(pluginPath).isSymbolicLink()) return false;
|
|
1420
|
+
if (!existsSync(resolve(pluginPath, HERMES_PLUGIN_MARKER))) return false;
|
|
1421
|
+
return HERMES_PLUGIN_FILES.every((file) => existsSync(resolve(pluginPath, file)));
|
|
1422
|
+
} catch {
|
|
1423
|
+
return false;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
function hasHermesPluginMarker(settingsPath: string): boolean {
|
|
1428
|
+
const pluginPath = hermesPluginPathForSettings(settingsPath);
|
|
1429
|
+
try {
|
|
1430
|
+
return !lstatSync(pluginPath).isSymbolicLink() && existsSync(resolve(pluginPath, HERMES_PLUGIN_MARKER));
|
|
1431
|
+
} catch {
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
function installHermesPlugin(settingsPath: string): void {
|
|
1437
|
+
const source = getHermesPluginSourcePath();
|
|
1438
|
+
for (const file of HERMES_PLUGIN_FILES) {
|
|
1439
|
+
if (!existsSync(resolve(source, file))) {
|
|
1440
|
+
throw new Error(`Hermes plugin asset is missing from the failproofai package: ${file}`);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
const destination = hermesPluginPathForSettings(settingsPath);
|
|
1445
|
+
if (existsSync(destination)) {
|
|
1446
|
+
if (lstatSync(destination).isSymbolicLink() || !existsSync(resolve(destination, HERMES_PLUGIN_MARKER))) {
|
|
1447
|
+
throw new Error(
|
|
1448
|
+
`Refusing to overwrite an unmanaged Hermes plugin at ${destination}. ` +
|
|
1449
|
+
`Move it aside or install FailproofAI under a different Hermes profile.`,
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
1455
|
+
const suffix = `${process.pid}-${Date.now()}`;
|
|
1456
|
+
const temporary = `${destination}.install-${suffix}`;
|
|
1457
|
+
const backup = `${destination}.backup-${suffix}`;
|
|
1458
|
+
let movedExisting = false;
|
|
1459
|
+
try {
|
|
1460
|
+
mkdirSync(temporary, { recursive: true });
|
|
1461
|
+
for (const file of HERMES_PLUGIN_FILES) {
|
|
1462
|
+
cpSync(resolve(source, file), resolve(temporary, file));
|
|
1463
|
+
}
|
|
1464
|
+
writeFileSync(
|
|
1465
|
+
resolve(temporary, HERMES_PLUGIN_MARKER),
|
|
1466
|
+
"Managed by failproofai. Remove with `failproofai policies --uninstall --cli hermes`.\n",
|
|
1467
|
+
{ encoding: "utf8", mode: 0o600 },
|
|
1468
|
+
);
|
|
1469
|
+
if (existsSync(destination)) {
|
|
1470
|
+
renameSync(destination, backup);
|
|
1471
|
+
movedExisting = true;
|
|
1472
|
+
}
|
|
1473
|
+
renameSync(temporary, destination);
|
|
1474
|
+
if (movedExisting) rmSync(backup, { recursive: true, force: true });
|
|
1475
|
+
} catch (err) {
|
|
1476
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
1477
|
+
if (movedExisting && !existsSync(destination) && existsSync(backup)) {
|
|
1478
|
+
renameSync(backup, destination);
|
|
1479
|
+
}
|
|
1480
|
+
throw err;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
function removeManagedHermesPlugin(settingsPath: string): boolean {
|
|
1485
|
+
const pluginPath = hermesPluginPathForSettings(settingsPath);
|
|
1486
|
+
// The marker is the ownership boundary. Remove even an incomplete managed
|
|
1487
|
+
// install so an interrupted copy can always be repaired or uninstalled.
|
|
1488
|
+
if (!hasHermesPluginMarker(settingsPath)) return false;
|
|
1489
|
+
rmSync(pluginPath, { recursive: true, force: true });
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
function removeLegacyHermesHooks(doc: Document): number {
|
|
1494
|
+
const js = (doc.toJS() ?? {}) as { hooks?: Record<string, HermesHookEntry[]> };
|
|
1495
|
+
const hooks = js.hooks;
|
|
1496
|
+
let removed = 0;
|
|
1497
|
+
if (hooks && typeof hooks === "object") {
|
|
1498
|
+
for (const eventType of Object.keys(hooks)) {
|
|
1499
|
+
const entries = hooks[eventType];
|
|
1500
|
+
if (!Array.isArray(entries)) continue;
|
|
1501
|
+
const before = entries.length;
|
|
1502
|
+
const filtered = entries.filter((hook) => !isMarkedHook(hook));
|
|
1503
|
+
removed += before - filtered.length;
|
|
1504
|
+
if (filtered.length === 0) delete hooks[eventType];
|
|
1505
|
+
else hooks[eventType] = filtered;
|
|
1506
|
+
}
|
|
1507
|
+
if (Object.keys(hooks).length === 0) {
|
|
1508
|
+
doc.delete("hooks");
|
|
1509
|
+
// Older FailproofAI installs set this broad Hermes switch so headless
|
|
1510
|
+
// shell hooks could run. It is safe to remove only when no operator hook
|
|
1511
|
+
// remains that may rely on it.
|
|
1512
|
+
if (removed > 0) doc.delete("hooks_auto_accept");
|
|
1513
|
+
} else {
|
|
1514
|
+
doc.set("hooks", hooks);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
return removed;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
function hermesConfigState(settingsPath: string): {
|
|
1521
|
+
pluginEnabled: boolean;
|
|
1522
|
+
legacyShellHookPresent: boolean;
|
|
1523
|
+
} {
|
|
1524
|
+
if (!existsSync(settingsPath)) {
|
|
1525
|
+
return { pluginEnabled: false, legacyShellHookPresent: false };
|
|
1526
|
+
}
|
|
1527
|
+
const doc = readYamlDoc(settingsPath);
|
|
1528
|
+
const js = (doc.toJS() ?? {}) as {
|
|
1529
|
+
hooks?: Record<string, HermesHookEntry[]>;
|
|
1530
|
+
plugins?: HermesPluginsConfig;
|
|
1531
|
+
};
|
|
1532
|
+
const enabled = js.plugins?.enabled;
|
|
1533
|
+
const legacyShellHookPresent =
|
|
1534
|
+
!!js.hooks &&
|
|
1535
|
+
typeof js.hooks === "object" &&
|
|
1536
|
+
Object.values(js.hooks).some(
|
|
1537
|
+
(entries) => Array.isArray(entries) && entries.some((entry) => isMarkedHook(entry)),
|
|
1538
|
+
);
|
|
1539
|
+
return {
|
|
1540
|
+
pluginEnabled: Array.isArray(enabled) && enabled.includes(HERMES_PLUGIN_ID),
|
|
1541
|
+
legacyShellHookPresent,
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
export interface HermesProfileHealth {
|
|
1546
|
+
name: string;
|
|
1547
|
+
home: string;
|
|
1548
|
+
settingsPath: string;
|
|
1549
|
+
pluginInstalled: boolean;
|
|
1550
|
+
pluginEnabled: boolean;
|
|
1551
|
+
legacyShellHookPresent: boolean;
|
|
1552
|
+
healthy: boolean;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/** Read-only, per-profile Hermes installation health for setup/status surfaces. */
|
|
1556
|
+
export function hermesProfileHealth(): HermesProfileHealth[] {
|
|
1557
|
+
return listHermesProfiles().map((profile) => {
|
|
1558
|
+
const settingsPath = resolve(profile.home, "config.yaml");
|
|
1559
|
+
const config = hermesConfigState(settingsPath);
|
|
1560
|
+
const pluginInstalled = hasManagedHermesPlugin(settingsPath);
|
|
1561
|
+
return {
|
|
1562
|
+
name: profile.name,
|
|
1563
|
+
home: profile.home,
|
|
1564
|
+
settingsPath,
|
|
1565
|
+
pluginInstalled,
|
|
1566
|
+
pluginEnabled: config.pluginEnabled,
|
|
1567
|
+
legacyShellHookPresent: config.legacyShellHookPresent,
|
|
1568
|
+
healthy: pluginInstalled && config.pluginEnabled && !config.legacyShellHookPresent,
|
|
1569
|
+
};
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
/** Compact rows for the machine-status display; absent Hermes homes produce no noise. */
|
|
1574
|
+
export function hermesProfileStatusRows(): Array<[string, string]> {
|
|
1575
|
+
return hermesProfileHealth()
|
|
1576
|
+
.filter((profile) => existsSync(profile.home))
|
|
1577
|
+
.map((profile) => {
|
|
1578
|
+
const problems: string[] = [];
|
|
1579
|
+
if (!profile.pluginInstalled) problems.push("plugin files missing or incomplete");
|
|
1580
|
+
if (!profile.pluginEnabled) problems.push("plugin not enabled");
|
|
1581
|
+
if (profile.legacyShellHookPresent) problems.push("legacy shell hook also present");
|
|
1582
|
+
return [
|
|
1583
|
+
"hermes/" + profile.name,
|
|
1584
|
+
problems.length === 0 ? "native plugin enabled" : "UNHEALTHY — " + problems.join("; "),
|
|
1585
|
+
];
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1382
1589
|
export const hermes: Integration = {
|
|
1383
1590
|
id: "hermes",
|
|
1384
1591
|
displayName: "Hermes",
|
|
@@ -1410,75 +1617,66 @@ export const hermes: Integration = {
|
|
|
1410
1617
|
writeYamlDoc(settingsPath, settings as unknown as Document);
|
|
1411
1618
|
},
|
|
1412
1619
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
: `"${binaryPath}" --hook ${eventType} --cli hermes`;
|
|
1620
|
+
prepareInstall(settingsPath) {
|
|
1621
|
+
installHermesPlugin(settingsPath);
|
|
1622
|
+
},
|
|
1623
|
+
|
|
1624
|
+
buildHookEntry(_binaryPath, _eventType, _scope) {
|
|
1625
|
+
// Native plugins are package-level registrations. This sentinel preserves
|
|
1626
|
+
// the common Integration interface; writeHookEntries does the real work.
|
|
1421
1627
|
return {
|
|
1422
|
-
command,
|
|
1423
|
-
timeout: 30,
|
|
1424
1628
|
[FAILPROOFAI_HOOK_MARKER]: true,
|
|
1629
|
+
_hermesPluginPath: getHermesPluginSourcePath(),
|
|
1425
1630
|
};
|
|
1426
1631
|
},
|
|
1427
1632
|
|
|
1428
1633
|
isFailproofaiHook: isMarkedHook,
|
|
1429
1634
|
|
|
1430
|
-
writeHookEntries(settings
|
|
1635
|
+
writeHookEntries(settings) {
|
|
1431
1636
|
const doc = settings as unknown as Document;
|
|
1432
|
-
//
|
|
1433
|
-
//
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1637
|
+
// A native plugin and the legacy shell bridge running together would
|
|
1638
|
+
// evaluate every event twice. Remove only failproofai-owned shell entries;
|
|
1639
|
+
// unrelated operator hooks remain intact.
|
|
1640
|
+
removeLegacyHermesHooks(doc);
|
|
1641
|
+
|
|
1642
|
+
const js = (doc.toJS() ?? {}) as { plugins?: HermesPluginsConfig };
|
|
1643
|
+
const plugins: HermesPluginsConfig =
|
|
1644
|
+
js.plugins && typeof js.plugins === "object" ? js.plugins : {};
|
|
1645
|
+
const enabled = Array.isArray(plugins.enabled) ? [...plugins.enabled] : [];
|
|
1646
|
+
if (!enabled.includes(HERMES_PLUGIN_ID)) enabled.push(HERMES_PLUGIN_ID);
|
|
1647
|
+
plugins.enabled = enabled;
|
|
1648
|
+
if (Array.isArray(plugins.disabled)) {
|
|
1649
|
+
plugins.disabled = plugins.disabled.filter((name) => name !== HERMES_PLUGIN_ID);
|
|
1650
|
+
if (plugins.disabled.length === 0) delete plugins.disabled;
|
|
1445
1651
|
}
|
|
1446
|
-
doc.set("
|
|
1447
|
-
// The headless gateway has no TTY to answer Hermes's first-use hook-consent
|
|
1448
|
-
// prompt, so auto-accept declared hooks. Tradeoff: also auto-accepts any
|
|
1449
|
-
// other hook the operator adds; a targeted `shell-hooks-allowlist.json`
|
|
1450
|
-
// pre-seed is a future refinement.
|
|
1451
|
-
doc.set("hooks_auto_accept", true);
|
|
1652
|
+
doc.set("plugins", plugins);
|
|
1452
1653
|
},
|
|
1453
1654
|
|
|
1454
1655
|
removeHooksFromFile(settingsPath) {
|
|
1455
|
-
if (!existsSync(settingsPath)) return 0;
|
|
1456
1656
|
const doc = readYamlDoc(settingsPath);
|
|
1457
|
-
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
let
|
|
1461
|
-
if (
|
|
1462
|
-
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1657
|
+
let removed = removeLegacyHermesHooks(doc);
|
|
1658
|
+
const js = (doc.toJS() ?? {}) as { plugins?: HermesPluginsConfig };
|
|
1659
|
+
const plugins = js.plugins;
|
|
1660
|
+
let configChanged = removed > 0;
|
|
1661
|
+
if (plugins && typeof plugins === "object") {
|
|
1662
|
+
if (Array.isArray(plugins.enabled)) {
|
|
1663
|
+
const before = plugins.enabled.length;
|
|
1664
|
+
plugins.enabled = plugins.enabled.filter((name) => name !== HERMES_PLUGIN_ID);
|
|
1665
|
+
if (plugins.enabled.length !== before) {
|
|
1666
|
+
removed += 1;
|
|
1667
|
+
configChanged = true;
|
|
1668
|
+
}
|
|
1669
|
+
if (plugins.enabled.length === 0) delete plugins.enabled;
|
|
1470
1670
|
}
|
|
1471
|
-
if (
|
|
1472
|
-
|
|
1473
|
-
|
|
1671
|
+
if (plugins.entries && HERMES_PLUGIN_ID in plugins.entries) {
|
|
1672
|
+
delete plugins.entries[HERMES_PLUGIN_ID];
|
|
1673
|
+
configChanged = true;
|
|
1474
1674
|
}
|
|
1675
|
+
if (Object.keys(plugins).length === 0) doc.delete("plugins");
|
|
1676
|
+
else doc.set("plugins", plugins);
|
|
1475
1677
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
// already removed manually — so it can't silently auto-accept future operator
|
|
1479
|
-
// hooks. `doc.delete` returns true iff the key was present.
|
|
1480
|
-
const droppedAutoAccept = doc.delete("hooks_auto_accept");
|
|
1481
|
-
if (removed > 0 || droppedAutoAccept) writeYamlDoc(settingsPath, doc);
|
|
1678
|
+
if (configChanged) writeYamlDoc(settingsPath, doc);
|
|
1679
|
+
if (removeManagedHermesPlugin(settingsPath)) removed += 1;
|
|
1482
1680
|
return removed;
|
|
1483
1681
|
},
|
|
1484
1682
|
|
|
@@ -1497,15 +1695,9 @@ export const hermes: Integration = {
|
|
|
1497
1695
|
};
|
|
1498
1696
|
|
|
1499
1697
|
function hermesConfigHasHooks(settingsPath: string): boolean {
|
|
1500
|
-
if (!existsSync(settingsPath)) return false;
|
|
1501
1698
|
try {
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
const hooks = js.hooks;
|
|
1505
|
-
if (!hooks || typeof hooks !== "object") return false;
|
|
1506
|
-
for (const entries of Object.values(hooks)) {
|
|
1507
|
-
if (Array.isArray(entries) && entries.some((h) => isMarkedHook(h))) return true;
|
|
1508
|
-
}
|
|
1699
|
+
const state = hermesConfigState(settingsPath);
|
|
1700
|
+
return state.pluginEnabled && hasManagedHermesPlugin(settingsPath) && !state.legacyShellHookPresent;
|
|
1509
1701
|
} catch {
|
|
1510
1702
|
// Corrupt config — treat as not installed.
|
|
1511
1703
|
}
|
|
@@ -1523,10 +1715,9 @@ function hermesConfigHasHooks(settingsPath: string): boolean {
|
|
|
1523
1715
|
* warning.
|
|
1524
1716
|
*/
|
|
1525
1717
|
export function unhookedHermesProfiles(): string[] {
|
|
1526
|
-
return
|
|
1527
|
-
.filter((
|
|
1528
|
-
.
|
|
1529
|
-
.map((p) => p.name);
|
|
1718
|
+
return hermesProfileHealth()
|
|
1719
|
+
.filter((profile) => existsSync(profile.home) && !profile.healthy)
|
|
1720
|
+
.map((profile) => profile.name);
|
|
1530
1721
|
}
|
|
1531
1722
|
|
|
1532
1723
|
// ── OpenClaw integration ────────────────────────────────────────────────────
|
package/src/hooks/manager.ts
CHANGED
|
@@ -672,6 +672,7 @@ async function installHooksImpl(
|
|
|
672
672
|
const settingsPaths = settingsPathsFor(integration, scope, cwd);
|
|
673
673
|
try {
|
|
674
674
|
for (const settingsPath of settingsPaths) {
|
|
675
|
+
integration.prepareInstall?.(settingsPath);
|
|
675
676
|
const settings = integration.readSettings(settingsPath);
|
|
676
677
|
integration.writeHookEntries(settings, binaryPath, scope);
|
|
677
678
|
integration.writeSettings(settingsPath, settings);
|
|
@@ -738,10 +739,17 @@ async function installHooksImpl(
|
|
|
738
739
|
|
|
739
740
|
for (const { cli: cliId, path } of writtenSettingsPaths) {
|
|
740
741
|
const integration = getIntegration(cliId);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
`
|
|
744
|
-
|
|
742
|
+
if (cliId === "hermes") {
|
|
743
|
+
console.log(
|
|
744
|
+
`Failproof AI native plugin installed for ${integration.displayName} ` +
|
|
745
|
+
`(8 registered hooks, scope: ${scope}).`
|
|
746
|
+
);
|
|
747
|
+
} else {
|
|
748
|
+
console.log(
|
|
749
|
+
`Failproof AI hooks installed for ${integration.displayName} ` +
|
|
750
|
+
`(${integration.eventTypes.length} event types, scope: ${scope}).`
|
|
751
|
+
);
|
|
752
|
+
}
|
|
745
753
|
console.log(`Settings: ${path}`);
|
|
746
754
|
}
|
|
747
755
|
if (scope === "project") {
|
|
@@ -876,7 +884,11 @@ export async function removeHooks(policyNames?: string[], scope: HookScope | "al
|
|
|
876
884
|
for (const s of scopesToRemove) {
|
|
877
885
|
// Usually one path; Hermes returns one per profile.
|
|
878
886
|
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
879
|
-
|
|
887
|
+
// A Hermes install copies its managed plugin before updating config.yaml.
|
|
888
|
+
// If the config write is interrupted, uninstall must still call the
|
|
889
|
+
// integration so it can remove that orphaned managed directory.
|
|
890
|
+
const existing =
|
|
891
|
+
cliId === "hermes" ? settingsPaths : settingsPaths.filter((p) => existsSync(p));
|
|
880
892
|
|
|
881
893
|
if (existing.length === 0) {
|
|
882
894
|
if (scope !== "all" && selectedClis.length === 1) {
|
package/src/hooks/types.ts
CHANGED
|
@@ -113,13 +113,13 @@ export const HERMES_TOOL_INPUT_MAP: Record<string, Record<string, string>> = {
|
|
|
113
113
|
Edit: { path: "file_path" },
|
|
114
114
|
};
|
|
115
115
|
|
|
116
|
-
// Hermes live-hook (Pillar 1) events + scopes.
|
|
117
|
-
// events
|
|
118
|
-
// `
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
116
|
+
// Hermes live-hook (Pillar 1) events + scopes. The native Python plugin sends
|
|
117
|
+
// these events to failproofaid's warm worker. It also handles `pre_llm_call`,
|
|
118
|
+
// `on_session_reset`, and `on_session_finalize` locally for instruction context
|
|
119
|
+
// and ledger cleanup, so those do not need canonical policy-event mappings.
|
|
120
|
+
// Config is USER-scope only (`~/.hermes/config.yaml`; Hermes has no project
|
|
121
|
+
// scope). `pre_tool_call` is the core gate and fires for tool calls from every
|
|
122
|
+
// source (Slack/Telegram/CLI/cron) and internal subagents.
|
|
123
123
|
//
|
|
124
124
|
// `pre_verify` IS a turn-end gate — the earlier claim here that Hermes has none
|
|
125
125
|
// was wrong. We deliberately do NOT install it (product decision, 2026-07-29);
|
|
@@ -143,7 +143,7 @@ export const HERMES_TOOL_INPUT_MAP: Record<string, Record<string, string>> = {
|
|
|
143
143
|
// 2. Capped at 3 nudges per turn (`DEFAULT_MAX_VERIFY_NUDGES`,
|
|
144
144
|
// `agent/verify_hooks.py:21`), operator-overridable; resets each turn.
|
|
145
145
|
// 3. It landed upstream ~2026-06-30. Older Hermes fails the key against
|
|
146
|
-
// VALID_HOOKS and warn-and-skips it
|
|
146
|
+
// VALID_HOOKS and warn-and-skips it silently.
|
|
147
147
|
//
|
|
148
148
|
// Until it is installed, `HERMES_EVENT_MAP` emits no `Stop` and those 5
|
|
149
149
|
// builtins remain inapplicable on Hermes.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[78500,(e,o,r)=>{o.exports=e.x("node:async_hooks",()=>require("node:async_hooks"))},2157,(e,o,r)=>{o.exports=e.x("node:fs",()=>require("node:fs"))},12714,(e,o,r)=>{o.exports=e.x("node:fs/promises",()=>require("node:fs/promises"))},50227,(e,o,r)=>{o.exports=e.x("node:path",()=>require("node:path"))},1457,(e,o,r)=>{o.exports=e.x("node:readline",()=>require("node:readline"))},12057,(e,o,r)=>{o.exports=e.x("node:util",()=>require("node:util"))},27028,(e,o,r)=>{o.exports=e.x("node:zlib",()=>require("node:zlib"))},14747,(e,o,r)=>{o.exports=e.x("path",()=>require("path"))}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Bexternals%5D__157pf8j._.js.map
|