@agen-ai/agent-runtime 0.1.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/dist/adapterValidation.d.ts +4 -0
  4. package/dist/adapterValidation.js +242 -0
  5. package/dist/artifacts.d.ts +28 -0
  6. package/dist/artifacts.js +87 -0
  7. package/dist/configurationValidation.d.ts +3 -0
  8. package/dist/configurationValidation.js +35 -0
  9. package/dist/contractErrors.d.ts +17 -0
  10. package/dist/contractErrors.js +59 -0
  11. package/dist/evidence.d.ts +50 -0
  12. package/dist/evidence.js +368 -0
  13. package/dist/foundation.d.ts +8 -0
  14. package/dist/foundation.js +37 -0
  15. package/dist/index.d.ts +13 -0
  16. package/dist/index.js +12 -0
  17. package/dist/internal/controlCharacters.d.ts +2 -0
  18. package/dist/internal/controlCharacters.js +7 -0
  19. package/dist/internal/serializedJsonBytes.d.ts +3 -0
  20. package/dist/internal/serializedJsonBytes.js +57 -0
  21. package/dist/outputValidation.d.ts +13 -0
  22. package/dist/outputValidation.js +174 -0
  23. package/dist/outputs.d.ts +81 -0
  24. package/dist/outputs.js +217 -0
  25. package/dist/providerCatalog.d.ts +13 -0
  26. package/dist/providerCatalog.js +33 -0
  27. package/dist/providerDriver.d.ts +41 -0
  28. package/dist/providerDriver.js +52 -0
  29. package/dist/providerInstanceRegistry.d.ts +40 -0
  30. package/dist/providerInstanceRegistry.js +322 -0
  31. package/dist/readiness.d.ts +22 -0
  32. package/dist/readiness.js +58 -0
  33. package/dist/sessionValidation.d.ts +19 -0
  34. package/dist/sessionValidation.js +767 -0
  35. package/dist/sessions.d.ts +133 -0
  36. package/dist/sessions.js +0 -0
  37. package/dist/steeringValidation.d.ts +4 -0
  38. package/dist/steeringValidation.js +36 -0
  39. package/dist/testing/conformance.d.ts +30 -0
  40. package/dist/testing/conformance.js +379 -0
  41. package/dist/testing/fakeProvider.d.ts +35 -0
  42. package/dist/testing/fakeProvider.js +367 -0
  43. package/dist/testing/index.d.ts +3 -0
  44. package/dist/testing/index.js +2 -0
  45. package/dist/text.d.ts +2 -0
  46. package/dist/text.js +16 -0
  47. package/package.json +62 -0
