@nifrajs/agent-protocol 4.0.2 → 5.0.0
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/dist/conformance.d.ts +41 -0
- package/dist/conformance.d.ts.map +1 -0
- package/dist/conformance.js +393 -0
- package/dist/conformance.js.map +1 -0
- package/dist/index.d.ts +97 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +348 -92
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/src/conformance.ts +490 -0
- package/src/index.ts +467 -94
package/README.md
CHANGED
|
@@ -13,10 +13,57 @@ const events = createAgentEventStream(256)
|
|
|
13
13
|
events.push(/* a versioned AgentEvent */)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
`isAgentEvent` refuses an event past the protocol's bounds: 256 KiB characters of `text`, `prompt`, or
|
|
17
|
+
error message, and 64 KiB per string inside `input`, `output`, or `details`. A backend keeps its
|
|
18
|
+
events inside them with `splitAgentText` (a long reply as consecutive events that join back),
|
|
19
|
+
`boundAgentText` (one field, cut with a marker), and `boundAgentValue` (a display copy of a tool's
|
|
20
|
+
input or output; the tool itself still gets the whole value). `agentEventProblem` says why an event
|
|
21
|
+
was refused, naming the field and the limit but never its content.
|
|
22
|
+
|
|
23
|
+
Three events carry what most coding agents report besides their reply: `reasoning.delta` (a piece
|
|
24
|
+
of the model's reasoning, grouped by an optional `itemId`), `plan.updated` (the whole current plan,
|
|
25
|
+
entries of `content`, `status`, and optional `priority`, each event replacing the last), and
|
|
26
|
+
`context.usage` (`usedTokens` of the latest request, the model's `windowTokens` when known, and
|
|
27
|
+
optional input, output, cached, and reasoning counts).
|
|
28
|
+
|
|
29
|
+
Any other frame a backend has of its own travels as an extension event:
|
|
30
|
+
`type` is `x-<vendor>.<name>` (lowercase, at most 64 characters), with an optional `turnId` and a
|
|
31
|
+
`data` value inside the same payload bounds. Hosts that do not know the type pass it through; a host
|
|
32
|
+
that does can register a check for it.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
yield { version: 1, sessionId, seq, at: Date.now(), type: "x-codex.plan", turnId, data: { steps } }
|
|
36
|
+
```
|
|
37
|
+
|
|
16
38
|
Use `@nifrajs/pi` for the Pi backend or implement `AgentBackend` for another
|
|
17
39
|
runtime. The protocol is intentionally small so CLI, desktop, mobile, and CI
|
|
18
40
|
clients can share it without pulling in the Nifra application framework.
|
|
19
41
|
|
|
42
|
+
## Conformance kit
|
|
43
|
+
|
|
44
|
+
`@nifrajs/agent-protocol/conformance` drives an `AgentBackend` through what a host relies on and
|
|
45
|
+
returns a report, so an adapter for another agent can prove itself in any test runner:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { checkAgentBackend } from "@nifrajs/agent-protocol/conformance"
|
|
49
|
+
|
|
50
|
+
const report = await checkAgentBackend({
|
|
51
|
+
backend: () => new MyBackend(),
|
|
52
|
+
cwd: process.cwd(),
|
|
53
|
+
prompt: "say hi",
|
|
54
|
+
slowPrompt: "wait until cancelled", // enables turn.busy and turn.cancel
|
|
55
|
+
approvalPrompt: "write a file", // enables the approval checks
|
|
56
|
+
})
|
|
57
|
+
expect(report.checks.filter((check) => check.status === "failed")).toEqual([])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Each scenario gets a fresh backend and session. The checks: a valid session snapshot; a turn whose
|
|
61
|
+
events are valid, ordered, of one turn, and end with exactly one terminal event; `session.completed`
|
|
62
|
+
for a plain turn and an idle snapshot after it; a second message refused while a turn runs, without
|
|
63
|
+
ending it; a cancel that ends with `session.stopped` and leaves the session usable; approvals allowed
|
|
64
|
+
and denied; `undefined` from `resolveApproval` for an approval it never asked for or whose turn was
|
|
65
|
+
cancelled; and no turn for a closed session.
|
|
66
|
+
|
|
20
67
|
## Version 1 compatibility
|
|
21
68
|
|
|
22
69
|
Version 1 is additive. Hosts advertise capabilities and clients use only the negotiated intersection;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A conformance kit for `AgentBackend` implementations: it drives a backend through the scenarios a
|
|
3
|
+
* host relies on and reports each check, so an adapter for a vendor's agent can prove it before a
|
|
4
|
+
* host ships it. It uses no test runner; assert on the returned report in whichever one you use.
|
|
5
|
+
*/
|
|
6
|
+
import { type AgentBackend } from "./index.js";
|
|
7
|
+
export interface AgentBackendConformanceOptions {
|
|
8
|
+
/** A fresh backend for each scenario. The kit closes every session it opens. */
|
|
9
|
+
readonly backend: () => AgentBackend | PromiseLike<AgentBackend>;
|
|
10
|
+
/** The working directory each session is created in. */
|
|
11
|
+
readonly cwd: string;
|
|
12
|
+
/** A message the backend answers with a turn that ends by itself. */
|
|
13
|
+
readonly prompt: string;
|
|
14
|
+
/** A message that makes the backend ask for an approval. Enables the approval checks. */
|
|
15
|
+
readonly approvalPrompt?: string;
|
|
16
|
+
/** A message whose turn keeps running until it is cancelled. Enables the busy and cancel checks. */
|
|
17
|
+
readonly slowPrompt?: string;
|
|
18
|
+
/** Longest one scenario may wait for the backend. Default 10 seconds. */
|
|
19
|
+
readonly timeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export type AgentConformanceStatus = "passed" | "failed" | "skipped";
|
|
22
|
+
export interface AgentConformanceCheck {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly status: AgentConformanceStatus;
|
|
25
|
+
/** What went wrong, or why the check was skipped. */
|
|
26
|
+
readonly detail?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentBackendConformanceReport {
|
|
29
|
+
readonly backend: string;
|
|
30
|
+
/** No check failed. Skipped checks do not count against it. */
|
|
31
|
+
readonly ok: boolean;
|
|
32
|
+
readonly checks: readonly AgentConformanceCheck[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Drive a backend through the scenarios a host depends on: a session's snapshot, a whole turn's
|
|
36
|
+
* events (valid, in order, one turn, one terminal event), a refused second message while busy,
|
|
37
|
+
* cancellation, approvals granted, denied, unknown and cancelled, and a closed session. Scenarios
|
|
38
|
+
* whose prompt is not given are reported as skipped.
|
|
39
|
+
*/
|
|
40
|
+
export declare function checkAgentBackend(options: AgentBackendConformanceOptions): Promise<AgentBackendConformanceReport>;
|
|
41
|
+
//# sourceMappingURL=conformance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../src/conformance.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,YAAY,EAAgD,MAAM,YAAY,CAAA;AAE5F,MAAM,WAAW,8BAA8B;IAC7C,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,YAAY,GAAG,WAAW,CAAC,YAAY,CAAC,CAAA;IAChE,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,yFAAyF;IACzF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,oGAAoG;IACpG,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEpE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAA;IACvC,qDAAqD;IACrD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAA;CAClD;AA2ID;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,6BAA6B,CAAC,CAkTxC"}
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A conformance kit for `AgentBackend` implementations: it drives a backend through the scenarios a
|
|
3
|
+
* host relies on and reports each check, so an adapter for a vendor's agent can prove it before a
|
|
4
|
+
* host ships it. It uses no test runner; assert on the returned report in whichever one you use.
|
|
5
|
+
*/
|
|
6
|
+
import { agentEventProblem } from "./index.js";
|
|
7
|
+
const TERMINAL = new Set([
|
|
8
|
+
"session.completed",
|
|
9
|
+
"session.failed",
|
|
10
|
+
"session.stopped",
|
|
11
|
+
]);
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
13
|
+
function isRecord(value) {
|
|
14
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
function recordOf(value) {
|
|
17
|
+
return isRecord(value) ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
function typeOf(event) {
|
|
20
|
+
return recordOf(event)?.type;
|
|
21
|
+
}
|
|
22
|
+
/** Reads a backend's stream one event at a time, never waiting past a deadline. */
|
|
23
|
+
class EventReader {
|
|
24
|
+
events = [];
|
|
25
|
+
error;
|
|
26
|
+
iterator;
|
|
27
|
+
pending;
|
|
28
|
+
finished = false;
|
|
29
|
+
constructor(open) {
|
|
30
|
+
try {
|
|
31
|
+
this.iterator = open()[Symbol.asyncIterator]();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
this.error = error;
|
|
35
|
+
this.finished = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async readUntil(until, deadline) {
|
|
39
|
+
for (;;) {
|
|
40
|
+
if (this.finished)
|
|
41
|
+
return this.error === undefined ? "ended" : "threw";
|
|
42
|
+
const iterator = this.iterator;
|
|
43
|
+
if (iterator === undefined)
|
|
44
|
+
return "ended";
|
|
45
|
+
this.pending ??= iterator.next();
|
|
46
|
+
const result = await settleBefore(this.pending, deadline);
|
|
47
|
+
if (result === TIMED_OUT)
|
|
48
|
+
return "timeout";
|
|
49
|
+
this.pending = undefined;
|
|
50
|
+
if (result.kind === "error") {
|
|
51
|
+
this.error = result.error ?? new Error("stream failed");
|
|
52
|
+
this.finished = true;
|
|
53
|
+
return "threw";
|
|
54
|
+
}
|
|
55
|
+
if (result.value.done === true) {
|
|
56
|
+
this.finished = true;
|
|
57
|
+
return "ended";
|
|
58
|
+
}
|
|
59
|
+
this.events.push(result.value.value);
|
|
60
|
+
if (until(result.value.value))
|
|
61
|
+
return "matched";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
readToEnd(deadline) {
|
|
65
|
+
return this.readUntil(() => false, deadline);
|
|
66
|
+
}
|
|
67
|
+
async stop() {
|
|
68
|
+
if (this.finished)
|
|
69
|
+
return;
|
|
70
|
+
this.finished = true;
|
|
71
|
+
const closing = Promise.resolve()
|
|
72
|
+
.then(() => this.iterator?.return?.())
|
|
73
|
+
.catch(() => undefined);
|
|
74
|
+
// A generator parked in an await cannot return until it resumes, so do not wait on that one.
|
|
75
|
+
if (this.pending === undefined)
|
|
76
|
+
await settleBefore(closing, Date.now() + 1_000);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const TIMED_OUT = Symbol("timed out");
|
|
80
|
+
function settleBefore(promise, deadline) {
|
|
81
|
+
let timer;
|
|
82
|
+
const timeout = new Promise((resolve) => {
|
|
83
|
+
timer = setTimeout(() => resolve(TIMED_OUT), Math.max(0, deadline - Date.now()));
|
|
84
|
+
});
|
|
85
|
+
const settled = promise.then((value) => ({ kind: "value", value }), (error) => ({ kind: "error", error }));
|
|
86
|
+
return Promise.race([settled, timeout]).finally(() => clearTimeout(timer));
|
|
87
|
+
}
|
|
88
|
+
function describe(error) {
|
|
89
|
+
if (error instanceof Error)
|
|
90
|
+
return error.message;
|
|
91
|
+
const message = recordOf(error)?.message;
|
|
92
|
+
return typeof message === "string" ? message : String(error);
|
|
93
|
+
}
|
|
94
|
+
/** The first way the events break the protocol, or undefined when they are a valid turn. */
|
|
95
|
+
function turnProblem(events, sessionId, expectEnd) {
|
|
96
|
+
let lastSeq = -1;
|
|
97
|
+
let turnId;
|
|
98
|
+
for (const [index, event] of events.entries()) {
|
|
99
|
+
const problem = agentEventProblem(event);
|
|
100
|
+
if (problem !== undefined || !isRecord(event))
|
|
101
|
+
return `event ${index}: ${problem ?? "not an object"}`;
|
|
102
|
+
const record = event;
|
|
103
|
+
if (record.sessionId !== sessionId)
|
|
104
|
+
return `event ${index} names another session`;
|
|
105
|
+
const seq = typeof record.seq === "number" ? record.seq : -1;
|
|
106
|
+
if (seq <= lastSeq)
|
|
107
|
+
return `event ${index} has seq ${seq} after ${lastSeq}`;
|
|
108
|
+
lastSeq = seq;
|
|
109
|
+
if (record.type === "turn.started") {
|
|
110
|
+
if (turnId !== undefined)
|
|
111
|
+
return `event ${index} starts a second turn`;
|
|
112
|
+
turnId = record.turnId;
|
|
113
|
+
}
|
|
114
|
+
else if (record.turnId !== undefined && record.turnId !== turnId) {
|
|
115
|
+
return turnId === undefined
|
|
116
|
+
? `event ${index} (${String(record.type)}) comes before turn.started`
|
|
117
|
+
: `event ${index} (${String(record.type)}) names another turn`;
|
|
118
|
+
}
|
|
119
|
+
if (TERMINAL.has(record.type) && index !== events.length - 1)
|
|
120
|
+
return `event ${index} (${String(record.type)}) ends the turn but more events follow`;
|
|
121
|
+
}
|
|
122
|
+
if (turnId === undefined)
|
|
123
|
+
return "no turn.started event";
|
|
124
|
+
if (expectEnd && !TERMINAL.has(typeOf(events.at(-1))))
|
|
125
|
+
return "the stream ended without session.completed, session.failed, or session.stopped";
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Drive a backend through the scenarios a host depends on: a session's snapshot, a whole turn's
|
|
130
|
+
* events (valid, in order, one turn, one terminal event), a refused second message while busy,
|
|
131
|
+
* cancellation, approvals granted, denied, unknown and cancelled, and a closed session. Scenarios
|
|
132
|
+
* whose prompt is not given are reported as skipped.
|
|
133
|
+
*/
|
|
134
|
+
export async function checkAgentBackend(options) {
|
|
135
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
136
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1)
|
|
137
|
+
throw new RangeError("checkAgentBackend: timeoutMs must be a positive integer");
|
|
138
|
+
const checks = [];
|
|
139
|
+
let backendName = "unknown";
|
|
140
|
+
const pass = (id) => {
|
|
141
|
+
checks.push({ id, status: "passed" });
|
|
142
|
+
};
|
|
143
|
+
const fail = (id, detail) => {
|
|
144
|
+
checks.push({ id, status: "failed", detail });
|
|
145
|
+
};
|
|
146
|
+
const skip = (id, detail) => {
|
|
147
|
+
checks.push({ id, status: "skipped", detail });
|
|
148
|
+
};
|
|
149
|
+
const scenario = async (ids, run) => {
|
|
150
|
+
let backend;
|
|
151
|
+
let session;
|
|
152
|
+
const before = checks.length;
|
|
153
|
+
try {
|
|
154
|
+
backend = await options.backend();
|
|
155
|
+
backendName = backend.info.name;
|
|
156
|
+
session = await backend.createSession({ cwd: options.cwd });
|
|
157
|
+
await run(backend, session, Date.now() + timeoutMs);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
const reported = new Set(checks.slice(before).map((check) => check.id));
|
|
161
|
+
for (const id of ids)
|
|
162
|
+
if (!reported.has(id))
|
|
163
|
+
fail(id, `the scenario threw: ${describe(error)}`);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
if (backend !== undefined && session !== undefined)
|
|
167
|
+
await settleBefore(backend.close(session.id), Date.now() + timeoutMs);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
await scenario(["session.create", "turn.events", "turn.completes", "turn.snapshot"], async (backend, session, deadline) => {
|
|
171
|
+
const problem = agentEventProblem({
|
|
172
|
+
version: 1,
|
|
173
|
+
sessionId: session.id,
|
|
174
|
+
seq: 0,
|
|
175
|
+
at: 0,
|
|
176
|
+
type: "session.started",
|
|
177
|
+
snapshot: session,
|
|
178
|
+
});
|
|
179
|
+
if (problem !== undefined)
|
|
180
|
+
fail("session.create", problem.replace("session.started.", ""));
|
|
181
|
+
else if (session.cwd !== options.cwd)
|
|
182
|
+
fail("session.create", "snapshot.cwd is not the cwd asked for");
|
|
183
|
+
else if (session.status === "running")
|
|
184
|
+
fail("session.create", "a new session is already running");
|
|
185
|
+
else
|
|
186
|
+
pass("session.create");
|
|
187
|
+
const reader = new EventReader(() => backend.send({ sessionId: session.id, message: options.prompt }));
|
|
188
|
+
const outcome = await reader.readToEnd(deadline);
|
|
189
|
+
if (outcome === "timeout") {
|
|
190
|
+
await reader.stop();
|
|
191
|
+
fail("turn.events", `the turn did not end within ${timeoutMs} ms`);
|
|
192
|
+
fail("turn.completes", "the turn did not end");
|
|
193
|
+
skip("turn.snapshot", "the turn did not end");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (outcome === "threw") {
|
|
197
|
+
fail("turn.events", `the stream threw: ${describe(reader.error)}`);
|
|
198
|
+
fail("turn.completes", "the stream threw");
|
|
199
|
+
skip("turn.snapshot", "the turn did not end");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const eventsProblem = turnProblem(reader.events, session.id, true);
|
|
203
|
+
if (eventsProblem === undefined)
|
|
204
|
+
pass("turn.events");
|
|
205
|
+
else
|
|
206
|
+
fail("turn.events", eventsProblem);
|
|
207
|
+
const last = typeOf(reader.events.at(-1));
|
|
208
|
+
if (last === "session.completed")
|
|
209
|
+
pass("turn.completes");
|
|
210
|
+
else
|
|
211
|
+
fail("turn.completes", `the turn ended with ${String(last ?? "no event")}, not session.completed`);
|
|
212
|
+
const after = await backend.snapshot(session.id);
|
|
213
|
+
if (after.id !== session.id)
|
|
214
|
+
fail("turn.snapshot", "snapshot names another session");
|
|
215
|
+
else if (after.status === "running")
|
|
216
|
+
fail("turn.snapshot", "the session is still running after its turn");
|
|
217
|
+
else
|
|
218
|
+
pass("turn.snapshot");
|
|
219
|
+
});
|
|
220
|
+
const slowPrompt = options.slowPrompt;
|
|
221
|
+
if (slowPrompt === undefined) {
|
|
222
|
+
skip("turn.busy", "no slowPrompt");
|
|
223
|
+
skip("turn.cancel", "no slowPrompt");
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
await scenario(["turn.busy"], async (backend, session, deadline) => {
|
|
227
|
+
const first = new EventReader(() => backend.send({ sessionId: session.id, message: slowPrompt }));
|
|
228
|
+
if ((await first.readUntil((event) => typeOf(event) === "turn.started", deadline)) !== "matched") {
|
|
229
|
+
await first.stop();
|
|
230
|
+
fail("turn.busy", "the slow turn never started");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const second = new EventReader(() => backend.send({ sessionId: session.id, message: options.prompt }));
|
|
234
|
+
const outcome = await second.readUntil((event) => TERMINAL.has(typeOf(event)), deadline);
|
|
235
|
+
const refused = outcome === "threw" ||
|
|
236
|
+
(outcome === "matched" &&
|
|
237
|
+
second.events.length === 1 &&
|
|
238
|
+
typeOf(second.events[0]) === "session.failed");
|
|
239
|
+
if (!refused)
|
|
240
|
+
fail("turn.busy", "a second message while a turn runs was not refused on its own stream");
|
|
241
|
+
else if ((await backend.snapshot(session.id)).status !== "running")
|
|
242
|
+
fail("turn.busy", "refusing the second message ended the running turn");
|
|
243
|
+
else
|
|
244
|
+
pass("turn.busy");
|
|
245
|
+
await second.stop();
|
|
246
|
+
await backend.cancel(session.id, "conformance");
|
|
247
|
+
await first.readToEnd(deadline);
|
|
248
|
+
});
|
|
249
|
+
await scenario(["turn.cancel"], async (backend, session, deadline) => {
|
|
250
|
+
const reader = new EventReader(() => backend.send({ sessionId: session.id, message: slowPrompt }));
|
|
251
|
+
if ((await reader.readUntil((event) => typeOf(event) === "turn.started", deadline)) !==
|
|
252
|
+
"matched") {
|
|
253
|
+
await reader.stop();
|
|
254
|
+
fail("turn.cancel", "the slow turn never started");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
await backend.cancel(session.id, "conformance");
|
|
258
|
+
const outcome = await reader.readToEnd(deadline);
|
|
259
|
+
if (outcome === "timeout") {
|
|
260
|
+
await reader.stop();
|
|
261
|
+
fail("turn.cancel", `the turn did not end within ${timeoutMs} ms of cancel`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const problem = turnProblem(reader.events, session.id, true);
|
|
265
|
+
if (problem !== undefined) {
|
|
266
|
+
fail("turn.cancel", problem);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (typeOf(reader.events.at(-1)) !== "session.stopped") {
|
|
270
|
+
fail("turn.cancel", `a cancelled turn ended with ${String(typeOf(reader.events.at(-1)))}`);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const next = new EventReader(() => backend.send({ sessionId: session.id, message: options.prompt }));
|
|
274
|
+
if ((await next.readToEnd(deadline)) !== "ended" ||
|
|
275
|
+
typeOf(next.events.at(-1)) !== "session.completed") {
|
|
276
|
+
await next.stop();
|
|
277
|
+
fail("turn.cancel", "the session did not complete a turn after a cancel");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
pass("turn.cancel");
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
const approvalPrompt = options.approvalPrompt;
|
|
284
|
+
if (approvalPrompt === undefined) {
|
|
285
|
+
for (const id of ["approval.allow", "approval.deny", "approval.unknown", "approval.cancel"])
|
|
286
|
+
skip(id, "no approvalPrompt");
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const decide = (id, approved) => scenario([id], async (backend, session, deadline) => {
|
|
290
|
+
const reader = new EventReader(() => backend.send({ sessionId: session.id, message: approvalPrompt }));
|
|
291
|
+
const asked = await reader.readUntil((event) => typeOf(event) === "approval.required", deadline);
|
|
292
|
+
if (asked !== "matched") {
|
|
293
|
+
await reader.stop();
|
|
294
|
+
fail(id, "the turn never asked for an approval");
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const approvalId = recordOf(reader.events.at(-1))?.approvalId;
|
|
298
|
+
if (typeof approvalId !== "string") {
|
|
299
|
+
await reader.stop();
|
|
300
|
+
fail(id, "approval.required carried no approvalId");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (backend.resolveApproval === undefined) {
|
|
304
|
+
await reader.stop();
|
|
305
|
+
fail(id, "the backend asks for approvals but has no resolveApproval");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const accepted = await backend.resolveApproval(session.id, approvalId, approved, "conformance");
|
|
309
|
+
if (accepted === undefined) {
|
|
310
|
+
await reader.stop();
|
|
311
|
+
fail(id, "resolveApproval did not know the approval it asked for");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const outcome = await reader.readToEnd(deadline);
|
|
315
|
+
if (outcome !== "ended") {
|
|
316
|
+
await reader.stop();
|
|
317
|
+
fail(id, outcome === "timeout" ? "the turn did not end after the answer" : "the stream threw");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const problem = turnProblem(reader.events, session.id, true);
|
|
321
|
+
// A host records the answer itself, so a backend need not echo it; one that does must agree.
|
|
322
|
+
const contradicted = reader.events.some((event) => {
|
|
323
|
+
const record = recordOf(event);
|
|
324
|
+
return (record?.type === "approval.resolved" &&
|
|
325
|
+
record.approvalId === approvalId &&
|
|
326
|
+
record.approved !== approved);
|
|
327
|
+
});
|
|
328
|
+
if (problem !== undefined)
|
|
329
|
+
fail(id, problem);
|
|
330
|
+
else if (contradicted)
|
|
331
|
+
fail(id, `approval.resolved for ${approvalId} contradicts the answer`);
|
|
332
|
+
else
|
|
333
|
+
pass(id);
|
|
334
|
+
});
|
|
335
|
+
await decide("approval.allow", true);
|
|
336
|
+
await decide("approval.deny", false);
|
|
337
|
+
await scenario(["approval.unknown"], async (backend, session) => {
|
|
338
|
+
if (backend.resolveApproval === undefined) {
|
|
339
|
+
skip("approval.unknown", "the backend has no resolveApproval");
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const answer = await backend.resolveApproval(session.id, "conformance-unknown-approval", true);
|
|
343
|
+
if (answer !== undefined)
|
|
344
|
+
fail("approval.unknown", "an approval nobody asked for took a decision instead of undefined");
|
|
345
|
+
else
|
|
346
|
+
pass("approval.unknown");
|
|
347
|
+
});
|
|
348
|
+
await scenario(["approval.cancel"], async (backend, session, deadline) => {
|
|
349
|
+
const reader = new EventReader(() => backend.send({ sessionId: session.id, message: approvalPrompt }));
|
|
350
|
+
if ((await reader.readUntil((event) => typeOf(event) === "approval.required", deadline)) !==
|
|
351
|
+
"matched") {
|
|
352
|
+
await reader.stop();
|
|
353
|
+
fail("approval.cancel", "the turn never asked for an approval");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const approvalId = recordOf(reader.events.at(-1))?.approvalId;
|
|
357
|
+
await backend.cancel(session.id, "conformance");
|
|
358
|
+
if ((await reader.readToEnd(deadline)) !== "ended") {
|
|
359
|
+
await reader.stop();
|
|
360
|
+
fail("approval.cancel", "a turn waiting on an approval did not end after cancel");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (typeOf(reader.events.at(-1)) !== "session.stopped") {
|
|
364
|
+
fail("approval.cancel", `the cancelled turn ended with ${String(typeOf(reader.events.at(-1)))}`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const late = typeof approvalId === "string"
|
|
368
|
+
? await backend.resolveApproval?.(session.id, approvalId, true)
|
|
369
|
+
: undefined;
|
|
370
|
+
if (late !== undefined)
|
|
371
|
+
fail("approval.cancel", "an approval stayed open after its turn was cancelled");
|
|
372
|
+
else
|
|
373
|
+
pass("approval.cancel");
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
await scenario(["session.close"], async (backend, session, deadline) => {
|
|
377
|
+
await backend.close(session.id);
|
|
378
|
+
const reader = new EventReader(() => backend.send({ sessionId: session.id, message: options.prompt }));
|
|
379
|
+
const outcome = await reader.readToEnd(deadline);
|
|
380
|
+
if (outcome === "timeout") {
|
|
381
|
+
await reader.stop();
|
|
382
|
+
fail("session.close", "a message to a closed session neither failed nor ended");
|
|
383
|
+
}
|
|
384
|
+
else if (outcome === "threw" ||
|
|
385
|
+
(reader.events.length > 0 &&
|
|
386
|
+
reader.events.every((event) => typeOf(event) === "session.failed")))
|
|
387
|
+
pass("session.close");
|
|
388
|
+
else
|
|
389
|
+
fail("session.close", "a closed session ran a turn");
|
|
390
|
+
});
|
|
391
|
+
return { backend: backendName, ok: checks.every((check) => check.status !== "failed"), checks };
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=conformance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conformance.js","sourceRoot":"","sources":["../src/conformance.ts"],"names":[],"mappings":"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"}
|