agent-control-plane-core 0.11.0 → 0.11.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.
- package/README.md +3 -2
- package/package.json +1 -1
- package/src/runtime.mjs +13 -5
- package/types/runtime.d.mts +3 -1
package/README.md
CHANGED
|
@@ -170,8 +170,9 @@ emit(claudeAdapter.render(verdict, event));
|
|
|
170
170
|
`agent-control-plane-core/runtime` publishes the rest of the plumbing the four
|
|
171
171
|
hook entries share. `runHookEntry(adapter, judge)` is a whole entry: each
|
|
172
172
|
`bin/<host>-hook.mjs` is that one call. `renderHookResponse` runs parse → judge
|
|
173
|
-
→
|
|
174
|
-
|
|
173
|
+
→ `renderEscalated`, so a judge's `ask` on a host with no ask tier blocks a
|
|
174
|
+
vetoable call instead of running it. It returns the failure response THAT host
|
|
175
|
+
expects when the payload does not parse. `emit`
|
|
175
176
|
drains the whole body before it exits, so a large deny body is never truncated
|
|
176
177
|
on a non-blocking pipe. The entries ship as `acp-claude-hook`, `acp-codex-hook`,
|
|
177
178
|
`acp-amp-hook` and `acp-gemini-hook`. Codex's hook stdin carries no version, so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-control-plane-core",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
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": {
|
package/src/runtime.mjs
CHANGED
|
@@ -13,7 +13,12 @@
|
|
|
13
13
|
* `process.exit` (see {@link writeAllSync}).
|
|
14
14
|
*/
|
|
15
15
|
import { writeSync } from "node:fs";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
Decision,
|
|
18
|
+
gatedResponse,
|
|
19
|
+
renderEscalated,
|
|
20
|
+
sanitizeVerdict,
|
|
21
|
+
} from "./control-plane.mjs";
|
|
17
22
|
|
|
18
23
|
/**
|
|
19
24
|
* Read all of stdin to a string.
|
|
@@ -63,7 +68,9 @@ export function demoJudge(event) {
|
|
|
63
68
|
* A judge that THROWS is not a malformed payload: the call parsed, and the judge
|
|
64
69
|
* failed to answer. That takes the same path as a judge that answers garbage —
|
|
65
70
|
* the clamp's `ask` (see below) — so a crashing guardrail puts the call in front
|
|
66
|
-
* of a human instead of taking an exit-0 fail-safe that runs it.
|
|
71
|
+
* of a human instead of taking an exit-0 fail-safe that runs it. The verdict
|
|
72
|
+
* renders through `renderEscalated`, so on a host with no ask tier that ask
|
|
73
|
+
* blocks a vetoable call rather than running it.
|
|
67
74
|
*
|
|
68
75
|
* Every error is written to STDERR (never stdout, which belongs to the host
|
|
69
76
|
* transport) so a regression that silently disables enforcement leaves a
|
|
@@ -107,8 +114,9 @@ export function renderHookResponse(
|
|
|
107
114
|
report("hook judge error", err);
|
|
108
115
|
}
|
|
109
116
|
// A host-supplied `onFailure` that BLOCKS (see {@link failurePosture}) wins
|
|
110
|
-
// over the clamp's ask
|
|
111
|
-
//
|
|
117
|
+
// over the clamp's ask. `renderEscalated` keeps an ask on a host that
|
|
118
|
+
// honours one (Claude, Amp), so without this a fail-closed entry would ask
|
|
119
|
+
// about a call no judge approved instead of blocking it.
|
|
112
120
|
if (onFailure.enforced && !isDecision(answer)) return onFailure;
|
|
113
121
|
// A judge's answer is clamped, not trusted. `normalizeVerdict` (inside
|
|
114
122
|
// every adapter's render) THROWS on a decision outside allow/deny/ask, and
|
|
@@ -118,7 +126,7 @@ export function renderHookResponse(
|
|
|
118
126
|
// in front of a human. Prose is passed through: scrubbing monitor-authored
|
|
119
127
|
// text needs a scrubber this transport module has no business choosing.
|
|
120
128
|
const verdict = sanitizeVerdict(answer, (text) => text);
|
|
121
|
-
return adapter
|
|
129
|
+
return renderEscalated(adapter, verdict, event);
|
|
122
130
|
} catch (err) {
|
|
123
131
|
report("hook pipeline error", err);
|
|
124
132
|
return onFailure;
|
package/types/runtime.d.mts
CHANGED
|
@@ -22,7 +22,9 @@ export function demoJudge(event: import("./control-plane.mjs").ToolCallEvent): i
|
|
|
22
22
|
* A judge that THROWS is not a malformed payload: the call parsed, and the judge
|
|
23
23
|
* failed to answer. That takes the same path as a judge that answers garbage —
|
|
24
24
|
* the clamp's `ask` (see below) — so a crashing guardrail puts the call in front
|
|
25
|
-
* of a human instead of taking an exit-0 fail-safe that runs it.
|
|
25
|
+
* of a human instead of taking an exit-0 fail-safe that runs it. The verdict
|
|
26
|
+
* renders through `renderEscalated`, so on a host with no ask tier that ask
|
|
27
|
+
* blocks a vetoable call rather than running it.
|
|
26
28
|
*
|
|
27
29
|
* Every error is written to STDERR (never stdout, which belongs to the host
|
|
28
30
|
* transport) so a regression that silently disables enforcement leaves a
|