agent-control-plane-core 0.9.0 → 0.9.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-control-plane-core",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Vendor-neutral control-plane contract for coding agents: a normalized ToolCallEvent/Verdict schema with per-agent adapters (Claude Code, Codex, Amp, Gemini CLI) and a conformance suite.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -54,25 +54,25 @@ export const AGENT = "codex";
54
54
  export const INTEGRATION_MODE = IntegrationMode.EXTERNAL_HOOK;
55
55
 
56
56
  /**
57
- * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). `PreToolUse`
58
- * intercepts the shell (Bash) tool ONLY — other builtins and MCP tools never
59
- * reach it — so builtin is PARTIAL and MCP is UNCOVERED. A live probe
60
- * ([X3]/[X4]) fired `PreToolUse` for a SUBAGENT's call and for a call in a
61
- * RESUMED session, so both are PARTIAL too: covered, under the same Bash-only
62
- * limit as [X1], rather than the ❓ the matrix carried before the probe ran.
57
+ * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). `PreToolUse` fires
58
+ * for the shell tools, `apply_patch`, every MCP tool call and every other
59
+ * function tool ([X1]/[X2], read from Codex's own source at the enforcement
60
+ * floor). It never fires for `write_stdin`, which types into a process an
61
+ * earlier call started, nor for a tool OpenAI runs on its own side, so builtin
62
+ * and MCP are both PARTIAL. A live probe ([X3]/[X4]) fired `PreToolUse` for a
63
+ * SUBAGENT's call and for a call in a RESUMED session, so both are PARTIAL too.
63
64
  *
64
65
  * These rows describe PRE-TOOL routing, which is the only thing the matrix's
65
66
  * [X1]/[X2] sources speak about. `parse` applies them to a pre-tool event only
66
- * (see {@link vetoableCall}); Codex's `PostToolUse` fires for tools `PreToolUse`
67
- * never sees, so a post-tool event judged by the MCP row would drop a block
68
- * Codex documents it honours.
67
+ * (see {@link vetoableCall}); a `PostToolUse` payload in hand is proof the
68
+ * post-tool hook fired for that call, whatever the pre-tool row says.
69
69
  *
70
70
  * The SUBAGENT row is the one the classifier now reaches on this host: the same
71
71
  * probe found `agent_id`/`agent_type` present on a subagent's `PreToolUse`
72
72
  * payload and ABSENT on the main thread's, so {@link classifyCallClass} answers
73
73
  * SUBAGENT from that payload alone. It is PARTIAL, so such a call stays vetoable
74
74
  * — the row's job here is to be honest, not to degrade. An MCP-named call takes
75
- * the UNCOVERED MCP row either way, subagent or not.
75
+ * the MCP row either way, subagent or not.
76
76
  *
77
77
  * The RESUMED row is DECLARATIVE ONLY, for a reason no longer about coverage: a
78
78
  * resumed session announces itself on `SessionStart` (`source: "resume"`), not on
@@ -84,7 +84,7 @@ export const INTEGRATION_MODE = IntegrationMode.EXTERNAL_HOOK;
84
84
  /** @type {import("../control-plane.mjs").CoverageMap} */
85
85
  export const COVERAGE = Object.freeze({
86
86
  [CallClass.BUILTIN]: CoverageStatus.PARTIAL,
87
- [CallClass.MCP]: CoverageStatus.UNCOVERED,
87
+ [CallClass.MCP]: CoverageStatus.PARTIAL,
88
88
  [CallClass.SUBAGENT]: CoverageStatus.PARTIAL,
89
89
  [CallClass.RESUMED]: CoverageStatus.PARTIAL,
90
90
  });
