agent-control-plane-core 0.1.0 → 0.2.1

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.
@@ -3,6 +3,7 @@
3
3
  "cases": [
4
4
  {
5
5
  "name": "PreToolUse v0.135 enforcing, all verdicts",
6
+ "call_class": "builtin",
6
7
  "native": {
7
8
  "hook_event_name": "PreToolUse",
8
9
  "version": "0.135.0",
@@ -26,6 +27,7 @@
26
27
  "meta": {
27
28
  "agent": "codex",
28
29
  "native_event": "PreToolUse",
30
+ "native_tool": "Bash",
29
31
  "integration_mode": "external_hook",
30
32
  "primary_gate_present": true,
31
33
  "passthrough": {},
@@ -109,6 +111,70 @@
109
111
  }
110
112
  }
111
113
  },
114
+ {
115
+ "name": "PreToolUse v0.135 MCP tool is non-vetoable (hook fires for Bash only)",
116
+ "call_class": "mcp",
117
+ "native": {
118
+ "hook_event_name": "PreToolUse",
119
+ "version": "0.135.0",
120
+ "tool_name": "mcp__github__create_issue",
121
+ "tool_input": {
122
+ "title": "hi"
123
+ }
124
+ },
125
+ "event": {
126
+ "schema_version": 1,
127
+ "event": "pre_tool",
128
+ "tool": "mcp__github__create_issue",
129
+ "input": {
130
+ "title": "hi"
131
+ },
132
+ "this_call_vetoable": false,
133
+ "meta": {
134
+ "agent": "codex",
135
+ "native_event": "PreToolUse",
136
+ "native_tool": "mcp__github__create_issue",
137
+ "integration_mode": "external_hook",
138
+ "primary_gate_present": true,
139
+ "passthrough": {}
140
+ }
141
+ },
142
+ "render": {
143
+ "allow": {
144
+ "verdict": {
145
+ "decision": "allow"
146
+ },
147
+ "native": {
148
+ "transport": "external_hook",
149
+ "exit_code": 0,
150
+ "enforced": false,
151
+ "stdout": {
152
+ "hookSpecificOutput": {
153
+ "hookEventName": "PreToolUse"
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "deny": {
159
+ "verdict": {
160
+ "decision": "deny",
161
+ "reason": "mcp blocked"
162
+ },
163
+ "native": {
164
+ "transport": "external_hook",
165
+ "exit_code": 0,
166
+ "enforced": false,
167
+ "stdout": {
168
+ "hookSpecificOutput": {
169
+ "hookEventName": "PreToolUse",
170
+ "permissionDecision": "deny",
171
+ "permissionDecisionReason": "mcp blocked"
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ },
112
178
  {
113
179
  "name": "PermissionRequest round-trips the native event name",
114
180
  "native": {
@@ -130,6 +196,7 @@
130
196
  "meta": {
131
197
  "agent": "codex",
132
198
  "native_event": "PermissionRequest",
199
+ "native_tool": "Bash",
133
200
  "integration_mode": "external_hook",
134
201
  "primary_gate_present": true,
135
202
  "passthrough": {}
@@ -177,6 +244,7 @@
177
244
  "meta": {
178
245
  "agent": "codex",
179
246
  "native_event": "PreToolUse",
247
+ "native_tool": "Bash",
180
248
  "integration_mode": "observe_only",
181
249
  "primary_gate_present": true,
182
250
  "passthrough": {}
@@ -3,6 +3,7 @@
3
3
  "cases": [
4
4
  {
5
5
  "name": "BeforeTool run_shell_command, full meta, all verdicts",
6
+ "call_class": "builtin",
6
7
  "native": {
7
8
  "hook_event_name": "BeforeTool",
8
9
  "session_id": "g1",
@@ -17,7 +18,7 @@
17
18
  "event": {
18
19
  "schema_version": 1,
19
20
  "event": "pre_tool",
20
- "tool": "run_shell_command",
21
+ "tool": "Bash",
21
22
  "input": {
22
23
  "command": "echo hi"
23
24
  },
@@ -25,6 +26,7 @@
25
26
  "meta": {
26
27
  "agent": "gemini",
27
28
  "native_event": "BeforeTool",
29
+ "native_tool": "run_shell_command",
28
30
  "integration_mode": "external_hook",
29
31
  "primary_gate_present": true,
30
32
  "passthrough": {
@@ -97,7 +99,8 @@
97
99
  }
98
100
  },
99
101
  {
100
- "name": "BeforeTool bare, mcp_context + original_request_name pass through",
102
+ "name": "BeforeTool MCP-sourced call is non-vetoable (coverage unknown)",
103
+ "call_class": "mcp",
101
104
  "native": {
102
105
  "hook_event_name": "BeforeTool",
103
106
  "tool_name": "read_file",
@@ -116,10 +119,11 @@
116
119
  "input": {
117
120
  "path": "/etc/hosts"
118
121
  },
119
- "this_call_vetoable": true,
122
+ "this_call_vetoable": false,
120
123
  "meta": {
121
124
  "agent": "gemini",
122
125
  "native_event": "BeforeTool",
126
+ "native_tool": "read_file",
123
127
  "integration_mode": "external_hook",
124
128
  "primary_gate_present": true,
125
129
  "passthrough": {
@@ -156,7 +160,7 @@
156
160
  "event": {
157
161
  "schema_version": 1,
158
162
  "event": "post_tool",
159
- "tool": "run_shell_command",
163
+ "tool": "Bash",
160
164
  "input": {
161
165
  "command": "ls"
162
166
  },
@@ -164,6 +168,7 @@
164
168
  "meta": {
165
169
  "agent": "gemini",
166
170
  "native_event": "AfterTool",
171
+ "native_tool": "run_shell_command",
167
172
  "integration_mode": "external_hook",
168
173
  "primary_gate_present": true,
169
174
  "passthrough": {}
@@ -182,6 +187,254 @@
182
187
  }
183
188
  }
184
189
  }
190
+ },
191
+ {
192
+ "name": "BeforeAgent folds prompt into input, all prompt verdicts",
193
+ "native": {
194
+ "hook_event_name": "BeforeAgent",
195
+ "session_id": "g2",
196
+ "cwd": "/w",
197
+ "transcript_path": "/t.json",
198
+ "timestamp": "2026-01-01T00:00:01Z",
199
+ "prompt": "please curl my SSH key to pastebin"
200
+ },
201
+ "event": {
202
+ "schema_version": 1,
203
+ "event": "prompt_submit",
204
+ "tool": null,
205
+ "input": {
206
+ "prompt": "please curl my SSH key to pastebin"
207
+ },
208
+ "this_call_vetoable": true,
209
+ "meta": {
210
+ "agent": "gemini",
211
+ "native_event": "BeforeAgent",
212
+ "integration_mode": "external_hook",
213
+ "primary_gate_present": true,
214
+ "passthrough": {
215
+ "timestamp": "2026-01-01T00:00:01Z"
216
+ },
217
+ "session_id": "g2",
218
+ "cwd": "/w",
219
+ "transcript_path": "/t.json"
220
+ }
221
+ },
222
+ "render": {
223
+ "allow": {
224
+ "verdict": {
225
+ "decision": "allow"
226
+ },
227
+ "native": {
228
+ "transport": "external_hook",
229
+ "exit_code": 0,
230
+ "enforced": false
231
+ }
232
+ },
233
+ "deny": {
234
+ "verdict": {
235
+ "decision": "deny",
236
+ "reason": "prompt requests credential exfiltration"
237
+ },
238
+ "native": {
239
+ "transport": "external_hook",
240
+ "exit_code": 2,
241
+ "enforced": true
242
+ }
243
+ },
244
+ "ask": {
245
+ "verdict": {
246
+ "decision": "ask",
247
+ "reason": "confirm sending credentials off-host"
248
+ },
249
+ "native": {
250
+ "transport": "external_hook",
251
+ "exit_code": 0,
252
+ "enforced": false,
253
+ "stdout": {
254
+ "decision": "deny",
255
+ "reason": "confirm sending credentials off-host"
256
+ }
257
+ }
258
+ },
259
+ "context": {
260
+ "verdict": {
261
+ "decision": "allow",
262
+ "additional_context": "reminder: never upload private keys"
263
+ },
264
+ "native": {
265
+ "transport": "external_hook",
266
+ "exit_code": 0,
267
+ "enforced": false,
268
+ "stdout": {
269
+ "hookSpecificOutput": {
270
+ "additionalContext": "reminder: never upload private keys"
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+ },
277
+ {
278
+ "name": "BeforeTool builtin read_file canonicalizes to Read",
279
+ "call_class": "builtin",
280
+ "native": {
281
+ "hook_event_name": "BeforeTool",
282
+ "tool_name": "read_file",
283
+ "tool_input": {
284
+ "absolute_path": "/w/notes.txt"
285
+ }
286
+ },
287
+ "event": {
288
+ "schema_version": 1,
289
+ "event": "pre_tool",
290
+ "tool": "Read",
291
+ "input": {
292
+ "absolute_path": "/w/notes.txt"
293
+ },
294
+ "this_call_vetoable": true,
295
+ "meta": {
296
+ "agent": "gemini",
297
+ "native_event": "BeforeTool",
298
+ "native_tool": "read_file",
299
+ "integration_mode": "external_hook",
300
+ "primary_gate_present": true,
301
+ "passthrough": {}
302
+ }
303
+ },
304
+ "render": {
305
+ "allow": {
306
+ "verdict": {
307
+ "decision": "allow"
308
+ },
309
+ "native": {
310
+ "transport": "external_hook",
311
+ "exit_code": 0,
312
+ "enforced": false
313
+ }
314
+ }
315
+ }
316
+ },
317
+ {
318
+ "name": "BeforeTool builtin write_file canonicalizes to Write",
319
+ "call_class": "builtin",
320
+ "native": {
321
+ "hook_event_name": "BeforeTool",
322
+ "tool_name": "write_file",
323
+ "tool_input": {
324
+ "file_path": "/w/out.txt",
325
+ "content": "hi"
326
+ }
327
+ },
328
+ "event": {
329
+ "schema_version": 1,
330
+ "event": "pre_tool",
331
+ "tool": "Write",
332
+ "input": {
333
+ "file_path": "/w/out.txt",
334
+ "content": "hi"
335
+ },
336
+ "this_call_vetoable": true,
337
+ "meta": {
338
+ "agent": "gemini",
339
+ "native_event": "BeforeTool",
340
+ "native_tool": "write_file",
341
+ "integration_mode": "external_hook",
342
+ "primary_gate_present": true,
343
+ "passthrough": {}
344
+ }
345
+ },
346
+ "render": {
347
+ "allow": {
348
+ "verdict": {
349
+ "decision": "allow"
350
+ },
351
+ "native": {
352
+ "transport": "external_hook",
353
+ "exit_code": 0,
354
+ "enforced": false
355
+ }
356
+ }
357
+ }
358
+ },
359
+ {
360
+ "name": "BeforeTool builtin web_fetch canonicalizes to WebFetch",
361
+ "call_class": "builtin",
362
+ "native": {
363
+ "hook_event_name": "BeforeTool",
364
+ "tool_name": "web_fetch",
365
+ "tool_input": {
366
+ "prompt": "summarize https://example.com"
367
+ }
368
+ },
369
+ "event": {
370
+ "schema_version": 1,
371
+ "event": "pre_tool",
372
+ "tool": "WebFetch",
373
+ "input": {
374
+ "prompt": "summarize https://example.com"
375
+ },
376
+ "this_call_vetoable": true,
377
+ "meta": {
378
+ "agent": "gemini",
379
+ "native_event": "BeforeTool",
380
+ "native_tool": "web_fetch",
381
+ "integration_mode": "external_hook",
382
+ "primary_gate_present": true,
383
+ "passthrough": {}
384
+ }
385
+ },
386
+ "render": {
387
+ "allow": {
388
+ "verdict": {
389
+ "decision": "allow"
390
+ },
391
+ "native": {
392
+ "transport": "external_hook",
393
+ "exit_code": 0,
394
+ "enforced": false
395
+ }
396
+ }
397
+ }
398
+ },
399
+ {
400
+ "name": "BeforeTool MCP FQN read_file is never aliased (mcp_ prefix)",
401
+ "call_class": "mcp",
402
+ "native": {
403
+ "hook_event_name": "BeforeTool",
404
+ "tool_name": "mcp_fs_read_file",
405
+ "tool_input": {
406
+ "path": "/etc/hosts"
407
+ }
408
+ },
409
+ "event": {
410
+ "schema_version": 1,
411
+ "event": "pre_tool",
412
+ "tool": "mcp_fs_read_file",
413
+ "input": {
414
+ "path": "/etc/hosts"
415
+ },
416
+ "this_call_vetoable": false,
417
+ "meta": {
418
+ "agent": "gemini",
419
+ "native_event": "BeforeTool",
420
+ "native_tool": "mcp_fs_read_file",
421
+ "integration_mode": "external_hook",
422
+ "primary_gate_present": true,
423
+ "passthrough": {}
424
+ }
425
+ },
426
+ "render": {
427
+ "allow": {
428
+ "verdict": {
429
+ "decision": "allow"
430
+ },
431
+ "native": {
432
+ "transport": "external_hook",
433
+ "exit_code": 0,
434
+ "enforced": false
435
+ }
436
+ }
437
+ }
185
438
  }
186
439
  ]
187
440
  }
package/src/index.mjs CHANGED
@@ -6,12 +6,23 @@
6
6
  * reference adapters, and the conformance harness every adapter must pass.
7
7
  * Individual adapters are also reachable at the subpath exports
8
8
  * `agent-control-plane-core/claude`, `/codex`, `/amp`, `/gemini`, and the
9
- * harness at `/conformance`.
9
+ * harness at `/conformance`. The agent-id → adapter registry (the SSOT a
10
+ * host/dispatcher selects from) is at `/registry`.
10
11
  */
11
12
 
12
13
  export * from "./control-plane.mjs";
13
14
  export { claudeAdapter, HookEvent } from "./adapters/claude.mjs";
14
15
  export { codexAdapter } from "./adapters/codex.mjs";
15
16
  export { ampAdapter } from "./adapters/amp.mjs";
16
- export { geminiAdapter } from "./adapters/gemini.mjs";
17
- export { runAdapterConformance } from "./conformance.mjs";
17
+ export { geminiAdapter, GEMINI_TOOL_ALIASES } from "./adapters/gemini.mjs";
18
+ export {
19
+ ADAPTERS,
20
+ AGENT_IDS,
21
+ adapterFor,
22
+ assertRegistryConsistent,
23
+ } from "./registry.mjs";
24
+ export {
25
+ runAdapterConformance,
26
+ assertCoverageWellFormed,
27
+ assertToolAliasesCovered,
28
+ } from "./conformance.mjs";
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The agent-id → {@link import("./control-plane.mjs").Adapter} registry — the
3
+ * single SSOT for selecting an adapter from a launch/env signal.
4
+ *
5
+ * Both sides of a deployment resolve the SAME adapter from the SAME id: a host
6
+ * that launches an in-VM agent writes an agent id into a trust anchor, and the
7
+ * in-process dispatcher reads that id to pick the translator for the payloads it
8
+ * will see. Keying both off one frozen map means the id vocabulary can never
9
+ * drift between selector and dispatcher — an id one side accepts, the other
10
+ * resolves identically, and an unknown id fails loud on both.
11
+ *
12
+ * Each entry's key is asserted (at import) to equal its adapter's own `AGENT`,
13
+ * so the id a consumer passes is exactly the `AGENT` the adapter reports — there
14
+ * is no second spelling to reconcile.
15
+ */
16
+
17
+ import { claudeAdapter } from "./adapters/claude.mjs";
18
+ import { codexAdapter } from "./adapters/codex.mjs";
19
+ import { ampAdapter } from "./adapters/amp.mjs";
20
+ import { geminiAdapter } from "./adapters/gemini.mjs";
21
+
22
+ /** @typedef {import("./control-plane.mjs").Adapter} Adapter */
23
+
24
+ /**
25
+ * Every shipped adapter. Adding an adapter to the package means adding it here —
26
+ * the registry is the enrollment point, and {@link assertRegistryConsistent}
27
+ * (run at import and in the conformance suite) fails if a key does not equal its
28
+ * adapter's `AGENT`, so a typo'd or mismatched id cannot ship.
29
+ * @type {Readonly<Record<string, Adapter>>}
30
+ */
31
+ export const ADAPTERS = Object.freeze({
32
+ [claudeAdapter.AGENT]: claudeAdapter,
33
+ [codexAdapter.AGENT]: codexAdapter,
34
+ [ampAdapter.AGENT]: ampAdapter,
35
+ [geminiAdapter.AGENT]: geminiAdapter,
36
+ });
37
+
38
+ /**
39
+ * The canonical set of agent ids, frozen — the closed vocabulary a launch
40
+ * signal is validated against. Derived from {@link ADAPTERS} so it cannot list
41
+ * an id the registry does not resolve.
42
+ * @type {readonly string[]}
43
+ */
44
+ export const AGENT_IDS = Object.freeze(Object.keys(ADAPTERS));
45
+
46
+ /**
47
+ * Assert every {@link ADAPTERS} key equals its adapter's own `AGENT`. A registry
48
+ * whose id disagrees with the adapter it resolves is a latent selector/dispatcher
49
+ * split, so this throws (fail loud) rather than resolving a surprising adapter.
50
+ * Called at import against {@link ADAPTERS}; exported so the conformance suite can
51
+ * drive it against a constructed-bad map too.
52
+ * @param {Record<string, Adapter>} adapters
53
+ */
54
+ export function assertRegistryConsistent(adapters) {
55
+ for (const [id, adapter] of Object.entries(adapters)) {
56
+ if (adapter.AGENT !== id)
57
+ throw new Error(
58
+ `registry: id ${JSON.stringify(id)} resolves adapter whose AGENT is ${JSON.stringify(adapter.AGENT)}`,
59
+ );
60
+ }
61
+ }
62
+
63
+ assertRegistryConsistent(ADAPTERS);
64
+
65
+ /**
66
+ * Resolve an agent id to its adapter. Throws (fail loud) on an unknown id — a
67
+ * selection signal that names no shipped adapter is a misconfiguration, not a
68
+ * reason to silently fall back to a default agent (which would run the wrong
69
+ * translator against the payloads and mis-render every verdict).
70
+ * @param {string} id agent id (a {@link AGENT_IDS} member)
71
+ * @returns {Adapter}
72
+ */
73
+ export function adapterFor(id) {
74
+ const adapter = ADAPTERS[id];
75
+ if (adapter === undefined)
76
+ throw new Error(
77
+ `registry: no adapter for agent id ${JSON.stringify(id)} (known: ${AGENT_IDS.join(", ")})`,
78
+ );
79
+ return adapter;
80
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @param {any} native
3
+ * @returns {ToolCallEvent}
4
+ */
5
+ export function parse(native: any): ToolCallEvent;
6
+ /**
7
+ * Render into Amp's pure exit-code transport: the decision is the exit code,
8
+ * with no stdout body. `reason` has no native channel here, so it is dropped
9
+ * (Amp surfaces the helper's own stderr). No `soleGate` option — an allow
10
+ * already renders as exit 0 either way, so there's no distinct "real approve"
11
+ * signal for this transport to opt into.
12
+ * @param {Verdict} verdict
13
+ * @param {ToolCallEvent} event
14
+ * @returns {NativeResponse}
15
+ */
16
+ export function render(verdict: Verdict, event: ToolCallEvent): NativeResponse;
17
+ /** @typedef {import("../control-plane.mjs").ToolCallEvent} ToolCallEvent */
18
+ /** @typedef {import("../control-plane.mjs").Verdict} Verdict */
19
+ /** @typedef {import("../control-plane.mjs").EventMeta} EventMeta */
20
+ /** @typedef {import("../control-plane.mjs").NativeResponse} NativeResponse */
21
+ export const AGENT: "amp";
22
+ export const INTEGRATION_MODE: "external_hook";
23
+ /**
24
+ * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). Every tool call is
25
+ * checked against the permission engine; `amp.mcpPermissions` runs MCP tools
26
+ * through the same rule syntax, and a rule's `context: "subagent"` selector
27
+ * gates calls inside subagents — so builtin, MCP, and subagent are all COVERED.
28
+ * Resumed-thread firing is a strong-but-uncited structural argument, so it is
29
+ * held at UNKNOWN until an item-⑤ probe confirms it.
30
+ */
31
+ /** @type {import("../control-plane.mjs").CoverageMap} */
32
+ export const COVERAGE: import("../control-plane.mjs").CoverageMap;
33
+ /** @type {import("../control-plane.mjs").Adapter} */
34
+ export const ampAdapter: import("../control-plane.mjs").Adapter;
35
+ export type ToolCallEvent = import("../control-plane.mjs").ToolCallEvent;
36
+ export type Verdict = import("../control-plane.mjs").Verdict;
37
+ export type EventMeta = import("../control-plane.mjs").EventMeta;
38
+ export type NativeResponse = import("../control-plane.mjs").NativeResponse;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Parse a raw Claude Code hook payload into a normalized {@link ToolCallEvent}.
3
+ * Never throws on an unmodelled event type or tool-input field.
4
+ * @param {any} native
5
+ * @returns {ToolCallEvent}
6
+ */
7
+ export function parse(native: any): ToolCallEvent;
8
+ /**
9
+ * Render into Claude Code's native external-hook transport: a
10
+ * `hookSpecificOutput` JSON body on stdout plus the exit code that carries the
11
+ * decision (deny ⇒ exit 2). A deny only counts as `enforced` when the event's
12
+ * `this_call_vetoable` holds.
13
+ *
14
+ * `soleGate` (default `false`) is a dangerous, explicit opt-in: when `true` AND
15
+ * the verdict is `allow`, the render emits Claude Code's REAL
16
+ * `permissionDecision: "allow"`, which bypasses the native permission prompt.
17
+ * Default behavior always abstains on allow (see {@link gatingBody}) so the
18
+ * guardrail can never silently become the sole gate by accident.
19
+ * @param {Verdict} verdict
20
+ * @param {ToolCallEvent} event
21
+ * @param {{ soleGate?: boolean }} [options]
22
+ * @returns {NativeResponse}
23
+ */
24
+ export function render(verdict: Verdict, event: ToolCallEvent, { soleGate }?: {
25
+ soleGate?: boolean;
26
+ }): NativeResponse;
27
+ /** @typedef {import("../control-plane.mjs").ToolCallEvent} ToolCallEvent */
28
+ /** @typedef {import("../control-plane.mjs").Verdict} Verdict */
29
+ /** @typedef {import("../control-plane.mjs").EventMeta} EventMeta */
30
+ /** @typedef {import("../control-plane.mjs").NativeResponse} NativeResponse */
31
+ /** Producing-agent id stamped onto every event this adapter parses. */
32
+ export const AGENT: "claude";
33
+ /** How this adapter attaches to the agent. */
34
+ export const INTEGRATION_MODE: "external_hook";
35
+ /**
36
+ * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). `PreToolUse` fires
37
+ * for builtins, for MCP tools (surfaced as `mcp__<server>__<tool>` through the
38
+ * same hook), and for every tool a subagent uses (subagents do not inherit the
39
+ * parent's permissions, so the hook is often their only gate). Resumed sessions
40
+ * re-read hooks from settings and fire per new call — structural, uncontested.
41
+ */
42
+ /** @type {import("../control-plane.mjs").CoverageMap} */
43
+ export const COVERAGE: import("../control-plane.mjs").CoverageMap;
44
+ /** Claude Code native hook event names (the `hook_event_name` field). */
45
+ export const HookEvent: Readonly<{
46
+ PRE_TOOL_USE: "PreToolUse";
47
+ POST_TOOL_USE: "PostToolUse";
48
+ USER_PROMPT_SUBMIT: "UserPromptSubmit";
49
+ SESSION_START: "SessionStart";
50
+ }>;
51
+ /** @type {import("../control-plane.mjs").Adapter} */
52
+ export const claudeAdapter: import("../control-plane.mjs").Adapter;
53
+ export type ToolCallEvent = import("../control-plane.mjs").ToolCallEvent;
54
+ export type Verdict = import("../control-plane.mjs").Verdict;
55
+ export type EventMeta = import("../control-plane.mjs").EventMeta;
56
+ export type NativeResponse = import("../control-plane.mjs").NativeResponse;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * True when `version` ("0.135.0") is at or above {@link MIN_ENFORCING_VERSION}.
3
+ * A missing/garbage version is treated as too old (fail closed to advisory).
4
+ * @param {unknown} version
5
+ * @returns {boolean}
6
+ */
7
+ export function canEnforce(version: unknown): boolean;
8
+ /**
9
+ * Parse a raw Codex hook payload. Never throws on unmodelled input. Marks the
10
+ * event observe-only (this_call_vetoable=false) when the Codex version
11
+ * predates hook enforcement.
12
+ * @param {any} native
13
+ * @returns {ToolCallEvent}
14
+ */
15
+ export function parse(native: any): ToolCallEvent;
16
+ /**
17
+ * Render into Codex's native external-hook transport: a `hookSpecificOutput`
18
+ * body preserving the native event name (`PreToolUse`/`PermissionRequest`) plus
19
+ * exit 2 on an enforceable deny. On a pre-v0.135 Codex (`this_call_vetoable`
20
+ * false) the body still renders but exit stays 0 and `enforced` is false —
21
+ * advisory only.
22
+ *
23
+ * `soleGate` (default `false`) is the same dangerous, explicit opt-in as the
24
+ * Claude adapter: when `true` AND the verdict is `allow`, the render emits the
25
+ * real `permissionDecision: "allow"` instead of abstaining.
26
+ * @param {Verdict} verdict
27
+ * @param {ToolCallEvent} event
28
+ * @param {{ soleGate?: boolean }} [options]
29
+ * @returns {NativeResponse}
30
+ */
31
+ export function render(verdict: Verdict, event: ToolCallEvent, { soleGate }?: {
32
+ soleGate?: boolean;
33
+ }): NativeResponse;
34
+ /** @typedef {import("../control-plane.mjs").ToolCallEvent} ToolCallEvent */
35
+ /** @typedef {import("../control-plane.mjs").Verdict} Verdict */
36
+ /** @typedef {import("../control-plane.mjs").EventMeta} EventMeta */
37
+ /** @typedef {import("../control-plane.mjs").NativeResponse} NativeResponse */
38
+ export const AGENT: "codex";
39
+ export const INTEGRATION_MODE: "external_hook";
40
+ /**
41
+ * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). `PreToolUse`
42
+ * intercepts the shell (Bash) tool ONLY — other builtins and MCP tools never
43
+ * reach it — so builtin is PARTIAL and MCP is UNCOVERED. Subagent and resumed
44
+ * firing are undocumented ⇒ UNKNOWN (treated as uncovered until an item-⑤ probe
45
+ * proves otherwise).
46
+ */
47
+ /** @type {import("../control-plane.mjs").CoverageMap} */
48
+ export const COVERAGE: import("../control-plane.mjs").CoverageMap;
49
+ /** Minimum Codex version whose hook can actually veto a tool call. */
50
+ export const MIN_ENFORCING_VERSION: readonly number[];
51
+ /** @type {import("../control-plane.mjs").Adapter} */
52
+ export const codexAdapter: import("../control-plane.mjs").Adapter;
53
+ export type ToolCallEvent = import("../control-plane.mjs").ToolCallEvent;
54
+ export type Verdict = import("../control-plane.mjs").Verdict;
55
+ export type EventMeta = import("../control-plane.mjs").EventMeta;
56
+ export type NativeResponse = import("../control-plane.mjs").NativeResponse;