@responsibleai/agent-hooks 0.1.0-alpha.5 → 0.1.0-beta.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/README.md +74 -5
- package/binding.d.ts +34 -0
- package/binding.js +164 -82
- package/dist/ctk/index.d.ts +53 -5
- package/dist/ctk/index.js +5 -1
- package/dist/ctk/reference.d.ts +15 -1
- package/dist/ctk/reference.declaration.json +33 -0
- package/dist/ctk/reference.js +84 -31
- package/dist/ctk/runner.d.ts +34 -4
- package/dist/ctk/runner.js +569 -36
- package/dist/declaration.d.ts +393 -0
- package/dist/declaration.js +752 -0
- package/dist/emitter.d.ts +111 -15
- package/dist/emitter.js +329 -41
- package/dist/index.d.ts +15 -3
- package/dist/index.js +26 -1
- package/dist/native.d.ts +8 -1
- package/dist/native.js +9 -1
- package/package.json +6 -6
package/dist/emitter.d.ts
CHANGED
|
@@ -17,8 +17,15 @@
|
|
|
17
17
|
* serial dispatch over isolated snapshots — §7.2: parallel names
|
|
18
18
|
* isolation semantics, not scheduling.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* An emitter can also be built from a host declaration document (§7.7)
|
|
21
|
+
* through {@link InterceptionEmitter.fromDeclaration} and the path, JSON
|
|
22
|
+
* and value forms. The document is validated and resolved by the Rust
|
|
23
|
+
* core; this module runs the host's kind resolvers (step 11 of §7.7.6),
|
|
24
|
+
* seals the emitter (§7.7.7) and stamps the contract version on every
|
|
25
|
+
* record (§7.7.8).
|
|
26
|
+
*
|
|
27
|
+
* Fail-closed defaults: an `enforce`-mode emission with zero interceptors
|
|
28
|
+
* bound at the point yields `deny host_error:no_interceptor` (§7), and
|
|
22
29
|
* {@link InterceptionEmitter.emit} **throws** {@link InterceptionBlocked}
|
|
23
30
|
* on any block — the ignorable-result variant is the explicitly named
|
|
24
31
|
* {@link InterceptionEmitter.emitUnchecked}.
|
|
@@ -27,22 +34,52 @@
|
|
|
27
34
|
* on the event loop; sequence assignment and record append are atomic on
|
|
28
35
|
* a single JS thread. Sharing one emitter across workers is unsupported.
|
|
29
36
|
*/
|
|
30
|
-
import { AgentContext, ApprovalResolver, CompositionConfig, EnforcementMode, IdentityProvider, EmitOutcome, InterceptionRecord, Interceptor } from "./index";
|
|
37
|
+
import { AgentContext, ApprovalResolver, CompositionConfig, EnforcementMode, IdentityProvider, EmitOutcome, InterceptionPoint, InterceptionRecord, Interceptor } from "./index";
|
|
38
|
+
import { HostDeclaration, HostRegistry, ResolvedDeclaration } from "./declaration";
|
|
31
39
|
/** §7 RECOMMENDED interceptor/resolver timeout (milliseconds). */
|
|
32
40
|
export declare const DEFAULT_TIMEOUT_MS = 5000;
|
|
41
|
+
export declare class EmitterSealed extends Error {
|
|
42
|
+
constructor(what: string);
|
|
43
|
+
}
|
|
44
|
+
/** Envelope facts for {@link InterceptionEmitter.recordHostFailure}:
|
|
45
|
+
* what the host still knows about an emission whose context it could
|
|
46
|
+
* not construct (§10.3 "Host projection failure"). Everything is
|
|
47
|
+
* optional — an absent member records the §10.3 unknown value
|
|
48
|
+
* (`session_id: ""`, `sequence: -1`, `timestamp` absent). */
|
|
49
|
+
export interface HostFailure {
|
|
50
|
+
/** Payload-free failure detail — an exception **type name** or a
|
|
51
|
+
* path, never the content that failed to project (§14 data
|
|
52
|
+
* minimization). Recorded as the verdict `message` (truncated by
|
|
53
|
+
* the §10.3 projection). */
|
|
54
|
+
detail?: string;
|
|
55
|
+
/** `session.id` of the failed emission, when the host knows it. */
|
|
56
|
+
session_id?: string;
|
|
57
|
+
/** The sequence number the failed emission would have carried. The
|
|
58
|
+
* host SHOULD consume the next number from its context source so
|
|
59
|
+
* records stay totally ordered within the session (§10.3). */
|
|
60
|
+
sequence?: number;
|
|
61
|
+
/** RFC 3339 event time, when the host has one. */
|
|
62
|
+
timestamp?: string;
|
|
63
|
+
}
|
|
33
64
|
export declare class InterceptionEmitter {
|
|
34
|
-
private readonly mode;
|
|
35
|
-
private readonly resolver;
|
|
36
|
-
private readonly timeoutMs;
|
|
37
65
|
private readonly interceptors;
|
|
38
66
|
private _records;
|
|
39
67
|
private composition;
|
|
40
68
|
private identity;
|
|
41
|
-
private readonly names;
|
|
42
69
|
private approvalRedactor;
|
|
43
70
|
private recordSink;
|
|
44
71
|
private maxRecords;
|
|
45
72
|
private _recordsDropped;
|
|
73
|
+
private readonly mode;
|
|
74
|
+
private resolver;
|
|
75
|
+
/** §7 emitter-wide interceptor bound; `null` disables enforcement. */
|
|
76
|
+
private timeoutMs;
|
|
77
|
+
/** §9 resolver bound (`approval_resolver_ms`, §7.7.3); `null` disables. */
|
|
78
|
+
private resolverTimeoutMs;
|
|
79
|
+
/** §7.7.7: a declaration-built emitter refuses reconfiguration. */
|
|
80
|
+
private sealed;
|
|
81
|
+
/** The resolved declaration this emitter was built from, if any. */
|
|
82
|
+
private _declaration;
|
|
46
83
|
/**
|
|
47
84
|
* `timeoutMs` bounds each interceptor `intercept()` and resolver
|
|
48
85
|
* `resolve()` call (§7, RECOMMENDED default 5000 ms); breach fails
|
|
@@ -52,12 +89,43 @@ export declare class InterceptionEmitter {
|
|
|
52
89
|
* loop cannot be interrupted. `timeoutMs: null` disables enforcement.
|
|
53
90
|
*/
|
|
54
91
|
constructor(mode?: EnforcementMode, resolver?: ApprovalResolver | null, timeoutMs?: number | null);
|
|
55
|
-
/**
|
|
92
|
+
/** Build an emitter from a validated declaration: steps 8 to 11 of
|
|
93
|
+
* §7.7.6 (resolve against the registry's surface and names in the
|
|
94
|
+
* core, then run every binding's kind resolver). The result is
|
|
95
|
+
* sealed (§7.7.7) and stamps `declaration` on every record (§7.7.8).
|
|
96
|
+
* Throws {@link DeclarationError} on refusal; no emitter exists then. */
|
|
97
|
+
static fromDeclaration(declaration: HostDeclaration, registry: HostRegistry): InterceptionEmitter;
|
|
98
|
+
/** {@link HostDeclaration.fromPath} then {@link fromDeclaration}. */
|
|
99
|
+
static fromDeclarationPath(path: string, registry: HostRegistry): Promise<InterceptionEmitter>;
|
|
100
|
+
/** {@link HostDeclaration.fromJson} then {@link fromDeclaration}. */
|
|
101
|
+
static fromDeclarationJson(text: string, registry: HostRegistry): InterceptionEmitter;
|
|
102
|
+
/** {@link HostDeclaration.fromValue} then {@link fromDeclaration}. */
|
|
103
|
+
static fromDeclarationValue(value: unknown, registry: HostRegistry): InterceptionEmitter;
|
|
104
|
+
/** The resolved declaration this emitter runs under, when it was
|
|
105
|
+
* built from one (§7.7.7). Its canonical JSON is the equivalence
|
|
106
|
+
* oracle for the construction paths. The value is frozen all the
|
|
107
|
+
* way down: writing through it has no effect on dispatch or records.
|
|
108
|
+
* `null` for an emitter configured in code. */
|
|
109
|
+
get declaration(): ResolvedDeclaration | null;
|
|
110
|
+
/** Step 11 and construction. Every reference is looked up again in
|
|
111
|
+
* the registry's own maps, so bookkeeping drift between the names the
|
|
112
|
+
* core checked and what the registry holds fails closed. */
|
|
113
|
+
private static fromResolved;
|
|
114
|
+
/** §7.7.7: a declaration-built emitter refuses reconfiguration;
|
|
115
|
+
* otherwise the declaration would not be what ran. */
|
|
116
|
+
private unsealed;
|
|
117
|
+
/** Race `fn`'s result against a timeout (§7). `limitMs: null` runs
|
|
118
|
+
* unbounded. */
|
|
56
119
|
private withTimeout;
|
|
57
120
|
get records(): readonly InterceptionRecord[];
|
|
58
121
|
/** Register an interceptor, optionally with a host-chosen
|
|
59
|
-
* payload-free `name` recorded on `verdicts[].name` (§10.3)
|
|
60
|
-
|
|
122
|
+
* payload-free `name` recorded on `verdicts[].name` (§10.3) and the
|
|
123
|
+
* points it runs at (`at`; omitted = every point). At point P the
|
|
124
|
+
* interceptors that run are those bound there, in registration
|
|
125
|
+
* order; `interceptors_registered`, `verdicts[].index` and
|
|
126
|
+
* `decided_by` count and index that list (§7.7.8). Throws
|
|
127
|
+
* {@link EmitterSealed} on a declaration-built emitter. */
|
|
128
|
+
register(interceptor: Interceptor, name?: string, at?: Iterable<InterceptionPoint>): this;
|
|
61
129
|
/**
|
|
62
130
|
* Declare the composition profile for subsequent emissions (§7.1).
|
|
63
131
|
* The profile and knob values are validated against the closed §7.2
|
|
@@ -88,7 +156,8 @@ export declare class InterceptionEmitter {
|
|
|
88
156
|
/** Register a per-emission record callback (§10.3), invoked
|
|
89
157
|
* synchronously after every emission before buffering; a sink
|
|
90
158
|
* exception is swallowed (audit delivery is the host's liveness
|
|
91
|
-
* concern, not the control plane's).
|
|
159
|
+
* concern, not the control plane's). Allowed on a sealed emitter: it
|
|
160
|
+
* changes where records go, not what they say (§7.7.7). */
|
|
92
161
|
setRecordSink(sink: (record: InterceptionRecord) => void): this;
|
|
93
162
|
/** Bound the in-memory record buffer: when full, the OLDEST record
|
|
94
163
|
* is dropped and {@link recordsDropped} increments. Unbounded by
|
|
@@ -110,6 +179,33 @@ export declare class InterceptionEmitter {
|
|
|
110
179
|
* caller MUST inspect `proceeds` and halt the guarded action itself;
|
|
111
180
|
* prefer {@link emit}. */
|
|
112
181
|
emitUnchecked(ctx: AgentContext): Promise<InterceptionRecord>;
|
|
182
|
+
/** §10.3/§11 host projection failure: synthesize and deliver the
|
|
183
|
+
* fail-closed record for an emission whose `AgentContext` the host
|
|
184
|
+
* could not construct at all — its own projection to the wire failed
|
|
185
|
+
* before anything existed to {@link emit} (e.g. a tool-call argument
|
|
186
|
+
* getter threw during to-wire conversion at the chat seam). Without
|
|
187
|
+
* this the host can only fail the action closed *recordless*; with
|
|
188
|
+
* it the trail stays complete under host-side faults.
|
|
189
|
+
*
|
|
190
|
+
* The record is the §10.3 rejection shape: the payload-free
|
|
191
|
+
* projection of a `deny host_error:context_invalid` carrying
|
|
192
|
+
* `failure.detail` (payload-free: a type name or path, never
|
|
193
|
+
* content) as its message; `null` identities under the declared
|
|
194
|
+
* provider; `decided_by: null`; no per-interceptor summaries (no
|
|
195
|
+
* interceptor ran); envelope members from {@link HostFailure}, with
|
|
196
|
+
* the §10.3 unknown values (`""`/`-1`) where absent. It takes the
|
|
197
|
+
* next slot in the record stream (sink, then buffer) like any
|
|
198
|
+
* emission. In `enforce` mode the host MUST still fail the action
|
|
199
|
+
* closed; in `evaluate_only` the record documents the host fault
|
|
200
|
+
* without implying enforcement (§8). */
|
|
201
|
+
recordHostFailure(point: InterceptionPoint, failure?: HostFailure): InterceptionRecord;
|
|
202
|
+
/** Deliver a record to the sink and the bounded buffer (§10.3). */
|
|
203
|
+
private deliver;
|
|
204
|
+
/** The interceptors bound at the context's point, in dispatch order
|
|
205
|
+
* (§7.7.8). */
|
|
206
|
+
private active;
|
|
207
|
+
/** The bound a registration runs under: its own, else the emitter's. */
|
|
208
|
+
private limit;
|
|
113
209
|
/** Profile dispatch (§7.4–§7.5). Returns the combined verdict and
|
|
114
210
|
* its record metadata. */
|
|
115
211
|
private dispatch;
|
|
@@ -125,7 +221,7 @@ export declare class InterceptionEmitter {
|
|
|
125
221
|
* short-circuits; a liftable deny consults the seam, then `stop` or
|
|
126
222
|
* `resume` per the knob.
|
|
127
223
|
*
|
|
128
|
-
* `perInterceptor` stays index-aligned with
|
|
224
|
+
* `perInterceptor` stays index-aligned with dispatch order (one
|
|
129
225
|
* entry per invoked interceptor, §10.3 summaries); `pool`
|
|
130
226
|
* additionally holds substituted resolutions for the §7.3 unions. */
|
|
131
227
|
private dispatchFirstDeny;
|
|
@@ -147,12 +243,12 @@ export declare class InterceptionEmitter {
|
|
|
147
243
|
private aggregateAndConsult;
|
|
148
244
|
/** Apply (enforce) or validate (evaluate_only) one transform (§7.4, §8). */
|
|
149
245
|
private foldTransform;
|
|
150
|
-
/** The declared provider's output for `ctx` (§10.1); `null` iff the
|
|
151
|
-
* provider is `null`. Throws {@link AgentHooksCoreError} iff the
|
|
152
|
-
* provider rejected the context (§10.2 fail-closed value domain). */
|
|
153
246
|
/** Custom-provider identity, or null when the provider fails (the
|
|
154
247
|
* emission has already been decided at this point). */
|
|
155
248
|
private tryCustomIdentity;
|
|
249
|
+
/** The declared provider's output for `ctx` (§10.1); `null` iff the
|
|
250
|
+
* provider is `null`. Throws {@link AgentHooksCoreError} iff the
|
|
251
|
+
* provider rejected the context (§10.2 fail-closed value domain). */
|
|
156
252
|
private identityOf;
|
|
157
253
|
/** Consult the approval seam for a liftable deny (§9), when the
|
|
158
254
|
* profile conditions allow it: `enforce` mode, not `agent_shutdown`,
|