@@ -221,17 +221,13 @@ export function canEnforce(version) {
221
221
  /**
222
222
  * Whether an event of `kind` may be marked vetoable, given the tool it names.
223
223
  *
224
- * PRE_TOOL takes the coverage floor: `PreToolUse` fires for Bash only, so an
225
- * MCP-sourced call is un-vetoable regardless of version (COVERAGE.mcp).
224
+ * PRE_TOOL takes the coverage floor ({@link COVERAGE}).
226
225
  *
227
226
  * POST_TOOL does NOT, and that is deliberate: {@link COVERAGE} records which
228
227
  * calls reach `PreToolUse`, and a `PostToolUse` payload in hand is proof the
229
- * post-tool hook already fired for this call — Codex documents PostToolUse
230
- * firing for tools (apply_patch, MCP) that `PreToolUse` never sees. Judging it
231
- * by the pre-tool MCP row would degrade a block Codex honours to a notify,
232
- * which is a fail-CLOSED error but still a wrong one. It stays gated on
233
- * GATED_EVENTS and on the version floor, so nothing here can mark an unmodelled
234
- * or pre-0.135 event vetoable.
228
+ * post-tool hook already fired for this call. It stays gated on GATED_EVENTS
229
+ * and on the version floor, so nothing here can mark an unmodelled or pre-0.135
230
+ * event vetoable.
235
231
  * @param {string} kind an {@link EventKind} value
236
232
  * @param {string|null} nativeTool
237
233
  * @param {Record<string, unknown>} raw the native payload, for `mcp_context`
@@ -146,7 +146,7 @@
146
146
  }
147
147
  },
148
148
  {
149
- "name": "PreToolUse MCP tool is non-vetoable (hook fires for Bash only)",
149
+ "name": "PreToolUse MCP tool is vetoable (the hook fires for MCP calls)",
150
150
  "call_class": "mcp",
151
151
  "native": {
152
152
  "hook_event_name": "PreToolUse",
@@ -162,7 +162,7 @@
162
162
  "input": {
163
163
  "title": "hi"
164
164
  },
165
- "this_call_vetoable": false,
165
+ "this_call_vetoable": true,
166
166
  "meta": {
167
167
  "agent": "codex",
168
168
  "native_event": "PreToolUse",
@@ -195,15 +195,16 @@
195
195
  },
196
196
  "native": {
197
197
  "transport": "external_hook",
198
- "exit_code": 0,
199
- "enforced": false,
198
+ "exit_code": 2,
199
+ "enforced": true,
200
200
  "stdout": {
201
201
  "hookSpecificOutput": {
202
202
  "hookEventName": "PreToolUse",
203
203
  "permissionDecision": "deny",
204
204
  "permissionDecisionReason": "mcp blocked"
205
205
  }
206
- }
206
+ },
207
+ "stderr": "mcp blocked"
207
208
  }
208
209
  }
209
210
  }
@@ -72,25 +72,25 @@ export function createCodexAdapter({ version }?: {
72
72
  export const AGENT: "codex";
73
73
  export const INTEGRATION_MODE: "external_hook";
74
74
  /**
75
- * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). `PreToolUse`
76
- * intercepts the shell (Bash) tool ONLY — other builtins and MCP tools never
77
- * reach it — so builtin is PARTIAL and MCP is UNCOVERED. A live probe
78
- * ([X3]/[X4]) fired `PreToolUse` for a SUBAGENT's call and for a call in a
79
- * RESUMED session, so both are PARTIAL too: covered, under the same Bash-only
80
- * limit as [X1], rather than the ❓ the matrix carried before the probe ran.
75
+ * Hook-coverage matrix row (`docs/hook-coverage-matrix.md`). `PreToolUse` fires
76
+ * for the shell tools, `apply_patch`, every MCP tool call and every other
77
+ * function tool ([X1]/[X2], read from Codex's own source at the enforcement
78
+ * floor). It never fires for `write_stdin`, which types into a process an
79
+ * earlier call started, nor for a tool OpenAI runs on its own side, so builtin
80
+ * and MCP are both PARTIAL. A live probe ([X3]/[X4]) fired `PreToolUse` for a
81
+ * SUBAGENT's call and for a call in a RESUMED session, so both are PARTIAL too.
81
82
  *
82
83
  * These rows describe PRE-TOOL routing, which is the only thing the matrix's
83
84
  * [X1]/[X2] sources speak about. `parse` applies them to a pre-tool event only
84
- * (see {@link vetoableCall}); Codex's `PostToolUse` fires for tools `PreToolUse`
85
- * never sees, so a post-tool event judged by the MCP row would drop a block
86
- * Codex documents it honours.
85
+ * (see {@link vetoableCall}); a `PostToolUse` payload in hand is proof the
86
+ * post-tool hook fired for that call, whatever the pre-tool row says.
87
87
  *
88
88
  * The SUBAGENT row is the one the classifier now reaches on this host: the same
89
89
  * probe found `agent_id`/`agent_type` present on a subagent's `PreToolUse`
90
90
  * payload and ABSENT on the main thread's, so {@link classifyCallClass} answers
91
91
  * SUBAGENT from that payload alone. It is PARTIAL, so such a call stays vetoable
92
92
  * — the row's job here is to be honest, not to degrade. An MCP-named call takes
93
- * the UNCOVERED MCP row either way, subagent or not.
93
+ * the MCP row either way, subagent or not.
94
94
  *
95
95
  * The RESUMED row is DECLARATIVE ONLY, for a reason no longer about coverage: a
96
96
  * resumed session announces itself on `SessionStart` (`source: "resume"`), not on