@@ -0,0 +1,367 @@
1
+ import {
2
+ parseAgentCapabilities,
3
+ parseAgentInstanceId,
4
+ parseAgentIsoDateTime,
5
+ parseAgentProviderConversationId,
6
+ parseAgentProviderHistoryAnchor,
7
+ parseAgentProviderKey,
8
+ parseAgentRequestId,
9
+ parseAgentRequestResolutionFor,
10
+ parseAgentSessionId,
11
+ parseAgentTurnId
12
+ } from "@agen-ai/agent-protocol";
13
+ import { createAgentEventOutput } from "../outputs.js";
14
+ import {
15
+ defineAgentProviderDriver
16
+ } from "../providerDriver.js";
17
+ import { createAgentProviderReadiness } from "../readiness.js";
18
+ import { throwIfAgentOperationAborted } from "../foundation.js";
19
+ function defaultCapabilities(providerKey) {
20
+ const input = {
21
+ text: true,
22
+ images: {
23
+ kind: "supported",
24
+ sourceKinds: ["url", "base64", "local_file"],
25
+ mediaTypes: ["image/png", "image/jpeg", "image/webp"],
26
+ maxImages: 6,
27
+ maxBytesPerImage: 10 * 1024 * 1024,
28
+ maxTotalBytes: 25 * 1024 * 1024,
29
+ maxWidthPixels: 6e3,
30
+ maxHeightPixels: 6e3,
31
+ maxPixelsPerImage: 36e6,
32
+ supportsImageOnly: true
33
+ }
34
+ };
35
+ return parseAgentCapabilities({
36
+ protocolVersion: 6,
37
+ providerKey,
38
+ sessions: { create: true, resume: true, branch: { kind: "through_turn" } },
39
+ turns: {
40
+ interactionModes: ["default"],
41
+ interrupt: true,
42
+ steer: { kind: "supported", input }
43
+ },
44
+ requests: { approval: true, elicitation: { kind: "structured" } },
45
+ input,
46
+ output: {
47
+ streaming: true,
48
+ plans: true,
49
+ fileChanges: "structured",
50
+ artifactKinds: ["plan", "diff"]
51
+ },
52
+ configuration: {
53
+ kind: "selectable",
54
+ fields: [
55
+ { key: "mode", optionIds: ["agent"] },
56
+ {
57
+ key: "model",
58
+ optionIds: ["fake-model", "fake-model-2"]
59
+ }
60
+ ]
61
+ },
62
+ interactionExtensions: {
63
+ slashCommands: false,
64
+ mcp: false,
65
+ subagents: false,
66
+ imageGeneration: false
67
+ },
68
+ authentication: { kind: "unsupported" },
69
+ versionReporting: true
70
+ });
71
+ }
72
+ function sessionBinding(state, sessionId) {
73
+ state.bindingSequence += 1;
74
+ return {
75
+ conversationId: parseAgentProviderConversationId(
76
+ `fake-conversation:${sessionId}:${state.bindingSequence}`
77
+ ),
78
+ historyAnchor: parseAgentProviderHistoryAnchor(
79
+ `fake-history:${sessionId}:${state.bindingSequence}`
80
+ )
81
+ };
82
+ }
83
+ function eventBase(sessionId, turnId, occurredAt) {
84
+ return { protocolVersion: 6, sessionId, turnId, occurredAt };
85
+ }
86
+ function requestForTurn(turnId) {
87
+ return {
88
+ requestKind: "approval",
89
+ requestId: parseAgentRequestId(`fake-request:${turnId}`),
90
+ prompt: "Approve the deterministic fake operation?",
91
+ subject: { kind: "other", title: "Fake provider operation" }
92
+ };
93
+ }
94
+ function completedResult(outputs = []) {
95
+ return Object.freeze({
96
+ status: "completed",
97
+ outputs: Object.freeze(outputs)
98
+ });
99
+ }
100
+ function fakeSession(input) {
101
+ const pendingRequests = /* @__PURE__ */ new Map();
102
+ let closed = false;
103
+ const runTurn = async function* (turnInput) {
104
+ throwIfAgentOperationAborted(turnInput.signal);
105
+ input.state.turnIds.push(turnInput.turnId);
106
+ const occurredAt = input.now();
107
+ yield createAgentEventOutput({
108
+ ...eventBase(input.context.sessionId, turnInput.turnId, occurredAt),
109
+ type: "turn.started",
110
+ payload: { message: "Fake provider turn started." }
111
+ });
112
+ if (input.capabilities.output.streaming) {
113
+ yield createAgentEventOutput({
114
+ ...eventBase(input.context.sessionId, turnInput.turnId, occurredAt),
115
+ type: "content.delta",
116
+ payload: {
117
+ itemId: `fake-item:${turnInput.turnId}`,
118
+ streamKind: "assistant_text",
119
+ delta: "Deterministic fake output."
120
+ }
121
+ });
122
+ }
123
+ if (input.capabilities.requests.approval) {
124
+ const request = requestForTurn(turnInput.turnId);
125
+ pendingRequests.set(request.requestId, {
126
+ request,
127
+ turnId: turnInput.turnId
128
+ });
129
+ yield createAgentEventOutput({
130
+ ...eventBase(input.context.sessionId, turnInput.turnId, occurredAt),
131
+ type: "request.opened",
132
+ payload: { request }
133
+ });
134
+ yield createAgentEventOutput({
135
+ ...eventBase(input.context.sessionId, turnInput.turnId, occurredAt),
136
+ type: "turn.state_changed",
137
+ payload: { state: "waiting_for_request", requestId: request.requestId }
138
+ });
139
+ return;
140
+ }
141
+ yield createAgentEventOutput({
142
+ ...eventBase(input.context.sessionId, turnInput.turnId, occurredAt),
143
+ type: "turn.completed",
144
+ payload: { outcome: "completed", reason: "Fake provider completed." }
145
+ });
146
+ };
147
+ const close = async () => {
148
+ if (closed) return;
149
+ closed = true;
150
+ input.state.closeCounts.set(
151
+ input.context.sessionId,
152
+ (input.state.closeCounts.get(input.context.sessionId) ?? 0) + 1
153
+ );
154
+ };
155
+ return {
156
+ binding: input.binding,
157
+ runTurn,
158
+ resolveRequest: async function* (requestInput) {
159
+ throwIfAgentOperationAborted(requestInput.signal);
160
+ const pending = pendingRequests.get(requestInput.resolution.requestId);
161
+ if (!pending)
162
+ throw new TypeError("Fake provider has no matching pending request.");
163
+ const resolution = parseAgentRequestResolutionFor(
164
+ pending.request,
165
+ requestInput.resolution
166
+ );
167
+ input.state.resolutions.push(resolution);
168
+ pendingRequests.delete(resolution.requestId);
169
+ const occurredAt = input.now();
170
+ yield createAgentEventOutput({
171
+ ...eventBase(input.context.sessionId, pending.turnId, occurredAt),
172
+ type: "turn.state_changed",
173
+ payload: { state: "running" }
174
+ });
175
+ yield createAgentEventOutput({
176
+ ...eventBase(input.context.sessionId, pending.turnId, occurredAt),
177
+ type: "turn.completed",
178
+ payload: { outcome: "completed", reason: "Fake request resolved." }
179
+ });
180
+ },
181
+ interruption: input.capabilities.turns.interrupt ? {
182
+ kind: "supported",
183
+ interruptTurn: async (interruptionInput) => {
184
+ throwIfAgentOperationAborted(interruptionInput.signal);
185
+ input.state.interruptedTurnIds.push(interruptionInput.turnId);
186
+ return completedResult([
187
+ createAgentEventOutput({
188
+ ...eventBase(
189
+ input.context.sessionId,
190
+ interruptionInput.turnId,
191
+ input.now()
192
+ ),
193
+ type: "turn.completed",
194
+ payload: {
195
+ outcome: "canceled",
196
+ reason: interruptionInput.reason
197
+ }
198
+ })
199
+ ]);
200
+ }
201
+ } : { kind: "unsupported" },
202
+ steering: input.capabilities.turns.steer.kind === "supported" ? {
203
+ kind: "supported",
204
+ steerTurn: async (steeringInput) => {
205
+ throwIfAgentOperationAborted(steeringInput.signal);
206
+ input.state.steeringInputs.push(
207
+ Object.freeze({
208
+ turnId: steeringInput.turnId,
209
+ parts: Object.freeze([...steeringInput.parts]),
210
+ ...steeringInput.summary === void 0 ? {} : { summary: steeringInput.summary }
211
+ })
212
+ );
213
+ return input.steeringResult;
214
+ }
215
+ } : { kind: "unsupported" },
216
+ configuration: input.capabilities.configuration.kind === "selectable" ? {
217
+ kind: "selectable",
218
+ applyConfiguration: async (configurationInput) => {
219
+ throwIfAgentOperationAborted(configurationInput.signal);
220
+ input.state.configurationRevisions.push(
221
+ configurationInput.configuration.revision
222
+ );
223
+ return completedResult();
224
+ }
225
+ } : { kind: "managed" },
226
+ close
227
+ };
228
+ }
229
+ function fakeAdapter(input) {
230
+ const open = (context, binding) => fakeSession({ ...input, context, binding });
231
+ return {
232
+ createSession(createInput) {
233
+ throwIfAgentOperationAborted(createInput.signal);
234
+ input.state.createdSessionIds.push(createInput.sessionId);
235
+ const binding = sessionBinding(input.state, createInput.sessionId);
236
+ createInput.onBindingCreated(binding);
237
+ return open(createInput, binding);
238
+ },
239
+ resumption: input.capabilities.sessions.resume ? {
240
+ kind: "supported",
241
+ resumeSession(resumeInput) {
242
+ throwIfAgentOperationAborted(resumeInput.signal);
243
+ input.state.resumedSessionIds.push(resumeInput.sessionId);
244
+ return open(resumeInput, resumeInput.binding);
245
+ }
246
+ } : { kind: "unsupported" },
247
+ branching: input.capabilities.sessions.branch.kind === "through_turn" ? {
248
+ kind: "through_turn",
249
+ branchSession(branchInput) {
250
+ throwIfAgentOperationAborted(branchInput.signal);
251
+ input.state.branchedSessionIds.push(branchInput.sessionId);
252
+ const binding = sessionBinding(
253
+ input.state,
254
+ branchInput.sessionId
255
+ );
256
+ branchInput.onBindingCreated(binding);
257
+ return open(branchInput, binding);
258
+ }
259
+ } : { kind: "unsupported" },
260
+ authentication: input.capabilities.authentication.kind === "supported" ? {
261
+ kind: "supported",
262
+ start: async function* () {
263
+ return;
264
+ },
265
+ cancel: async () => completedResult()
266
+ } : { kind: "unsupported" }
267
+ };
268
+ }
269
+ function createFakeAgentProvider(options = {}) {
270
+ const providerKey = parseAgentProviderKey(
271
+ options.providerKey ?? "fake-provider"
272
+ );
273
+ const instanceId = parseAgentInstanceId(
274
+ options.instanceId ?? "fake-instance:primary"
275
+ );
276
+ const capabilities = parseAgentCapabilities(
277
+ options.capabilities ?? defaultCapabilities(providerKey)
278
+ );
279
+ if (capabilities.providerKey !== providerKey) {
280
+ throw new TypeError("Fake provider capabilities must match providerKey.");
281
+ }
282
+ const now = () => parseAgentIsoDateTime(options.now?.() ?? "2026-08-04T00:00:00.000Z");
283
+ const steeringResult = options.steeringResult ?? Object.freeze({ status: "delivered" });
284
+ const state = {
285
+ materializationCount: 0,
286
+ instanceDisposeCount: 0,
287
+ bindingSequence: 0,
288
+ createdSessionIds: [],
289
+ resumedSessionIds: [],
290
+ branchedSessionIds: [],
291
+ turnIds: [],
292
+ resolutions: [],
293
+ interruptedTurnIds: [],
294
+ steeringInputs: [],
295
+ configurationRevisions: [],
296
+ closeCounts: /* @__PURE__ */ new Map()
297
+ };
298
+ const driver = defineAgentProviderDriver({
299
+ providerKey,
300
+ supportsMultipleInstances: true,
301
+ parseConfiguration(input) {
302
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
303
+ throw new TypeError("Fake driver configuration must be an object.");
304
+ }
305
+ return Object.freeze({ ...input });
306
+ },
307
+ createInstance({ instanceId: createdInstanceId }) {
308
+ state.materializationCount += 1;
309
+ let disposed = false;
310
+ return {
311
+ instanceId: createdInstanceId,
312
+ capabilities,
313
+ adapter: fakeAdapter({ state, capabilities, now, steeringResult }),
314
+ checkReadiness: () => createAgentProviderReadiness({
315
+ status: "ready",
316
+ checkedAt: now(),
317
+ version: "1.0.0-fake",
318
+ diagnostics: { deterministic: true }
319
+ }),
320
+ dispose: async () => {
321
+ if (disposed) return;
322
+ disposed = true;
323
+ state.instanceDisposeCount += 1;
324
+ }
325
+ };
326
+ }
327
+ });
328
+ return Object.freeze({
329
+ driver,
330
+ definition: Object.freeze({
331
+ providerKey,
332
+ instanceId,
333
+ driverConfiguration: Object.freeze({})
334
+ }),
335
+ capabilities,
336
+ snapshot: () => Object.freeze({
337
+ materializationCount: state.materializationCount,
338
+ instanceDisposeCount: state.instanceDisposeCount,
339
+ createdSessionIds: Object.freeze([...state.createdSessionIds]),
340
+ resumedSessionIds: Object.freeze([...state.resumedSessionIds]),
341
+ branchedSessionIds: Object.freeze([...state.branchedSessionIds]),
342
+ turnIds: Object.freeze([...state.turnIds]),
343
+ resolutions: Object.freeze([...state.resolutions]),
344
+ interruptedTurnIds: Object.freeze([...state.interruptedTurnIds]),
345
+ steeringInputs: Object.freeze([...state.steeringInputs]),
346
+ configurationRevisions: Object.freeze([
347
+ ...state.configurationRevisions
348
+ ]),
349
+ closeCounts: Object.freeze(Object.fromEntries(state.closeCounts))
350
+ })
351
+ });
352
+ }
353
+ function createFakeAgentSessionId(value) {
354
+ return parseAgentSessionId(value);
355
+ }
356
+ function createFakeAgentTurnId(value) {
357
+ return parseAgentTurnId(value);
358
+ }
359
+ function createFakeAgentInstanceId(value) {
360
+ return parseAgentInstanceId(value);
361
+ }
362
+ export {
363
+ createFakeAgentInstanceId,
364
+ createFakeAgentProvider,
365
+ createFakeAgentSessionId,
366
+ createFakeAgentTurnId
367
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./conformance.js";
2
+ export * from "./fakeProvider.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export * from "./conformance.js";
2
+ export * from "./fakeProvider.js";
package/dist/text.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function chunkAgentCanonicalText(value: string): readonly string[];
2
+ //# sourceMappingURL=text.d.ts.map
package/dist/text.js ADDED
@@ -0,0 +1,16 @@
1
+ import { AGENT_PROTOCOL_TEXT_MAX_LENGTH } from "@agen-ai/agent-protocol";
2
+ function chunkAgentCanonicalText(value) {
3
+ const chunks = [];
4
+ let offset = 0;
5
+ while (offset < value.length) {
6
+ let end = Math.min(offset + AGENT_PROTOCOL_TEXT_MAX_LENGTH, value.length);
7
+ const splitsSurrogatePair = end < value.length && /[\uD800-\uDBFF]/u.test(value[end - 1] ?? "") && /[\uDC00-\uDFFF]/u.test(value[end] ?? "");
8
+ if (splitsSurrogatePair) end -= 1;
9
+ chunks.push(value.slice(offset, end));
10
+ offset = end;
11
+ }
12
+ return Object.freeze(chunks);
13
+ }
14
+ export {
15
+ chunkAgentCanonicalText
16
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@agen-ai/agent-runtime",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Provider-neutral driver, instance, adapter, session, output, and conformance contracts for coding-agent runtimes.",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "license": "MIT",
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "files": [
13
+ "dist/**/*.js",
14
+ "dist/**/*.d.ts",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ },
23
+ "./testing": {
24
+ "types": "./dist/testing/index.d.ts",
25
+ "import": "./dist/testing/index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "dependencies": {
30
+ "@agen-ai/agent-protocol": "^0.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "tsup": "^8.5.0"
34
+ },
35
+ "engines": {
36
+ "node": ">=22.0.0"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/trevor-nichols/agenai-agent-sdk.git",
41
+ "directory": "packages/agent-runtime"
42
+ },
43
+ "homepage": "https://github.com/trevor-nichols/agenai-agent-sdk#readme",
44
+ "bugs": {
45
+ "url": "https://github.com/trevor-nichols/agenai-agent-sdk/issues"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "provenance": true
50
+ },
51
+ "scripts": {
52
+ "build": "pnpm run clean && pnpm run build:runtime && pnpm run build:types",
53
+ "build:runtime": "tsup",
54
+ "build:types": "tsc --project tsconfig.json --emitDeclarationOnly --outDir dist",
55
+ "dev": "pnpm run build:runtime -- --watch",
56
+ "dev:types": "tsc --project tsconfig.json --emitDeclarationOnly --outDir dist --watch",
57
+ "test": "node --import tsx --test tests/*.test.ts",
58
+ "typecheck": "tsc --noEmit && tsc --project tsconfig.test.json --noEmit && tsc --project tsconfig.type-tests.json --noEmit && tsc --project tsconfig.external.json --noEmit",
59
+ "typecheck:external": "tsc --project tsconfig.external.json --noEmit",
60
+ "clean": "rimraf dist *.tsbuildinfo"
61
+ }
62
+ }