@responsibleai/agent-hooks 0.1.0-beta.1 → 0.1.0-beta.3

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,33 @@
1
+ {
2
+ "$schema": "https://responsibleai.github.io/agent-hooks/schema/v0.1/host-declaration-1.0.schema.json",
3
+ "declaration": "agent-hooks-declaration/1.0",
4
+ "spec": "agent-hooks/0.1",
5
+ "id": "reference-agent",
6
+ "host": { "name": "reference-agent", "version": "0.1.0" },
7
+ "configuration": {
8
+ "mode": "enforce",
9
+ "composition": { "profile": "sequential/first_deny", "on_approval": "stop" },
10
+ "identity_provider": "jcs-sha256",
11
+ "approval": { "resolver": null, "redactor": null },
12
+ "posture": { "tool_seam_host_error": "continue" },
13
+ "timeouts": { "interceptor_ms": 5000, "approval_resolver_ms": 5000 },
14
+ "records": { "max_buffered": null }
15
+ },
16
+ "surface": {
17
+ "interception_points": [
18
+ "agent_startup", "input", "pre_model_call", "post_model_call",
19
+ "pre_tool_call", "post_tool_call", "output", "agent_shutdown"
20
+ ],
21
+ "capabilities": ["host_declaration", "model_calls", "tool_calls"],
22
+ "profiles": {
23
+ "sequential/first_deny": { "on_approval": ["resume", "stop"] },
24
+ "sequential/run_all": {},
25
+ "parallel/strictest": { "on_transform_conflict": ["approval", "deny"] },
26
+ "parallel/unanimous": { "on_disagreement": ["approval", "deny"] }
27
+ },
28
+ "buffered_output": true,
29
+ "declaration_versions": ["agent-hooks-declaration/1.0"]
30
+ },
31
+ "bindings": [],
32
+ "extensions": {}
33
+ }
@@ -1,63 +1,115 @@
1
1
  "use strict";
2
- // Copyright (c) Microsoft Corporation.
3
- // Licensed under the MIT License.
2
+ // Copyright (c) Microsoft. All rights reserved.
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ReferenceHarness = exports.REFERENCE_DECLARATION = void 0;
4
8
  /**
5
9
  * Reference in-memory agent + harness.
6
10
  *
7
11
  * The simplest possible conformant agent loop; exists so the
8
12
  * CTK can self-test without a real framework. Port of
9
13
  * `sdk/python/python/agent_hooks/ctk/reference.py`.
14
+ *
15
+ * Every emitter it builds goes through the host declaration loader
16
+ * (§7.7): for a field-based vector it writes the vector's mode,
17
+ * composition and provider into a copy of its own document
18
+ * (`reference.declaration.json`) and binds the scripted interceptors
19
+ * through a `ctk.instance` kind, so the field-based vectors exercise
20
+ * the loader too.
10
21
  */
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.ReferenceHarness = void 0;
13
22
  const node_crypto_1 = require("node:crypto");
14
23
  const index_1 = require("../index");
15
24
  const builder_1 = require("../builder");
16
25
  const emitter_1 = require("../emitter");
