@fusengine/harness 0.1.63 → 0.1.65
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/dist/adapters/claude/index.d.mts +1 -1
- package/dist/adapters/claude/index.mjs +1 -1
- package/dist/adapters/cline/index.mjs +1 -1
- package/dist/adapters/codex/index.d.mts +8 -3
- package/dist/adapters/codex/index.mjs +13 -7
- package/dist/adapters/cursor/index.mjs +1 -1
- package/dist/adapters/gemini/index.mjs +1 -1
- package/dist/adapters/hermes/index.mjs +1 -1
- package/dist/{claude-C0xGMbeA.mjs → claude-CVQlXOl8.mjs} +19 -10
- package/dist/cli/bin.mjs +30 -11
- package/dist/cli/index.mjs +1 -1
- package/dist/{evaluate-wDDbojx6.mjs → evaluate-I9CAwUlI.mjs} +55 -1
- package/dist/{handle-CjwRTMOv.mjs → handle-DnxRzNuY.mjs} +65 -13
- package/dist/{hermes-C1gt6ooE.mjs → hermes-DKTAji46.mjs} +2 -2
- package/dist/{index-C9jYdElK.d.mts → index-CU3oHxOe.d.mts} +2 -0
- package/dist/{index-ce2J1bHC.d.mts → index-a6kvHEPa.d.mts} +2 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{apply-patch-CIS2EZ_q.mjs → permission-mode-BN3MNgbm.mjs} +19 -1
- package/dist/policy/index.d.mts +1 -1
- package/dist/policy/index.mjs +1 -1
- package/dist/{run-kVHXFVug.mjs → run-BCQAHUrx.mjs} +1 -1
- package/dist/runtime/index.d.mts +5 -3
- package/dist/runtime/index.mjs +1 -1
- package/package.json +4 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as denyResponse, c as informResponse, d as toClaudeResponse, i as contextResponse, l as readClaudeInput, n as attachSystemMessage, o as fileSizeGuard, r as blockResponse, s as guard, t as ClaudeHookInput, u as systemMessage } from "../../index-
|
|
1
|
+
import { a as denyResponse, c as informResponse, d as toClaudeResponse, i as contextResponse, l as readClaudeInput, n as attachSystemMessage, o as fileSizeGuard, r as blockResponse, s as guard, t as ClaudeHookInput, u as systemMessage } from "../../index-a6kvHEPa.mjs";
|
|
2
2
|
export { ClaudeHookInput, attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as fileSizeGuard, c as readClaudeInput, i as denyResponse, l as systemMessage, n as blockResponse, o as guard, r as contextResponse, s as informResponse, t as attachSystemMessage, u as toClaudeResponse } from "../../claude-
|
|
1
|
+
import { a as fileSizeGuard, c as readClaudeInput, i as denyResponse, l as systemMessage, n as blockResponse, o as guard, r as contextResponse, s as informResponse, t as attachSystemMessage, u as toClaudeResponse } from "../../claude-CVQlXOl8.mjs";
|
|
2
2
|
export { attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { t as Prompt } from "../../types-DVbIl9md.mjs";
|
|
2
|
-
import { a as denyResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-
|
|
2
|
+
import { a as denyResponse, c as informResponse, i as contextResponse, l as readClaudeInput, t as ClaudeHookInput } from "../../index-a6kvHEPa.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/adapters/codex/index.d.ts
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny.
|
|
7
|
+
* NOTE: the REAL wired route is `harness hook codex` → handleHook → respond.ts
|
|
8
|
+
* (source of truth for the ask→deny downgrade); this thin export exists for
|
|
9
|
+
* direct package consumers and is kept aligned so it never silently diverges.
|
|
10
|
+
*/
|
|
6
11
|
declare function toCodexResponse(prompt: Prompt): string;
|
|
7
12
|
/**
|
|
8
13
|
* Run the bundled policy over a Codex payload and return the native response
|
|
@@ -11,4 +16,4 @@ declare function toCodexResponse(prompt: Prompt): string;
|
|
|
11
16
|
*/
|
|
12
17
|
declare function guard(input: ClaudeHookInput): string | null;
|
|
13
18
|
//#endregion
|
|
14
|
-
export { type ClaudeHookInput as CodexHookInput, contextResponse, denyResponse, guard, readClaudeInput as readCodexInput, toCodexResponse };
|
|
19
|
+
export { type ClaudeHookInput as CodexHookInput, contextResponse, denyResponse, guard, informResponse, readClaudeInput as readCodexInput, toCodexResponse };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { f as countLines } from "../../home-state-D0RLWP8J.mjs";
|
|
2
|
-
import { t as evaluate } from "../../evaluate-
|
|
2
|
+
import { t as evaluate } from "../../evaluate-I9CAwUlI.mjs";
|
|
3
3
|
import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { n as parseApplyPatch, t as isBypassPermissions } from "../../permission-mode-BN3MNgbm.mjs";
|
|
5
|
+
import { c as readClaudeInput, i as denyResponse, r as contextResponse, s as informResponse, v as commandToString } from "../../claude-CVQlXOl8.mjs";
|
|
6
6
|
//#region src/adapters/codex/index.ts
|
|
7
7
|
/**
|
|
8
8
|
* OpenAI Codex CLI adapter (hook-mode). Codex's `PreToolUse` hook (since 2026)
|
|
@@ -21,10 +21,15 @@ import { _ as commandToString, c as readClaudeInput, i as denyResponse, r as con
|
|
|
21
21
|
* an explicit deny.
|
|
22
22
|
*/
|
|
23
23
|
const ASK_PREFIX = "[downgraded from ask — Codex has no interactive approval]";
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny.
|
|
26
|
+
* NOTE: the REAL wired route is `harness hook codex` → handleHook → respond.ts
|
|
27
|
+
* (source of truth for the ask→deny downgrade); this thin export exists for
|
|
28
|
+
* direct package consumers and is kept aligned so it never silently diverges.
|
|
29
|
+
*/
|
|
25
30
|
function toCodexResponse(prompt) {
|
|
26
31
|
const message = formatPrompt(prompt);
|
|
27
|
-
if (prompt.kind === "inform") return contextResponse("PreToolUse", message);
|
|
32
|
+
if (prompt.kind === "inform") return prompt.userMessage ? informResponse("PreToolUse", prompt.userMessage, prompt.reason ? message : "") : contextResponse("PreToolUse", message);
|
|
28
33
|
if (prompt.kind === "ask") return denyResponse("PreToolUse", `${ASK_PREFIX}\n${message}`);
|
|
29
34
|
return denyResponse("PreToolUse", message);
|
|
30
35
|
}
|
|
@@ -49,7 +54,8 @@ function resolvePrompt(input) {
|
|
|
49
54
|
tool: input.tool_name ?? "Write",
|
|
50
55
|
filePath: i?.file_path,
|
|
51
56
|
content: i?.content ?? i?.new_string,
|
|
52
|
-
command: commandToString(i?.command)
|
|
57
|
+
command: commandToString(i?.command),
|
|
58
|
+
neverApproval: isBypassPermissions(input.permission_mode)
|
|
53
59
|
});
|
|
54
60
|
return r.decision === "allow" || !r.prompt ? null : r.prompt;
|
|
55
61
|
}
|
|
@@ -63,4 +69,4 @@ function guard(input) {
|
|
|
63
69
|
return prompt ? toCodexResponse(prompt) : null;
|
|
64
70
|
}
|
|
65
71
|
//#endregion
|
|
66
|
-
export { contextResponse, denyResponse, guard, readClaudeInput as readCodexInput, toCodexResponse };
|
|
72
|
+
export { contextResponse, denyResponse, guard, informResponse, readClaudeInput as readCodexInput, toCodexResponse };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-
|
|
1
|
+
import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-DKTAji46.mjs";
|
|
2
2
|
export { guard, readHermesInput, toHermesResponse };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as evaluate } from "./evaluate-
|
|
1
|
+
import { t as evaluate } from "./evaluate-I9CAwUlI.mjs";
|
|
2
2
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
@@ -81,11 +81,23 @@ function spawnCapture(cmd, args, cwd) {
|
|
|
81
81
|
return "";
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Read the full process stdin as UTF-8 text (works under Node and Bun).
|
|
86
|
+
* Synchronous fd-0 read — never `for await (const c of process.stdin)` —
|
|
87
|
+
* because Bun has a confirmed Linux-only bug where merely adding a new
|
|
88
|
+
* top-level module import shifts stdin's internal init order and makes
|
|
89
|
+
* that async iterator silently yield zero bytes (oven-sh/bun#25320,
|
|
90
|
+
* #27849): exactly what broke every `harness hook` invocation on CI the
|
|
91
|
+
* moment `figures` became this package's first runtime dependency.
|
|
92
|
+
* `readFileSync(0)` bypasses that stream/ownership machinery entirely and
|
|
93
|
+
* is well-supported for piped (non-TTY) stdin under both runtimes.
|
|
94
|
+
*/
|
|
85
95
|
async function readStdin() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
try {
|
|
97
|
+
return readFileSync(0, "utf8");
|
|
98
|
+
} catch {
|
|
99
|
+
return "";
|
|
100
|
+
}
|
|
89
101
|
}
|
|
90
102
|
/**
|
|
91
103
|
* Recursively collect files under `dir` whose extension is in `exts`, skipping
|
|
@@ -113,10 +125,7 @@ function collectFiles(dir, exts, out, cap) {
|
|
|
113
125
|
}
|
|
114
126
|
//#endregion
|
|
115
127
|
//#region src/adapters/claude/index.ts
|
|
116
|
-
/**
|
|
117
|
-
* Claude Code adapter — the thin Claude-only shim over the portable policy core.
|
|
118
|
-
* Reads the hook stdin payload and emits hookSpecificOutput responses.
|
|
119
|
-
*/
|
|
128
|
+
/** Claude Code adapter — the thin Claude-only shim over the portable policy core; reads the hook stdin payload and emits hookSpecificOutput responses. */
|
|
120
129
|
/** Read & parse the Claude hook payload from stdin (empty object on bad input). */
|
|
121
130
|
async function readClaudeInput() {
|
|
122
131
|
const text = await readStdin();
|
|
@@ -214,4 +223,4 @@ function guard(input) {
|
|
|
214
223
|
/** @deprecated use {@link guard}. Kept for back-compat. */
|
|
215
224
|
const fileSizeGuard = guard;
|
|
216
225
|
//#endregion
|
|
217
|
-
export {
|
|
226
|
+
export { writeText as _, fileSizeGuard as a, readClaudeInput as c, collectFiles as d, pathExists as f, spawnCapture as g, sleep as h, denyResponse as i, systemMessage as l, readText as m, blockResponse as n, guard as o, readStdin as p, contextResponse as r, informResponse as s, attachSystemMessage as t, toClaudeResponse as u, commandToString as v };
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import { r as loadDotenv, s as resolveTtlSec } from "../dotenv-Jj8aL1FL.mjs";
|
|
3
3
|
import { t as detectHarness } from "../harness-Cb9xR8dC.mjs";
|
|
4
4
|
import { t as claudeHome } from "../home-state-D0RLWP8J.mjs";
|
|
5
|
-
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-
|
|
5
|
+
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-BCQAHUrx.mjs";
|
|
6
6
|
import { n as writeInitFile, t as initFor } from "../run-Do2JltgU.mjs";
|
|
7
|
-
import { J as runningVersion, Lt as todayUtc, Y as versionBanner, q as runDoctor, t as handleHook } from "../handle-
|
|
7
|
+
import { J as runningVersion, Lt as todayUtc, Y as versionBanner, q as runDoctor, t as handleHook } from "../handle-DnxRzNuY.mjs";
|
|
8
|
+
import { p as readStdin$1 } from "../claude-CVQlXOl8.mjs";
|
|
8
9
|
import { delimiter, join } from "node:path";
|
|
9
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
10
11
|
import { homedir } from "node:os";
|
|
@@ -361,15 +362,19 @@ function discoverRefs(home, cwd, marketplaces) {
|
|
|
361
362
|
* harness hook <id> runtime: read a hook payload on stdin, route to the adapter, print the response
|
|
362
363
|
* harness changelog fetch + diff the Claude Code changelog, print a JSON summary (changelog-watcher)
|
|
363
364
|
*/
|
|
365
|
+
const hookDebug = process.env.FUSE_HARNESS_DEBUG === "1";
|
|
366
|
+
function traceHook(label, data) {
|
|
367
|
+
if (hookDebug) process.stderr.write(`[hook-debug] ${label}: ${typeof data === "string" ? data : JSON.stringify(data)}\n`);
|
|
368
|
+
}
|
|
364
369
|
async function readStdin() {
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
370
|
+
const text = (await readStdin$1()).trim();
|
|
371
|
+
traceHook("stdin-text-length", text.length);
|
|
368
372
|
if (!text) return {};
|
|
369
373
|
try {
|
|
370
374
|
const parsed = JSON.parse(text);
|
|
371
375
|
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
372
|
-
} catch {
|
|
376
|
+
} catch (e) {
|
|
377
|
+
traceHook("stdin-parse-error", e instanceof Error ? e.message : String(e));
|
|
373
378
|
return {};
|
|
374
379
|
}
|
|
375
380
|
}
|
|
@@ -399,13 +404,27 @@ if (cmd === "--version" || cmd === "-v") {
|
|
|
399
404
|
])).has(scopeArg) ? scopeArg : "core";
|
|
400
405
|
const marketplaces = (process.env.FUSE_HARNESS_MARKETPLACES ?? "fusengine-plugins").split(",").map((s) => s.trim()).filter(Boolean);
|
|
401
406
|
const refsDir = process.env.FUSE_HARNESS_REFS || discoverRefs(homedir(), process.cwd(), marketplaces) || void 0;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
cwd: process.cwd(),
|
|
405
|
-
refsDir,
|
|
406
|
-
windowMs: resolveTtlSec(process.env) * 1e3,
|
|
407
|
+
traceHook("args", {
|
|
408
|
+
id,
|
|
407
409
|
scope
|
|
408
410
|
});
|
|
411
|
+
let outcome;
|
|
412
|
+
try {
|
|
413
|
+
outcome = await handleHook(id, await readStdin(), {
|
|
414
|
+
now: Date.now(),
|
|
415
|
+
cwd: process.cwd(),
|
|
416
|
+
refsDir,
|
|
417
|
+
windowMs: resolveTtlSec(process.env) * 1e3,
|
|
418
|
+
scope
|
|
419
|
+
});
|
|
420
|
+
} catch (e) {
|
|
421
|
+
traceHook("handleHook-threw", e instanceof Error ? `${e.message}\n${e.stack}` : String(e));
|
|
422
|
+
throw e;
|
|
423
|
+
}
|
|
424
|
+
traceHook("outcome", {
|
|
425
|
+
stdoutLength: outcome.stdout.length,
|
|
426
|
+
exit: outcome.exit
|
|
427
|
+
});
|
|
409
428
|
if (outcome.stdout) process.stdout.write(outcome.stdout);
|
|
410
429
|
process.exit(outcome.exit);
|
|
411
430
|
} else if (cmd === "init") {
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-
|
|
1
|
+
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-BCQAHUrx.mjs";
|
|
2
2
|
export { checkStaged, stagedContent, stagedFiles };
|
|
@@ -305,7 +305,9 @@ const PROTECTED_FRAGMENTS = [
|
|
|
305
305
|
".fuse-harness/cache/sessions",
|
|
306
306
|
".claude/apex/",
|
|
307
307
|
".harness/track",
|
|
308
|
-
".harness/memory/state"
|
|
308
|
+
".harness/memory/state",
|
|
309
|
+
".codex/config.toml",
|
|
310
|
+
".codex/rules/"
|
|
309
311
|
];
|
|
310
312
|
/**
|
|
311
313
|
* Matches a real `.git` directory segment (`/.git/`, `~/.git`, leading or
|
|
@@ -733,6 +735,50 @@ function runGuards(ctx) {
|
|
|
733
735
|
return null;
|
|
734
736
|
}
|
|
735
737
|
//#endregion
|
|
738
|
+
//#region src/policy/never-approval.ts
|
|
739
|
+
/**
|
|
740
|
+
* Shell metacharacters that turn a "simple" command into a chained/composite
|
|
741
|
+
* one: `&&`, `||`, `;`, `|`, a lone background `&` (POSIX control operator —
|
|
742
|
+
* `cmd1 & cmd2` runs BOTH; `&(?!>)` excludes bash's `&>`/`&>>` redirects, which
|
|
743
|
+
* cannot introduce a second command), a backtick, `$(`, or a literal newline.
|
|
744
|
+
* Bounded alternation, no nested quantifiers — no ReDoS. Quote-unaware by
|
|
745
|
+
* design: a quoted `"R&D"` gates as ask (fail-closed).
|
|
746
|
+
*/
|
|
747
|
+
const CHAIN_RE = /&&|\|\||[;|`]|&(?!>)|\$\(|\n/;
|
|
748
|
+
/**
|
|
749
|
+
* True when `cmd` has no chaining operator. evaluate.ts's neverApproval
|
|
750
|
+
* exemption must NEVER fire on a composite command — `git commit -m x &&
|
|
751
|
+
* git push --force` would otherwise slip a blocked op past the RALPH_SAFE
|
|
752
|
+
* subset check, since that check is a `startsWith` on the leading verb only.
|
|
753
|
+
*/
|
|
754
|
+
function isSingleCommand(cmd) {
|
|
755
|
+
return !CHAIN_RE.test(cmd);
|
|
756
|
+
}
|
|
757
|
+
/** Next steps offered on the auto-approve notice — the only channel left once `ask` is unavailable. */
|
|
758
|
+
const ACTIONS = [
|
|
759
|
+
"Set approval_policy=on-request for interactive confirmation",
|
|
760
|
+
"Or set RALPH_MODE=1 to auto-approve silently (no notice)",
|
|
761
|
+
"Or run the command manually outside the agent loop"
|
|
762
|
+
];
|
|
763
|
+
/**
|
|
764
|
+
* Builds the auto-approve `inform` prompt for a command evaluate.ts has
|
|
765
|
+
* already proven safe (RALPH_SAFE subset, not GIT_BLOCKED, not chained —
|
|
766
|
+
* see {@link isSingleCommand}). Pure formatting only; the 3-condition
|
|
767
|
+
* decision itself lives in evaluate.ts, where the anti-chaining guarantee
|
|
768
|
+
* must stay visible and auditable.
|
|
769
|
+
* @param cmd - The already-vetted command.
|
|
770
|
+
*/
|
|
771
|
+
function buildNeverApprovalPrompt(cmd) {
|
|
772
|
+
const trimmed = cmd.trim();
|
|
773
|
+
return {
|
|
774
|
+
kind: "inform",
|
|
775
|
+
title: "Auto-approved (approval_policy=never)",
|
|
776
|
+
reason: `Auto-approved "${trimmed}" under approval_policy=never.`,
|
|
777
|
+
actions: [...ACTIONS],
|
|
778
|
+
userMessage: `[fuse-harness] Auto-approved "${trimmed}" — approval_policy=never has no ask channel. Destructive git commands still deny. Set approval_policy=on-request, or RALPH_MODE=1, to keep asking.`
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
//#endregion
|
|
736
782
|
//#region src/policy/evaluate.ts
|
|
737
783
|
/**
|
|
738
784
|
* Evaluate a single tool-use against the bundled policies, returning a pure
|
|
@@ -748,6 +794,14 @@ function evaluate(ctx) {
|
|
|
748
794
|
};
|
|
749
795
|
const cmd = ctx.command;
|
|
750
796
|
const ralphSafe = !!cmd && isRalphMode() && RALPH_SAFE.some((s) => cmd.startsWith(s));
|
|
797
|
+
if (!ralphSafe && !!ctx.neverApproval && !!cmd && !matchPatterns(cmd, GIT_BLOCKED) && isSingleCommand(cmd) && RALPH_SAFE.some((s) => cmd.startsWith(s)) && cmd) {
|
|
798
|
+
const prompt = buildNeverApprovalPrompt(cmd);
|
|
799
|
+
return {
|
|
800
|
+
decision: "warn",
|
|
801
|
+
message: prompt.reason,
|
|
802
|
+
prompt
|
|
803
|
+
};
|
|
804
|
+
}
|
|
751
805
|
if (!ralphSafe && ctx.command && matchPatterns(ctx.command, GIT_BLOCKED)) {
|
|
752
806
|
const reason = `Destructive git command: ${ctx.command}`;
|
|
753
807
|
return {
|
|
@@ -4,7 +4,7 @@ import { r as projectLayout } from "./layout-C0jaaCQC.mjs";
|
|
|
4
4
|
import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
|
|
5
5
|
import { A as detectCreationIntent, E as isExcludedSwiftPath, F as docConsultedGate, H as detectProjectType$1, I as evaluateApex, M as POST_AUTH_GATES, N as PRE_AUTH_GATES, S as usesTailwindUtilities, T as isExcludedJsPath, V as detectModularArchitecture, W as requiredArchSkill, _ as scanPlugin, c as EXCLUDE_DIRS$1, d as buildApexTaskInjection, h as buildClaudeMdContext, k as capVerbosity, l as PROJECT_INDICATORS, n as missingSeoElements, o as parseEnrichment, r as descFromText, s as parseEntry, t as isHtmlLike, w as frameworkSolidGate, x as skillTriggerGate, y as parseField, z as detectFramework } from "./validate-DnOqIZD_.mjs";
|
|
6
6
|
import { a as sanitizeSessionId, c as sessionsDir, d as countFrameworkCodeLines, f as countLines, i as loadSessionState, l as PLUGINS_DIR, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, t as claudeHome } from "./home-state-D0RLWP8J.mjs";
|
|
7
|
-
import { n as FAIL_CLOSED, t as evaluate } from "./evaluate-
|
|
7
|
+
import { n as FAIL_CLOSED, t as evaluate } from "./evaluate-I9CAwUlI.mjs";
|
|
8
8
|
import { a as writeJsonFile, i as readJsonFile, r as hashText, t as atomicWrite } from "./json-io-DisYd2fb.mjs";
|
|
9
9
|
import { r as isDocConsulted } from "./doc-helpers-CWZegVdR.mjs";
|
|
10
10
|
import { n as findMarketplacePlugins, r as readPluginMeta, t as resolveSkillPath } from "./skill-path-DhItkBzk.mjs";
|
|
@@ -13,9 +13,9 @@ import { a as nowStamp, l as throttleMs, n as readRoots, o as readState, s as se
|
|
|
13
13
|
import { d as loadIndex, i as cacheLookupMeta, n as webfetchCacheWrite, o as cacheLookupSubstringMeta, t as mcpCacheWrite, u as extractText } from "./mcp-store-BkBDmuxN.mjs";
|
|
14
14
|
import { t as loadRefs } from "./loader-AGz4nK7d.mjs";
|
|
15
15
|
import { a as writeLastNonce, c as recordAgent, d as recordRefRead, f as recordTarget, h as apexAuthorizationGate, i as verifyTrack, l as recordBrainstormRequired, m as trivialCount, n as saveTrack, o as agentsFresh, p as recordTrivialEdit, r as signTrack, s as emptyTrack, t as loadTrack, u as recordDoc } from "./store-CQ4roWrU.mjs";
|
|
16
|
-
import {
|
|
17
|
-
import { _ as
|
|
18
|
-
import { r as toHermesResponse } from "./hermes-
|
|
16
|
+
import { n as parseApplyPatch, t as isBypassPermissions } from "./permission-mode-BN3MNgbm.mjs";
|
|
17
|
+
import { _ as writeText, d as collectFiles, f as pathExists, g as spawnCapture, h as sleep, i as denyResponse, l as systemMessage, m as readText, n as blockResponse, r as contextResponse, s as informResponse, t as attachSystemMessage, v as commandToString } from "./claude-CVQlXOl8.mjs";
|
|
18
|
+
import { r as toHermesResponse } from "./hermes-DKTAji46.mjs";
|
|
19
19
|
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
20
20
|
import { appendFileSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
21
21
|
import { homedir } from "node:os";
|
|
@@ -23,6 +23,7 @@ import { mkdir, rmdir } from "node:fs/promises";
|
|
|
23
23
|
import { createHash } from "node:crypto";
|
|
24
24
|
import { execFileSync, execSync } from "node:child_process";
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
|
+
import figures from "figures";
|
|
26
27
|
//#region src/runtime/lifecycle/security/skill-state.ts
|
|
27
28
|
/**
|
|
28
29
|
* Shared security-tracker state: per-UTC-day JSON under
|
|
@@ -93,7 +94,8 @@ function normalizeEvent(id, payload) {
|
|
|
93
94
|
tool,
|
|
94
95
|
input,
|
|
95
96
|
sessionId: str(payload.session_id) ?? str(payload.conversation_id) ?? "",
|
|
96
|
-
agentType: str(payload.agent_type) ?? str(input.subagent_type)
|
|
97
|
+
agentType: str(payload.agent_type) ?? str(input.subagent_type),
|
|
98
|
+
permissionMode: str(payload.permission_mode)
|
|
97
99
|
};
|
|
98
100
|
if (tool === "apply_patch") {
|
|
99
101
|
const files = parseApplyPatch(str(input.command) ?? str(payload.command) ?? "").map((f) => ({
|
|
@@ -6812,7 +6814,8 @@ async function runGates(input) {
|
|
|
6812
6814
|
content: input.content,
|
|
6813
6815
|
command: input.command,
|
|
6814
6816
|
agentType: input.agentType,
|
|
6815
|
-
existingLines
|
|
6817
|
+
existingLines,
|
|
6818
|
+
neverApproval: input.neverApproval
|
|
6816
6819
|
});
|
|
6817
6820
|
} catch {
|
|
6818
6821
|
return FAIL_CLOSED;
|
|
@@ -7053,6 +7056,56 @@ function respond(id, prompt) {
|
|
|
7053
7056
|
}
|
|
7054
7057
|
}
|
|
7055
7058
|
//#endregion
|
|
7059
|
+
//#region src/runtime/deny-notice.ts
|
|
7060
|
+
/**
|
|
7061
|
+
* @module deny-notice
|
|
7062
|
+
* Attach a user-visible notice to a deny/ask hook response — the owner-reported
|
|
7063
|
+
* gap where `permissionDecision: deny/ask` (the agent-only channel) left the
|
|
7064
|
+
* human staring at a silent terminal. Mirrors {@link module:notices}'s
|
|
7065
|
+
* compliance notices but for the BLOCKING outcomes those never covered.
|
|
7066
|
+
* @packageDocumentation
|
|
7067
|
+
*/
|
|
7068
|
+
/**
|
|
7069
|
+
* The human-facing line for a block/ask outcome: the gate's own
|
|
7070
|
+
* {@link Prompt.userMessage} when it set one, else a generic symbol + title —
|
|
7071
|
+
* text-presentation Unicode only (`figures.cross`/`?`), never emoji: terminal-safe,
|
|
7072
|
+
* single-cell width, with the Windows fallbacks `figures` already resolves, matching
|
|
7073
|
+
* the existing notice family (`notices.ts`'s `✓`/`⚠`, left untouched by this module).
|
|
7074
|
+
* Null for `inform` ({@link module:respond}'s own `userMessage` path already
|
|
7075
|
+
* covers it) or when neither applies.
|
|
7076
|
+
*/
|
|
7077
|
+
function denyAskNotice(prompt) {
|
|
7078
|
+
if (prompt.kind === "block") return prompt.userMessage ?? `${figures.cross} ${prompt.title}`;
|
|
7079
|
+
if (prompt.kind === "ask") return prompt.userMessage ?? `? ${prompt.title}`;
|
|
7080
|
+
return null;
|
|
7081
|
+
}
|
|
7082
|
+
/**
|
|
7083
|
+
* Attach {@link denyAskNotice} onto an already-rendered deny/ask `stdout`, for
|
|
7084
|
+
* the harnesses whose human channel is `systemMessage` (claude-code/codex —
|
|
7085
|
+
* `permissionDecision`/`permissionDecisionReason` stay byte-intact, only the
|
|
7086
|
+
* top-level field is added). Every other harness passes `stdout` through
|
|
7087
|
+
* unchanged: Cursor already emits `user_message` natively (respond.ts), Hermes
|
|
7088
|
+
* and cline have no human channel. Deduped via {@link onceExclusive} against
|
|
7089
|
+
* the ~11 sibling-plugin fan-out for one real event (same window as the
|
|
7090
|
+
* sniper reminder / compliance notices).
|
|
7091
|
+
* @param id - Harness id (`ctx.id` from `PreContext`).
|
|
7092
|
+
* @param stdout - The rendered hook response (from {@link module:respond.respond}).
|
|
7093
|
+
* @param prompt - The {@link Prompt} that produced `stdout`.
|
|
7094
|
+
* @param sessionId - Current session id (dedup scope).
|
|
7095
|
+
* @param dir - State-dir for the dedup marker (per-project state dir).
|
|
7096
|
+
* @param now - Event clock (tests pass a fake one).
|
|
7097
|
+
*/
|
|
7098
|
+
function withDenyNotice(id, stdout, prompt, sessionId, dir, now) {
|
|
7099
|
+
if (id !== "claude-code" && id !== "codex") return stdout;
|
|
7100
|
+
const notice = denyAskNotice(prompt);
|
|
7101
|
+
if (!notice) return stdout;
|
|
7102
|
+
if (!onceExclusive(`deny-notice:${sessionId}:${prompt.title}`, 2e3, {
|
|
7103
|
+
now,
|
|
7104
|
+
dir
|
|
7105
|
+
})) return stdout;
|
|
7106
|
+
return attachSystemMessage(stdout, notice);
|
|
7107
|
+
}
|
|
7108
|
+
//#endregion
|
|
7056
7109
|
//#region src/policy/design/content-checks.ts
|
|
7057
7110
|
/** Accessibility warnings: icon buttons need aria-label, images need alt. */
|
|
7058
7111
|
function checkAccessibility(content) {
|
|
@@ -7595,9 +7648,7 @@ function applyPatchGate(files, cwd) {
|
|
|
7595
7648
|
//#endregion
|
|
7596
7649
|
//#region src/runtime/handle-pre.ts
|
|
7597
7650
|
/**
|
|
7598
|
-
* Run the PreToolUse pipeline: MCP/WebFetch cache intercept, design gate, APEX
|
|
7599
|
-
* Task context injection, then the stateless+APEX gate chain. Returns the native
|
|
7600
|
-
* hook outcome (deny/ask/inject or allow).
|
|
7651
|
+
* Run the PreToolUse pipeline: MCP/WebFetch cache intercept, design gate, APEX Task context injection, then the stateless+APEX gate chain, returning the native hook outcome (deny/ask/inject or allow).
|
|
7601
7652
|
* @param ctx - The resolved pre-context.
|
|
7602
7653
|
* @returns The hook outcome.
|
|
7603
7654
|
*/
|
|
@@ -7618,7 +7669,7 @@ async function handlePre(ctx) {
|
|
|
7618
7669
|
}
|
|
7619
7670
|
const designBlock = designGate(payload, event, mcpDir, opts.cwd);
|
|
7620
7671
|
if (designBlock) return {
|
|
7621
|
-
stdout: respond(id, designBlock),
|
|
7672
|
+
stdout: withDenyNotice(id, respond(id, designBlock), designBlock, event.sessionId, dirname(file), opts.now),
|
|
7622
7673
|
exit: 0
|
|
7623
7674
|
};
|
|
7624
7675
|
if (opts.scope === "security") return {
|
|
@@ -7639,7 +7690,7 @@ async function handlePre(ctx) {
|
|
|
7639
7690
|
if (event.files && event.files.length > 0) {
|
|
7640
7691
|
const patchPrompt = applyPatchGate(event.files, opts.cwd);
|
|
7641
7692
|
if (patchPrompt) return {
|
|
7642
|
-
stdout: respond(id, patchPrompt),
|
|
7693
|
+
stdout: withDenyNotice(id, respond(id, patchPrompt), patchPrompt, event.sessionId, dirname(file), opts.now),
|
|
7643
7694
|
exit: 0
|
|
7644
7695
|
};
|
|
7645
7696
|
}
|
|
@@ -7658,10 +7709,11 @@ async function handlePre(ctx) {
|
|
|
7658
7709
|
windowMs: opts.windowMs,
|
|
7659
7710
|
now: opts.now,
|
|
7660
7711
|
trackFile: file,
|
|
7661
|
-
transcriptPath: typeof payload.transcript_path === "string" ? payload.transcript_path : void 0
|
|
7712
|
+
transcriptPath: typeof payload.transcript_path === "string" ? payload.transcript_path : void 0,
|
|
7713
|
+
neverApproval: id === "codex" && isBypassPermissions(event.permissionMode)
|
|
7662
7714
|
});
|
|
7663
7715
|
if (prompt) return {
|
|
7664
|
-
stdout: respond(id, prompt),
|
|
7716
|
+
stdout: withDenyNotice(id, respond(id, prompt), prompt, event.sessionId, dirname(file), opts.now),
|
|
7665
7717
|
exit: 0
|
|
7666
7718
|
};
|
|
7667
7719
|
return allowOutcome(id, event, payload, mcpDir, opts.cwd, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as evaluate } from "./evaluate-
|
|
1
|
+
import { t as evaluate } from "./evaluate-I9CAwUlI.mjs";
|
|
2
2
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { c as readClaudeInput, v as commandToString } from "./claude-CVQlXOl8.mjs";
|
|
4
4
|
//#region src/adapters/hermes/index.ts
|
|
5
5
|
/**
|
|
6
6
|
* Hermes Agent adapter (hook-mode). Nous Research's hermes-agent (2026) pipes a
|
|
@@ -131,6 +131,8 @@ interface PolicyContext {
|
|
|
131
131
|
agentType?: string;
|
|
132
132
|
/** Line count of the existing on-disk file (so an Edit on an oversized file blocks). */
|
|
133
133
|
existingLines?: number;
|
|
134
|
+
/** Codex-only, populated by handle-pre.ts from the resolved `permission_mode` of an `approval_policy=never` session (adapters/codex/permission-mode.ts) — auto-approve gate, wired through evaluate.ts's anti-chaining check. */
|
|
135
|
+
neverApproval?: boolean;
|
|
134
136
|
}
|
|
135
137
|
/** Harness-agnostic policy decision (+ a portable prompt for adapters to render). */
|
|
136
138
|
interface PolicyResult {
|
|
@@ -12,6 +12,8 @@ interface ClaudeHookInput {
|
|
|
12
12
|
command?: string | string[];
|
|
13
13
|
};
|
|
14
14
|
cwd?: string;
|
|
15
|
+
/** Shared Claude/Codex hook field — Codex maps approval_policy=never to "bypassPermissions" (adapters/codex/permission-mode.ts). */
|
|
16
|
+
permission_mode?: string;
|
|
15
17
|
}
|
|
16
18
|
/** Read & parse the Claude hook payload from stdin (empty object on bad input). */
|
|
17
19
|
declare function readClaudeInput(): Promise<ClaudeHookInput>;
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import { a as HarnessInfo, i as HarnessId, n as detectMode, o as HarnessMode, r
|
|
|
5
5
|
import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CEKzGg2u.mjs";
|
|
6
6
|
import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
|
|
7
7
|
import { a as compactJson, i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-BEMumjOw.mjs";
|
|
8
|
-
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "./index-
|
|
8
|
+
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "./index-CU3oHxOe.mjs";
|
|
9
9
|
import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
10
10
|
import { a as PRE_AUTH_GATES, c as evaluateApex, i as POST_AUTH_GATES, l as freshnessGate, n as ApexContext, o as brainstormGate, r as ApexGate, s as docConsultedGate, t as APEX_GATES, u as solidReadGate } from "./apex-Wdi1nq_w.mjs";
|
|
11
11
|
import { a as ReminderState, c as readState, d as throttleMs, i as registryFile, l as setStateField, n as addRoot, o as lessonsFileFor, r as readRoots, s as nowStamp, t as ensureMemoryGitignore, u as stateFileFor } from "./index-DLYhervv.mjs";
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { i as walkUpFor, n as projectRoot, r as projectRootOrNull, t as isCodeFi
|
|
|
6
6
|
import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-Cb9xR8dC.mjs";
|
|
7
7
|
import { A as detectCreationIntent, B as DEV_KEYWORDS, C as SKILL_TRIGGERS, D as MAX_EXA_RESULTS, F as docConsultedGate, H as detectProjectType, I as evaluateApex, L as freshnessGate, M as POST_AUTH_GATES, N as PRE_AUTH_GATES, O as MAX_TOKENS, P as brainstormGate, R as solidReadGate, S as usesTailwindUtilities, U as isApexCommand, V as detectModularArchitecture, W as requiredArchSkill, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, w as frameworkSolidGate, x as skillTriggerGate, y as parseField, z as detectFramework } from "./validate-DnOqIZD_.mjs";
|
|
8
8
|
import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "./home-state-D0RLWP8J.mjs";
|
|
9
|
-
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "./evaluate-
|
|
9
|
+
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "./evaluate-I9CAwUlI.mjs";
|
|
10
10
|
import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-CWZegVdR.mjs";
|
|
11
11
|
import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-PKVNBHge.mjs";
|
|
12
12
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
@@ -78,4 +78,22 @@ function parseApplyPatch(text) {
|
|
|
78
78
|
return files;
|
|
79
79
|
}
|
|
80
80
|
//#endregion
|
|
81
|
-
|
|
81
|
+
//#region src/adapters/codex/permission-mode.ts
|
|
82
|
+
/**
|
|
83
|
+
* Codex maps `AskForApproval::Never` -> `permission_mode: "bypassPermissions"`
|
|
84
|
+
* in its hook payload (codex-rs/core/src/hook_runtime.rs::hook_permission_mode)
|
|
85
|
+
* — the only value that isolates `never`; the other three approval policies
|
|
86
|
+
* (`untrusted`, `on-failure`, `on-request`) all collapse to `"default"`.
|
|
87
|
+
* Verified at openai/codex@342e4d4b, hook_runtime.rs run_pre_tool_use_hooks +
|
|
88
|
+
* pre_tool_use.rs PreToolUseRequest. Undocumented implementation detail, not
|
|
89
|
+
* a public contract — see the mapping-regression test that pins this string.
|
|
90
|
+
* @param permissionMode - The payload's `permission_mode` field, if present.
|
|
91
|
+
* @returns True only on an EXACT match — fail-closed. A near-miss
|
|
92
|
+
* (`"BypassPermissions"`, `"bypassPermissions "`, or a substring match) must
|
|
93
|
+
* never accidentally grant the neverApproval exemption.
|
|
94
|
+
*/
|
|
95
|
+
function isBypassPermissions(permissionMode) {
|
|
96
|
+
return permissionMode === "bypassPermissions";
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { parseApplyPatch as n, isBypassPermissions as t };
|
package/dist/policy/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "../index-
|
|
1
|
+
import { $ as ASK_PATTERNS, A as FAIL_CLOSED, B as SWIFT_PROTO_RE, C as usesTailwindUtilities, Ct as ModularArchitecture, D as MAX_TOKENS, Dt as isApexCommand, E as MAX_EXA_RESULTS, Et as detectProjectType, F as installGuard, G as CODE_MUTATORS, H as interfaceSeparationGuard, I as GO_DECL_RE, J as SAFE_PREFIXES, K as CODE_REDIRECT, L as JAVA_DECL_RE, M as clearUserGuards, N as registerGuard, O as capVerbosity, Ot as requiredArchSkill, P as runGuards, Q as protectedPathGuard, R as PHP_DECL_RE, S as skillTriggerGate, St as DEV_KEYWORDS, T as frameworkSolidGate, Tt as detectModularArchitecture, U as bashWriteGuard, V as TS_DECL_RE, W as ASK_WRITERS, X as PROTECTED_FRAGMENTS, Y as SESSION_STATE_FRAGMENT, Z as PROTECTED_GIT_RE, _ as DEV_VERBS, _t as SOLID_REF, a as firstHeading, at as evaluate, b as detectClaudeMdProjectType, bt as evaluateFileSize, c as parseEntry, ct as GIT_ASK, d as EXCLUDE_DIRS, dt as RALPH_SAFE, et as CRITICAL_PATTERNS, f as PROJECT_INDICATORS, ft as SYSTEM_INSTALL, g as loadApexTaskState, gt as PLUGINS_DIR, h as buildApexTaskInjection, ht as FileSizeVerdict, i as firstComment, it as GuardContext, j as GUARDS, k as detectCreationIntent, l as parseBodyDesc, lt as GIT_BLOCKED, m as buildApexTaskContext, mt as matchPatterns, n as missingSeoElements, nt as securityGuard, o as TreeEntry, ot as PolicyContext, p as ApexTaskState, pt as isRalphMode, q as FILE_REDIRECT, r as descFromText, rt as Guard, s as parseEnrichment, st as PolicyResult, t as isHtmlLike, tt as LabeledPattern, u as parseField, ut as PROJECT_INSTALL, v as buildApexInstruction, vt as countFrameworkCodeLines, w as SKILL_TRIGGERS, wt as ProjectType, x as detectRequiredSkills, xt as detectFramework, y as buildClaudeMdContext, yt as countLines, z as PY_MODEL_RE } from "../index-CU3oHxOe.mjs";
|
|
2
2
|
import { a as PRE_AUTH_GATES, c as evaluateApex, i as POST_AUTH_GATES, l as freshnessGate, n as ApexContext, o as brainstormGate, r as ApexGate, s as docConsultedGate, t as APEX_GATES, u as solidReadGate } from "../apex-Wdi1nq_w.mjs";
|
|
3
3
|
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexTaskState, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, Guard, GuardContext, JAVA_DECL_RE, LabeledPattern, MAX_EXA_RESULTS, MAX_TOKENS, ModularArchitecture, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, type PolicyContext, type PolicyResult, ProjectType, RALPH_SAFE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, TreeEntry, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
package/dist/policy/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as detectCreationIntent, B as DEV_KEYWORDS, C as SKILL_TRIGGERS, D as MAX_EXA_RESULTS, F as docConsultedGate, H as detectProjectType, I as evaluateApex, L as freshnessGate, M as POST_AUTH_GATES, N as PRE_AUTH_GATES, O as MAX_TOKENS, P as brainstormGate, R as solidReadGate, S as usesTailwindUtilities, U as isApexCommand, V as detectModularArchitecture, W as requiredArchSkill, a as firstHeading, b as detectRequiredSkills, c as EXCLUDE_DIRS, d as buildApexTaskInjection, f as loadApexTaskState, g as detectClaudeMdProjectType, h as buildClaudeMdContext, i as firstComment, j as APEX_GATES, k as capVerbosity, l as PROJECT_INDICATORS, m as buildApexInstruction, n as missingSeoElements, o as parseEnrichment, p as DEV_VERBS, r as descFromText, s as parseEntry, t as isHtmlLike, u as buildApexTaskContext, v as parseBodyDesc, w as frameworkSolidGate, x as skillTriggerGate, y as parseField, z as detectFramework } from "../validate-DnOqIZD_.mjs";
|
|
2
2
|
import { d as countFrameworkCodeLines, f as countLines, l as PLUGINS_DIR, p as evaluateFileSize, u as SOLID_REF } from "../home-state-D0RLWP8J.mjs";
|
|
3
|
-
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "../evaluate-
|
|
3
|
+
import { A as PROJECT_INSTALL, C as PROTECTED_GIT_RE, D as securityGuard, E as CRITICAL_PATTERNS, M as SYSTEM_INSTALL, N as isRalphMode, O as GIT_ASK, P as matchPatterns, S as PROTECTED_FRAGMENTS, T as ASK_PATTERNS, _ as CODE_MUTATORS, a as registerGuard, b as SAFE_PREFIXES, c as GO_DECL_RE, d as PY_MODEL_RE, f as SWIFT_PROTO_RE, g as ASK_WRITERS, h as bashWriteGuard, i as clearUserGuards, j as RALPH_SAFE, k as GIT_BLOCKED, l as JAVA_DECL_RE, m as interfaceSeparationGuard, n as FAIL_CLOSED, o as runGuards, p as TS_DECL_RE, r as GUARDS, s as installGuard, t as evaluate, u as PHP_DECL_RE, v as CODE_REDIRECT, w as protectedPathGuard, x as SESSION_STATE_FRAGMENT, y as FILE_REDIRECT } from "../evaluate-I9CAwUlI.mjs";
|
|
4
4
|
import "../policy-la_KkjCS.mjs";
|
|
5
5
|
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, DEV_VERBS, EXCLUDE_DIRS, FAIL_CLOSED, FILE_REDIRECT, GIT_ASK, GIT_BLOCKED, GO_DECL_RE, GUARDS, JAVA_DECL_RE, MAX_EXA_RESULTS, MAX_TOKENS, PHP_DECL_RE, PLUGINS_DIR, POST_AUTH_GATES, PRE_AUTH_GATES, PROJECT_INDICATORS, PROJECT_INSTALL, PROTECTED_FRAGMENTS, PROTECTED_GIT_RE, PY_MODEL_RE, RALPH_SAFE, SAFE_PREFIXES, SESSION_STATE_FRAGMENT, SKILL_TRIGGERS, SOLID_REF, SWIFT_PROTO_RE, SYSTEM_INSTALL, TS_DECL_RE, bashWriteGuard, brainstormGate, buildApexInstruction, buildApexTaskContext, buildApexTaskInjection, buildClaudeMdContext, capVerbosity, clearUserGuards, countFrameworkCodeLines, countLines, descFromText, detectClaudeMdProjectType, detectCreationIntent, detectFramework, detectModularArchitecture, detectProjectType, detectRequiredSkills, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, firstComment, firstHeading, frameworkSolidGate, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, isHtmlLike, isRalphMode, loadApexTaskState, matchPatterns, missingSeoElements, parseBodyDesc, parseEnrichment, parseEntry, parseField, protectedPathGuard, registerGuard, requiredArchSkill, runGuards, securityGuard, skillTriggerGate, solidReadGate, usesTailwindUtilities };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as isCodeFile } from "./project-root-3kk7gCOp.mjs";
|
|
2
|
-
import { t as evaluate } from "./evaluate-
|
|
2
|
+
import { t as evaluate } from "./evaluate-I9CAwUlI.mjs";
|
|
3
3
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
//#region src/cli/run.ts
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -102,6 +102,8 @@ interface GateInput {
|
|
|
102
102
|
agentId?: string;
|
|
103
103
|
/** Absolute path to the session transcript (Claude `transcript_path`) for evidence-based freshness. */
|
|
104
104
|
transcriptPath?: string;
|
|
105
|
+
/** See PolicyContext.neverApproval — populated only by handle-pre.ts for id==="codex" (approval_policy=never has no interactive ask channel). */
|
|
106
|
+
neverApproval?: boolean;
|
|
105
107
|
}
|
|
106
108
|
//#endregion
|
|
107
109
|
//#region src/runtime/gate.d.ts
|
|
@@ -167,6 +169,8 @@ interface NormalizedEvent {
|
|
|
167
169
|
command?: string;
|
|
168
170
|
/** Subagent type, if the tool-use came from one (Explore/Plan are file-size-exempt). */
|
|
169
171
|
agentType?: string;
|
|
172
|
+
/** Harness-resolved permission mode (Claude emits it natively; Codex maps `AskForApproval::Never` to the same "bypassPermissions" string — see adapters/codex/permission-mode.ts). Generic field, Codex-only consumer today. */
|
|
173
|
+
permissionMode?: string;
|
|
170
174
|
/**
|
|
171
175
|
* Per-file changes when the tool is a multi-file edit primitive (Codex
|
|
172
176
|
* `apply_patch`). Present ONLY for `apply_patch`; the file gates OR each
|
|
@@ -835,9 +839,7 @@ interface PreContext {
|
|
|
835
839
|
opts: HandleOptions;
|
|
836
840
|
}
|
|
837
841
|
/**
|
|
838
|
-
* Run the PreToolUse pipeline: MCP/WebFetch cache intercept, design gate, APEX
|
|
839
|
-
* Task context injection, then the stateless+APEX gate chain. Returns the native
|
|
840
|
-
* hook outcome (deny/ask/inject or allow).
|
|
842
|
+
* Run the PreToolUse pipeline: MCP/WebFetch cache intercept, design gate, APEX Task context injection, then the stateless+APEX gate chain, returning the native hook outcome (deny/ask/inject or allow).
|
|
841
843
|
* @param ctx - The resolved pre-context.
|
|
842
844
|
* @returns The hook outcome.
|
|
843
845
|
*/
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as projectLayout } from "../layout-C0jaaCQC.mjs";
|
|
2
2
|
import { a as sanitizeSessionId, c as sessionsDir, i as loadSessionState, n as fuseHarnessHome, o as saveSessionState, r as fusengineCache, s as sessionStatePath, t as claudeHome } from "../home-state-D0RLWP8J.mjs";
|
|
3
|
-
import { $ as postEditTypescript, A as trackSkillRead, At as trackFile, B as isProject, C as seoPostToolUse, Ct as gitContext, D as postTrackingSideEffects, Dt as taskContext, E as securityAdvisory, Et as promptSubmitContext, F as dispatchLessons, Ft as securityStateDir, G as getFileDesc, H as loadEnriched, I as cartoSessionStart, It as securityStatePath, K as listChildren, L as generateEcosystemMap, Lt as todayUtc, M as dispatchLifecycle, Mt as isoUtc, N as aipilotPostToolUse, Nt as loadSecurityState, O as trackWatchResearch, Ot as defaultStateDir, P as dispatchAipilot, Pt as saveSecurityState, Q as lessonsStateFileFor, R as writePluginMap, S as postEditContext, St as devContext, T as dispatchMemory, Tt as claudeMdKey, U as mergeLines, V as writeTree, W as countFiles, X as lessonsArchiveFileFor, Z as lessonsFileFor, _ as preCommitGate, _t as sessionStartCore, a as recordActivity, at as validateTeammateOutput, b as extractSymbols, bt as removeOldFiles, c as MCP_TTL_MS, ct as validateTailwind, d as isMcpTool, dt as countLoc, et as trackSessionChanges, f as queryOf, ft as detectSolidProfile, g as gate, gt as runSessionStartCleanups, h as TRIVIAL_BUDGET, ht as readRules, i as respond, it as logToolFailure, j as trackEnrichment, jt as normalizeEvent, k as trackMcpResearch, kt as projectHash, l as WEBFETCH_TTL_MS, lt as validateSolidGate, m as REQUIRED_AGENTS, mt as injectRules, n as activityFor, nt as cleanupSession, o as mcpPostStore, ot as trackAgentMemory, p as DEFAULT_WINDOW_MS, pt as solidDetectStart, r as handlePre, rt as saveApexState, s as mcpPreIntercept, st as subagentCacheContext, t as handleHook, tt as validateRulesLoaded, u as cacheQueryOf, ut as checkFileSize, v as detectDuplication, vt as pruneEmptyDirs, w as seoPostToolUseResponse, wt as projectContext, x as lifecycleStdout, xt as trimLogFile, y as dryGate, yt as purgeTtlTree, z as generateProjectMap } from "../handle-
|
|
3
|
+
import { $ as postEditTypescript, A as trackSkillRead, At as trackFile, B as isProject, C as seoPostToolUse, Ct as gitContext, D as postTrackingSideEffects, Dt as taskContext, E as securityAdvisory, Et as promptSubmitContext, F as dispatchLessons, Ft as securityStateDir, G as getFileDesc, H as loadEnriched, I as cartoSessionStart, It as securityStatePath, K as listChildren, L as generateEcosystemMap, Lt as todayUtc, M as dispatchLifecycle, Mt as isoUtc, N as aipilotPostToolUse, Nt as loadSecurityState, O as trackWatchResearch, Ot as defaultStateDir, P as dispatchAipilot, Pt as saveSecurityState, Q as lessonsStateFileFor, R as writePluginMap, S as postEditContext, St as devContext, T as dispatchMemory, Tt as claudeMdKey, U as mergeLines, V as writeTree, W as countFiles, X as lessonsArchiveFileFor, Z as lessonsFileFor, _ as preCommitGate, _t as sessionStartCore, a as recordActivity, at as validateTeammateOutput, b as extractSymbols, bt as removeOldFiles, c as MCP_TTL_MS, ct as validateTailwind, d as isMcpTool, dt as countLoc, et as trackSessionChanges, f as queryOf, ft as detectSolidProfile, g as gate, gt as runSessionStartCleanups, h as TRIVIAL_BUDGET, ht as readRules, i as respond, it as logToolFailure, j as trackEnrichment, jt as normalizeEvent, k as trackMcpResearch, kt as projectHash, l as WEBFETCH_TTL_MS, lt as validateSolidGate, m as REQUIRED_AGENTS, mt as injectRules, n as activityFor, nt as cleanupSession, o as mcpPostStore, ot as trackAgentMemory, p as DEFAULT_WINDOW_MS, pt as solidDetectStart, r as handlePre, rt as saveApexState, s as mcpPreIntercept, st as subagentCacheContext, t as handleHook, tt as validateRulesLoaded, u as cacheQueryOf, ut as checkFileSize, v as detectDuplication, vt as pruneEmptyDirs, w as seoPostToolUseResponse, wt as projectContext, x as lifecycleStdout, xt as trimLogFile, y as dryGate, yt as purgeTtlTree, z as generateProjectMap } from "../handle-DnxRzNuY.mjs";
|
|
4
4
|
//#region src/runtime/storage.ts
|
|
5
5
|
/**
|
|
6
6
|
* The project's single state dir (`<root>/.harness`) — neutral + harness-agnostic,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.65",
|
|
4
4
|
"description": "Harness-agnostic toolkit for AI coding agents: runtime harness detection (Claude Code, Codex, Cursor, Cline, Gemini, Aider...), pure policy core (env config, project/framework detection, SOLID/file-size limits, APEX freshness, guard patterns, portable prompts), cache, project memory, ref routing, state/locks, statusline, per-harness adapters (Claude/Cursor/Cline/Gemini) and a cli-mode harness-check binary. Bun-native, with a built dist for Node + bundlers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -152,5 +152,8 @@
|
|
|
152
152
|
"tsdown": "^0.22.3",
|
|
153
153
|
"typedoc": "^0.28.19",
|
|
154
154
|
"typescript": "^6.0.3"
|
|
155
|
+
},
|
|
156
|
+
"dependencies": {
|
|
157
|
+
"figures": "^6.1.0"
|
|
155
158
|
}
|
|
156
159
|
}
|