@responsibleai/agent-hooks 0.1.0-alpha.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 +47 -0
- package/agent-hooks.linux-x64-gnu.node +0 -0
- package/binding.d.ts +18 -0
- package/binding.js +327 -0
- package/dist/builder.d.ts +37 -0
- package/dist/builder.js +94 -0
- package/dist/ctk/index.d.ts +70 -0
- package/dist/ctk/index.js +18 -0
- package/dist/ctk/reference.d.ts +20 -0
- package/dist/ctk/reference.js +124 -0
- package/dist/ctk/runner.d.ts +26 -0
- package/dist/ctk/runner.js +144 -0
- package/dist/emitter.d.ts +111 -0
- package/dist/emitter.js +615 -0
- package/dist/index.d.ts +328 -0
- package/dist/index.js +273 -0
- package/dist/native.d.ts +28 -0
- package/dist/native.js +56 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# agent-hooks (TypeScript SDK)
|
|
2
|
+
|
|
3
|
+
TypeScript implementation of
|
|
4
|
+
[AGENT-HOOKS-0.1](https://github.com/responsibleai/agent-hooks/blob/main/spec/AGENT-HOOKS-0.1.md)
|
|
5
|
+
over the canonical Rust core (napi-rs native module): interception
|
|
6
|
+
points, `AgentContextBuilder`, `Verdict` types, host-side
|
|
7
|
+
`InterceptionEmitter` with the four composition profiles, the
|
|
8
|
+
identity-provider seam, and the CTK runner.
|
|
9
|
+
|
|
10
|
+
> **Trust model.** agent-hooks is a *cooperative contract*, not a security
|
|
11
|
+
> boundary: the host framework is fully trusted, interceptors run in-process
|
|
12
|
+
> with full data access, and no complete-mediation claim is made. Read
|
|
13
|
+
> [SECURITY.md](https://github.com/responsibleai/agent-hooks/blob/main/SECURITY.md)
|
|
14
|
+
> and [spec §1.4](https://github.com/responsibleai/agent-hooks/blob/main/spec/AGENT-HOOKS-0.1.md#14-trust-model-and-non-goals)
|
|
15
|
+
> before relying on it.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Not yet published to npm — install from source:
|
|
19
|
+
git clone https://github.com/responsibleai/agent-hooks && cd agent-hooks/sdk/typescript
|
|
20
|
+
npm install && npm run build # builds the native module (needs a Rust toolchain)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { AgentContextBuilder, InterceptionEmitter, Verdict } from "@responsibleai/agent-hooks";
|
|
27
|
+
|
|
28
|
+
const emitter = new InterceptionEmitter();
|
|
29
|
+
emitter.register({
|
|
30
|
+
intercept(ctx) {
|
|
31
|
+
if (ctx.interception_point === "pre_tool_call" && ctx.tool_call.name === "rm") {
|
|
32
|
+
return { decision: "deny", reason: "dangerous" };
|
|
33
|
+
}
|
|
34
|
+
return { decision: "allow" };
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const builder = new AgentContextBuilder({ agentId: "my-agent", framework: "my-fw", sessionId: "s-1" });
|
|
39
|
+
const ctx = builder.preToolCall("tc-1", "http_get", { url });
|
|
40
|
+
await emitter.emit(ctx); // throws InterceptionBlocked on a combined deny
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**JavaScript value-domain caveat (spec §4.4):** `JSON.parse` rounds
|
|
44
|
+
integers beyond 2^53 before any guard can run, so this SDK cannot claim
|
|
45
|
+
the `int64_json`/`bigint_json` CTK capabilities — string-encode
|
|
46
|
+
64-bit identifiers at the adapter boundary. Non-finite numbers are
|
|
47
|
+
rejected fail-closed by a pre-serialization scan.
|
|
Binary file
|
package/binding.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/* auto-generated by NAPI-RS */
|
|
5
|
+
|
|
6
|
+
export declare function specVersion(): string
|
|
7
|
+
export declare function canonicalJson(valueJson: string): string
|
|
8
|
+
export declare function contextIdentity(ctxJson: string): string
|
|
9
|
+
export declare function validateVerdict(verdictJson: string): string
|
|
10
|
+
export declare function applyTransform(targetJson: string, path: string, valueJson: string): string
|
|
11
|
+
export declare function applyTransformCtx(ctxJson: string, path: string, valueJson: string): string
|
|
12
|
+
export declare function validateTransformCtx(ctxJson: string, path: string, valueJson: string): string
|
|
13
|
+
export declare function finalize(ctxJson: string, verdictJson: string, mode: string, optionsJson: string): string
|
|
14
|
+
export declare function composeAggregate(compositionJson: string, verdictsJson: string): string
|
|
15
|
+
export declare function ctkScriptedIntercept(rulesJson: string, ctxJson: string): string
|
|
16
|
+
export declare function ctkScriptedResolve(rulesJson: string, ctxJson: string, identity: string): string
|
|
17
|
+
export declare function ctkShouldSkip(vectorJson: string, harnessCapsJson: string): string
|
|
18
|
+
export declare function ctkAssert(vectorJson: string, recordedJson: string, runRecordJson: string): string
|
package/binding.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/* prettier-ignore */
|
|
4
|
+
|
|
5
|
+
/* auto-generated by NAPI-RS */
|
|
6
|
+
|
|
7
|
+
const { existsSync, readFileSync } = require('fs')
|
|
8
|
+
const { join } = require('path')
|
|
9
|
+
|
|
10
|
+
const { platform, arch } = process
|
|
11
|
+
|
|
12
|
+
let nativeBinding = null
|
|
13
|
+
let localFileExisted = false
|
|
14
|
+
let loadError = null
|
|
15
|
+
|
|
16
|
+
function isMusl() {
|
|
17
|
+
// For Node 10
|
|
18
|
+
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
19
|
+
try {
|
|
20
|
+
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
21
|
+
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const { glibcVersionRuntime } = process.report.getReport().header
|
|
27
|
+
return !glibcVersionRuntime
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
switch (platform) {
|
|
32
|
+
case 'android':
|
|
33
|
+
switch (arch) {
|
|
34
|
+
case 'arm64':
|
|
35
|
+
localFileExisted = existsSync(join(__dirname, 'agent-hooks.android-arm64.node'))
|
|
36
|
+
try {
|
|
37
|
+
if (localFileExisted) {
|
|
38
|
+
nativeBinding = require('./agent-hooks.android-arm64.node')
|
|
39
|
+
} else {
|
|
40
|
+
nativeBinding = require('@responsibleai/agent-hooks-android-arm64')
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
loadError = e
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
case 'arm':
|
|
47
|
+
localFileExisted = existsSync(join(__dirname, 'agent-hooks.android-arm-eabi.node'))
|
|
48
|
+
try {
|
|
49
|
+
if (localFileExisted) {
|
|
50
|
+
nativeBinding = require('./agent-hooks.android-arm-eabi.node')
|
|
51
|
+
} else {
|
|
52
|
+
nativeBinding = require('@responsibleai/agent-hooks-android-arm-eabi')
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
loadError = e
|
|
56
|
+
}
|
|
57
|
+
break
|
|
58
|
+
default:
|
|
59
|
+
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
60
|
+
}
|
|
61
|
+
break
|
|
62
|
+
case 'win32':
|
|
63
|
+
switch (arch) {
|
|
64
|
+
case 'x64':
|
|
65
|
+
localFileExisted = existsSync(
|
|
66
|
+
join(__dirname, 'agent-hooks.win32-x64-msvc.node')
|
|
67
|
+
)
|
|
68
|
+
try {
|
|
69
|
+
if (localFileExisted) {
|
|
70
|
+
nativeBinding = require('./agent-hooks.win32-x64-msvc.node')
|
|
71
|
+
} else {
|
|
72
|
+
nativeBinding = require('@responsibleai/agent-hooks-win32-x64-msvc')
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
loadError = e
|
|
76
|
+
}
|
|
77
|
+
break
|
|
78
|
+
case 'ia32':
|
|
79
|
+
localFileExisted = existsSync(
|
|
80
|
+
join(__dirname, 'agent-hooks.win32-ia32-msvc.node')
|
|
81
|
+
)
|
|
82
|
+
try {
|
|
83
|
+
if (localFileExisted) {
|
|
84
|
+
nativeBinding = require('./agent-hooks.win32-ia32-msvc.node')
|
|
85
|
+
} else {
|
|
86
|
+
nativeBinding = require('@responsibleai/agent-hooks-win32-ia32-msvc')
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
loadError = e
|
|
90
|
+
}
|
|
91
|
+
break
|
|
92
|
+
case 'arm64':
|
|
93
|
+
localFileExisted = existsSync(
|
|
94
|
+
join(__dirname, 'agent-hooks.win32-arm64-msvc.node')
|
|
95
|
+
)
|
|
96
|
+
try {
|
|
97
|
+
if (localFileExisted) {
|
|
98
|
+
nativeBinding = require('./agent-hooks.win32-arm64-msvc.node')
|
|
99
|
+
} else {
|
|
100
|
+
nativeBinding = require('@responsibleai/agent-hooks-win32-arm64-msvc')
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
loadError = e
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
case 'darwin':
|
|
111
|
+
localFileExisted = existsSync(join(__dirname, 'agent-hooks.darwin-universal.node'))
|
|
112
|
+
try {
|
|
113
|
+
if (localFileExisted) {
|
|
114
|
+
nativeBinding = require('./agent-hooks.darwin-universal.node')
|
|
115
|
+
} else {
|
|
116
|
+
nativeBinding = require('@responsibleai/agent-hooks-darwin-universal')
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
} catch {}
|
|
120
|
+
switch (arch) {
|
|
121
|
+
case 'x64':
|
|
122
|
+
localFileExisted = existsSync(join(__dirname, 'agent-hooks.darwin-x64.node'))
|
|
123
|
+
try {
|
|
124
|
+
if (localFileExisted) {
|
|
125
|
+
nativeBinding = require('./agent-hooks.darwin-x64.node')
|
|
126
|
+
} else {
|
|
127
|
+
nativeBinding = require('@responsibleai/agent-hooks-darwin-x64')
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
loadError = e
|
|
131
|
+
}
|
|
132
|
+
break
|
|
133
|
+
case 'arm64':
|
|
134
|
+
localFileExisted = existsSync(
|
|
135
|
+
join(__dirname, 'agent-hooks.darwin-arm64.node')
|
|
136
|
+
)
|
|
137
|
+
try {
|
|
138
|
+
if (localFileExisted) {
|
|
139
|
+
nativeBinding = require('./agent-hooks.darwin-arm64.node')
|
|
140
|
+
} else {
|
|
141
|
+
nativeBinding = require('@responsibleai/agent-hooks-darwin-arm64')
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
loadError = e
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
case 'freebsd':
|
|
152
|
+
if (arch !== 'x64') {
|
|
153
|
+
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
|
154
|
+
}
|
|
155
|
+
localFileExisted = existsSync(join(__dirname, 'agent-hooks.freebsd-x64.node'))
|
|
156
|
+
try {
|
|
157
|
+
if (localFileExisted) {
|
|
158
|
+
nativeBinding = require('./agent-hooks.freebsd-x64.node')
|
|
159
|
+
} else {
|
|
160
|
+
nativeBinding = require('@responsibleai/agent-hooks-freebsd-x64')
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
loadError = e
|
|
164
|
+
}
|
|
165
|
+
break
|
|
166
|
+
case 'linux':
|
|
167
|
+
switch (arch) {
|
|
168
|
+
case 'x64':
|
|
169
|
+
if (isMusl()) {
|
|
170
|
+
localFileExisted = existsSync(
|
|
171
|
+
join(__dirname, 'agent-hooks.linux-x64-musl.node')
|
|
172
|
+
)
|
|
173
|
+
try {
|
|
174
|
+
if (localFileExisted) {
|
|
175
|
+
nativeBinding = require('./agent-hooks.linux-x64-musl.node')
|
|
176
|
+
} else {
|
|
177
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-x64-musl')
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
loadError = e
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
localFileExisted = existsSync(
|
|
184
|
+
join(__dirname, 'agent-hooks.linux-x64-gnu.node')
|
|
185
|
+
)
|
|
186
|
+
try {
|
|
187
|
+
if (localFileExisted) {
|
|
188
|
+
nativeBinding = require('./agent-hooks.linux-x64-gnu.node')
|
|
189
|
+
} else {
|
|
190
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-x64-gnu')
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
loadError = e
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
case 'arm64':
|
|
198
|
+
if (isMusl()) {
|
|
199
|
+
localFileExisted = existsSync(
|
|
200
|
+
join(__dirname, 'agent-hooks.linux-arm64-musl.node')
|
|
201
|
+
)
|
|
202
|
+
try {
|
|
203
|
+
if (localFileExisted) {
|
|
204
|
+
nativeBinding = require('./agent-hooks.linux-arm64-musl.node')
|
|
205
|
+
} else {
|
|
206
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-arm64-musl')
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
loadError = e
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
localFileExisted = existsSync(
|
|
213
|
+
join(__dirname, 'agent-hooks.linux-arm64-gnu.node')
|
|
214
|
+
)
|
|
215
|
+
try {
|
|
216
|
+
if (localFileExisted) {
|
|
217
|
+
nativeBinding = require('./agent-hooks.linux-arm64-gnu.node')
|
|
218
|
+
} else {
|
|
219
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-arm64-gnu')
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
loadError = e
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
case 'arm':
|
|
227
|
+
if (isMusl()) {
|
|
228
|
+
localFileExisted = existsSync(
|
|
229
|
+
join(__dirname, 'agent-hooks.linux-arm-musleabihf.node')
|
|
230
|
+
)
|
|
231
|
+
try {
|
|
232
|
+
if (localFileExisted) {
|
|
233
|
+
nativeBinding = require('./agent-hooks.linux-arm-musleabihf.node')
|
|
234
|
+
} else {
|
|
235
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-arm-musleabihf')
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
loadError = e
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
localFileExisted = existsSync(
|
|
242
|
+
join(__dirname, 'agent-hooks.linux-arm-gnueabihf.node')
|
|
243
|
+
)
|
|
244
|
+
try {
|
|
245
|
+
if (localFileExisted) {
|
|
246
|
+
nativeBinding = require('./agent-hooks.linux-arm-gnueabihf.node')
|
|
247
|
+
} else {
|
|
248
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-arm-gnueabihf')
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
loadError = e
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
case 'riscv64':
|
|
256
|
+
if (isMusl()) {
|
|
257
|
+
localFileExisted = existsSync(
|
|
258
|
+
join(__dirname, 'agent-hooks.linux-riscv64-musl.node')
|
|
259
|
+
)
|
|
260
|
+
try {
|
|
261
|
+
if (localFileExisted) {
|
|
262
|
+
nativeBinding = require('./agent-hooks.linux-riscv64-musl.node')
|
|
263
|
+
} else {
|
|
264
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-riscv64-musl')
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
loadError = e
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
localFileExisted = existsSync(
|
|
271
|
+
join(__dirname, 'agent-hooks.linux-riscv64-gnu.node')
|
|
272
|
+
)
|
|
273
|
+
try {
|
|
274
|
+
if (localFileExisted) {
|
|
275
|
+
nativeBinding = require('./agent-hooks.linux-riscv64-gnu.node')
|
|
276
|
+
} else {
|
|
277
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-riscv64-gnu')
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
loadError = e
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
case 's390x':
|
|
285
|
+
localFileExisted = existsSync(
|
|
286
|
+
join(__dirname, 'agent-hooks.linux-s390x-gnu.node')
|
|
287
|
+
)
|
|
288
|
+
try {
|
|
289
|
+
if (localFileExisted) {
|
|
290
|
+
nativeBinding = require('./agent-hooks.linux-s390x-gnu.node')
|
|
291
|
+
} else {
|
|
292
|
+
nativeBinding = require('@responsibleai/agent-hooks-linux-s390x-gnu')
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
loadError = e
|
|
296
|
+
}
|
|
297
|
+
break
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
|
300
|
+
}
|
|
301
|
+
break
|
|
302
|
+
default:
|
|
303
|
+
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!nativeBinding) {
|
|
307
|
+
if (loadError) {
|
|
308
|
+
throw loadError
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Failed to load native binding`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { specVersion, canonicalJson, contextIdentity, validateVerdict, applyTransform, applyTransformCtx, validateTransformCtx, finalize, composeAggregate, ctkScriptedIntercept, ctkScriptedResolve, ctkShouldSkip, ctkAssert } = nativeBinding
|
|
314
|
+
|
|
315
|
+
module.exports.specVersion = specVersion
|
|
316
|
+
module.exports.canonicalJson = canonicalJson
|
|
317
|
+
module.exports.contextIdentity = contextIdentity
|
|
318
|
+
module.exports.validateVerdict = validateVerdict
|
|
319
|
+
module.exports.applyTransform = applyTransform
|
|
320
|
+
module.exports.applyTransformCtx = applyTransformCtx
|
|
321
|
+
module.exports.validateTransformCtx = validateTransformCtx
|
|
322
|
+
module.exports.finalize = finalize
|
|
323
|
+
module.exports.composeAggregate = composeAggregate
|
|
324
|
+
module.exports.ctkScriptedIntercept = ctkScriptedIntercept
|
|
325
|
+
module.exports.ctkScriptedResolve = ctkScriptedResolve
|
|
326
|
+
module.exports.ctkShouldSkip = ctkShouldSkip
|
|
327
|
+
module.exports.ctkAssert = ctkAssert
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `AgentContext` construction (§4).
|
|
3
|
+
*
|
|
4
|
+
* Stateful per-session builder that owns the L0 envelope
|
|
5
|
+
* (agent/session/sequence) and exposes one method per interception
|
|
6
|
+
* point that fills L1 and sets `target`.
|
|
7
|
+
*/
|
|
8
|
+
import { AgentContext, JsonValue } from "./index";
|
|
9
|
+
export declare class AgentContextBuilder {
|
|
10
|
+
private seq;
|
|
11
|
+
private readonly agent;
|
|
12
|
+
private readonly session;
|
|
13
|
+
constructor(opts: {
|
|
14
|
+
agentId: string;
|
|
15
|
+
framework: string;
|
|
16
|
+
sessionId: string;
|
|
17
|
+
agentName?: string;
|
|
18
|
+
agentVersion?: string;
|
|
19
|
+
sessionStartedAt?: string;
|
|
20
|
+
});
|
|
21
|
+
private envelope;
|
|
22
|
+
agentStartup(toolsRegistered: string[]): AgentContext;
|
|
23
|
+
input(content: JsonValue, role?: "user" | "system" | "external"): AgentContext;
|
|
24
|
+
preModelCall(modelId: string, messages: Array<{
|
|
25
|
+
role: string;
|
|
26
|
+
content: JsonValue;
|
|
27
|
+
}>): AgentContext;
|
|
28
|
+
postModelCall(modelId: string, content: JsonValue, toolCalls: Array<{
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
args: Record<string, JsonValue>;
|
|
32
|
+
}>, finishReason: string): AgentContext;
|
|
33
|
+
preToolCall(callId: string, name: string, args: Record<string, JsonValue>): AgentContext;
|
|
34
|
+
postToolCall(callId: string, name: string, args: Record<string, JsonValue>, value: JsonValue, isError?: boolean): AgentContext;
|
|
35
|
+
output(content: JsonValue): AgentContext;
|
|
36
|
+
agentShutdown(reason: "completed" | "error" | "cancelled"): AgentContext;
|
|
37
|
+
}
|
package/dist/builder.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
/**
|
|
5
|
+
* `AgentContext` construction (§4).
|
|
6
|
+
*
|
|
7
|
+
* Stateful per-session builder that owns the L0 envelope
|
|
8
|
+
* (agent/session/sequence) and exposes one method per interception
|
|
9
|
+
* point that fills L1 and sets `target`.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AgentContextBuilder = void 0;
|
|
13
|
+
const index_1 = require("./index");
|
|
14
|
+
function now() {
|
|
15
|
+
return new Date().toISOString();
|
|
16
|
+
}
|
|
17
|
+
class AgentContextBuilder {
|
|
18
|
+
seq = 0;
|
|
19
|
+
agent;
|
|
20
|
+
session;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
this.agent = { id: opts.agentId, framework: opts.framework };
|
|
23
|
+
if (opts.agentName)
|
|
24
|
+
this.agent.name = opts.agentName;
|
|
25
|
+
if (opts.agentVersion)
|
|
26
|
+
this.agent.version = opts.agentVersion;
|
|
27
|
+
this.session = { id: opts.sessionId };
|
|
28
|
+
if (opts.sessionStartedAt)
|
|
29
|
+
this.session.started_at = opts.sessionStartedAt;
|
|
30
|
+
}
|
|
31
|
+
envelope(ip, target) {
|
|
32
|
+
const ctx = {
|
|
33
|
+
spec: index_1.SPEC_VERSION,
|
|
34
|
+
interception_point: ip,
|
|
35
|
+
timestamp: now(),
|
|
36
|
+
sequence: this.seq++,
|
|
37
|
+
agent: { ...this.agent },
|
|
38
|
+
session: { ...this.session },
|
|
39
|
+
target,
|
|
40
|
+
};
|
|
41
|
+
return ctx;
|
|
42
|
+
}
|
|
43
|
+
agentStartup(toolsRegistered) {
|
|
44
|
+
const agentInit = { tools_registered: [...toolsRegistered] };
|
|
45
|
+
const ctx = this.envelope(index_1.InterceptionPoint.AgentStartup, agentInit);
|
|
46
|
+
ctx.agent_init = agentInit;
|
|
47
|
+
return ctx;
|
|
48
|
+
}
|
|
49
|
+
input(content, role = "user") {
|
|
50
|
+
const inp = { content, role };
|
|
51
|
+
const ctx = this.envelope(index_1.InterceptionPoint.Input, inp);
|
|
52
|
+
ctx.input = inp;
|
|
53
|
+
return ctx;
|
|
54
|
+
}
|
|
55
|
+
preModelCall(modelId, messages) {
|
|
56
|
+
const ctx = this.envelope(index_1.InterceptionPoint.PreModelCall, messages);
|
|
57
|
+
ctx.model = { id: modelId };
|
|
58
|
+
ctx.messages = messages;
|
|
59
|
+
return ctx;
|
|
60
|
+
}
|
|
61
|
+
postModelCall(modelId, content, toolCalls, finishReason) {
|
|
62
|
+
const response = { content, tool_calls: toolCalls, finish_reason: finishReason };
|
|
63
|
+
const ctx = this.envelope(index_1.InterceptionPoint.PostModelCall, response);
|
|
64
|
+
ctx.model = { id: modelId };
|
|
65
|
+
ctx.response = response;
|
|
66
|
+
return ctx;
|
|
67
|
+
}
|
|
68
|
+
preToolCall(callId, name, args) {
|
|
69
|
+
const tc = { id: callId, name, args };
|
|
70
|
+
const ctx = this.envelope(index_1.InterceptionPoint.PreToolCall, args);
|
|
71
|
+
ctx.tool_call = tc;
|
|
72
|
+
return ctx;
|
|
73
|
+
}
|
|
74
|
+
postToolCall(callId, name, args, value, isError = false) {
|
|
75
|
+
const tr = { value, is_error: isError };
|
|
76
|
+
const ctx = this.envelope(index_1.InterceptionPoint.PostToolCall, value);
|
|
77
|
+
ctx.tool_call = { id: callId, name, args };
|
|
78
|
+
ctx.tool_result = tr;
|
|
79
|
+
return ctx;
|
|
80
|
+
}
|
|
81
|
+
output(content) {
|
|
82
|
+
const out = { content };
|
|
83
|
+
const ctx = this.envelope(index_1.InterceptionPoint.Output, out);
|
|
84
|
+
ctx.output = out;
|
|
85
|
+
return ctx;
|
|
86
|
+
}
|
|
87
|
+
agentShutdown(reason) {
|
|
88
|
+
const summary = { reason };
|
|
89
|
+
const ctx = this.envelope(index_1.InterceptionPoint.AgentShutdown, summary);
|
|
90
|
+
ctx.summary = summary;
|
|
91
|
+
return ctx;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.AgentContextBuilder = AgentContextBuilder;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conformance Test Kit (§13.2).
|
|
3
|
+
*
|
|
4
|
+
* The assertion engine and scripted interceptor/resolver live in the
|
|
5
|
+
* Rust core; this module defines the `Harness` interface framework
|
|
6
|
+
* adapters implement, plus the runner and reference harness that use it.
|
|
7
|
+
*/
|
|
8
|
+
import type { ApprovalResolver, CompositionConfig, EnforcementMode, Interceptor, JsonValue } from "../index";
|
|
9
|
+
export { loadVectors, runVector, runVectors, VectorResult } from "./runner";
|
|
10
|
+
export { ReferenceHarness } from "./reference";
|
|
11
|
+
/** Host-declared capability subset (§3.2). */
|
|
12
|
+
export type Capability = "model_calls" | "tool_calls" | "parallel_tool_calls" | "streaming" | "multi_turn";
|
|
13
|
+
export type RunOutcome = "completed" | "blocked" | "suspended" | "error";
|
|
14
|
+
/** Hermetic scripted run loaded from a CTK vector (wire-shaped). */
|
|
15
|
+
export interface Scenario {
|
|
16
|
+
input: {
|
|
17
|
+
content: JsonValue;
|
|
18
|
+
role: "user" | "system" | "external";
|
|
19
|
+
};
|
|
20
|
+
tools?: Array<{
|
|
21
|
+
name: string;
|
|
22
|
+
schema?: Record<string, JsonValue>;
|
|
23
|
+
behavior: Array<{
|
|
24
|
+
when_args?: Record<string, JsonValue>;
|
|
25
|
+
return: JsonValue;
|
|
26
|
+
is_error?: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
}>;
|
|
29
|
+
model_script?: Array<{
|
|
30
|
+
respond: {
|
|
31
|
+
content: JsonValue;
|
|
32
|
+
tool_calls: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
args: Record<string, JsonValue>;
|
|
36
|
+
}>;
|
|
37
|
+
finish_reason: string;
|
|
38
|
+
};
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
/** What `Harness.run` returns to the CTK runner. */
|
|
42
|
+
export interface RunRecord {
|
|
43
|
+
outcome: RunOutcome;
|
|
44
|
+
final_output: JsonValue | null;
|
|
45
|
+
tool_invocations: Array<{
|
|
46
|
+
name: string;
|
|
47
|
+
args: Record<string, JsonValue>;
|
|
48
|
+
}>;
|
|
49
|
+
error?: string;
|
|
50
|
+
/** `(input_identity, enforced_identity)` per interception, in order,
|
|
51
|
+
* from the harness's emitter (`null` under a `null` identity
|
|
52
|
+
* provider, §10.1). Enables `expect.identities_equal`. */
|
|
53
|
+
identities: Array<[string | null, string | null]>;
|
|
54
|
+
/** Wire-shaped `InterceptionRecord`s (§10.3), one per emission, in
|
|
55
|
+
* order. Enables `expect.records` assertions. */
|
|
56
|
+
records: JsonValue[];
|
|
57
|
+
}
|
|
58
|
+
/** The single interface a framework adapter implements for the CTK. */
|
|
59
|
+
export interface Harness {
|
|
60
|
+
readonly name: string;
|
|
61
|
+
readonly capabilities: ReadonlySet<Capability>;
|
|
62
|
+
/** Wire the scenario's mock model + tools into the framework,
|
|
63
|
+
* register the interceptors and resolver, set the enforcement mode,
|
|
64
|
+
* the vector's composition profile (§7.1), and its identity provider
|
|
65
|
+
* (§10.1; vectors declare `"jcs-sha256"` or `null` — custom providers
|
|
66
|
+
* are functions and not vector-expressible). */
|
|
67
|
+
setup(scenario: Scenario, interceptors: Interceptor[], resolver: ApprovalResolver | null, mode: EnforcementMode, composition: CompositionConfig, identityProvider: 'jcs-sha256' | null): void;
|
|
68
|
+
run(): Promise<RunRecord>;
|
|
69
|
+
teardown(): void;
|
|
70
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
/**
|
|
5
|
+
* Conformance Test Kit (§13.2).
|
|
6
|
+
*
|
|
7
|
+
* The assertion engine and scripted interceptor/resolver live in the
|
|
8
|
+
* Rust core; this module defines the `Harness` interface framework
|
|
9
|
+
* adapters implement, plus the runner and reference harness that use it.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ReferenceHarness = exports.runVectors = exports.runVector = exports.loadVectors = void 0;
|
|
13
|
+
var runner_1 = require("./runner");
|
|
14
|
+
Object.defineProperty(exports, "loadVectors", { enumerable: true, get: function () { return runner_1.loadVectors; } });
|
|
15
|
+
Object.defineProperty(exports, "runVector", { enumerable: true, get: function () { return runner_1.runVector; } });
|
|
16
|
+
Object.defineProperty(exports, "runVectors", { enumerable: true, get: function () { return runner_1.runVectors; } });
|
|
17
|
+
var reference_1 = require("./reference");
|
|
18
|
+
Object.defineProperty(exports, "ReferenceHarness", { enumerable: true, get: function () { return reference_1.ReferenceHarness; } });
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference in-memory agent + harness.
|
|
3
|
+
*
|
|
4
|
+
* The simplest possible conformant agent loop; exists so the
|
|
5
|
+
* CTK can self-test without a real framework. Port of
|
|
6
|
+
* `sdk/python/python/agent_hooks/ctk/reference.py`.
|
|
7
|
+
*/
|
|
8
|
+
import { ApprovalResolver, CompositionConfig, EnforcementMode, Interceptor } from "../index";
|
|
9
|
+
import type { Capability, Harness, RunRecord, Scenario } from "./index";
|
|
10
|
+
export declare class ReferenceHarness implements Harness {
|
|
11
|
+
readonly name = "reference-agent";
|
|
12
|
+
readonly capabilities: ReadonlySet<Capability>;
|
|
13
|
+
private scenario;
|
|
14
|
+
private emitter;
|
|
15
|
+
private builder;
|
|
16
|
+
private toolLog;
|
|
17
|
+
setup(scenario: Scenario, interceptors: Interceptor[], resolver: ApprovalResolver | null, mode: EnforcementMode, composition: CompositionConfig, identityProvider: 'jcs-sha256' | null): void;
|
|
18
|
+
run(): Promise<RunRecord>;
|
|
19
|
+
teardown(): void;
|
|
20
|
+
}
|