@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.
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Host declaration document (spec §7.7): the loader, the registry it
3
+ * resolves against, and the builder for the code path.
4
+ *
5
+ * A declaration is one JSON object that fixes a host's configuration
6
+ * (§7.1, §8, §10.1, §13.1), its declared surface (§13.1) and its
7
+ * interceptor bindings. It is a versioned contract of its own
8
+ * (`agent-hooks-declaration/<major>.<minor>`, §7.7.2), separate from
9
+ * the wire version `SPEC_VERSION` and from the package version.
10
+ *
11
+ * Every normative check lives in the Rust core. This module does the
12
+ * two things only the wrapper can do: read a file (step 1 of §7.7.6)
13
+ * and run the host's kind resolvers (step 11, in `emitter.ts`). Steps
14
+ * 2 to 10 cross the native boundary as JSON text, so a document yields
15
+ * the same refusal class here as in every other SDK.
16
+ */
17
+ import { AgentContext, ApprovalResolver, CompositionConfig, CompositionProfile, EnforcementMode, InterceptionPoint, Interceptor, JsonValue, OnApproval, SynthesisPolicy } from "./index";
18
+ /** The declaration contract version this SDK writes and accepts
19
+ * (§7.7.2). A test pins it to the core's `declaration_versions()`. */
20
+ export declare const DECLARATION_VERSION = "agent-hooks-declaration/1.0";
21
+ /** Every contract version the loader accepts (§7.7.2). Any other
22
+ * `declaration` value is refused as `declaration_error:version_unsupported`. */
23
+ export declare const SUPPORTED_DECLARATION_VERSIONS: readonly string[];
24
+ /** Largest document the text and file paths accept, in bytes (§7.7.3). */
25
+ export declare const MAX_DOCUMENT_BYTES: number;
26
+ /** Deepest nesting the text paths accept (§7.7.3). */
27
+ export declare const MAX_DEPTH = 32;
28
+ /** Most entries in `bindings` (§7.7.3). */
29
+ export declare const MAX_BINDINGS = 256;
30
+ /** Longest binding `id`, registered name and document `id` (§7.7.3). */
31
+ export declare const MAX_ID_LEN = 64;
32
+ /** Longest binding `kind` (§7.7.5). */
33
+ export declare const MAX_KIND_LEN = 128;
34
+ /** Longest refusal detail (§7.7.3). */
35
+ export declare const MAX_DETAIL_LEN = 512;
36
+ /** Largest `timeout_ms`, `interceptor_ms` and `approval_resolver_ms` (§7.7.3). */
37
+ export declare const MAX_TIMEOUT_MS = 3600000;
38
+ /** The closed capability vocabulary a surface may name (§7.7.4). */
39
+ export declare const CAPABILITIES: readonly string[];
40
+ /** The eleven refusal classes (§7.7.6), in pipeline order. */
41
+ export declare const DeclarationErrorClass: Readonly<{
42
+ readonly Unreadable: "declaration_error:unreadable";
43
+ readonly Malformed: "declaration_error:malformed";
44
+ readonly VersionUnsupported: "declaration_error:version_unsupported";
45
+ readonly SpecUnsupported: "declaration_error:spec_unsupported";
46
+ readonly UnknownField: "declaration_error:unknown_field";
47
+ readonly InvalidField: "declaration_error:invalid_field";
48
+ readonly Inconsistent: "declaration_error:inconsistent";
49
+ readonly SurfaceUnsupported: "declaration_error:surface_unsupported";
50
+ readonly ReferenceUnresolved: "declaration_error:reference_unresolved";
51
+ readonly KindUnknown: "declaration_error:kind_unknown";
52
+ readonly BindingRejected: "declaration_error:binding_rejected";
53
+ }>;
54
+ export type DeclarationErrorClass = (typeof DeclarationErrorClass)[keyof typeof DeclarationErrorClass];
55
+ /** One problem a load step found: a JSON pointer into the document
56
+ * (`/bindings/1/kind`; the empty string is the root) and a detail that
57
+ * names members, kinds and ids but never binding configuration. */
58
+ export interface Finding {
59
+ pointer: string;
60
+ detail: string;
61
+ }
62
+ /** A refused declaration (§7.7.6). Carries one class, every finding
63
+ * that step produced and, for `version_unsupported`, the accepted
64
+ * version set. Thrown by every construction path; never a verdict. */
65
+ export declare class DeclarationError extends Error {
66
+ /** The namespaced code, `declaration_error:<class>`. */
67
+ readonly code: DeclarationErrorClass;
68
+ readonly findings: readonly Finding[];
69
+ readonly accepted: readonly string[];
70
+ constructor(
71
+ /** The namespaced code, `declaration_error:<class>`. */
72
+ code: DeclarationErrorClass, findings: readonly Finding[], accepted?: readonly string[]);
73
+ /** One finding under `code`. */
74
+ static single(code: DeclarationErrorClass, pointer: string, detail: string): DeclarationError;
75
+ /** Rebuild the error the core reported across the native boundary:
76
+ * `code` is the `declaration_error:*` string and `detail` the JSON
77
+ * `{"findings": [...], "accepted": [...]}`. */
78
+ static fromCore(code: string, detail: string): DeclarationError;
79
+ /** Copy `findings` with every detail truncated to {@link MAX_DETAIL_LEN}. */
80
+ private static bound;
81
+ /** `code: pointer: detail; pointer: detail` (the Rust `Display`). */
82
+ private static describe;
83
+ }
84
+ /** A host registry programming error: a duplicate or reserved name, or
85
+ * a code surface the core rejects. Distinct from
86
+ * {@link DeclarationError}: the document is not at fault. */
87
+ export declare class HostRegistryError extends Error {
88
+ constructor(detail: string);
89
+ }
90
+ /** What the host does with the run after a `host_error:*` deny at the
91
+ * tool seam (§6.2, §13.1). */
92
+ export type ToolSeamPosture = "continue" | "terminate";
93
+ /** Whether a build bounds interceptor and resolver execution (§7).
94
+ * This SDK always does, in its own event loop. */
95
+ export type TimeoutSupport = "bounded" | "unbounded";
96
+ /** The knob values a host supports for one profile (§13.1). Only the
97
+ * knobs the profile consults are present. */
98
+ export interface KnobSupport {
99
+ on_approval?: OnApproval[];
100
+ on_disagreement?: SynthesisPolicy[];
101
+ on_transform_conflict?: SynthesisPolicy[];
102
+ }
103
+ /** Every value of every knob the profile consults (§7.2). */
104
+ export declare function fullKnobSupport(profile: CompositionProfile): KnobSupport;
105
+ /** The informative `host` block of a document. */
106
+ export interface HostInfo {
107
+ name: string;
108
+ version?: string;
109
+ }
110
+ /** What the host's code can honour (§7.7.4, §13.1): the one value the
111
+ * loader checks a document against and the CTK derives the harness
112
+ * surface from. A document may select a subset of this, never more.
113
+ * The shape is the core's `HostSurface` JSON form. */
114
+ export interface HostSurface {
115
+ /** Points the host emits. Always includes the §3.2 floor. */
116
+ interception_points: InterceptionPoint[];
117
+ /** Closed vocabulary ({@link CAPABILITIES}). */
118
+ capabilities: string[];
119
+ /** Profiles and the knob values supported under each. */
120
+ profiles: Partial<Record<CompositionProfile, KnobSupport>>;
121
+ /** The posture the code implements (§13.1). */
122
+ tool_seam_host_error: ToolSeamPosture;
123
+ /** Whether the host may declare `buffered_output: false` (§12.1a). */
124
+ streams_unbuffered: boolean;
125
+ /** The §12.1a exposure bound the host enforces. Required when
126
+ * `capabilities` names `incremental_output`. */
127
+ exposure_bound?: string;
128
+ /** Whether this build bounds execution. Always `"bounded"` here:
129
+ * the emitter races every call against its timeout. */
130
+ interceptor_timeout: TimeoutSupport;
131
+ /** Contract versions the host accepts; a subset of
132
+ * {@link SUPPORTED_DECLARATION_VERSIONS}. */
133
+ declaration_versions: string[];
134
+ }
135
+ /** Constructors for {@link HostSurface}. The core validates the
136
+ * surface (floor, pairs, closed vocabularies) when a document is
137
+ * resolved against it; a surface that fails there is a
138
+ * {@link HostRegistryError}, never a refusal of the document. */
139
+ export declare const HostSurface: Readonly<{
140
+ /** The smallest honest surface for this SDK: the lifecycle floor,
141
+ * `host_declaration`, every profile with every knob value, posture
142
+ * `continue`, buffered output, bounded timeouts and every accepted
143
+ * contract version. A host adds what its runtime does. */
144
+ sdkDefault(): HostSurface;
145
+ /** The surface the CTK derives from a harness's capability list and
146
+ * posture (§7.7.9): the floor plus the model points iff
147
+ * `model_calls` plus the tool points iff `tool_calls`. A list naming
148
+ * `incremental_output` needs {@link HostSurface.withExposureBound}
149
+ * on top, or the core refuses the surface. */
150
+ fromCapabilities(caps: Iterable<string>, posture?: ToolSeamPosture): HostSurface;
151
+ /** Add points (the model pair, the tool pair, or both). */
152
+ withPoints(surface: HostSurface, points: Iterable<InterceptionPoint>): HostSurface;
153
+ /** Add capabilities. */
154
+ withCapabilities(surface: HostSurface, caps: Iterable<string>): HostSurface;
155
+ /** State the §12.1a exposure bound an incremental host enforces. Also
156
+ * marks the host as able to declare `buffered_output: false`. */
157
+ withExposureBound(surface: HostSurface, bound: string): HostSurface;
158
+ }>;
159
+ /** What a kind resolver learns about the binding it builds (§7.7.5). */
160
+ export interface BindingContext {
161
+ id: string;
162
+ kind: string;
163
+ /** The resolved `at` set, in lifecycle order. */
164
+ at: readonly InterceptionPoint[];
165
+ /** The resolved per-binding bound in milliseconds; `null` is unbounded. */
166
+ timeoutMs: number | null;
167
+ host: HostInfo | null;
168
+ declarationVersion: string;
169
+ }
170
+ /** Host code that turns one binding's `config` into one interceptor,
171
+ * or refuses it by throwing. The thrown message MUST NOT echo the
172
+ * config (§7.7.5); the loader never does. */
173
+ export type KindResolver = (config: JsonValue, context: BindingContext) => Interceptor;
174
+ /** The names a registry holds, derived from what was registered and
175
+ * never hand-written (§7.7.5). */
176
+ export interface RegistryNames {
177
+ identity_providers: string[];
178
+ approval_resolvers: string[];
179
+ approval_redactors: string[];
180
+ kinds: string[];
181
+ }
182
+ /** Reference grammar for resolver and redactor names and binding ids:
183
+ * `^[a-z][a-z0-9_-]{0,63}$`. */
184
+ export declare function validReference(s: string): boolean;
185
+ /** Binding kind grammar (§7.7.5): dot-separated lowercase segments, at
186
+ * least two, at most 128 characters. */
187
+ export declare function validKind(s: string): boolean;
188
+ /** Everything a host registers in code for a declaration to reference
189
+ * (§7.7.5): the code surface, kind resolvers, custom identity
190
+ * providers, approval resolvers and approval redactors. One registry
191
+ * can build many emitters. */
192
+ export declare class HostRegistry {
193
+ readonly surface: HostSurface;
194
+ private readonly allowReserved;
195
+ private readonly kinds;
196
+ private readonly identityProviders;
197
+ private readonly approvalResolvers;
198
+ private readonly approvalRedactors;
199
+ /** A registry over the given code surface. Kinds under the reserved
200
+ * `agent_hooks` and `ctk` segments are refused. */
201
+ constructor(surface: HostSurface, allowReserved?: boolean);
202
+ /** The conformance kit's registry: as the constructor, but the `ctk`
203
+ * kind segment may be registered. */
204
+ static forConformance(surface: HostSurface): HostRegistry;
205
+ /** Register a kind resolver. */
206
+ kind(kind: string, resolver: KindResolver): this;
207
+ /** Register a custom identity provider (§10.1 name rules apply). */
208
+ identityProvider(name: string, fn: (ctx: AgentContext) => string): this;
209
+ /** Register an approval resolver under a reference name. */
210
+ approvalResolver(name: string, resolver: ApprovalResolver): this;
211
+ /** Register an approval redactor under a reference name. */
212
+ approvalRedactor(name: string, fn: (ctx: AgentContext) => AgentContext): this;
213
+ /** The registered names, derived (§7.7.5). */
214
+ names(): RegistryNames;
215
+ /** The `host_json` the core's `declaration_resolve` takes: the surface
216
+ * plus the registered names. */
217
+ hostDescription(): string;
218
+ /** @internal */
219
+ kindResolver(kind: string): KindResolver | undefined;
220
+ /** @internal */
221
+ identityFn(name: string): ((ctx: AgentContext) => string) | undefined;
222
+ /** @internal */
223
+ approvalResolverRef(name: string): ApprovalResolver | undefined;
224
+ /** @internal */
225
+ redactorFn(name: string): ((ctx: AgentContext) => AgentContext) | undefined;
226
+ private static checkReference;
227
+ }
228
+ /** A declaration that passed steps 2 to 7 (§7.7.6): parsed, of an
229
+ * accepted version, closed, well-typed and internally consistent as
230
+ * stated. Not yet checked against any host; `InterceptionEmitter.
231
+ * fromDeclaration` does that. */
232
+ export declare class HostDeclaration {
233
+ private readonly doc;
234
+ private readonly text;
235
+ /** `text` is the core's serialization of the validated document and
236
+ * is what every later core call receives, so integers beyond 2^53
237
+ * inside `bindings[].config` reach steps 8 to 10 as the file had
238
+ * them. `doc` is the JavaScript view of the same text. */
239
+ private constructor();
240
+ /** Step 1 then {@link HostDeclaration.fromJson}: open exactly `path`,
241
+ * require a regular file of at most {@link MAX_DOCUMENT_BYTES},
242
+ * strict UTF-8 without a byte-order mark, read once. */
243
+ static fromPath(path: string): Promise<HostDeclaration>;
244
+ /** Steps 2 to 7 over JSON text, in the core. */
245
+ static fromJson(text: string): HostDeclaration;
246
+ /** Steps 2 to 7 over an in-memory value: serialized and handed to
247
+ * {@link HostDeclaration.fromJson}, so size, depth and shape checks
248
+ * are the same code on every path. A non-finite number, which
249
+ * `JSON.stringify` would corrupt to `null`, is refused as
250
+ * `malformed` first. */
251
+ static fromValue(value: unknown): HostDeclaration;
252
+ /** A builder for the code path (§7.7.7). */
253
+ static builder(): DeclarationBuilder;
254
+ /** The document's own `declaration` value. */
255
+ get version(): string;
256
+ /** The validated document, verbatim (including `$schema`). A copy.
257
+ * Integers beyond 2^53 inside `bindings[].config` are rounded in this
258
+ * view; {@link HostDeclaration.toJson} keeps them exact. */
259
+ toValue(): Record<string, JsonValue>;
260
+ /** @internal The validated document as JSON text, as the core
261
+ * serialized it. */
262
+ toJson(): string;
263
+ }
264
+ /** Resolved configuration: every default filled (§7.7.3). */
265
+ export interface ResolvedConfiguration {
266
+ mode: EnforcementMode;
267
+ composition: CompositionConfig;
268
+ identity_provider: string | null;
269
+ approval: {
270
+ resolver: string | null;
271
+ redactor: string | null;
272
+ };
273
+ posture: {
274
+ tool_seam_host_error: ToolSeamPosture;
275
+ };
276
+ timeouts: {
277
+ interceptor_ms: number | null;
278
+ approval_resolver_ms: number | null;
279
+ };
280
+ records: {
281
+ max_buffered: number | null;
282
+ };
283
+ }
284
+ /** Resolved surface (§7.7.4): the document's, or the code's when the
285
+ * document stated none. */
286
+ export interface ResolvedSurface {
287
+ interception_points: InterceptionPoint[];
288
+ capabilities: string[];
289
+ profiles: Partial<Record<CompositionProfile, KnobSupport>>;
290
+ buffered_output: boolean;
291
+ exposure_bound?: string;
292
+ declaration_versions: string[];
293
+ }
294
+ /** One binding with `at` and `timeout_ms` filled. */
295
+ export interface ResolvedBinding {
296
+ id: string;
297
+ kind: string;
298
+ config: JsonValue;
299
+ at: InterceptionPoint[];
300
+ timeout_ms: number | null;
301
+ }
302
+ /** The resolved declaration (§7.7.3 "Resolved form"): every default
303
+ * filled, composition knobs resolved exactly as §7.2 resolves them,
304
+ * `$schema` dropped, sets sorted. Its canonical JSON
305
+ * ({@link canonicalDeclaration}) is the equivalence oracle for the
306
+ * construction paths (§7.7.7). */
307
+ export interface ResolvedDeclaration {
308
+ declaration: string;
309
+ spec: string;
310
+ id?: string;
311
+ host?: HostInfo;
312
+ configuration: ResolvedConfiguration;
313
+ surface: ResolvedSurface;
314
+ bindings: ResolvedBinding[];
315
+ extensions: Record<string, JsonValue>;
316
+ }
317
+ /** RFC 8785 canonical JSON of a resolved declaration (the core's
318
+ * `canonical_json`). */
319
+ export declare function canonicalDeclaration(resolved: ResolvedDeclaration): string;
320
+ /** Steps 8 to 10 of §7.7.6 (after steps 2 to 7 again, in the core):
321
+ * resolve a validated declaration against the registry's surface and
322
+ * names. Throws {@link DeclarationError} on refusal. */
323
+ export declare function resolveDeclaration(declaration: HostDeclaration, registry: HostRegistry): ResolvedDeclaration;
324
+ /** Steps 7 (on the filled surface) and 8 against a code surface alone,
325
+ * without a registry: what the CTK runner uses on a harness's own
326
+ * document to learn the surface a run is assessed against (§7.7.9). */
327
+ export declare function resolveSurfaceOnly(declaration: HostDeclaration, surface: HostSurface): ResolvedDeclaration;
328
+ /** The contract versions the core writes and accepts (§7.7.2). */
329
+ export declare function declarationVersions(): {
330
+ current: string;
331
+ supported: string[];
332
+ };
333
+ /** Builds a declaration document in code, one setter per member
334
+ * (§7.7.7). {@link DeclarationBuilder.build} hands the document to
335
+ * {@link HostDeclaration.fromValue}, so the code path is validated by
336
+ * the same function, with the same classes, as a file. */
337
+ export declare class DeclarationBuilder {
338
+ private readonly doc;
339
+ /** A builder with `declaration` set to {@link DECLARATION_VERSION}
340
+ * and an empty `bindings` array. */
341
+ constructor();
342
+ /** A builder with no member set at all, not even `declaration` or
343
+ * `bindings`. For harnesses that must express an incomplete
344
+ * document; a host wants the constructor. */
345
+ static empty(): DeclarationBuilder;
346
+ private configuration;
347
+ private configurationSub;
348
+ private surface;
349
+ private sub;
350
+ /** `declaration` (defaults to {@link DECLARATION_VERSION}). */
351
+ version(v: string): this;
352
+ spec(v: string): this;
353
+ id(v: string): this;
354
+ host(name: string, version?: string): this;
355
+ mode(mode: EnforcementMode): this;
356
+ /** The composition as the host states it. Knobs the profile does not
357
+ * consult are written out and refused by {@link build}, exactly as
358
+ * in a file. */
359
+ composition(c: CompositionConfig): this;
360
+ /** A name for `jcs-sha256` or a custom provider, `null` for
361
+ * identity-unbound. */
362
+ identityProvider(name: string | null): this;
363
+ approvalResolver(name: string | null): this;
364
+ approvalRedactor(name: string | null): this;
365
+ toolSeamHostError(posture: ToolSeamPosture): this;
366
+ /** `null` writes `null` (unbounded). */
367
+ interceptorTimeoutMs(ms: number | null): this;
368
+ /** `null` writes `null` (unbounded). */
369
+ approvalResolverTimeoutMs(ms: number | null): this;
370
+ /** `null` writes `null` (unbounded). */
371
+ maxBufferedRecords(n: number | null): this;
372
+ surfacePoints(points: Iterable<InterceptionPoint>): this;
373
+ surfaceCapabilities(caps: Iterable<string>): this;
374
+ /** Declare support for one profile and its knob values. */
375
+ surfaceProfile(profile: CompositionProfile, knobs: KnobSupport): this;
376
+ /** `buffered_output` and, when `false`, the required exposure bound. */
377
+ bufferedOutput(buffered: boolean, exposureBound?: string): this;
378
+ surfaceDeclarationVersions(versions: Iterable<string>): this;
379
+ /** Append one binding. `at` omitted binds at every surface point;
380
+ * `timeoutMs` omitted inherits the configured interceptor timeout,
381
+ * `null` writes `null` (unbounded), a number bounds. */
382
+ bind(id: string, kind: string, config?: JsonValue, at?: Iterable<InterceptionPoint>, timeoutMs?: number | null): this;
383
+ /** One `extensions` entry, kept verbatim. */
384
+ extension(key: string, value: JsonValue): this;
385
+ /** Set a top-level member verbatim. For members this builder has no
386
+ * setter for; the result is validated like any other document (an
387
+ * unknown member is refused as `unknown_field`). */
388
+ raw(key: string, value: JsonValue): this;
389
+ /** The document as built, before validation. A copy. */
390
+ toValue(): Record<string, JsonValue>;
391
+ /** Validate (steps 2 to 7) through {@link HostDeclaration.fromValue}. */
392
+ build(): HostDeclaration;
393
+ }