agent-control-plane-core 0.5.2 → 0.6.0
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 +35 -6
- package/bin/codex-hook.mjs +3 -2
- package/package.json +1 -1
- package/src/adapters/amp.mjs +89 -17
- package/src/adapters/claude.mjs +75 -36
- package/src/adapters/codex.mjs +234 -61
- package/src/adapters/gemini.mjs +196 -78
- package/src/conformance.mjs +614 -1
- package/src/control-plane.mjs +173 -10
- package/src/fixtures/amp.json +8 -3
- package/src/fixtures/codex.json +277 -0
- package/src/fixtures/gemini.json +99 -2
- package/src/index.mjs +5 -1
- package/types/adapters/amp.d.mts +48 -6
- package/types/adapters/claude.d.mts +34 -3
- package/types/adapters/codex.d.mts +63 -11
- package/types/adapters/gemini.d.mts +58 -8
- package/types/conformance.d.mts +16 -1
- package/types/control-plane.d.mts +178 -75
- package/types/index.d.mts +1 -1
|
@@ -76,63 +76,54 @@ export function assertGatedKinds(kinds: Set<string>, agent: string): void;
|
|
|
76
76
|
export function isCoverageStatus(status: unknown): boolean;
|
|
77
77
|
/**
|
|
78
78
|
* Classify a tool call into a {@link CallClass} from what a single pre-tool
|
|
79
|
-
* payload reveals.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
79
|
+
* payload reveals. Two classes are detectable here:
|
|
80
|
+
*
|
|
81
|
+
* - MCP, from the tool NAME (`mcp__server__tool` / `mcp_server_tool`) or an
|
|
82
|
+
* explicit `mcp_context` object on the native payload. Checked FIRST: an MCP
|
|
83
|
+
* call made from inside a subagent is still routed through the host's MCP
|
|
84
|
+
* surface, and that is the axis a host most often leaves un-gated.
|
|
85
|
+
* - SUBAGENT, from a non-empty `agent_type` on the payload — the field a host
|
|
86
|
+
* stamps on a call made by a subagent rather than the main thread. Claude Code
|
|
87
|
+
* carries it (with `agent_id`) on its hook inputs, and a live Codex probe
|
|
88
|
+
* ([X3] in `docs/hook-coverage-matrix.md`) found both fields present on a
|
|
89
|
+
* subagent's `PreToolUse` and ABSENT on the main thread's, with the `agent_id`
|
|
90
|
+
* matching the one `SubagentStart` announced. Absence is what makes the field
|
|
91
|
+
* a DISCRIMINATOR rather than decoration, which is why it is read here.
|
|
92
|
+
*
|
|
93
|
+
* RESUMED is not detectable from a tool payload on any host measured so far.
|
|
94
|
+
* Codex does announce a resumed session — `SessionStart` carries
|
|
95
|
+
* `source: "resume"` ([X4]) — but that is a different event, and correlating it
|
|
96
|
+
* would mean keeping cross-event state this package deliberately does not have:
|
|
97
|
+
* `parse` is a pure function of one payload. So a resumed session's call falls
|
|
98
|
+
* through to BUILTIN (or MCP) and is judged by that row. An adapter feeds the
|
|
99
|
+
* result into `coverageAllowsVeto(this.COVERAGE[class])` so a call in an
|
|
100
|
+
* uncovered/unknown class parses non-vetoable.
|
|
101
|
+
*
|
|
102
|
+
* The fail-closed reading of a ❓ row therefore reaches BUILTIN, MCP, and — on a
|
|
103
|
+
* host that stamps `agent_type` — SUBAGENT. An adapter's RESUMED row is still
|
|
104
|
+
* never selected, so declaring it UNKNOWN would not make a resumed session's
|
|
105
|
+
* call non-vetoable: it is judged by the row its TOOL selects. Honouring that
|
|
106
|
+
* row would mean taking the minimum status across the classes this classifier
|
|
107
|
+
* cannot separate — which collapses BUILTIN to UNKNOWN and disables enforcement
|
|
108
|
+
* outright for every adapter that has a ❓ there, so it is deliberately not
|
|
109
|
+
* done. Where a host's resumed coverage is unmeasured, that gap is the
|
|
110
|
+
* sandbox's to cover, and the row stands as documentation.
|
|
87
111
|
* @param {string|null} tool tool name (null for non-tool events)
|
|
88
|
-
* @param {Record<string, unknown>} [native] the raw native payload, for `mcp_context`
|
|
112
|
+
* @param {Record<string, unknown>} [native] the raw native payload, for `mcp_context` and `agent_type`
|
|
89
113
|
* @returns {string} a {@link CallClass} value
|
|
90
114
|
*/
|
|
91
115
|
export function classifyCallClass(tool: string | null, native?: Record<string, unknown>): string;
|
|
92
116
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* @
|
|
100
|
-
* @
|
|
101
|
-
* @property {string} [permission_mode]
|
|
102
|
-
* @property {string} [transcript_path]
|
|
103
|
-
* @property {Record<string, unknown>} passthrough unmodelled native top-level fields, verbatim
|
|
104
|
-
*/
|
|
105
|
-
/**
|
|
106
|
-
* The result of rendering a {@link Verdict} for a specific agent's transport —
|
|
107
|
-
* the ATTACH mechanism, not the decision. A caller applies whichever channels
|
|
108
|
-
* are present: write `stdout`, exit with `exit_code`, or `throw`.
|
|
109
|
-
* @typedef {object} NativeResponse
|
|
110
|
-
* @property {"external_hook"|"in_process"|"observe_only"} transport
|
|
111
|
-
* @property {number} exit_code process exit code carrying the decision (0 = proceed)
|
|
112
|
-
* @property {boolean} enforced whether THIS render actually blocks (false ⇒ advisory only)
|
|
113
|
-
* @property {unknown} [stdout] native JSON body to write to stdout, when the transport uses one
|
|
114
|
-
* @property {string} [stderr] text the host reads from STDERR — the block reason on a transport (e.g. Gemini CLI's exit-2 System Block) that takes its rationale from stderr rather than the stdout body. The caller writes it to fd 2 before exiting.
|
|
115
|
-
*/
|
|
116
|
-
/**
|
|
117
|
-
* A normalized, agent-agnostic view of one agent event.
|
|
118
|
-
* @typedef {object} ToolCallEvent
|
|
119
|
-
* @property {number} schema_version stamped {@link SCHEMA_VERSION}
|
|
120
|
-
* @property {"pre_tool"|"post_tool"|"prompt_submit"|"session_start"|"unknown"} event
|
|
121
|
-
* @property {string|null} tool CANONICAL tool name — a native alias (e.g. Gemini's `run_shell_command`) is normalized to its {@link MODELED_TOOLS} canon (`Bash`); the raw native name is preserved on `meta.native_tool`. An unknown tool passes through verbatim. null for prompt/session events.
|
|
122
|
-
* @property {Record<string, unknown>} input passthrough tool input; a submitted prompt is folded into `input.prompt`
|
|
123
|
-
* @property {unknown} [response] tool output, post_tool only (string or structured), verbatim
|
|
124
|
-
* @property {boolean} this_call_vetoable false ⇒ the guardrail cannot veto THIS call; a monitor must auto-degrade deny to notify, and any render of it stays advisory (never `enforced`)
|
|
125
|
-
* @property {EventMeta} meta
|
|
126
|
-
*/
|
|
127
|
-
/**
|
|
128
|
-
* A normalized guardrail decision.
|
|
129
|
-
* @typedef {object} Verdict
|
|
130
|
-
* @property {"allow"|"deny"|"ask"} decision
|
|
131
|
-
* @property {Record<string, unknown>} [mutated_input] replacement tool input (pre_tool)
|
|
132
|
-
* @property {unknown} [mutated_output] replacement tool output (post_tool) — the normalized channel for a PostToolUse content transform (redaction/sanitize); a string or the tool's structured output, verbatim. An adapter renders it into whatever native output-mutation channel the host has, or drops it when the host has none (the same per-adapter fidelity gap `reason` has on Amp).
|
|
133
|
-
* @property {string} [additional_context] extra context to splice into the agent's stream
|
|
134
|
-
* @property {string} [reason] human-readable rationale (shown on deny/ask)
|
|
117
|
+
* A genuinely immutable set of `values`, for a row a consumer can reach.
|
|
118
|
+
*
|
|
119
|
+
* `Object.freeze` does not freeze a Set's CONTENTS — `frozen.delete(x)` still
|
|
120
|
+
* succeeds — and these rows are shared between adapters, so one `delete` would
|
|
121
|
+
* make several declarations report a channel their renders still discard. The
|
|
122
|
+
* wrapper holds the Set privately and exposes only the read half, frozen.
|
|
123
|
+
* @param {Iterable<string>} values
|
|
124
|
+
* @returns {ReadonlySet<string>}
|
|
135
125
|
*/
|
|
126
|
+
export function readonlySet(values: Iterable<string>): ReadonlySet<string>;
|
|
136
127
|
/**
|
|
137
128
|
* The translator for one agent's protocol. `parse` maps a native event to a
|
|
138
129
|
* {@link ToolCallEvent} (never throwing on unmodelled input, stamping the
|
|
@@ -144,6 +135,8 @@ export function classifyCallClass(tool: string | null, native?: Record<string, u
|
|
|
144
135
|
* @property {Record<string, "covered"|"partial"|"uncovered"|"unknown">} COVERAGE per-{@link CallClass} hook-coverage status; must classify every {@link CALL_CLASSES} entry
|
|
145
136
|
* @property {(native: any) => ToolCallEvent} parse
|
|
146
137
|
* @property {(verdict: Verdict, event: ToolCallEvent, options?: { soleGate?: boolean }) => NativeResponse} render
|
|
138
|
+
* @property {Record<string, string|undefined>} [NATIVE_EVENT_FOR] the native event name this host uses for each {@link EventKind}, for a conformance probe of a kind no fixture produced. Optional: a kind absent here (`unknown` always, plus any kind this transport does not carry) is probed with a marker name, which takes the adapter's unrecognized-event branch. Never read at runtime — `parse` stamps the real name on `meta.native_event`.
|
|
139
|
+
* @property {Record<string, ReadonlySet<string>|undefined>} UNRENDERED_FIELDS per-event-kind set of {@link VERDICT_CONTENT_FIELDS} this host has no native channel for, so `render` drops them. EVERY {@link EventKind} carries a row, including one this adapter's `parse` cannot emit — an omission would otherwise read as "every content field reaches a channel here", the reverse of the truth on a transport that carries none. The value type still admits `undefined` so a consumer handles a lookup miss rather than indexing straight into `.has(...)`; the conformance harness refuses a missing row. SCOPE — a row describes the ALLOW path only. An enforceable deny may drop more: Gemini's exit-2 System Block returns no stdout at all, so a deny there carries no `additional_context` whatever the row says. Do not read this map to infer what a deny delivers. The conformance harness fails an adapter whose renders disagree with its declaration either way, so a stale entry cannot survive.
|
|
147
140
|
*/
|
|
148
141
|
/**
|
|
149
142
|
* Build a normalized {@link ToolCallEvent}, stamping the schema version. Pure —
|
|
@@ -208,6 +201,26 @@ export function sanitizeVerdict(verdict: unknown, sanitizeText: (text: string) =
|
|
|
208
201
|
* @returns {Record<string, unknown>}
|
|
209
202
|
*/
|
|
210
203
|
export function collectPassthrough(native: Record<string, unknown>, consumed: Set<string>): Record<string, unknown>;
|
|
204
|
+
/**
|
|
205
|
+
* Build the {@link EventMeta} base every adapter shares: the four required
|
|
206
|
+
* fields, whichever {@link STANDARD_META_FIELDS} the payload carries, and the
|
|
207
|
+
* unmodelled remainder in `passthrough`. A mapped field is consumed here too, so
|
|
208
|
+
* a value that reached `meta` can never also appear in `meta.passthrough`.
|
|
209
|
+
*
|
|
210
|
+
* A non-string value leaves its field ABSENT rather than stamping a number or
|
|
211
|
+
* null onto a contract field consumers read as text. The result is deliberately
|
|
212
|
+
* mutable: the caller adds the agent-specific `native_tool` after the base.
|
|
213
|
+
* @param {{ agent: string, native_event: string, integration_mode: string, primary_gate_present: boolean, native: Record<string, unknown>, consumed: Set<string> }} parts
|
|
214
|
+
* @returns {EventMeta}
|
|
215
|
+
*/
|
|
216
|
+
export function baseMeta({ agent, native_event, integration_mode, primary_gate_present, native, consumed, }: {
|
|
217
|
+
agent: string;
|
|
218
|
+
native_event: string;
|
|
219
|
+
integration_mode: string;
|
|
220
|
+
primary_gate_present: boolean;
|
|
221
|
+
native: Record<string, unknown>;
|
|
222
|
+
consumed: Set<string>;
|
|
223
|
+
}): EventMeta;
|
|
211
224
|
/**
|
|
212
225
|
* The value if it is a plain (non-array) object, else `{}`.
|
|
213
226
|
* @param {unknown} value
|
|
@@ -275,6 +288,14 @@ export function nativeResponse({ transport, exit_code, enforced, stdout, stderr,
|
|
|
275
288
|
* field, a new modeled tool, a new tool alias) is backward-compatible and stays
|
|
276
289
|
* at v1; RENAMING or REMOVING a field, or changing a decision/event vocabulary,
|
|
277
290
|
* is breaking and bumps the version.
|
|
291
|
+
*
|
|
292
|
+
* That version covers the WIRE shapes an event or verdict carries, not the
|
|
293
|
+
* {@link Adapter} interface an integrator implements. A new REQUIRED adapter
|
|
294
|
+
* member breaks every third-party adapter while every event on the wire stays
|
|
295
|
+
* byte-identical, so it is a package-semver break and leaves
|
|
296
|
+
* {@link CONTROL_PLANE_SCHEMA} alone. `UNRENDERED_FIELDS` is one: adding it is
|
|
297
|
+
* a one-line change per adapter, and the conformance harness names the member
|
|
298
|
+
* and the migration rather than throwing from inside itself.
|
|
278
299
|
*/
|
|
279
300
|
/** Wire identifier for this schema version; bump on a breaking shape change. */
|
|
280
301
|
export const CONTROL_PLANE_SCHEMA: "control-plane/v1";
|
|
@@ -399,6 +420,111 @@ export const CoverageStatus: Readonly<{
|
|
|
399
420
|
UNCOVERED: "uncovered";
|
|
400
421
|
UNKNOWN: "unknown";
|
|
401
422
|
}>;
|
|
423
|
+
/**
|
|
424
|
+
* @typedef {object} EventMeta
|
|
425
|
+
* @property {string} agent producing agent id ("claude", "codex", …)
|
|
426
|
+
* @property {string} native_event original native event name, preserved verbatim
|
|
427
|
+
* @property {string} [native_tool] original native tool name, preserved verbatim when `event.tool` was canonicalized (present iff the event carries a tool)
|
|
428
|
+
* @property {"external_hook"|"in_process"|"observe_only"} integration_mode how the guardrail attaches
|
|
429
|
+
* @property {boolean} primary_gate_present the agent's own native gate already ran (⇒ the monitor is a SECOND opinion; the LLM call can be skipped when the native gate already blocked)
|
|
430
|
+
* @property {string} [session_id]
|
|
431
|
+
* @property {string} [cwd]
|
|
432
|
+
* @property {string} [permission_mode]
|
|
433
|
+
* @property {string} [transcript_path]
|
|
434
|
+
* @property {Record<string, unknown>} passthrough unmodelled native top-level fields, verbatim
|
|
435
|
+
*/
|
|
436
|
+
/**
|
|
437
|
+
* The result of rendering a {@link Verdict} for a specific agent's transport —
|
|
438
|
+
* the ATTACH mechanism, not the decision. A caller applies whichever channels
|
|
439
|
+
* are present: write `stdout`, exit with `exit_code`, or `throw`.
|
|
440
|
+
* @typedef {object} NativeResponse
|
|
441
|
+
* @property {"external_hook"|"in_process"|"observe_only"} transport
|
|
442
|
+
* @property {number} exit_code process exit code carrying the decision (0 = proceed)
|
|
443
|
+
* @property {boolean} enforced whether THIS render actually blocks (false ⇒ advisory only)
|
|
444
|
+
* @property {unknown} [stdout] native JSON body to write to stdout, when the transport uses one
|
|
445
|
+
* @property {string} [stderr] text the host reads from STDERR — the block reason on a transport (e.g. Gemini CLI's exit-2 System Block) that takes its rationale from stderr rather than the stdout body. The caller writes it to fd 2 before exiting.
|
|
446
|
+
*/
|
|
447
|
+
/**
|
|
448
|
+
* A normalized, agent-agnostic view of one agent event.
|
|
449
|
+
* @typedef {object} ToolCallEvent
|
|
450
|
+
* @property {number} schema_version stamped {@link SCHEMA_VERSION}
|
|
451
|
+
* @property {"pre_tool"|"post_tool"|"prompt_submit"|"session_start"|"unknown"} event
|
|
452
|
+
* @property {string|null} tool CANONICAL tool name — a native alias (e.g. Gemini's `run_shell_command`) is normalized to its {@link MODELED_TOOLS} canon (`Bash`); the raw native name is preserved on `meta.native_tool`. An unknown tool passes through verbatim. null for prompt/session events.
|
|
453
|
+
* @property {Record<string, unknown>} input passthrough tool input; a submitted prompt is folded into `input.prompt`
|
|
454
|
+
* @property {unknown} [response] tool output, post_tool only (string or structured), verbatim
|
|
455
|
+
* @property {boolean} this_call_vetoable false ⇒ the guardrail cannot veto THIS call; a monitor must auto-degrade deny to notify, and any render of it stays advisory (never `enforced`)
|
|
456
|
+
* @property {EventMeta} meta
|
|
457
|
+
*/
|
|
458
|
+
/**
|
|
459
|
+
* A normalized guardrail decision.
|
|
460
|
+
* @typedef {object} Verdict
|
|
461
|
+
* @property {"allow"|"deny"|"ask"} decision
|
|
462
|
+
* @property {Record<string, unknown>} [mutated_input] replacement tool input (pre_tool)
|
|
463
|
+
* @property {unknown} [mutated_output] replacement tool output (post_tool) — the normalized channel for a PostToolUse content transform (redaction/sanitize); a string or the tool's structured output, verbatim. An adapter renders it into whatever native output-mutation channel the host has, or drops it when the host has none (the same per-adapter fidelity gap `additional_context` has on Codex).
|
|
464
|
+
* @property {string} [additional_context] extra context to splice into the agent's stream
|
|
465
|
+
* @property {string} [reason] human-readable rationale (shown on deny/ask)
|
|
466
|
+
*/
|
|
467
|
+
/**
|
|
468
|
+
* The {@link Verdict} fields that carry CONTENT into the agent's stream, as
|
|
469
|
+
* opposed to the decision itself. Every adapter either renders each one into a
|
|
470
|
+
* native channel or has no channel for it and drops it; `UNRENDERED_FIELDS`
|
|
471
|
+
* declares which, and the conformance harness holds each adapter to its own
|
|
472
|
+
* declaration. `reason` is not listed: it is only live on deny/ask, so it is not
|
|
473
|
+
* probeable from a single abstaining verdict the way these three are.
|
|
474
|
+
*/
|
|
475
|
+
export const VERDICT_CONTENT_FIELDS: readonly string[];
|
|
476
|
+
/**
|
|
477
|
+
* The `UNRENDERED_FIELDS` row every adapter uses for {@link EventKind.UNKNOWN}.
|
|
478
|
+
* An event the adapter could not name is one whose host channels nobody
|
|
479
|
+
* established, so claiming any of them is the same fail-open `assertGatedKinds`
|
|
480
|
+
* refuses for the veto: the caller reads a mutation as applied while the host
|
|
481
|
+
* ignores the key it was written into.
|
|
482
|
+
*/
|
|
483
|
+
export const UNRENDERED_ON_UNKNOWN: ReadonlySet<string>;
|
|
484
|
+
/**
|
|
485
|
+
* The optional {@link EventMeta} string fields a host may carry, and the SSOT
|
|
486
|
+
* {@link baseMeta} reads off every native payload.
|
|
487
|
+
*
|
|
488
|
+
* PROBLEM CLASS — an adapter drops a normalized metadata field. Each adapter
|
|
489
|
+
* hand-copied these, one copy omitted `permission_mode`, and a guardrail keyed
|
|
490
|
+
* on `event.meta.permission_mode` then read `undefined` for that agent alone and
|
|
491
|
+
* took its no-value branch. Nothing distinguished "this host never sends the
|
|
492
|
+
* field" from "this adapter forgot it". Every adapter now maps the WHOLE set
|
|
493
|
+
* through {@link baseMeta}, so a field a host does not send is simply absent and
|
|
494
|
+
* no adapter carries an omission of its own.
|
|
495
|
+
*/
|
|
496
|
+
export const STANDARD_META_FIELDS: readonly string[];
|
|
497
|
+
/**
|
|
498
|
+
* The translator for one agent's protocol. `parse` maps a native event to a
|
|
499
|
+
* {@link ToolCallEvent} (never throwing on unmodelled input, stamping the
|
|
500
|
+
* integration mode / enforcement flags on `meta`); `render` maps a {@link Verdict}
|
|
501
|
+
* to that agent's native transport ({@link NativeResponse}), not just a JSON body.
|
|
502
|
+
*/
|
|
503
|
+
export type Adapter = {
|
|
504
|
+
AGENT: string;
|
|
505
|
+
INTEGRATION_MODE: "external_hook" | "in_process" | "observe_only";
|
|
506
|
+
/**
|
|
507
|
+
* per-{@link CallClass} hook-coverage status; must classify every {@link CALL_CLASSES} entry
|
|
508
|
+
*/
|
|
509
|
+
COVERAGE: Record<string, "covered" | "partial" | "uncovered" | "unknown">;
|
|
510
|
+
parse: (native: any) => ToolCallEvent;
|
|
511
|
+
render: (verdict: Verdict, event: ToolCallEvent, options?: {
|
|
512
|
+
soleGate?: boolean;
|
|
513
|
+
}) => NativeResponse;
|
|
514
|
+
/**
|
|
515
|
+
* the native event name this host uses for each {@link EventKind}, for a conformance probe of a kind no fixture produced. Optional: a kind absent here (`unknown` always, plus any kind this transport does not carry) is probed with a marker name, which takes the adapter's unrecognized-event branch. Never read at runtime — `parse` stamps the real name on `meta.native_event`.
|
|
516
|
+
*/
|
|
517
|
+
NATIVE_EVENT_FOR?: Record<string, string | undefined> | undefined;
|
|
518
|
+
/**
|
|
519
|
+
* per-event-kind set of {@link VERDICT_CONTENT_FIELDS} this host has no native channel for, so `render` drops them. EVERY {@link EventKind} carries a row, including one this adapter's `parse` cannot emit — an omission would otherwise read as "every content field reaches a channel here", the reverse of the truth on a transport that carries none. The value type still admits `undefined` so a consumer handles a lookup miss rather than indexing straight into `.has(...)`; the conformance harness refuses a missing row. SCOPE — a row describes the ALLOW path only. An enforceable deny may drop more: Gemini's exit-2 System Block returns no stdout at all, so a deny there carries no `additional_context` whatever the row says. Do not read this map to infer what a deny delivers. The conformance harness fails an adapter whose renders disagree with its declaration either way, so a stale entry cannot survive.
|
|
520
|
+
*/
|
|
521
|
+
UNRENDERED_FIELDS: Record<string, ReadonlySet<string> | undefined>;
|
|
522
|
+
};
|
|
523
|
+
export type CoverageStatusValue = "covered" | "partial" | "uncovered" | "unknown";
|
|
524
|
+
/**
|
|
525
|
+
* a per-{@link CallClass} coverage map (an adapter's `COVERAGE`)
|
|
526
|
+
*/
|
|
527
|
+
export type CoverageMap = Record<string, CoverageStatusValue>;
|
|
402
528
|
export type EventMeta = {
|
|
403
529
|
/**
|
|
404
530
|
* producing agent id ("claude", "codex", …)
|
|
@@ -490,7 +616,7 @@ export type Verdict = {
|
|
|
490
616
|
*/
|
|
491
617
|
mutated_input?: Record<string, unknown> | undefined;
|
|
492
618
|
/**
|
|
493
|
-
* replacement tool output (post_tool) — the normalized channel for a PostToolUse content transform (redaction/sanitize); a string or the tool's structured output, verbatim. An adapter renders it into whatever native output-mutation channel the host has, or drops it when the host has none (the same per-adapter fidelity gap `
|
|
619
|
+
* replacement tool output (post_tool) — the normalized channel for a PostToolUse content transform (redaction/sanitize); a string or the tool's structured output, verbatim. An adapter renders it into whatever native output-mutation channel the host has, or drops it when the host has none (the same per-adapter fidelity gap `additional_context` has on Codex).
|
|
494
620
|
*/
|
|
495
621
|
mutated_output?: unknown;
|
|
496
622
|
/**
|
|
@@ -502,26 +628,3 @@ export type Verdict = {
|
|
|
502
628
|
*/
|
|
503
629
|
reason?: string | undefined;
|
|
504
630
|
};
|
|
505
|
-
/**
|
|
506
|
-
* The translator for one agent's protocol. `parse` maps a native event to a
|
|
507
|
-
* {@link ToolCallEvent} (never throwing on unmodelled input, stamping the
|
|
508
|
-
* integration mode / enforcement flags on `meta`); `render` maps a {@link Verdict}
|
|
509
|
-
* to that agent's native transport ({@link NativeResponse}), not just a JSON body.
|
|
510
|
-
*/
|
|
511
|
-
export type Adapter = {
|
|
512
|
-
AGENT: string;
|
|
513
|
-
INTEGRATION_MODE: "external_hook" | "in_process" | "observe_only";
|
|
514
|
-
/**
|
|
515
|
-
* per-{@link CallClass} hook-coverage status; must classify every {@link CALL_CLASSES} entry
|
|
516
|
-
*/
|
|
517
|
-
COVERAGE: Record<string, "covered" | "partial" | "uncovered" | "unknown">;
|
|
518
|
-
parse: (native: any) => ToolCallEvent;
|
|
519
|
-
render: (verdict: Verdict, event: ToolCallEvent, options?: {
|
|
520
|
-
soleGate?: boolean;
|
|
521
|
-
}) => NativeResponse;
|
|
522
|
-
};
|
|
523
|
-
export type CoverageStatusValue = "covered" | "partial" | "uncovered" | "unknown";
|
|
524
|
-
/**
|
|
525
|
-
* a per-{@link CallClass} coverage map (an adapter's `COVERAGE`)
|
|
526
|
-
*/
|
|
527
|
-
export type CoverageMap = Record<string, CoverageStatusValue>;
|
package/types/index.d.mts
CHANGED
|
@@ -2,6 +2,6 @@ export * from "./control-plane.mjs";
|
|
|
2
2
|
export { codexAdapter } from "./adapters/codex.mjs";
|
|
3
3
|
export { ampAdapter } from "./adapters/amp.mjs";
|
|
4
4
|
export { claudeAdapter, HookEvent } from "./adapters/claude.mjs";
|
|
5
|
-
export { geminiAdapter, GEMINI_TOOL_ALIASES } from "./adapters/gemini.mjs";
|
|
5
|
+
export { geminiAdapter, GEMINI_TOOL_ALIASES, POST_TOOL_REDACTION_UNSUPPORTED } from "./adapters/gemini.mjs";
|
|
6
6
|
export { ADAPTERS, AGENT_IDS, adapterFor, assertRegistryConsistent } from "./registry.mjs";
|
|
7
7
|
export { runAdapterConformance, assertCoverageWellFormed, assertToolAliasesCovered, assertAliasedInputsCanonical } from "./conformance.mjs";
|