failproofai 1.0.8-beta.0 → 1.0.9-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 +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- 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/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/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/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 +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/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/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +3 -3
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/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/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.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/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/server-reference-manifest.json +8 -8
- 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/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +20 -20
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0u._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__056wjo4._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n0xg95._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rwtwpm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__14o3ek1._.js → [root-of-the-server]__0tmz862._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11mayhe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15578wp._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1m_svbe._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pprgri._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1q4p5b8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0o4xkpl._.js → _0n0lja6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1gb0ifp._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1-7sqrb._.js → _1p3iz2d._.js} +2 -2
- 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_actions_get-scheduled-audit_ts_0ei9sni._.js +1 -1
- 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_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_02tf1h4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0aiy-os._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_semantic_pack-policies_ts_0gh_bu_._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- 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 +23 -23
- package/.next/standalone/.next/static/chunks/{2z7i-yg59w4pn.js → 0247hc9ath5t-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ahfwmbkpgfw4.js → 03001k9jy-kst.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2vo7qbdbvnc0o.js → 06k7gsfnb7jkh.js} +1 -1
- package/.next/standalone/.next/static/chunks/0qejem4-zt-hy.js +1 -0
- package/.next/standalone/.next/static/chunks/{1v_tp3hm8wyhe.js → 0snylg3y1i47a.js} +1 -1
- package/.next/standalone/.next/static/chunks/1h0if8ky362as.js +1 -0
- package/.next/standalone/.next/static/chunks/{3bgot5v6f6s3c.js → 2qsrrrl2maqsb.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0bhidk90e-07f.css → 3_e_wg5_oay87.css} +1 -1
- package/.next/standalone/.next/static/chunks/3ns-egfh8i8i5.js +6 -0
- package/.next/standalone/.next/static/chunks/3rq7fjj9we0v3.js +1 -0
- package/.next/standalone/.next/static/chunks/42m0f2hh8vfb1.js +1 -0
- package/.next/standalone/app/actions/get-jev-config.ts +2 -2
- package/.next/standalone/app/actions/update-jev-config.ts +12 -9
- package/.next/standalone/app/components/jev-notices.tsx +10 -10
- package/.next/standalone/app/settings/jev-panel.tsx +11 -11
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +2 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +26 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +27 -0
- package/.next/standalone/hermes-plugin/README.md +18 -8
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/sdk/typescript/CHANGELOG.md +18 -1
- package/.next/standalone/sdk/typescript/integration/fixtures/nextjs/package-lock.json +0 -274
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +33 -7
- package/dist/cli.mjs +664 -553
- package/dist/worker.mjs +164 -407
- package/hermes-plugin/README.md +18 -8
- package/package.json +10 -10
- package/scripts/build-policy-pack.mjs +6 -9
- package/src/hooks/cloud-connection.ts +12 -6
- package/src/hooks/custom-hooks-loader.ts +1 -1
- package/src/hooks/effective-reviewers.ts +50 -43
- package/src/hooks/handler.ts +25 -16
- package/src/hooks/hermes-update.ts +74 -0
- package/src/hooks/hook-activity-store.ts +3 -3
- package/src/hooks/integrations.ts +349 -55
- package/src/hooks/jev-activity.ts +3 -3
- package/src/hooks/jev-cli.ts +17 -17
- package/src/hooks/jev-cloud-connection.ts +4 -4
- package/src/hooks/manager.ts +3 -2
- package/src/hooks/pack-cli.ts +24 -23
- package/src/hooks/pack-manifest.ts +25 -2
- package/src/hooks/pack-store.ts +2 -2
- package/src/hooks/policy-authority.ts +51 -32
- package/src/hooks/policy-evaluator.ts +5 -5
- package/src/hooks/policy-registry.ts +5 -7
- package/src/hooks/policy-reviewability.ts +26 -10
- package/src/hooks/policy-types.ts +4 -4
- package/src/hooks/semantic/combine.ts +13 -13
- package/src/hooks/semantic/decide.ts +134 -29
- package/src/hooks/semantic/evaluator.ts +5 -5
- package/src/hooks/semantic/facts.ts +91 -2
- package/src/hooks/semantic/jev-client.ts +1 -1
- package/src/hooks/semantic/jev-config.ts +21 -12
- package/src/hooks/semantic/jev-review.ts +3 -3
- package/src/hooks/semantic/jev-stats.ts +17 -17
- package/src/hooks/semantic/pack-policies.ts +48 -48
- package/src/hooks/semantic/policies.ts +13 -425
- package/src/hooks/semantic/precondition-names.ts +1 -1
- package/src/hooks/semantic/preconditions.ts +1 -1
- package/src/hooks/semantic/types.ts +7 -1
- package/.next/standalone/.next/static/chunks/078gnymqoh3r4.js +0 -1
- package/.next/standalone/.next/static/chunks/1bu2-nv59ed6i.js +0 -1
- package/.next/standalone/.next/static/chunks/2a405_e1o26ol.js +0 -1
- package/.next/standalone/.next/static/chunks/36uhh9el_oz8e.js +0 -1
- package/.next/standalone/.next/static/chunks/3o3f1ibfci0p7.js +0 -6
- /package/.next/standalone/.next/static/{O_b5R1axf5cb4NxIXq2kS → z1C0kJVF0NCtWn5R88hW3}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{O_b5R1axf5cb4NxIXq2kS → z1C0kJVF0NCtWn5R88hW3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{O_b5R1axf5cb4NxIXq2kS → z1C0kJVF0NCtWn5R88hW3}/_ssgManifest.js +0 -0
package/hermes-plugin/README.md
CHANGED
|
@@ -21,11 +21,20 @@ Then run:
|
|
|
21
21
|
failproofai policies --install --cli hermes --scope user
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
The installer symlinks `<HERMES_HOME>/plugins/failproofai` in every discovered
|
|
25
|
+
profile to this directory and adds `failproofai` to `plugins.enabled` in that
|
|
26
|
+
profile's `config.yaml`. Hermes' plugin scan follows the link, so an npm
|
|
27
|
+
upgrade updates the plugin with no reinstall. Where a symlink cannot be
|
|
28
|
+
created, the files are copied instead and marked `.failproofai-managed`.
|
|
29
|
+
Reinstall replaces only such a marked copy or a link into a FailproofAI
|
|
30
|
+
`hermes-plugin/`; an unrelated plugin with the same name is never overwritten.
|
|
31
|
+
Python may write `__pycache__/` here when the package directory is writable;
|
|
32
|
+
it goes with the package on upgrade and is skipped when it cannot be written.
|
|
33
|
+
|
|
34
|
+
`failproofai update` moves profiles that already use FailproofAI (legacy shell
|
|
35
|
+
hooks or a copied plugin) to the link. Legacy shell hooks never ran for Hermes
|
|
36
|
+
cron jobs, so `update` exits non-zero and keeps the shell hooks when the
|
|
37
|
+
running daemon cannot serve the plugin.
|
|
29
38
|
|
|
30
39
|
Legacy FailproofAI shell hooks are removed during migration. Operator-owned
|
|
31
40
|
hooks and unrelated plugin settings are preserved. No dashboard deployment or
|
|
@@ -118,7 +127,8 @@ untrusted evaluation failures.
|
|
|
118
127
|
## Diagnostics and rollback
|
|
119
128
|
|
|
120
129
|
`failproofai config --status` reports each existing Hermes profile as healthy,
|
|
121
|
-
disabled, incomplete,
|
|
130
|
+
disabled, incomplete, duplicated with a legacy shell hook, or still on legacy
|
|
131
|
+
shell hooks alone (Hermes cron jobs are not checked). Hermes-side load
|
|
122
132
|
errors are available through:
|
|
123
133
|
|
|
124
134
|
```bash
|
|
@@ -134,5 +144,5 @@ the integration with:
|
|
|
134
144
|
failproofai policies --uninstall --cli hermes --scope user
|
|
135
145
|
```
|
|
136
146
|
|
|
137
|
-
Uninstall removes the config registration and
|
|
138
|
-
|
|
147
|
+
Uninstall removes the config registration and the profile's link (or marked
|
|
148
|
+
copy); it never removes this package directory.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "failproofai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9-beta.0",
|
|
4
4
|
"description": "Observability and enforcement for AI agent harnesses. 40 built-in policies hooked into 12 of them — Claude Code, Codex, Cursor, Hermes, OpenClaw and more — blocking the tool call before it runs. Local dashboard included, no account needed.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"failproofai": "./dist/cli.mjs",
|
|
@@ -116,21 +116,21 @@
|
|
|
116
116
|
"postcss": "8.5.26",
|
|
117
117
|
"eslint-plugin-react-hooks": "7.0.1",
|
|
118
118
|
"vite": "8.0.16",
|
|
119
|
-
"undici": "7.29.
|
|
119
|
+
"undici": "7.29.1",
|
|
120
120
|
"brace-expansion": "5.0.9",
|
|
121
121
|
"sharp": "0.35.4",
|
|
122
122
|
"browserslist": "4.28.8"
|
|
123
123
|
},
|
|
124
124
|
"optionalDependencies": {
|
|
125
|
-
"@failproofai/failproofaid-linux-x64": "1.0.
|
|
126
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.
|
|
127
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.
|
|
128
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.
|
|
125
|
+
"@failproofai/failproofaid-linux-x64": "1.0.9-beta.0",
|
|
126
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.9-beta.0",
|
|
127
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.9-beta.0",
|
|
128
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.9-beta.0"
|
|
129
129
|
},
|
|
130
130
|
"failproofaidBinaries": {
|
|
131
|
-
"linux-x64": "
|
|
132
|
-
"linux-arm64": "
|
|
133
|
-
"darwin-x64": "
|
|
134
|
-
"darwin-arm64": "
|
|
131
|
+
"linux-x64": "044b6619007227b8aa1fc1095b8c61a17bd90e11da53183ac1f5adecdcae6839",
|
|
132
|
+
"linux-arm64": "479450d7d15c52a59bd1a8cf4c7086be9210e8d31ad4f98d99b123f571e944bd",
|
|
133
|
+
"darwin-x64": "11562912ff726195bfb966ed24a774ffea38d01acc3edc1f9bfa3ea9c298977f",
|
|
134
|
+
"darwin-arm64": "5bbaee1ec89d2d8032b7f16e502fb96891fbc6aa2ee6c861f43a64a0a60e61ba"
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -40,15 +40,12 @@
|
|
|
40
40
|
* `customPolicies.add`, so a manifest rebuilt from the entry (`failproofai
|
|
41
41
|
* publish`) says the same thing.
|
|
42
42
|
*
|
|
43
|
-
* **It emits no `semantic` array, deliberately.**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* the
|
|
48
|
-
*
|
|
49
|
-
* list: the builtin reviewer names, which is what a machine installing this pack
|
|
50
|
-
* will still be running. The separate `FailproofAI/jev-policies` pack is where
|
|
51
|
-
* the semantic half ships, and it declares its own `minCliVersion`.
|
|
43
|
+
* **It emits no `semantic` array, deliberately.** Jev's sixteen checks ship in
|
|
44
|
+
* the separate `FailproofAI/jev-policies` pack (the npm package carries none),
|
|
45
|
+
* which declares its own `minCliVersion`. So `manifestAuthority` below judges
|
|
46
|
+
* `reviewedBy` against those sixteen NAMES (`SEMANTIC_REVIEWER_NAMES`, its
|
|
47
|
+
* default): the checks a machine asks once it installs that pack beside this
|
|
48
|
+
* one. Without it, every policy here registers hard.
|
|
52
49
|
*/
|
|
53
50
|
import { execFileSync } from "node:child_process";
|
|
54
51
|
import { createHash } from "node:crypto";
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
PERMISSION_POLICIES,
|
|
53
53
|
} from "./cloud-introspect";
|
|
54
54
|
import type { CloudJevConfigWrite } from "./jev-cloud-connection";
|
|
55
|
+
import { jevChecksDeclared } from "./effective-reviewers";
|
|
55
56
|
import {
|
|
56
57
|
ingestPath,
|
|
57
58
|
validateIngestKey,
|
|
@@ -177,7 +178,7 @@ export interface JevConnectOutcome {
|
|
|
177
178
|
* alone like any existing file (never overwritten), but a connection that
|
|
178
179
|
* asked for decisions only is told that Jev still sends more than that.
|
|
179
180
|
*/
|
|
180
|
-
stillOn?: "
|
|
181
|
+
stillOn?: "observe" | "enforce";
|
|
181
182
|
/**
|
|
182
183
|
* Introspect gave no answer about this key's permissions — the server could
|
|
183
184
|
* not be asked (`unreachable`) or predates the endpoint (`unsupported`) — so
|
|
@@ -434,13 +435,18 @@ function jevLines(outcome: ConnectOutcome): string[] {
|
|
|
434
435
|
}
|
|
435
436
|
const config = jev.config;
|
|
436
437
|
if (!config || config.status === "written") {
|
|
437
|
-
// Plain http (loopback only) is accepted in
|
|
438
|
+
// Plain http (loopback only) is accepted in observe mode and nowhere else, so
|
|
438
439
|
// the enforce command would be refused there, by the CLI and the dashboard.
|
|
439
440
|
const plainHttp = config?.status === "written" && new URL(config.baseUrl).protocol === "http:";
|
|
440
441
|
return [
|
|
441
|
-
` Jev on through FailproofAI Cloud, in
|
|
442
|
+
` Jev on through FailproofAI Cloud, in observe mode: logged, not enforced (${config?.path ?? "jev.json"}).`,
|
|
443
|
+
// The package ships no Jev checks, so "on" asks nothing until a pack
|
|
444
|
+
// gives it some — said here, where the user just turned it on.
|
|
445
|
+
...(jevChecksDeclared()
|
|
446
|
+
? []
|
|
447
|
+
: [" It has no checks installed yet, so it asks nothing. To enable it: `failproofai policies add FailproofAI/jev-policies`."]),
|
|
442
448
|
plainHttp
|
|
443
|
-
? " Enforce needs an https FailproofAI Cloud URL: reconnect with `failproofai config --token <key> --url https://…`. Over plain http Jev stays in
|
|
449
|
+
? " Enforce needs an https FailproofAI Cloud URL: reconnect with `failproofai config --token <key> --url https://…`. Over plain http Jev stays in observe mode."
|
|
444
450
|
: " Enforce it with `failproofai jev setup --mode enforce`, or from the dashboard.",
|
|
445
451
|
];
|
|
446
452
|
}
|
|
@@ -449,7 +455,7 @@ function jevLines(outcome: ConnectOutcome): string[] {
|
|
|
449
455
|
if (config.otherOrigin) {
|
|
450
456
|
lines.push(
|
|
451
457
|
config.jevOff?.why === "off"
|
|
452
|
-
? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here and turn it on: \`failproofai jev setup --provider failproofai --mode
|
|
458
|
+
? ` It points at ${config.otherOrigin} and has Jev switched off (mode off), so Jev is off. To point it here and turn it on: \`failproofai jev setup --provider failproofai --mode observe\`.`
|
|
453
459
|
: ` It points at ${config.otherOrigin}, so Jev stays off until you run \`failproofai jev setup --provider failproofai\`.`,
|
|
454
460
|
);
|
|
455
461
|
} else if (config.jevOff?.why === "refused") {
|
|
@@ -457,7 +463,7 @@ function jevLines(outcome: ConnectOutcome): string[] {
|
|
|
457
463
|
` It was refused (${config.jevOff.problem}), so Jev is off. ${config.jevOff.fix ? `Fix: \`${config.jevOff.fix}\`.` : "Write a valid one: `failproofai jev setup --provider failproofai`."}`,
|
|
458
464
|
);
|
|
459
465
|
} else if (config.jevOff?.why === "off") {
|
|
460
|
-
lines.push(" It has Jev switched off (mode off), so Jev is off. To turn it on: `failproofai jev setup --mode
|
|
466
|
+
lines.push(" It has Jev switched off (mode off), so Jev is off. To turn it on: `failproofai jev setup --mode observe`.");
|
|
461
467
|
}
|
|
462
468
|
if (jev.stillOn) {
|
|
463
469
|
lines.push(
|
|
@@ -535,7 +535,7 @@ export async function loadAllCustomHooks(
|
|
|
535
535
|
* policy reviewable, which is the one thing a manifest may never do.
|
|
536
536
|
*
|
|
537
537
|
* `knownReviewers` is what makes that hold for a pack that ships BOTH tiers.
|
|
538
|
-
* Judged against
|
|
538
|
+
* Judged against any other set, a `reviewedBy` naming one of the pack's
|
|
539
539
|
* own checks is a name nothing here has, so the reviewable entry and its hard
|
|
540
540
|
* peer resolved alike and the merge could not tell them apart — while
|
|
541
541
|
* registration, which reads the manifest's own checks, honoured it. The set
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The semantic checks a `reviewedBy` may name ON THIS MACHINE.
|
|
3
3
|
*
|
|
4
|
-
* ##
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* quietly removed: the deny half of the two-tier evaluator working exactly as
|
|
15
|
-
* documented while the clearing half cannot fire once — the failure
|
|
16
|
-
* `policy-reviewability.ts` was written to make audible, reintroduced one layer
|
|
17
|
-
* down.
|
|
4
|
+
* ## Only what an installed pack declares
|
|
5
|
+
*
|
|
6
|
+
* The npm package ships no Jev checks, so no name is a reviewer by default. A
|
|
7
|
+
* `reviewedBy` is honoured only for checks some installed pack declares — in
|
|
8
|
+
* practice `FailproofAI/jev-policies`, which the core pack's fifteen
|
|
9
|
+
* `reviewable` policies name. With no such pack the set is EMPTY, every policy
|
|
10
|
+
* registers `hard`, and nothing Jev says can clear a regex verdict. That is the
|
|
11
|
+
* fail-safe direction, and it is also what keeps Jev inert on a machine that
|
|
12
|
+
* never installed its checks: `jevChecksInstalled` below is the hook path's
|
|
13
|
+
* gate for starting a review at all.
|
|
18
14
|
*
|
|
19
15
|
* The names come from the MANIFEST, which is already where `authority` and
|
|
20
16
|
* `reviewedBy` themselves are read from, so this adds no new trust and imports
|
|
@@ -39,7 +35,7 @@
|
|
|
39
35
|
* silently.
|
|
40
36
|
*/
|
|
41
37
|
import { readInstalledPacks, type ResolvedPack } from "./pack-manifest";
|
|
42
|
-
import { SEMANTIC_REVIEWER_NAMES } from "./policy-authority";
|
|
38
|
+
import { NO_REVIEWERS, SEMANTIC_REVIEWER_NAMES } from "./policy-authority";
|
|
43
39
|
|
|
44
40
|
let cached: ReadonlySet<string> | null = null;
|
|
45
41
|
let cachedContested: ReadonlyMap<string, string[]> = new Map();
|
|
@@ -148,8 +144,9 @@ export function isFirstPartyPack(pack: { source?: string }): boolean {
|
|
|
148
144
|
}
|
|
149
145
|
|
|
150
146
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
147
|
+
* One of FailproofAI's sixteen check names (`SEMANTIC_REVIEWER_NAMES`) declared
|
|
148
|
+
* by a pack that is not FailproofAI's. Core and user policies name those checks
|
|
149
|
+
* in `reviewedBy`, so the third party's
|
|
153
150
|
* question would become the reviewer that clears them — an `instruct`-mode
|
|
154
151
|
* `destructive-deletion` beside the core pack turned `block-rm-rf` into a
|
|
155
152
|
* warning. The claim is void: never asked, never a reviewer, and never a
|
|
@@ -162,33 +159,26 @@ export function isReservedClaim(pack: { source?: string }, name: string): boolea
|
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
/**
|
|
165
|
-
* The
|
|
166
|
-
* this
|
|
162
|
+
* The semantic check names the installed packs declare — the only names a
|
|
163
|
+
* `reviewedBy` is honoured for on this machine. Empty when no pack declares any.
|
|
167
164
|
*
|
|
168
165
|
* A name two packs claim differently is left out — see
|
|
169
|
-
* {@link contestedSemanticNames}
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* asked, and each of those is one of ours.
|
|
175
|
-
*
|
|
176
|
-
* Never throws: an unreadable manifest declares nothing, and the builtin set
|
|
177
|
-
* stands. That is the same fail-open posture every other reader of the pack
|
|
178
|
-
* manifest takes, and here it also fails in the safe direction — a pack's
|
|
179
|
-
* `reviewedBy` will not match a builtin name, so its policies stay `hard`
|
|
180
|
-
* rather than being cleared by a question nobody could read.
|
|
166
|
+
* {@link contestedSemanticNames} — and so is one of FailproofAI's names claimed
|
|
167
|
+
* by anyone else ({@link isReservedClaim}).
|
|
168
|
+
*
|
|
169
|
+
* Never throws: an unreadable manifest declares nothing, so the set is empty and
|
|
170
|
+
* every policy stays `hard`. There is no compiled-in set to fall back to.
|
|
181
171
|
*/
|
|
182
172
|
export function effectiveReviewerNames(): ReadonlySet<string> {
|
|
183
173
|
if (cached) return cached;
|
|
184
|
-
let names: ReadonlySet<string> =
|
|
174
|
+
let names: ReadonlySet<string> = NO_REVIEWERS;
|
|
185
175
|
cachedContested = new Map();
|
|
186
176
|
try {
|
|
187
177
|
const packs = jevPacks(readInstalledPacks().packs, reviewerCli);
|
|
188
178
|
names = reviewerNamesFor(packs);
|
|
189
179
|
cachedContested = contestedSemanticNames(packs);
|
|
190
180
|
} catch {
|
|
191
|
-
// See above:
|
|
181
|
+
// See above: an unreadable manifest declares no checks.
|
|
192
182
|
}
|
|
193
183
|
cached = names;
|
|
194
184
|
return names;
|
|
@@ -201,17 +191,34 @@ export function contestedReviewerNames(): ReadonlyMap<string, string[]> {
|
|
|
201
191
|
}
|
|
202
192
|
|
|
203
193
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
194
|
+
* Whether any installed pack gives Jev something to ask. False on a machine
|
|
195
|
+
* with no pack declaring a `semantic` entry — the vanilla install — and then
|
|
196
|
+
* the hook path does not start a Jev review at all: no request, no intent
|
|
197
|
+
* capture, exactly as if Jev were unconfigured. `failproofai policies add
|
|
198
|
+
* FailproofAI/jev-policies` is what turns it true.
|
|
199
|
+
*
|
|
200
|
+
* Read off the same cached set registration just used, so it costs nothing on
|
|
201
|
+
* the hook path and cannot disagree with which policies registered reviewable.
|
|
202
|
+
*/
|
|
203
|
+
export function jevChecksInstalled(): boolean {
|
|
204
|
+
return effectiveReviewerNames().size > 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The same question for a CLI command, read fresh rather than from the
|
|
209
|
+
* registration cache (which only the hook path fills): any agent, packs as
|
|
210
|
+
* `jevPacks` narrows them. Never throws; an unreadable manifest has none.
|
|
208
211
|
*/
|
|
209
|
-
export function
|
|
210
|
-
|
|
212
|
+
export function jevChecksDeclared(): boolean {
|
|
213
|
+
try {
|
|
214
|
+
return reviewerNamesFor(jevPacks(readInstalledPacks().packs)).size > 0;
|
|
215
|
+
} catch {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
/**
|
|
214
|
-
* The reviewer set for the packs taking part:
|
|
221
|
+
* The reviewer set for the packs taking part: every usable name they declare. Pure.
|
|
215
222
|
*
|
|
216
223
|
* Manifest-only: it cannot see a check `semanticPoliciesFromPacks` drops for the
|
|
217
224
|
* question budget, because measuring questions means loading the semantic
|
|
@@ -226,9 +233,9 @@ export function reviewerNamesFor(
|
|
|
226
233
|
const declared = packs
|
|
227
234
|
.flatMap((p) => (p.semantic ?? []).filter((s) => !isReservedClaim(p, s.name)).map((s) => s.name))
|
|
228
235
|
.filter((name) => !contested.has(name));
|
|
229
|
-
// Nothing usable declared:
|
|
230
|
-
if (declared.length === 0) return
|
|
231
|
-
return new Set(
|
|
236
|
+
// Nothing usable declared: nothing is asked, so nothing may clear.
|
|
237
|
+
if (declared.length === 0) return NO_REVIEWERS;
|
|
238
|
+
return new Set(declared);
|
|
232
239
|
}
|
|
233
240
|
|
|
234
241
|
/**
|
package/src/hooks/handler.ts
CHANGED
|
@@ -38,7 +38,7 @@ import type { JevActivityFields } from "./semantic/combine";
|
|
|
38
38
|
import type { JevConfig } from "./semantic/jev-config";
|
|
39
39
|
import { clearPolicies, registerPolicy, getPoliciesForEvent } from "./policy-registry";
|
|
40
40
|
import { loadAllCustomHooks } from "./custom-hooks-loader";
|
|
41
|
-
import { contestedReviewerNames, effectiveReviewerNames } from "./effective-reviewers";
|
|
41
|
+
import { contestedReviewerNames, effectiveReviewerNames, jevChecksInstalled } from "./effective-reviewers";
|
|
42
42
|
import {
|
|
43
43
|
authorityDeclarationFor,
|
|
44
44
|
refusedAuthorityWarning,
|
|
@@ -57,7 +57,7 @@ import { getInstanceId } from "../../lib/telemetry-id";
|
|
|
57
57
|
import { hookLogInfo, hookLogWarn } from "./hook-logger";
|
|
58
58
|
import { readStdinPayload } from "./read-stdin";
|
|
59
59
|
import { readActiveCloudManagedPolicies, type CloudManagedPolicyArtifact } from "./cloud-managed-policies";
|
|
60
|
-
import {
|
|
60
|
+
import { hasInstalledRegexPacks, readInstalledPacks, type PackError, type ResolvedPack } from "./pack-manifest";
|
|
61
61
|
import { missingGuards, packFailureReason, combinedGuardMatch, guardsCover } from "./pack-failclosed";
|
|
62
62
|
import { readActivePause, type ActivePause } from "./session-pause";
|
|
63
63
|
import { jevConfigFile } from "./fp-home";
|
|
@@ -217,6 +217,10 @@ async function runObserved(
|
|
|
217
217
|
// alone — exactly as it did before two tiers existed — otherwise:
|
|
218
218
|
//
|
|
219
219
|
// - a valid BYOK config exists (`~/.failproofai/jev.json`, global only);
|
|
220
|
+
// - an installed pack gives Jev checks to ask (`jevChecksInstalled`). The npm
|
|
221
|
+
// package ships none: until `failproofai policies add FailproofAI/jev-policies`
|
|
222
|
+
// (or another pack declaring `semantic` checks) Jev is inert — no request, no
|
|
223
|
+
// intent capture — whether the config is BYOK or FailproofAI Cloud's;
|
|
220
224
|
// - `FAILPROOFAI_EVALUATOR` is not `legacy` (see "Turning Jev off" below);
|
|
221
225
|
// - this is not the fail-closed `forceDecision` path and no session pause is
|
|
222
226
|
// active — a pause suspends local policy, and Jev must not become a way to
|
|
@@ -230,7 +234,7 @@ async function runObserved(
|
|
|
230
234
|
// Turning Jev off. The switch that works everywhere is the config file itself:
|
|
231
235
|
// it is read on every event, here, in whichever process evaluates — the
|
|
232
236
|
// daemon's warm worker included — so `failproofai jev remove` (or a `mode:
|
|
233
|
-
// "
|
|
237
|
+
// "observe"` config, which keeps enforcing the regex result) applies from the
|
|
234
238
|
// next tool call, with no restart. `FAILPROOFAI_EVALUATOR=legacy` is the dev
|
|
235
239
|
// escape hatch, read from the EVALUATING process's environment: it covers a
|
|
236
240
|
// one-shot hook (no daemon) and a worker whose own environment sets it, but
|
|
@@ -311,6 +315,8 @@ async function startTwoTier(
|
|
|
311
315
|
if (isHumanAuthoredGate(session.rawHookEventName, cli)) return null;
|
|
312
316
|
if (typeof parsed.tool_name !== "string" || parsed.tool_name.length === 0) return null;
|
|
313
317
|
if (jevForcedOff(opts) || activePause) return null;
|
|
318
|
+
// Read off the reviewer set registration just cached, so it is free.
|
|
319
|
+
if (!jevChecksInstalled()) return null;
|
|
314
320
|
const loaded = await readJevConfig();
|
|
315
321
|
if (!loaded) return null;
|
|
316
322
|
const cfg = loaded.config;
|
|
@@ -334,7 +340,7 @@ async function startTwoTier(
|
|
|
334
340
|
// carries the fallback.
|
|
335
341
|
hookLogInfo(`Jev review could not start (${err instanceof Error ? err.message : String(err)})`);
|
|
336
342
|
return {
|
|
337
|
-
mode: cfg.mode === "
|
|
343
|
+
mode: cfg.mode === "observe" || cfg.mode === "enforce" ? cfg.mode : loaded.defaultMode,
|
|
338
344
|
review: Promise.resolve({ kind: "fallback", reason: "error", latencyMs: null, model: null }),
|
|
339
345
|
abort: () => {},
|
|
340
346
|
authorityOf: () => ({ authority: "hard", reviewedBy: [] }),
|
|
@@ -398,6 +404,7 @@ async function captureJevIntent(
|
|
|
398
404
|
): Promise<void> {
|
|
399
405
|
if (canonicalEventType !== "UserPromptSubmit" || jevForcedOff(opts)) return;
|
|
400
406
|
try {
|
|
407
|
+
if (!jevChecksInstalled()) return;
|
|
401
408
|
if (!(await readJevConfig())) return;
|
|
402
409
|
if (decision === "deny") {
|
|
403
410
|
const { ENFORCEMENT_CAPABILITY } = await import("./enforcement-capability");
|
|
@@ -561,7 +568,9 @@ export async function evaluateHookEvent(
|
|
|
561
568
|
// yet, and it must not lose enforcement in the gap before `failproofai
|
|
562
569
|
// update` runs. It disappears for that machine the moment a pack is
|
|
563
570
|
// installed, and never fires for a machine set up by this version.
|
|
564
|
-
|
|
571
|
+
// Only a pack with regex policies retires the shim: a Jev-only pack
|
|
572
|
+
// (FailproofAI/jev-policies) replaces none of them.
|
|
573
|
+
const packsInstalledHere = hasInstalledRegexPacks();
|
|
565
574
|
const legacyNames =
|
|
566
575
|
activePause || packsInstalledHere ? [] : config.enabledPolicies;
|
|
567
576
|
// `alwaysOn` policies bypass the enabled set inside `registerBuiltinPolicies`,
|
|
@@ -738,8 +747,8 @@ export async function evaluateHookEvent(
|
|
|
738
747
|
const observeOnly = cloudManaged?.effect === "observe" || pack?.effect === "observe";
|
|
739
748
|
const fn: PolicyFunction = async (ctx): Promise<PolicyResult> => {
|
|
740
749
|
if (observeOnly) {
|
|
741
|
-
const
|
|
742
|
-
if (
|
|
750
|
+
const observed = await runObserved(hook, ctx, hookName, eventType, cli);
|
|
751
|
+
if (observed.decision !== "allow") {
|
|
743
752
|
// Sourced from whichever layer asked to observe. This read
|
|
744
753
|
// `cloudManaged!.id` — a non-null assertion that is simply false
|
|
745
754
|
// for a pack, so an observe-mode PACK threw on its first non-allow
|
|
@@ -751,8 +760,8 @@ export async function evaluateHookEvent(
|
|
|
751
760
|
observedResults.push({
|
|
752
761
|
policyId: observer!.id,
|
|
753
762
|
version: observer!.version,
|
|
754
|
-
decision:
|
|
755
|
-
reason:
|
|
763
|
+
decision: observed.decision,
|
|
764
|
+
reason: observed.reason ?? null,
|
|
756
765
|
});
|
|
757
766
|
}
|
|
758
767
|
return { decision: "allow" };
|
|
@@ -928,19 +937,19 @@ export async function evaluateHookEvent(
|
|
|
928
937
|
releaseRegistry: opts?.releaseRegistry,
|
|
929
938
|
})
|
|
930
939
|
: await evaluatePolicies(canonicalEventType, parsed, session, config);
|
|
931
|
-
//
|
|
940
|
+
// Jev's observe mode: what Jev WOULD have done, filed where observe-mode policies
|
|
932
941
|
// file theirs, so the "would have" view counts it with no second channel.
|
|
933
942
|
// `semantic/<check>` cannot be mistaken for a cloud policy — cloud ids
|
|
934
943
|
// carry no `/` (`POLICY_ID_RE` in cloud-managed-policies.ts) — and the
|
|
935
944
|
// version is a Jev model id or `jev`, never a deployment number. Only on
|
|
936
945
|
// the two-tier path, so an unconfigured row is untouched.
|
|
937
|
-
const
|
|
938
|
-
if (
|
|
946
|
+
const observeVerdict = result.twoTier?.observeVerdict;
|
|
947
|
+
if (observeVerdict) {
|
|
939
948
|
observedResults.push({
|
|
940
|
-
policyId:
|
|
941
|
-
version:
|
|
942
|
-
decision:
|
|
943
|
-
reason:
|
|
949
|
+
policyId: observeVerdict.policyName,
|
|
950
|
+
version: observeVerdict.version,
|
|
951
|
+
decision: observeVerdict.decision,
|
|
952
|
+
reason: observeVerdict.reason,
|
|
944
953
|
});
|
|
945
954
|
}
|
|
946
955
|
const durationMs = Math.round(performance.now() - startTime);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Hermes half of `failproofai update`.
|
|
3
|
+
*
|
|
4
|
+
* npm replaces the CLI and nothing else, and until this ran `update` never
|
|
5
|
+
* looked at Hermes — so a machine that installed Hermes enforcement with ≤1.0.5
|
|
6
|
+
* kept its config.yaml shell hooks through every upgrade. Those hooks never
|
|
7
|
+
* reach Hermes cron jobs (each cron fire builds its own hook scope, which only
|
|
8
|
+
* discovered plugins join), so every scheduled job on such a machine ran
|
|
9
|
+
* unchecked while everything looked configured.
|
|
10
|
+
*
|
|
11
|
+
* This migrates each profile that already uses FailproofAI to the linked native
|
|
12
|
+
* plugin, and reports per profile. It fails (ok: false → non-zero exit) whenever
|
|
13
|
+
* a profile that uses FailproofAI is left on shell hooks or otherwise could not
|
|
14
|
+
* be brought current, because a green `update` is exactly how the gap stayed
|
|
15
|
+
* invisible.
|
|
16
|
+
*/
|
|
17
|
+
import { migrateHermesProfiles, type HermesPluginInstallDeps, type HermesProfileMigration } from "./integrations";
|
|
18
|
+
|
|
19
|
+
export interface HermesUpdateResult {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
lines: string[];
|
|
22
|
+
profiles: HermesProfileMigration[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const STATUS_LABEL: Record<HermesProfileMigration["status"], string> = {
|
|
26
|
+
migrated: "migrated",
|
|
27
|
+
current: "already current",
|
|
28
|
+
untouched: "skipped",
|
|
29
|
+
blocked: "NOT migrated",
|
|
30
|
+
failed: "FAILED",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export async function runHermesUpdateMigration(deps: {
|
|
34
|
+
daemonSupportsPolicyEvaluation: () => Promise<boolean>;
|
|
35
|
+
installDeps?: HermesPluginInstallDeps;
|
|
36
|
+
}): Promise<HermesUpdateResult> {
|
|
37
|
+
const profiles = await migrateHermesProfiles(deps);
|
|
38
|
+
const relevant = profiles.filter((p) => p.status !== "untouched");
|
|
39
|
+
// A machine without FailproofAI on Hermes hears nothing about Hermes.
|
|
40
|
+
if (relevant.length === 0) return { ok: true, lines: [], profiles };
|
|
41
|
+
|
|
42
|
+
const width = Math.max(...profiles.map((p) => ("hermes/" + p.name).length));
|
|
43
|
+
const lines = ["Hermes:"];
|
|
44
|
+
for (const p of profiles) {
|
|
45
|
+
const label = ("hermes/" + p.name).padEnd(width);
|
|
46
|
+
const detail = p.status === "current" ? "" : ` — ${p.detail}`;
|
|
47
|
+
lines.push(` ${label} ${STATUS_LABEL[p.status]}${detail}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const blocked = profiles.filter((p) => p.status === "blocked");
|
|
51
|
+
const failed = profiles.filter((p) => p.status === "failed");
|
|
52
|
+
if (profiles.some((p) => p.status === "migrated")) {
|
|
53
|
+
lines.push(
|
|
54
|
+
" Cron jobs load the plugin on their next run; restart running Hermes gateways and sessions to load it there.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (blocked.length > 0) {
|
|
58
|
+
lines.push(
|
|
59
|
+
"",
|
|
60
|
+
`Hermes is NOT migrated (${blocked.map((p) => p.name).join(", ")}): the running failproofaid cannot`,
|
|
61
|
+
"answer the native plugin (no policyEvaluation), so nothing was changed. Profiles still on",
|
|
62
|
+
"shell hooks do NOT check Hermes cron jobs. Run `failproofai config` (it asks for sudo to",
|
|
63
|
+
"replace the daemon), then `failproofai update` again.",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (failed.length > 0) {
|
|
67
|
+
lines.push(
|
|
68
|
+
"",
|
|
69
|
+
`Hermes could not be migrated for: ${failed.map((p) => p.name).join(", ")}. Fix the reason above,`,
|
|
70
|
+
"then run `failproofai update` again.",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return { ok: blocked.length === 0 && failed.length === 0, lines, profiles };
|
|
74
|
+
}
|
|
@@ -105,8 +105,8 @@ export interface HookActivityEntry {
|
|
|
105
105
|
jevFallbackReason?: string;
|
|
106
106
|
jevLatencyMs?: number;
|
|
107
107
|
jevModel?: string;
|
|
108
|
-
/** `
|
|
109
|
-
jevMode?: "
|
|
108
|
+
/** `observe` logs Jev but enforces the regex result; `enforce` applies the combine rules. */
|
|
109
|
+
jevMode?: "observe" | "enforce";
|
|
110
110
|
durationMs: number;
|
|
111
111
|
sessionId?: string;
|
|
112
112
|
transcriptPath?: string;
|
|
@@ -159,7 +159,7 @@ export interface HookActivityEntry {
|
|
|
159
159
|
* indistinguishable from one where the policy never matched, and the rollout
|
|
160
160
|
* being trialled is unmeasurable.
|
|
161
161
|
*
|
|
162
|
-
* Jev in
|
|
162
|
+
* Jev in observe mode files its own deny/instruct here too, as
|
|
163
163
|
* `{policyId: "semantic/<check>", version: <Jev model id or "jev">}`: the
|
|
164
164
|
* same question — what would this have done — for the same reader.
|
|
165
165
|
*/
|