17
- const native_1 = require("../native");
26
+ const runner_1 = require("./runner");
27
+ const reference_declaration_json_1 = __importDefault(require("./reference.declaration.json"));
28
+ /** The reference harness's own declaration (§7.7.9), with an explicit
29
+ * surface a claim can cite. */
30
+ exports.REFERENCE_DECLARATION = Object.freeze(reference_declaration_json_1.default);
18
31
  class ReferenceHarness {
19
32
  name = "reference-agent";
20
- capabilities = new Set(["model_calls", "tool_calls"]);
33
+ // host_declaration: every emitter is built through the loader.
34
+ // JSON.parse rounds integers beyond 2^53 before any guard can run,
35
+ // so neither int64_json nor bigint_json is declared (§4.4).
36
+ capabilities = new Set([
37
+ "model_calls",
38
+ "tool_calls",
39
+ "host_declaration",
40
+ ]);
21
41
  scenario;
22
42
  emitter;
23
43
  builder;
24
44
  toolLog = [];
25
- setup(scenario, interceptors, resolver, mode, composition, identityProvider, redactForApproval = []) {
45
+ hostSurface() {
46
+ return index_1.HostSurface.fromCapabilities(this.capabilities, "continue");
47
+ }
48
+ declaration() {
49
+ return JSON.parse(JSON.stringify(exports.REFERENCE_DECLARATION));
50
+ }
51
+ startSession(scenario, emitter) {
26
52
  this.scenario = scenario;
27
53
  this.toolLog = [];
28
- const em = new emitter_1.InterceptionEmitter(mode, resolver);
29
- em.setComposition(composition);
30
- if (redactForApproval.length > 0) {
31
- // §9 redaction seam, CTK convention: each listed path is replaced
32
- // with "[redacted]" via the §5.2/§4.3 transform machinery;
33
- // unresolvable paths are left untouched.
34
- em.setApprovalRedactor((ctx) => {
35
- let out = JSON.stringify(ctx);
36
- for (const path of redactForApproval) {
37
- try {
38
- out = native_1.native.applyTransformCtx(out, path, '"[redacted]"');
39
- }
40
- catch {
41
- /* unresolvable at this point — skip */
42
- }
43
- }
44
- return JSON.parse(out);
45
- });
46
- }
47
- // §13.2: "ctk-fault" is a custom provider that throws, pinning the
48
- // §10.1 provider-failure rule (deny context_invalid pre-dispatch).
49
- em.setIdentityProvider(identityProvider === 'ctk-fault'
50
- ? { name: 'ctk-fault', fn: () => { throw new Error('ctk scripted provider fault'); } }
51
- : identityProvider);
52
- for (const i of interceptors)
53
- em.register(i);
54
- this.emitter = em;
54
+ this.emitter = emitter;
55
55
  this.builder = new builder_1.AgentContextBuilder({
56
56
  agentId: "ref-agent",
57
57
  framework: "reference-agent",
58
58
  sessionId: (0, node_crypto_1.randomUUID)(),
59
59
  });
60
60
  }
61
+ setup(scenario, interceptors, resolver, mode, composition, identityProvider, redactForApproval = []) {
62
+ // Field-based vector: write the vector's configuration into a copy
63
+ // of the reference document and bind the interceptors by index
64
+ // through the `ctk.instance` kind.
65
+ const doc = this.declaration();
66
+ const cfg = doc["configuration"];
67
+ cfg["mode"] = mode;
68
+ cfg["composition"] = composition;
69
+ const registry = index_1.HostRegistry.forConformance(this.hostSurface());
70
+ if (identityProvider === "ctk-fault") {
71
+ // §13.2: "ctk-fault" is a custom provider that throws, pinning the
72
+ // §10.1 provider-failure rule (deny context_invalid pre-dispatch).
73
+ registry.identityProvider("ctk-fault", () => {
74
+ throw new Error("ctk scripted provider fault");
75
+ });
76
+ }
77
+ cfg["identity_provider"] = identityProvider;
78
+ const approval = { resolver: null, redactor: null };
79
+ if (resolver !== null) {
80
+ registry.approvalResolver("ctk-scripted", resolver);
81
+ approval["resolver"] = "ctk-scripted";
82
+ }
83
+ if (redactForApproval.length > 0) {
84
+ const paths = [...redactForApproval];
85
+ registry.approvalRedactor("ctk-redact", (ctx) => (0, runner_1.redactPaths)(ctx, paths));
86
+ approval["redactor"] = "ctk-redact";
87
+ }
88
+ cfg["approval"] = approval;
89
+ const slots = [...interceptors];
90
+ registry.kind("ctk.instance", (config, ctx) => {
91
+ const index = config?.["index"];
92
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0) {
93
+ throw new Error("config.index must be an unsigned integer");
94
+ }
95
+ const instance = slots[index];
96
+ if (instance === undefined) {
97
+ throw new Error(`no interceptor instance ${index} for binding ${ctx.id}`);
98
+ }
99
+ slots[index] = undefined;
100
+ return instance;
101
+ });
102
+ doc["bindings"] = interceptors.map((_, i) => ({
103
+ id: `interceptor-${i}`,
104
+ kind: "ctk.instance",
105
+ config: { index: i },
106
+ }));
107
+ this.startSession(scenario, emitter_1.InterceptionEmitter.fromDeclarationValue(doc, registry));
108
+ }
109
+ setupDeclared(scenario, document, registry) {
110
+ // A refusal (DeclarationError) propagates to the runner (§7.7.9).
111
+ this.startSession(scenario, emitter_1.InterceptionEmitter.fromDeclarationValue(document, registry));
112
+ }
61
113
  async run() {
62
114
  const s = this.scenario;
63
115
  const em = this.emitter;
@@ -1,20 +1,40 @@
1
- /**
2
- * CTK runner: load vectors, drive a harness, assert `expect`.
3
- *
4
- * The assertion engine, capability skip check, and scripted
5
- * interceptor/resolver evaluation live in the Rust core (native.ctk*).
6
- * This module keeps only vector globbing, the recording wrapper, and
7
- * the orchestration loop that calls the native `Harness`.
8
- */
9
- import { JsonValue } from "../index";
1
+ import { AgentContext, DeclarationBuilder, HostRegistry, JsonValue } from "../index";
10
2
  import type { Harness } from "./index";
11
3
  export interface VectorResult {
12
4
  id: string;
13
5
  title: string;
6
+ part?: string;
14
7
  status: "pass" | "fail" | "skip";
15
8
  detail: string;
16
9
  failures: string[];
17
10
  }
18
11
  export declare function loadVectors(dir: string): JsonValue[];
19
- export declare function runVector(harness: Harness, vector: JsonValue): Promise<VectorResult>;
12
+ /** §9 redaction seam, CTK convention: each listed path is replaced
13
+ * with "[redacted]" via the §5.2/§4.3 transform machinery; a path that
14
+ * does not resolve at the escalating point is left untouched. */
15
+ export declare function redactPaths(ctx: AgentContext, paths: readonly string[]): AgentContext;
16
+ /** Rebuild a document through {@link DeclarationBuilder}, member by
17
+ * member (§7.7.7, the code path). Members the typed setters cannot
18
+ * express exactly (an unknown member, a wrong type) go through
19
+ * {@link DeclarationBuilder.raw}, so the result is validated like the
20
+ * file it came from. Mirrors the Rust runner's `builder_from_value`. */
21
+ export declare function builderFromValue(doc: JsonValue): DeclarationBuilder;
22
+ /** Resolve `doc` through the four construction paths (§7.7.7) and
23
+ * compare: value, JSON text, a temporary file and the builder. Equal
24
+ * canonical forms, or equal refusal classes, prove the paths
25
+ * equivalent. Returns `[equivalent, detail]`. */
26
+ export declare function provePaths(doc: JsonValue, registry: HostRegistry, tag: string): Promise<[boolean, string]>;
27
+ /** What a run assesses the harness against: its capabilities and
28
+ * posture, read from its resolved declaration when it ships one
29
+ * (§7.7.9) and from the Harness fields otherwise. */
30
+ export interface AssessedSurface {
31
+ capabilities: string[];
32
+ posture: string;
33
+ }
34
+ /** Resolve the harness's own declaration against its code surface.
35
+ * Throws {@link DeclarationError} on refusal. {@link runVectors} calls
36
+ * this once before the first vector, as §7.7.9 asks; {@link runVector}
37
+ * calls it when no resolved surface is handed in. */
38
+ export declare function assessHarness(harness: Harness): AssessedSurface;
39
+ export declare function runVector(harness: Harness, vector: JsonValue, assessed?: AssessedSurface): Promise<VectorResult>;
20
40
  export declare function runVectors(harnessFactory: () => Harness, vectors: JsonValue[]): Promise<VectorResult[]>;