@responsibleai/agent-hooks 0.1.0-beta.1 → 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/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
- * Fail-closed defaults: an `enforce`-mode emission with zero registered
21
- * interceptors yields `deny host_error:no_interceptor` (§7), and
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}.
@@ -28,8 +35,12 @@
28
35
  * a single JS thread. Sharing one emitter across workers is unsupported.
29
36
  */
30
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
+ }
33
44
  /** Envelope facts for {@link InterceptionEmitter.recordHostFailure}:
34
45
  * what the host still knows about an emission whose context it could
35
46
  * not construct (§10.3 "Host projection failure"). Everything is
@@ -51,18 +62,24 @@ export interface HostFailure {
51
62
  timestamp?: string;
52
63
  }
53
64
  export declare class InterceptionEmitter {
54
- private readonly mode;
55
- private readonly resolver;
56
- private readonly timeoutMs;
57
65
  private readonly interceptors;
58
66
  private _records;
59
67
  private composition;
60
68
  private identity;
61
- private readonly names;
62
69
  private approvalRedactor;
63
70
  private recordSink;
64
71
  private maxRecords;
65
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;
66
83
  /**
67
84
  * `timeoutMs` bounds each interceptor `intercept()` and resolver
68
85
  * `resolve()` call (§7, RECOMMENDED default 5000 ms); breach fails
@@ -72,12 +89,43 @@ export declare class InterceptionEmitter {
72
89
  * loop cannot be interrupted. `timeoutMs: null` disables enforcement.
73
90
  */
74
91
  constructor(mode?: EnforcementMode, resolver?: ApprovalResolver | null, timeoutMs?: number | null);
75
- /** Race `fn`'s result against the §7 timeout. */
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. */
76
119
  private withTimeout;
77
120
  get records(): readonly InterceptionRecord[];
78
121
  /** Register an interceptor, optionally with a host-chosen
79
- * payload-free `name` recorded on `verdicts[].name` (§10.3). */
80
- register(interceptor: Interceptor, name?: string): this;
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;
81
129
  /**
82
130
  * Declare the composition profile for subsequent emissions (§7.1).
83
131
  * The profile and knob values are validated against the closed §7.2
@@ -108,7 +156,8 @@ export declare class InterceptionEmitter {
108
156
  /** Register a per-emission record callback (§10.3), invoked
109
157
  * synchronously after every emission before buffering; a sink
110
158
  * exception is swallowed (audit delivery is the host's liveness
111
- * 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). */
112
161
  setRecordSink(sink: (record: InterceptionRecord) => void): this;
113
162
  /** Bound the in-memory record buffer: when full, the OLDEST record
114
163
  * is dropped and {@link recordsDropped} increments. Unbounded by
@@ -152,6 +201,11 @@ export declare class InterceptionEmitter {
152
201
  recordHostFailure(point: InterceptionPoint, failure?: HostFailure): InterceptionRecord;
153
202
  /** Deliver a record to the sink and the bounded buffer (§10.3). */
154
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;
155
209
  /** Profile dispatch (§7.4–§7.5). Returns the combined verdict and
156
210
  * its record metadata. */
157
211
  private dispatch;
@@ -167,7 +221,7 @@ export declare class InterceptionEmitter {
167
221
  * short-circuits; a liftable deny consults the seam, then `stop` or
168
222
  * `resume` per the knob.
169
223
  *
170
- * `perInterceptor` stays index-aligned with registration order (one
224
+ * `perInterceptor` stays index-aligned with dispatch order (one
171
225
  * entry per invoked interceptor, §10.3 summaries); `pool`
172
226
  * additionally holds substituted resolutions for the §7.3 unions. */
173
227
  private dispatchFirstDeny;
@@ -189,12 +243,12 @@ export declare class InterceptionEmitter {
189
243
  private aggregateAndConsult;
190
244
  /** Apply (enforce) or validate (evaluate_only) one transform (§7.4, §8). */
191
245
  private foldTransform;
192
- /** The declared provider's output for `ctx` (§10.1); `null` iff the
193
- * provider is `null`. Throws {@link AgentHooksCoreError} iff the
194
- * provider rejected the context (§10.2 fail-closed value domain). */
195
246
  /** Custom-provider identity, or null when the provider fails (the
196
247
  * emission has already been decided at this point). */
197
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). */
198
252
  private identityOf;
199
253
  /** Consult the approval seam for a liftable deny (§9), when the
200
254
  * profile conditions allow it: `enforce` mode, not `agent_shutdown`,