@fusengine/harness 0.1.64 → 0.1.66

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.
@@ -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-ce2J1bHC.mjs";
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-CaBe4eBY.mjs";
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-Cu6rPxUZ.mjs";
2
2
  export { attachSystemMessage, blockResponse, contextResponse, denyResponse, fileSizeGuard, guard, informResponse, readClaudeInput, systemMessage, toClaudeResponse };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-wDDbojx6.mjs";
1
+ import { t as evaluate } from "../../evaluate-CxgYAODk.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cline/index.ts
4
4
  /**
@@ -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-ce2J1bHC.mjs";
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
- /** Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny. */
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-wDDbojx6.mjs";
2
+ import { t as evaluate } from "../../evaluate-CxgYAODk.mjs";
3
3
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
4
- import { t as parseApplyPatch } from "../../apply-patch-CIS2EZ_q.mjs";
5
- import { c as readClaudeInput, i as denyResponse, r as contextResponse, v as commandToString } from "../../claude-CaBe4eBY.mjs";
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-Cu6rPxUZ.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 { c as readClaudeInput, i as denyResponse, r as contextResponse, v as com
21
21
  * an explicit deny.
22
22
  */
23
23
  const ASK_PREFIX = "[downgraded from ask — Codex has no interactive approval]";
24
- /** Render a portable {@link Prompt} as a Codex hook response, `ask` → explicit deny. */
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,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-wDDbojx6.mjs";
1
+ import { t as evaluate } from "../../evaluate-CxgYAODk.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/cursor/index.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "../../evaluate-wDDbojx6.mjs";
1
+ import { t as evaluate } from "../../evaluate-CxgYAODk.mjs";
2
2
  import { t as formatPrompt } from "../../types-ernB1Dy3.mjs";
3
3
  //#region src/adapters/gemini/index.ts
4
4
  /**
@@ -1,2 +1,2 @@
1
- import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-BdwBBemn.mjs";
1
+ import { n as readHermesInput, r as toHermesResponse, t as guard } from "../../hermes-BpD0nlyR.mjs";
2
2
  export { guard, readHermesInput, toHermesResponse };
@@ -1,4 +1,4 @@
1
- import { t as evaluate } from "./evaluate-wDDbojx6.mjs";
1
+ import { t as evaluate } from "./evaluate-CxgYAODk.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";
@@ -125,10 +125,7 @@ function collectFiles(dir, exts, out, cap) {
125
125
  }
126
126
  //#endregion
127
127
  //#region src/adapters/claude/index.ts
128
- /**
129
- * Claude Code adapter — the thin Claude-only shim over the portable policy core.
130
- * Reads the hook stdin payload and emits hookSpecificOutput responses.
131
- */
128
+ /** Claude Code adapter — the thin Claude-only shim over the portable policy core; reads the hook stdin payload and emits hookSpecificOutput responses. */
132
129
  /** Read & parse the Claude hook payload from stdin (empty object on bad input). */
133
130
  async function readClaudeInput() {
134
131
  const text = await readStdin();
package/dist/cli/bin.mjs CHANGED
@@ -2,10 +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-kVHXFVug.mjs";
5
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-BfJytXnW.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-DE-753Mr.mjs";
8
- import { p as readStdin$1 } from "../claude-CaBe4eBY.mjs";
7
+ import { J as runningVersion, Lt as todayUtc, Y as versionBanner, q as runDoctor, t as handleHook } from "../handle-BBlvYhek.mjs";
8
+ import { p as readStdin$1 } from "../claude-Cu6rPxUZ.mjs";
9
9
  import { delimiter, join } from "node:path";
10
10
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
11
11
  import { homedir } from "node:os";
@@ -1,2 +1,2 @@
1
- import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-kVHXFVug.mjs";
1
+ import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-BfJytXnW.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,93 @@ 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
+ /**
758
+ * Commands exempt from `ask` under Codex `approval_policy=never` — a superset
759
+ * of {@link RALPH_SAFE} plus `git push`. Kept as its OWN list, never merged
760
+ * into RALPH_SAFE: the RALPH_MODE path (evaluate.ts's `ralphSafe`) has no
761
+ * equivalent to {@link isSafePushForm}, only the shared GIT_BLOCKED +
762
+ * isSingleCommand guards — adding push to RALPH_SAFE would auto-allow
763
+ * `git push origin --delete branch` under RALPH_MODE, since GIT_BLOCKED only
764
+ * covers force-push, not remote deletion. Only the neverApproval path in
765
+ * evaluate.ts consults NEVER_SAFE, and it always pairs it with
766
+ * isSafePushForm for any push entry.
767
+ */
768
+ const NEVER_SAFE = [...RALPH_SAFE, "git push"];
769
+ /**
770
+ * `--delete`/`-d` (git-push(1): "deleted from the remote repository", no
771
+ * `--force` needed), `--mirror` (force-updates AND deletes remote-only refs),
772
+ * `--prune` (removes remote refs with no local counterpart) — all delete or
773
+ * force-overwrite a remote ref without going through the `--force` flag
774
+ * GIT_BLOCKED already checks. Boundary-anchored like patterns.ts's FORCE_FLAG
775
+ * so `-d` never matches inside a longer token (e.g. a branch name).
776
+ */
777
+ const PUSH_DESTRUCTIVE_FLAG = /\s(?:--delete|--mirror|--prune|-d)(?:\s|=|$)/;
778
+ /** A refspec colon (e.g. `git push origin :branch`) deletes the remote ref — the legacy form of `--delete` (git-push(1): "prefixing all refs with a colon"). */
779
+ const REFSPEC_COLON = /\s:\S/;
780
+ /**
781
+ * A leading `+` on a pushed refspec force-updates the remote ref on a
782
+ * non-fast-forward — git-push(1): "The `+` is optional and does the same thing
783
+ * as `--force`". It carries NO `--force`/`-f` flag, so GIT_BLOCKED's FORCE_FLAG
784
+ * misses it; this guard is the `--force` twin. The `\s` anchor makes `+` open
785
+ * its token (`+main`, `+src:dst`), never a mid-token `feature+x` ref name.
786
+ */
787
+ const REFSPEC_FORCE = /\s\+\S/;
788
+ /**
789
+ * True unless `cmd` is a `git push` carrying an irreversible remote-mutation
790
+ * form GIT_BLOCKED doesn't already cover: `--delete`/`-d`/`--mirror`/`--prune`,
791
+ * a `:refspec` remote-delete, or a `+refspec` force-update (the `--force` twin
792
+ * FORCE_FLAG misses). Plain `--force`/`-f` stays GIT_BLOCKED's job, checked
793
+ * upstream in evaluate.ts. Non-push commands always pass — this guard exists
794
+ * solely to narrow the `git push` entry of {@link NEVER_SAFE}.
795
+ */
796
+ function isSafePushForm(cmd) {
797
+ if (!cmd.startsWith("git push")) return true;
798
+ return !PUSH_DESTRUCTIVE_FLAG.test(cmd) && !REFSPEC_COLON.test(cmd) && !REFSPEC_FORCE.test(cmd);
799
+ }
800
+ /** Next steps offered on the auto-approve notice — the only channel left once `ask` is unavailable. */
801
+ const ACTIONS = [
802
+ "Set approval_policy=on-request for interactive confirmation",
803
+ "Or set RALPH_MODE=1 to auto-approve silently (no notice)",
804
+ "Or run the command manually outside the agent loop"
805
+ ];
806
+ /**
807
+ * Builds the auto-approve `inform` prompt for a command evaluate.ts has
808
+ * already proven safe (RALPH_SAFE subset, not GIT_BLOCKED, not chained —
809
+ * see {@link isSingleCommand}). Pure formatting only; the 3-condition
810
+ * decision itself lives in evaluate.ts, where the anti-chaining guarantee
811
+ * must stay visible and auditable.
812
+ * @param cmd - The already-vetted command.
813
+ */
814
+ function buildNeverApprovalPrompt(cmd) {
815
+ const trimmed = cmd.trim();
816
+ return {
817
+ kind: "inform",
818
+ title: "Auto-approved (approval_policy=never)",
819
+ reason: `Auto-approved "${trimmed}" under approval_policy=never.`,
820
+ actions: [...ACTIONS],
821
+ 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.`
822
+ };
823
+ }
824
+ //#endregion
736
825
  //#region src/policy/evaluate.ts
737
826
  /**
738
827
  * Evaluate a single tool-use against the bundled policies, returning a pure
@@ -748,6 +837,14 @@ function evaluate(ctx) {
748
837
  };
749
838
  const cmd = ctx.command;
750
839
  const ralphSafe = !!cmd && isRalphMode() && RALPH_SAFE.some((s) => cmd.startsWith(s));
840
+ if (!ralphSafe && !!ctx.neverApproval && !!cmd && !matchPatterns(cmd, GIT_BLOCKED) && isSingleCommand(cmd) && NEVER_SAFE.some((s) => cmd.startsWith(s)) && isSafePushForm(cmd) && cmd) {
841
+ const prompt = buildNeverApprovalPrompt(cmd);
842
+ return {
843
+ decision: "warn",
844
+ message: prompt.reason,
845
+ prompt
846
+ };
847
+ }
751
848
  if (!ralphSafe && ctx.command && matchPatterns(ctx.command, GIT_BLOCKED)) {
752
849
  const reason = `Destructive git command: ${ctx.command}`;
753
850
  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-wDDbojx6.mjs";
7
+ import { n as FAIL_CLOSED, t as evaluate } from "./evaluate-CxgYAODk.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 { t as parseApplyPatch } from "./apply-patch-CIS2EZ_q.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-CaBe4eBY.mjs";
18
- import { r as toHermesResponse } from "./hermes-BdwBBemn.mjs";
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-Cu6rPxUZ.mjs";
18
+ import { r as toHermesResponse } from "./hermes-BpD0nlyR.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";
@@ -94,7 +94,8 @@ function normalizeEvent(id, payload) {
94
94
  tool,
95
95
  input,
96
96
  sessionId: str(payload.session_id) ?? str(payload.conversation_id) ?? "",
97
- 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)
98
99
  };
99
100
  if (tool === "apply_patch") {
100
101
  const files = parseApplyPatch(str(input.command) ?? str(payload.command) ?? "").map((f) => ({
@@ -6813,7 +6814,8 @@ async function runGates(input) {
6813
6814
  content: input.content,
6814
6815
  command: input.command,
6815
6816
  agentType: input.agentType,
6816
- existingLines
6817
+ existingLines,
6818
+ neverApproval: input.neverApproval
6817
6819
  });
6818
6820
  } catch {
6819
6821
  return FAIL_CLOSED;
@@ -7646,9 +7648,7 @@ function applyPatchGate(files, cwd) {
7646
7648
  //#endregion
7647
7649
  //#region src/runtime/handle-pre.ts
7648
7650
  /**
7649
- * Run the PreToolUse pipeline: MCP/WebFetch cache intercept, design gate, APEX
7650
- * Task context injection, then the stateless+APEX gate chain. Returns the native
7651
- * 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).
7652
7652
  * @param ctx - The resolved pre-context.
7653
7653
  * @returns The hook outcome.
7654
7654
  */
@@ -7709,7 +7709,8 @@ async function handlePre(ctx) {
7709
7709
  windowMs: opts.windowMs,
7710
7710
  now: opts.now,
7711
7711
  trackFile: file,
7712
- 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)
7713
7714
  });
7714
7715
  if (prompt) return {
7715
7716
  stdout: withDenyNotice(id, respond(id, prompt), prompt, event.sessionId, dirname(file), opts.now),
@@ -1,6 +1,6 @@
1
- import { t as evaluate } from "./evaluate-wDDbojx6.mjs";
1
+ import { t as evaluate } from "./evaluate-CxgYAODk.mjs";
2
2
  import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
3
- import { c as readClaudeInput, v as commandToString } from "./claude-CaBe4eBY.mjs";
3
+ import { c as readClaudeInput, v as commandToString } from "./claude-Cu6rPxUZ.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-C9jYdElK.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-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-wDDbojx6.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-CxgYAODk.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
- export { parseApplyPatch as t };
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 };
@@ -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-C9jYdElK.mjs";
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 };
@@ -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-wDDbojx6.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-CxgYAODk.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-wDDbojx6.mjs";
2
+ import { t as evaluate } from "./evaluate-CxgYAODk.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
@@ -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
  */
@@ -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-DE-753Mr.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-BBlvYhek.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.64",
3
+ "version": "0.1.66",
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",