@agentium/harness 4.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.
Files changed (54) hide show
  1. package/README.md +488 -0
  2. package/dist/definition.d.ts +39 -0
  3. package/dist/definition.d.ts.map +1 -0
  4. package/dist/driver-BvzvqvpB.js +2049 -0
  5. package/dist/driver-Cr2BGG4h.cjs +2138 -0
  6. package/dist/drivers.d.ts +14 -0
  7. package/dist/drivers.d.ts.map +1 -0
  8. package/dist/index.cjs +1531 -0
  9. package/dist/index.d.ts +16 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +1500 -0
  12. package/dist/mcp-resources.d.ts +42 -0
  13. package/dist/mcp-resources.d.ts.map +1 -0
  14. package/dist/policies.d.ts +29 -0
  15. package/dist/policies.d.ts.map +1 -0
  16. package/dist/presets.d.ts +41 -0
  17. package/dist/presets.d.ts.map +1 -0
  18. package/dist/runtime/context-policy.d.ts +20 -0
  19. package/dist/runtime/context-policy.d.ts.map +1 -0
  20. package/dist/runtime/context.d.ts +15 -0
  21. package/dist/runtime/context.d.ts.map +1 -0
  22. package/dist/runtime/controller.d.ts +51 -0
  23. package/dist/runtime/controller.d.ts.map +1 -0
  24. package/dist/runtime/driver.d.ts +146 -0
  25. package/dist/runtime/driver.d.ts.map +1 -0
  26. package/dist/runtime/events.d.ts +83 -0
  27. package/dist/runtime/events.d.ts.map +1 -0
  28. package/dist/runtime/index.d.ts +9 -0
  29. package/dist/runtime/index.d.ts.map +1 -0
  30. package/dist/runtime/middleware.d.ts +10 -0
  31. package/dist/runtime/middleware.d.ts.map +1 -0
  32. package/dist/runtime/resolve.d.ts +20 -0
  33. package/dist/runtime/resolve.d.ts.map +1 -0
  34. package/dist/runtime/runtime-registry.d.ts +35 -0
  35. package/dist/runtime/runtime-registry.d.ts.map +1 -0
  36. package/dist/runtime/session-bindings.d.ts +60 -0
  37. package/dist/runtime/session-bindings.d.ts.map +1 -0
  38. package/dist/runtime/types.d.ts +176 -0
  39. package/dist/runtime/types.d.ts.map +1 -0
  40. package/dist/testing.cjs +67 -0
  41. package/dist/testing.d.ts +7 -0
  42. package/dist/testing.d.ts.map +1 -0
  43. package/dist/testing.js +66 -0
  44. package/dist/watch/definition.d.ts +11 -0
  45. package/dist/watch/definition.d.ts.map +1 -0
  46. package/dist/watch/gmail.d.ts +104 -0
  47. package/dist/watch/gmail.d.ts.map +1 -0
  48. package/dist/watch/quiet-hours.d.ts +10 -0
  49. package/dist/watch/quiet-hours.d.ts.map +1 -0
  50. package/dist/watch/runtime.d.ts +42 -0
  51. package/dist/watch/runtime.d.ts.map +1 -0
  52. package/dist/watch/types.d.ts +147 -0
  53. package/dist/watch/types.d.ts.map +1 -0
  54. package/package.json +68 -0
@@ -0,0 +1,2138 @@
1
+ let node_crypto = require("node:crypto");
2
+ let _agentium_core = require("@agentium/core");
3
+ let node_path = require("node:path");
4
+ let zod_v3 = require("zod/v3");
5
+ //#region src/runtime/middleware.ts
6
+ /** Stable topological sort; declaration order breaks ties between ready nodes. */
7
+ function sortHarnessMiddleware(middleware) {
8
+ const byId = /* @__PURE__ */ new Map();
9
+ for (const item of middleware) {
10
+ if (!item.id || byId.has(item.id)) throw new Error("Harness middleware IDs must be nonempty and unique");
11
+ byId.set(item.id, item);
12
+ }
13
+ const edges = new Map(middleware.map((item) => [item.id, /* @__PURE__ */ new Set()]));
14
+ const degrees = new Map(middleware.map((item) => [item.id, 0]));
15
+ const link = (from, to) => {
16
+ if (!byId.has(from) || !byId.has(to)) throw new Error("Harness middleware references an unknown ordering ID");
17
+ if (!edges.get(from).has(to)) {
18
+ edges.get(from).add(to);
19
+ degrees.set(to, degrees.get(to) + 1);
20
+ }
21
+ };
22
+ for (const item of middleware) {
23
+ for (const id of item.before ?? []) link(item.id, id);
24
+ for (const id of item.after ?? []) link(id, item.id);
25
+ }
26
+ const result = [];
27
+ const consumed = /* @__PURE__ */ new Set();
28
+ while (result.length < middleware.length) {
29
+ const item = middleware.find((candidate) => !consumed.has(candidate.id) && degrees.get(candidate.id) === 0);
30
+ if (!item) throw new Error("Harness middleware ordering contains a cycle");
31
+ result.push(item);
32
+ consumed.add(item.id);
33
+ for (const id of edges.get(item.id)) degrees.set(id, degrees.get(id) - 1);
34
+ }
35
+ return result;
36
+ }
37
+ async function runBeforeModel(middleware, messages, ctx) {
38
+ let current = structuredClone(messages);
39
+ for (const item of middleware) {
40
+ ctx.signal?.throwIfAborted();
41
+ if (!item.beforeModel) continue;
42
+ const previous = structuredClone(current);
43
+ const next = await item.beforeModel(current, ctx);
44
+ (0, _agentium_core.validateConversationTransform)(previous, next);
45
+ current = next;
46
+ }
47
+ return current;
48
+ }
49
+ async function runAfterModel(middleware, response, ctx) {
50
+ for (const item of middleware) {
51
+ ctx.signal?.throwIfAborted();
52
+ await item.afterModel?.(structuredClone(response), ctx);
53
+ }
54
+ }
55
+ async function runAfterTool(middleware, result, ctx) {
56
+ for (const item of middleware) {
57
+ ctx.signal?.throwIfAborted();
58
+ await item.afterTool?.(structuredClone(result), ctx);
59
+ }
60
+ }
61
+ //#endregion
62
+ //#region src/runtime/context-policy.ts
63
+ /** Request projections never mutate the canonical history. */
64
+ async function projectHarnessContext(policy, history, ctx, entries = []) {
65
+ const original = structuredClone(history);
66
+ if (!policy) return {
67
+ messages: original,
68
+ provenance: []
69
+ };
70
+ const result = await policy.project({
71
+ history: structuredClone(original),
72
+ entries: structuredClone(entries)
73
+ }, ctx);
74
+ (0, _agentium_core.validateConversationTransform)(original, result.messages);
75
+ if (!Array.isArray(result.provenance) || result.provenance.some((item) => !item || typeof item.sourceId !== "string" || typeof item.included !== "boolean" || item.reason !== void 0 && typeof item.reason !== "string")) throw new Error("Context policy must return provenance");
76
+ return {
77
+ messages: structuredClone(result.messages),
78
+ provenance: structuredClone(result.provenance)
79
+ };
80
+ }
81
+ //#endregion
82
+ //#region src/runtime/context.ts
83
+ const DEFAULT_BUDGET = {
84
+ maxEntries: 32,
85
+ maxBytes: 65536,
86
+ maxTokens: 16e3,
87
+ deadlineMs: 5e3
88
+ };
89
+ function sourceMessage(sourceId, entry, text) {
90
+ const provenance = {
91
+ version: 1,
92
+ sourceId,
93
+ entryId: entry.id,
94
+ trust: "source",
95
+ ...entry.source ? { source: {
96
+ uri: entry.source.uri,
97
+ ...entry.source.version === void 0 ? {} : { version: entry.source.version },
98
+ ...entry.source.locator === void 0 ? {} : { locator: entry.source.locator }
99
+ } } : {}
100
+ };
101
+ return {
102
+ role: "user",
103
+ content: JSON.stringify({
104
+ kind: "harness-source-data",
105
+ ...provenance,
106
+ text
107
+ }),
108
+ providerExtras: { harnessContext: provenance }
109
+ };
110
+ }
111
+ /** All limits apply across every source. Tokens are centrally estimated, never accepted from the source. */
112
+ async function fetchHarnessContext(sources, query, ctx, configured) {
113
+ const budget = {
114
+ ...DEFAULT_BUDGET,
115
+ ...configured
116
+ };
117
+ for (const [key, value] of Object.entries(budget)) if (value !== void 0 && (!Number.isFinite(value) || !Number.isInteger(value) || value < 0)) throw new Error(`Invalid harness context budget: ${key}`);
118
+ const messages = [];
119
+ const diagnostics = [];
120
+ const sourceIds = /* @__PURE__ */ new Set();
121
+ for (const source of sources) {
122
+ if (typeof source.id !== "string" || !source.id || sourceIds.has(source.id)) throw new Error("Harness context source IDs must be nonempty and unique");
123
+ sourceIds.add(source.id);
124
+ }
125
+ const deadline = Date.now() + budget.deadlineMs;
126
+ let bytes = 0;
127
+ let tokens = 0;
128
+ for (const source of sources) {
129
+ if (ctx.signal?.aborted) throw new _agentium_core.RunCancelledError();
130
+ const remaining = {
131
+ maxEntries: budget.maxEntries - messages.length,
132
+ maxBytes: budget.maxBytes - bytes,
133
+ ...budget.maxTokens === void 0 ? {} : { maxTokens: budget.maxTokens - tokens },
134
+ deadlineMs: Math.max(0, deadline - Date.now())
135
+ };
136
+ if (!remaining.maxEntries || !remaining.maxBytes || remaining.maxTokens === 0 || !remaining.deadlineMs) {
137
+ diagnostics.push({
138
+ sourceId: source.id,
139
+ code: remaining.deadlineMs ? "budget" : "deadline",
140
+ message: "Shared context retrieval budget exhausted"
141
+ });
142
+ break;
143
+ }
144
+ const controller = new AbortController();
145
+ const childCtx = new _agentium_core.RunContext({
146
+ sessionId: ctx.sessionId,
147
+ runId: ctx.runId,
148
+ userId: ctx.userId,
149
+ tenantId: ctx.tenantId,
150
+ metadata: ctx.metadata,
151
+ eventBus: ctx.eventBus,
152
+ sessionState: ctx.sessionState,
153
+ dependencies: ctx.dependencies,
154
+ questions: ctx.questions,
155
+ executionPolicy: ctx.executionPolicy,
156
+ runMode: ctx.runMode,
157
+ signal: controller.signal
158
+ });
159
+ let timer;
160
+ let abort;
161
+ let entries;
162
+ try {
163
+ entries = await Promise.race([Promise.resolve().then(() => {
164
+ if (childCtx.signal?.aborted || ctx.signal?.aborted) throw new _agentium_core.RunCancelledError();
165
+ return source.fetch(query, childCtx, remaining);
166
+ }), new Promise((_resolve, reject) => {
167
+ timer = setTimeout(() => {
168
+ controller.abort();
169
+ reject(/* @__PURE__ */ new Error("deadline"));
170
+ }, remaining.deadlineMs);
171
+ abort = () => {
172
+ controller.abort();
173
+ reject(new _agentium_core.RunCancelledError());
174
+ };
175
+ ctx.signal?.addEventListener("abort", abort, { once: true });
176
+ if (ctx.signal?.aborted) abort();
177
+ })]);
178
+ } catch {
179
+ if (ctx.signal?.aborted) throw new _agentium_core.RunCancelledError();
180
+ diagnostics.push({
181
+ sourceId: source.id,
182
+ code: controller.signal.aborted ? "deadline" : "failed",
183
+ message: "Context source could not complete within its budget"
184
+ });
185
+ continue;
186
+ } finally {
187
+ if (timer) clearTimeout(timer);
188
+ if (abort) ctx.signal?.removeEventListener("abort", abort);
189
+ }
190
+ if (!Array.isArray(entries)) {
191
+ diagnostics.push({
192
+ sourceId: source.id,
193
+ code: "invalid",
194
+ message: "Context source must return an array"
195
+ });
196
+ continue;
197
+ }
198
+ if (entries.length > remaining.maxEntries) diagnostics.push({
199
+ sourceId: source.id,
200
+ code: "budget",
201
+ message: "Context entries beyond the shared entry limit were discarded"
202
+ });
203
+ const entryIds = /* @__PURE__ */ new Set();
204
+ for (const entry of entries.slice(0, remaining.maxEntries)) {
205
+ if (ctx.signal?.aborted) throw new _agentium_core.RunCancelledError();
206
+ if (Date.now() >= deadline) {
207
+ diagnostics.push({
208
+ sourceId: source.id,
209
+ code: "deadline",
210
+ message: "Shared context retrieval deadline expired"
211
+ });
212
+ break;
213
+ }
214
+ if (!entry || typeof entry.id !== "string" || !entry.id || entryIds.has(entry.id) || typeof entry.text !== "string" || entry.expiresAt !== void 0 && !Number.isFinite(entry.expiresAt)) {
215
+ diagnostics.push({
216
+ sourceId: source.id,
217
+ code: "invalid",
218
+ message: "Invalid or duplicate context entry was discarded"
219
+ });
220
+ continue;
221
+ }
222
+ if (entry.source && (typeof entry.source.uri !== "string" || entry.source.version !== void 0 && typeof entry.source.version !== "string" || entry.source.locator !== void 0 && typeof entry.source.locator !== "string")) {
223
+ diagnostics.push({
224
+ sourceId: source.id,
225
+ entryId: entry.id,
226
+ code: "invalid",
227
+ message: "Invalid context source provenance was discarded"
228
+ });
229
+ continue;
230
+ }
231
+ entryIds.add(entry.id);
232
+ if (entry.expiresAt !== void 0 && entry.expiresAt <= Date.now()) {
233
+ diagnostics.push({
234
+ sourceId: source.id,
235
+ entryId: entry.id,
236
+ code: "expired",
237
+ message: "Expired context entry was discarded"
238
+ });
239
+ continue;
240
+ }
241
+ if (entry.trust !== "source") diagnostics.push({
242
+ sourceId: source.id,
243
+ entryId: entry.id,
244
+ code: "trust_downgraded",
245
+ message: "Retrieved context is always source data; host trust declarations are ignored"
246
+ });
247
+ const fits = (message) => {
248
+ const text = message.content;
249
+ return Buffer.byteLength(text, "utf8") <= budget.maxBytes - bytes && (budget.maxTokens === void 0 || (0, _agentium_core.countTokens)(text) <= budget.maxTokens - tokens);
250
+ };
251
+ let message = sourceMessage(source.id, entry, entry.text);
252
+ if (!fits(message)) {
253
+ if (!fits(sourceMessage(source.id, entry, ""))) {
254
+ diagnostics.push({
255
+ sourceId: source.id,
256
+ entryId: entry.id,
257
+ code: "budget",
258
+ message: "Context entry metadata exceeds the remaining budget"
259
+ });
260
+ continue;
261
+ }
262
+ let low = 0;
263
+ let high = entry.text.length;
264
+ while (low < high) {
265
+ const middle = Math.ceil((low + high) / 2);
266
+ if (fits(sourceMessage(source.id, entry, entry.text.slice(0, middle)))) low = middle;
267
+ else high = middle - 1;
268
+ }
269
+ if (low > 0 && /[\uD800-\uDBFF]/.test(entry.text[low - 1])) low--;
270
+ message = sourceMessage(source.id, entry, entry.text.slice(0, low));
271
+ diagnostics.push({
272
+ sourceId: source.id,
273
+ entryId: entry.id,
274
+ code: "truncated",
275
+ message: "Context text truncated to the shared byte/token budget (tokens estimated)"
276
+ });
277
+ }
278
+ messages.push(message);
279
+ bytes += Buffer.byteLength(message.content, "utf8");
280
+ tokens += (0, _agentium_core.countTokens)(message.content);
281
+ }
282
+ }
283
+ return {
284
+ messages,
285
+ diagnostics
286
+ };
287
+ }
288
+ //#endregion
289
+ //#region src/runtime/events.ts
290
+ var HarnessEventGapError = class extends Error {
291
+ earliestCursor;
292
+ code = "event_gap";
293
+ constructor(earliestCursor) {
294
+ super(`Event history gap; earliest available cursor is ${earliestCursor}`);
295
+ this.earliestCursor = earliestCursor;
296
+ }
297
+ };
298
+ /** Bounded in-process history; no durability, network delivery, or audio backpressure promise. */
299
+ var InMemoryHarnessEventStore = class {
300
+ identity;
301
+ capacity;
302
+ durable = false;
303
+ history = [];
304
+ sequence = 0;
305
+ finished = false;
306
+ listeners = /* @__PURE__ */ new Set();
307
+ constructor(identity, capacity = 256) {
308
+ this.identity = identity;
309
+ this.capacity = capacity;
310
+ if (!Number.isInteger(capacity) || capacity < 2) throw new Error("Event capacity must be an integer >= 2");
311
+ }
312
+ get cursor() {
313
+ return this.sequence;
314
+ }
315
+ append(payload) {
316
+ if (this.finished) throw new Error("Event stream is already terminal");
317
+ const event = {
318
+ ...this.identity,
319
+ schemaVersion: 1,
320
+ eventId: (0, node_crypto.randomUUID)(),
321
+ sequence: ++this.sequence,
322
+ timestamp: Date.now(),
323
+ payload: structuredClone(payload)
324
+ };
325
+ this.history.push(event);
326
+ if (this.history.length > this.capacity) this.history.shift();
327
+ if (payload.type === "run.terminal") this.finished = true;
328
+ for (const notify of this.listeners) notify();
329
+ return structuredClone(event);
330
+ }
331
+ events(options = {}) {
332
+ let cursor = options.after ?? 0;
333
+ if (!Number.isInteger(cursor) || cursor < 0 || cursor > this.sequence) throw new Error("Invalid event cursor");
334
+ let closed = false;
335
+ let queued = Promise.resolve();
336
+ let wake;
337
+ const close = () => {
338
+ closed = true;
339
+ if (wake) {
340
+ this.listeners.delete(wake);
341
+ wake();
342
+ wake = void 0;
343
+ }
344
+ };
345
+ return {
346
+ [Symbol.asyncIterator]() {
347
+ return this;
348
+ },
349
+ [Symbol.asyncDispose]: async () => {
350
+ close();
351
+ },
352
+ next: () => {
353
+ const result = queued.then(async () => {
354
+ while (!closed) {
355
+ const earliest = this.history[0]?.sequence ?? 1;
356
+ if (cursor < earliest - 1) {
357
+ close();
358
+ throw new HarnessEventGapError(earliest - 1);
359
+ }
360
+ const next = this.history.find((event) => event.sequence > cursor);
361
+ if (next) {
362
+ cursor = next.sequence;
363
+ return {
364
+ value: structuredClone(next),
365
+ done: false
366
+ };
367
+ }
368
+ if (this.finished) break;
369
+ await new Promise((resolve) => {
370
+ wake = resolve;
371
+ this.listeners.add(resolve);
372
+ });
373
+ if (wake) this.listeners.delete(wake);
374
+ wake = void 0;
375
+ }
376
+ close();
377
+ return {
378
+ value: void 0,
379
+ done: true
380
+ };
381
+ });
382
+ queued = result.catch(() => {});
383
+ return result;
384
+ },
385
+ return: async () => {
386
+ close();
387
+ return {
388
+ value: void 0,
389
+ done: true
390
+ };
391
+ },
392
+ throw: async (error) => {
393
+ close();
394
+ throw error;
395
+ }
396
+ };
397
+ }
398
+ };
399
+ //#endregion
400
+ //#region src/runtime/runtime-registry.ts
401
+ /** Registry resolution is static; it never initializes providers or executes drivers. */
402
+ function resolveHarnessRuntime(definition) {
403
+ const references = definition.kind === "portable" ? definition.manifest.runtime : definition.runtimeReferences;
404
+ const direct = definition.kind === "local" ? definition.runtime : void 0;
405
+ const result = {
406
+ ...direct,
407
+ modelRoles: direct?.modelRoles ?? references?.modelRoles
408
+ };
409
+ for (const key of [
410
+ "driver",
411
+ "controller",
412
+ "contextPolicy",
413
+ "completionPolicy"
414
+ ]) {
415
+ const reference = references?.[key];
416
+ if (!reference) continue;
417
+ const matches = (definition.runtimeRegistry?.[key] ?? []).filter((entry) => entry.id === reference.id && entry.version === reference.version);
418
+ if (matches.length !== 1) throw new Error(`runtime.${key}: expected exactly one approved registry implementation for ${reference.id}@${reference.version}`);
419
+ const implementation = matches[0].implementation;
420
+ if (direct?.[key] && direct[key] !== implementation) throw new Error(`runtime.${key}: direct binding differs from its approved export mapping`);
421
+ result[key] = implementation;
422
+ }
423
+ return Object.freeze(result);
424
+ }
425
+ //#endregion
426
+ //#region src/runtime/resolve.ts
427
+ var HarnessValidationError = class extends Error {
428
+ diagnostics;
429
+ constructor(path, message, code = "invalid_harness") {
430
+ super(`${path}: ${message}`);
431
+ this.name = "HarnessValidationError";
432
+ this.diagnostics = [{
433
+ code,
434
+ path,
435
+ message,
436
+ severity: "error"
437
+ }];
438
+ }
439
+ };
440
+ const generatedIds = /* @__PURE__ */ new WeakSet();
441
+ const definitions = /* @__PURE__ */ new WeakSet();
442
+ const metadata = /* @__PURE__ */ new WeakMap();
443
+ const secretKeys = /* @__PURE__ */ new Set([
444
+ "apikey",
445
+ "password",
446
+ "secret",
447
+ "authorization",
448
+ "accesstoken",
449
+ "refreshtoken",
450
+ "clientsecret",
451
+ "privatekey",
452
+ "credentials"
453
+ ]);
454
+ function fail(path, message) {
455
+ throw new HarnessValidationError(path, message);
456
+ }
457
+ function object(input, path) {
458
+ if (!input || typeof input !== "object" || Array.isArray(input) || ![Object.prototype, null].includes(Object.getPrototypeOf(input))) fail(path, "expected a plain object");
459
+ return input;
460
+ }
461
+ function keys(input, allowed, path) {
462
+ for (const key of Object.keys(input)) if (!allowed.includes(key)) fail(`${path}.${key}`, "unknown field");
463
+ }
464
+ function id(input, path) {
465
+ if (typeof input !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:/-]*$/.test(input)) fail(path, "expected a stable nonempty identifier");
466
+ return input;
467
+ }
468
+ function integer(input, path, min = 0) {
469
+ if (typeof input !== "number" || !Number.isFinite(input) || !Number.isInteger(input) || input < min) fail(path, `expected an integer >= ${min}`);
470
+ return input;
471
+ }
472
+ function json(input, path, seen = /* @__PURE__ */ new Set()) {
473
+ if (input === null || typeof input === "boolean" || typeof input === "string") return input;
474
+ if (typeof input === "number" && Number.isFinite(input)) return input;
475
+ if (typeof input !== "object" || input === null) fail(path, "expected JSON data");
476
+ if (seen.has(input)) fail(path, "cyclic data is not portable");
477
+ seen.add(input);
478
+ try {
479
+ if (Array.isArray(input)) {
480
+ if (Object.getOwnPropertySymbols(input).length || Object.keys(input).some((key) => !/^(0|[1-9][0-9]*)$/.test(key))) fail(path, "array properties are not portable");
481
+ return Array.from({ length: input.length }, (_, index) => {
482
+ const descriptor = Object.getOwnPropertyDescriptor(input, index);
483
+ if (!descriptor || !("value" in descriptor)) fail(`${path}[${index}]`, "sparse arrays and accessors are not portable");
484
+ return json(descriptor.value, `${path}[${index}]`, seen);
485
+ });
486
+ }
487
+ const source = object(input, path);
488
+ if (Object.getOwnPropertySymbols(source).length) fail(path, "symbol properties are not portable");
489
+ const result = {};
490
+ for (const key of Object.keys(source)) {
491
+ if (secretKeys.has(key.replace(/[_-]/g, "").toLowerCase())) fail(`${path}.${key}`, "credentials belong in host bindings");
492
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
493
+ if (!("value" in descriptor)) fail(`${path}.${key}`, "accessors are not portable");
494
+ Object.defineProperty(result, key, {
495
+ value: json(descriptor.value, `${path}.${key}`, seen),
496
+ enumerable: true,
497
+ writable: true,
498
+ configurable: true
499
+ });
500
+ }
501
+ return result;
502
+ } finally {
503
+ seen.delete(input);
504
+ }
505
+ }
506
+ function freeze(value) {
507
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
508
+ for (const child of Object.values(value)) freeze(child);
509
+ Object.freeze(value);
510
+ }
511
+ return value;
512
+ }
513
+ function validateDefaults(input, path = "defaults") {
514
+ const data = object(json(input, path), path);
515
+ keys(data, [
516
+ "workspace",
517
+ "skillDirs",
518
+ "contextFiles",
519
+ "filesystem",
520
+ "subagents",
521
+ "fileMemory",
522
+ "searchPastSessions"
523
+ ], path);
524
+ for (const [key, value] of Object.entries(data)) if (key === "workspace") {
525
+ if (value === false) continue;
526
+ const workspace = object(value, `${path}.workspace`);
527
+ keys(workspace, ["path", "mode"], `${path}.workspace`);
528
+ if (typeof workspace.path !== "string" || !workspace.path.length || !["read", "write"].includes(workspace.mode)) fail(`${path}.workspace`, "expected a path and read/write mode");
529
+ } else if (key === "skillDirs") {
530
+ if (value !== false && (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.length))) fail(`${path}.skillDirs`, "expected a list of paths or false");
531
+ } else if (typeof value !== "boolean") fail(`${path}.${key}`, "expected a boolean");
532
+ return data;
533
+ }
534
+ function validateLimits(input, path = "limits") {
535
+ const data = object(json(input, path), path);
536
+ keys(data, ["toolRoundtrips", "maxChildDepth"], path);
537
+ for (const [key, value] of Object.entries(data)) integer(value, `${path}.${key}`);
538
+ return data;
539
+ }
540
+ function validateSelection(input, path) {
541
+ const data = object(input, path);
542
+ keys(data, [
543
+ "instanceId",
544
+ "type",
545
+ "version",
546
+ "options"
547
+ ], path);
548
+ return {
549
+ instanceId: id(data.instanceId, `${path}.instanceId`),
550
+ type: id(data.type, `${path}.type`),
551
+ version: integer(data.version, `${path}.version`, 1),
552
+ options: object(json(data.options, `${path}.options`), `${path}.options`)
553
+ };
554
+ }
555
+ function validateRuntimeReferences(input, path = "runtime") {
556
+ const data = object(json(input, path), path);
557
+ keys(data, [
558
+ "driver",
559
+ "controller",
560
+ "contextPolicy",
561
+ "completionPolicy",
562
+ "modelRoles"
563
+ ], path);
564
+ for (const [key, value] of Object.entries(data)) {
565
+ const item = object(value, `${path}.${key}`);
566
+ if (key === "modelRoles") for (const [role, name] of Object.entries(item)) {
567
+ id(role, `${path}.modelRoles`);
568
+ id(name, `${path}.modelRoles.${role}`);
569
+ }
570
+ else {
571
+ keys(item, ["id", "version"], `${path}.${key}`);
572
+ id(item.id, `${path}.${key}.id`);
573
+ integer(item.version, `${path}.${key}.version`, 1);
574
+ }
575
+ }
576
+ return freeze(data);
577
+ }
578
+ function validateHarnessManifest(input) {
579
+ const data = object(json(input, "manifest"), "manifest");
580
+ keys(data, [
581
+ "contractVersion",
582
+ "id",
583
+ "abilities",
584
+ "defaults",
585
+ "limits",
586
+ "runtime"
587
+ ], "manifest");
588
+ if (data.contractVersion !== 1) fail("manifest.contractVersion", "unsupported contract version");
589
+ if (!Array.isArray(data.abilities)) fail("manifest.abilities", "expected an array");
590
+ const abilities = data.abilities.map((ability, index) => validateSelection(ability, `manifest.abilities[${index}]`));
591
+ unique(abilities.map((ability) => ability.instanceId), "manifest.abilities");
592
+ return freeze({
593
+ contractVersion: 1,
594
+ id: id(data.id, "manifest.id"),
595
+ abilities,
596
+ ...data.runtime !== void 0 ? { runtime: validateRuntimeReferences(data.runtime) } : {},
597
+ ...data.defaults !== void 0 ? { defaults: validateDefaults(data.defaults) } : {},
598
+ ...data.limits !== void 0 ? { limits: validateLimits(data.limits) } : {}
599
+ });
600
+ }
601
+ function unique(values, path) {
602
+ if (new Set(values).size !== values.length) fail(path, "duplicate identifiers");
603
+ }
604
+ function registry(factories) {
605
+ if (!Array.isArray(factories)) fail("factories", "expected an approved factory registry");
606
+ const map = /* @__PURE__ */ new Map();
607
+ for (const factory of factories) {
608
+ if (!factory || typeof factory !== "object") fail("factory", "expected a trusted factory");
609
+ id(factory.type, "factory.type");
610
+ integer(factory.version, "factory.version", 1);
611
+ if (typeof factory.validateOptions !== "function" || typeof factory.describe !== "function" || typeof factory.bind !== "function") fail("factory", "missing validation, description or binding function");
612
+ const key = `${factory.type}@${factory.version}`;
613
+ if (map.has(key) && map.get(key) !== factory) fail("factories", "conflicting implementations for a registered type/version");
614
+ map.set(key, factory);
615
+ }
616
+ return map;
617
+ }
618
+ function snapshotRuntimeRegistry(input) {
619
+ keys(object(input, "runtimeRegistry"), [
620
+ "driver",
621
+ "controller",
622
+ "contextPolicy",
623
+ "completionPolicy"
624
+ ], "runtimeRegistry");
625
+ const result = {};
626
+ for (const [key, entries] of Object.entries(input)) {
627
+ if (!Array.isArray(entries)) fail(`runtimeRegistry.${key}`, "expected approved registry entries");
628
+ const names = [];
629
+ result[key] = Object.freeze(entries.map((entry) => {
630
+ id(entry.id, `runtimeRegistry.${key}.id`);
631
+ integer(entry.version, `runtimeRegistry.${key}.version`, 1);
632
+ names.push(`${entry.id}@${entry.version}`);
633
+ if (!entry.implementation || typeof entry.implementation !== "object") fail(`runtimeRegistry.${key}`, "missing trusted implementation");
634
+ return Object.freeze({ ...entry });
635
+ }));
636
+ unique(names, `runtimeRegistry.${key}`);
637
+ }
638
+ return Object.freeze(result);
639
+ }
640
+ function normalizePortable(manifest, factories, runtimeRegistry) {
641
+ const approved = registry(factories);
642
+ const abilities = manifest.abilities.map((selection, index) => {
643
+ const factory = approved.get(`${selection.type}@${selection.version}`);
644
+ if (!factory) fail(`manifest.abilities[${index}]`, "no approved factory for ability type/version");
645
+ let options;
646
+ try {
647
+ options = factory.validateOptions(json(selection.options, "options"));
648
+ } catch {
649
+ fail(`manifest.abilities[${index}].options`, "ability option validation failed");
650
+ }
651
+ return {
652
+ ...selection,
653
+ options: object(json(options, `manifest.abilities[${index}].options`), "options")
654
+ };
655
+ });
656
+ const definition = Object.freeze({
657
+ kind: "portable",
658
+ manifest: validateHarnessManifest({
659
+ ...manifest,
660
+ abilities
661
+ }),
662
+ factories: Object.freeze([...approved.values()]),
663
+ ...runtimeRegistry ? { runtimeRegistry: snapshotRuntimeRegistry(runtimeRegistry) } : {}
664
+ });
665
+ resolveHarnessRuntime(definition);
666
+ definitions.add(definition);
667
+ return definition;
668
+ }
669
+ function loadHarnessManifest(input, factories, runtimeRegistry) {
670
+ return normalizePortable(validateHarnessManifest(input), factories, runtimeRegistry);
671
+ }
672
+ function createHarnessDefinition(input) {
673
+ object(input, "definition");
674
+ if (definitions.has(input)) return input;
675
+ if ("kind" in input && input.kind === "portable") {
676
+ keys(object(input, "definition"), [
677
+ "kind",
678
+ "manifest",
679
+ "factories",
680
+ "runtimeRegistry"
681
+ ], "definition");
682
+ return loadHarnessManifest(input.manifest, input.factories, input.runtimeRegistry);
683
+ }
684
+ const local = input;
685
+ keys(object(input, "definition"), [
686
+ "kind",
687
+ "id",
688
+ "abilities",
689
+ "defaults",
690
+ "limits",
691
+ "runtime",
692
+ "runtimeReferences",
693
+ "runtimeRegistry"
694
+ ], "definition");
695
+ if ("kind" in input && input.kind !== "local") fail("definition.kind", "unsupported definition kind");
696
+ if (local.abilities !== void 0 && !Array.isArray(local.abilities)) fail("abilities", "expected an ability list");
697
+ const abilities = [...local.abilities ?? []].map((use, index) => {
698
+ if (!use || typeof use !== "object") fail(`abilities[${index}]`, "expected an ability use");
699
+ id(use.instanceId, `abilities[${index}].instanceId`);
700
+ if (typeof use.validate !== "function" || typeof use.describe !== "function" || typeof use.bind !== "function") fail(`abilities[${index}]`, "missing validation, description or binding function");
701
+ try {
702
+ use.validate();
703
+ } catch {
704
+ fail(`abilities[${index}]`, "ability option validation failed");
705
+ }
706
+ const portable = use.portable ? freeze(validateSelection(json(use.portable, `abilities[${index}].portable`), `abilities[${index}].portable`)) : void 0;
707
+ if (portable && portable.instanceId !== use.instanceId) fail(`abilities[${index}].portable.instanceId`, "portable and local IDs must match");
708
+ if (portable && use.factory && (portable.type !== use.factory.type || portable.version !== use.factory.version)) fail(`abilities[${index}].factory`, "factory mapping does not match selection");
709
+ return Object.freeze({
710
+ ...use,
711
+ ...portable ? { portable } : {}
712
+ });
713
+ });
714
+ unique(abilities.map((use) => use.instanceId), "abilities");
715
+ registry(abilities.flatMap((use) => use.factory ? [use.factory] : []));
716
+ const definition = Object.freeze({
717
+ kind: "local",
718
+ id: local.id === void 0 ? `local-${(0, node_crypto.randomUUID)()}` : id(local.id, "definition.id"),
719
+ abilities: Object.freeze(abilities),
720
+ ...local.runtime ? { runtime: Object.freeze({ ...local.runtime }) } : {},
721
+ ...local.runtimeReferences ? { runtimeReferences: validateRuntimeReferences(local.runtimeReferences) } : {},
722
+ ...local.runtimeRegistry ? { runtimeRegistry: snapshotRuntimeRegistry(local.runtimeRegistry) } : {},
723
+ ...local.defaults !== void 0 ? { defaults: freeze(validateDefaults(local.defaults)) } : {},
724
+ ...local.limits !== void 0 ? { limits: freeze(validateLimits(local.limits)) } : {}
725
+ });
726
+ resolveHarnessRuntime(definition);
727
+ definitions.add(definition);
728
+ if (local.id === void 0 || generatedIds.has(input)) generatedIds.add(definition);
729
+ metadata.set(definition, metadata.get(input) ?? {
730
+ provenance: [
731
+ ...Object.keys(local.defaults ?? {}).map((key) => ({
732
+ path: `defaults.${key}`,
733
+ origin: definition.id
734
+ })),
735
+ ...Object.keys(local.limits ?? {}).map((key) => ({
736
+ path: `limits.${key}`,
737
+ origin: definition.id
738
+ })),
739
+ ...abilities.map((use) => ({
740
+ path: `abilities.${use.instanceId}`,
741
+ origin: definition.id
742
+ }))
743
+ ],
744
+ diagnostics: []
745
+ });
746
+ return definition;
747
+ }
748
+ function asLocal(definition) {
749
+ if (definition.kind === "local") return definition;
750
+ const approved = registry(definition.factories);
751
+ return {
752
+ kind: "local",
753
+ id: definition.manifest.id,
754
+ defaults: definition.manifest.defaults,
755
+ limits: definition.manifest.limits,
756
+ runtimeReferences: definition.manifest.runtime,
757
+ runtimeRegistry: definition.runtimeRegistry,
758
+ abilities: definition.manifest.abilities.map((selection) => {
759
+ const factory = approved.get(`${selection.type}@${selection.version}`);
760
+ return {
761
+ instanceId: selection.instanceId,
762
+ portable: selection,
763
+ factory,
764
+ validate: () => {},
765
+ describe: () => factory.describe(selection.options),
766
+ bind: (ctx) => factory.bind(selection.options, ctx)
767
+ };
768
+ })
769
+ };
770
+ }
771
+ function extendHarnessDefinition(base, extension) {
772
+ keys(object(extension, "extension"), [
773
+ "id",
774
+ "abilities",
775
+ "disable",
776
+ "replaceAbilities",
777
+ "defaults",
778
+ "limits",
779
+ "runtime",
780
+ "runtimeReferences",
781
+ "runtimeRegistry"
782
+ ], "extension");
783
+ const source = asLocal(createHarnessDefinition(base));
784
+ const disabled = new Set(extension.disable ?? []);
785
+ unique([...extension.disable ?? []], "extension.disable");
786
+ for (const target of disabled) if (!source.abilities.some((use) => use.instanceId === target)) fail("extension.disable", "unknown ability identifier");
787
+ const replacements = new Map((extension.replaceAbilities ?? []).map((use) => [use.instanceId, use]));
788
+ unique([...(extension.replaceAbilities ?? []).map((use) => use.instanceId)], "extension.replaceAbilities");
789
+ for (const target of replacements.keys()) if (disabled.has(target) || !source.abilities.some((use) => use.instanceId === target)) fail("extension.replaceAbilities", "replacement must match an enabled existing ability");
790
+ const defaults = {
791
+ ...source.defaults,
792
+ ...extension.defaults ? validateDefaults(extension.defaults) : {}
793
+ };
794
+ if (extension.defaults?.skillDirs && source.defaults?.skillDirs) defaults.skillDirs = [.../* @__PURE__ */ new Set([...source.defaults.skillDirs, ...extension.defaults.skillDirs])];
795
+ const definition = createHarnessDefinition({
796
+ id: extension.id ?? source.id,
797
+ runtime: {
798
+ ...source.runtime,
799
+ ...extension.runtime
800
+ },
801
+ runtimeReferences: extension.runtimeReferences ?? source.runtimeReferences,
802
+ runtimeRegistry: extension.runtimeRegistry ?? source.runtimeRegistry,
803
+ abilities: [...source.abilities.filter((use) => !disabled.has(use.instanceId)).map((use) => replacements.get(use.instanceId) ?? use), ...extension.abilities ?? []],
804
+ defaults,
805
+ limits: {
806
+ ...source.limits,
807
+ ...extension.limits ? validateLimits(extension.limits) : {}
808
+ }
809
+ });
810
+ if (generatedIds.has(base) && !extension.id) generatedIds.add(definition);
811
+ const previous = metadata.get(base);
812
+ const origin = extension.id ?? source.id ?? "local";
813
+ metadata.set(definition, {
814
+ provenance: [
815
+ ...previous?.provenance ?? [],
816
+ ...Object.keys(extension.defaults ?? {}).map((key) => ({
817
+ path: `defaults.${key}`,
818
+ origin
819
+ })),
820
+ ...Object.keys(extension.limits ?? {}).map((key) => ({
821
+ path: `limits.${key}`,
822
+ origin
823
+ }))
824
+ ],
825
+ diagnostics: [...previous?.diagnostics ?? [], ...[...disabled].map((instanceId) => ({
826
+ code: "ability_disabled",
827
+ path: `abilities.${instanceId}`,
828
+ message: "Ability explicitly disabled",
829
+ severity: "info"
830
+ }))]
831
+ });
832
+ return definition;
833
+ }
834
+ function abilityDescription(input, path) {
835
+ if (!input || !Array.isArray(input.toolNames) || !Array.isArray(input.requirements)) fail(path, "expected tool and requirement declarations");
836
+ for (const name of input.toolNames) id(name, `${path}.toolNames`);
837
+ for (const requirement of input.requirements) id(requirement, `${path}.requirements`);
838
+ unique(input.toolNames, `${path}.toolNames`);
839
+ if (input.runtimeDependent !== void 0 && typeof input.runtimeDependent !== "boolean") fail(path, "invalid runtimeDependent declaration");
840
+ return {
841
+ toolNames: [...input.toolNames],
842
+ requirements: [...new Set(input.requirements)],
843
+ ...input.runtimeDependent !== void 0 ? { runtimeDependent: input.runtimeDependent } : {}
844
+ };
845
+ }
846
+ function describeHarnessDefinition(input) {
847
+ const definition = createHarnessDefinition(input);
848
+ const local = asLocal(definition);
849
+ const abilities = local.abilities.map((use, index) => {
850
+ let description;
851
+ try {
852
+ description = use.describe();
853
+ } catch {
854
+ fail(`abilities[${index}]`, "ability description failed");
855
+ }
856
+ return {
857
+ instanceId: use.instanceId,
858
+ ...use.portable ? {
859
+ type: use.portable.type,
860
+ version: use.portable.version
861
+ } : {},
862
+ ...abilityDescription(description, `abilities[${index}]`)
863
+ };
864
+ });
865
+ return freeze({
866
+ id: local.id,
867
+ kind: definition.kind,
868
+ contractVersion: 1,
869
+ abilities,
870
+ toolNames: abilities.flatMap((ability) => [...ability.toolNames]),
871
+ requirements: [...new Set(abilities.flatMap((ability) => [...ability.requirements]))],
872
+ runtimeDependent: abilities.some((ability) => ability.runtimeDependent),
873
+ defaults: local.defaults ?? {},
874
+ limits: local.limits ?? {},
875
+ diagnostics: metadata.get(input)?.diagnostics ?? []
876
+ });
877
+ }
878
+ function resolveHarness(input, options) {
879
+ if (typeof options.projectRoot !== "string" || !(0, node_path.isAbsolute)(options.projectRoot)) fail("projectRoot", "an absolute host project root is required");
880
+ const definition = createHarnessDefinition(input);
881
+ const description = describeHarnessDefinition(definition);
882
+ const provided = new Set(options.requirements ?? []);
883
+ for (const requirement of description.requirements) if (!provided.has(requirement)) fail(`requirements.${requirement}`, "missing host-provided requirement");
884
+ const defaults = { ...description.defaults };
885
+ if (defaults.workspace) defaults.workspace = typeof defaults.workspace === "string" ? {
886
+ path: (0, node_path.resolve)(options.projectRoot, defaults.workspace),
887
+ mode: "read"
888
+ } : {
889
+ ...defaults.workspace,
890
+ path: (0, node_path.resolve)(options.projectRoot, defaults.workspace.path)
891
+ };
892
+ if (defaults.skillDirs) defaults.skillDirs = [...new Set(defaults.skillDirs.map((path) => (0, node_path.resolve)(options.projectRoot, path)))];
893
+ const provenance = metadata.get(input)?.provenance ?? [
894
+ ...Object.keys(defaults).map((key) => ({
895
+ path: `defaults.${key}`,
896
+ origin: description.id
897
+ })),
898
+ ...Object.keys(description.limits).map((key) => ({
899
+ path: `limits.${key}`,
900
+ origin: description.id
901
+ })),
902
+ ...description.abilities.map((ability) => ({
903
+ path: `abilities.${ability.instanceId}`,
904
+ origin: description.id
905
+ }))
906
+ ];
907
+ return Object.freeze({
908
+ definition,
909
+ defaults: freeze(defaults),
910
+ limits: description.limits,
911
+ description,
912
+ provenance: freeze([...provenance])
913
+ });
914
+ }
915
+ function exportHarnessManifest(input) {
916
+ const definition = createHarnessDefinition(input);
917
+ if (definition.kind === "portable") return definition.manifest;
918
+ if (generatedIds.has(input) || generatedIds.has(definition)) fail("definition.id", "an explicit stable ID is required for export");
919
+ for (const key of [
920
+ "driver",
921
+ "controller",
922
+ "contextPolicy",
923
+ "completionPolicy"
924
+ ]) if (definition.runtime?.[key] && !definition.runtimeReferences?.[key]) fail(`runtime.${key}`, "approved registry reference required for export");
925
+ resolveHarnessRuntime(definition);
926
+ const abilities = definition.abilities.map((use, index) => {
927
+ if (!use.portable || !use.factory) fail(`abilities[${index}]`, "portable selection and approved factory mapping required for export");
928
+ return use.portable;
929
+ });
930
+ return normalizePortable(validateHarnessManifest({
931
+ contractVersion: 1,
932
+ id: definition.id,
933
+ abilities,
934
+ ...definition.runtimeReferences || definition.runtime?.modelRoles ? { runtime: {
935
+ ...definition.runtimeReferences,
936
+ ...definition.runtime?.modelRoles ? { modelRoles: definition.runtime.modelRoles } : {}
937
+ } } : {},
938
+ ...definition.defaults !== void 0 ? { defaults: definition.defaults } : {},
939
+ ...definition.limits !== void 0 ? { limits: definition.limits } : {}
940
+ }), definition.abilities.map((use) => use.factory), definition.runtimeRegistry).manifest;
941
+ }
942
+ function canonical(value) {
943
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
944
+ if (value !== null && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
945
+ return JSON.stringify(value);
946
+ }
947
+ function hashHarnessManifest(input) {
948
+ return (0, node_crypto.createHash)("sha256").update(canonical(validateHarnessManifest(input)), "utf8").digest("hex");
949
+ }
950
+ async function bindHarness(resolved, ctx) {
951
+ const local = asLocal(resolved.definition);
952
+ const bindings = [];
953
+ const diagnostics = [];
954
+ let disposing;
955
+ const dispose = () => disposing ??= (async () => {
956
+ for (let index = bindings.length - 1; index >= 0; index--) try {
957
+ await bindings[index].dispose?.();
958
+ } catch {
959
+ diagnostics.push({
960
+ code: "cleanup_failed",
961
+ path: `abilities[${index}]`,
962
+ message: "Ability cleanup failed",
963
+ severity: "warning"
964
+ });
965
+ }
966
+ return freeze([...diagnostics]);
967
+ })();
968
+ try {
969
+ for (const [index, ability] of local.abilities.entries()) {
970
+ if (ctx.signal?.aborted) fail(`abilities[${index}]`, "run cancelled before binding");
971
+ const binding = await ability.bind(ctx);
972
+ if (binding && typeof binding === "object") bindings.push(binding);
973
+ if (!binding || !Array.isArray(binding.tools)) fail(`abilities[${index}]`, "binding must contain tools");
974
+ if (ctx.signal?.aborted) fail(`abilities[${index}]`, "run cancelled during binding");
975
+ }
976
+ const tools = bindings.flatMap((binding) => [...binding.tools]);
977
+ const promptFragments = bindings.flatMap((binding) => [...binding.promptFragments ?? []]);
978
+ const contextSources = bindings.flatMap((binding) => [...binding.contextSources ?? []]);
979
+ const middleware = bindings.flatMap((binding) => [...binding.middleware ?? []]);
980
+ unique(tools.map((tool) => id(tool.name, "tools.name")), "tools");
981
+ unique(promptFragments.map((fragment) => id(fragment.id, "promptFragments.id")), "promptFragments");
982
+ unique(contextSources.map((source) => id(source.id, "contextSources.id")), "contextSources");
983
+ unique(middleware.map((item) => id(item.id, "middleware.id")), "middleware");
984
+ for (const tool of tools) if (typeof tool.execute !== "function" || !tool.parameters || ("_zod" in tool.parameters ? tool.parameters._zod.def.type !== "object" : typeof tool.parameters.safeParse !== "function")) fail("tools", "invalid tool binding");
985
+ for (const fragment of promptFragments) if (typeof fragment.text !== "string") fail("promptFragments", "expected text");
986
+ for (const source of contextSources) if (typeof source.fetch !== "function") fail("contextSources", "missing fetch function");
987
+ return {
988
+ tools,
989
+ promptFragments,
990
+ contextSources,
991
+ middleware: sortHarnessMiddleware(middleware),
992
+ dispose
993
+ };
994
+ } catch (error) {
995
+ await dispose();
996
+ if (error instanceof Error && diagnostics.length) try {
997
+ Object.defineProperty(error, "cleanupDiagnostics", { value: [...diagnostics] });
998
+ } catch {}
999
+ throw error;
1000
+ }
1001
+ }
1002
+ //#endregion
1003
+ //#region src/runtime/session-bindings.ts
1004
+ var HarnessSessionConflict = class extends Error {
1005
+ code = "session_conflict";
1006
+ constructor() {
1007
+ super("A writer already owns this tenant/session");
1008
+ }
1009
+ };
1010
+ function harnessSessionKey(identity, sessionId) {
1011
+ return JSON.stringify([
1012
+ identity.tenantId,
1013
+ identity.userId,
1014
+ sessionId
1015
+ ]);
1016
+ }
1017
+ /** One writer per scoped session; deliberately rejects rather than silently interleaving. */
1018
+ var InMemoryHarnessSessionStore = class {
1019
+ guarantees = {
1020
+ durable: false,
1021
+ compareAndSwap: false,
1022
+ singleWriter: "process"
1023
+ };
1024
+ records = /* @__PURE__ */ new Map();
1025
+ active = /* @__PURE__ */ new Set();
1026
+ async acquire(identity, sessionId) {
1027
+ const key = harnessSessionKey(identity, sessionId);
1028
+ if (this.active.has(key)) throw new HarnessSessionConflict();
1029
+ this.active.add(key);
1030
+ let released = false;
1031
+ const check = () => {
1032
+ if (released) throw new Error("Session lease has been released");
1033
+ };
1034
+ return {
1035
+ read: () => {
1036
+ check();
1037
+ return structuredClone(this.records.get(key) ?? {
1038
+ history: [],
1039
+ state: {},
1040
+ revision: 0,
1041
+ replayable: true
1042
+ });
1043
+ },
1044
+ commit: (snapshot) => {
1045
+ check();
1046
+ const revision = (this.records.get(key)?.revision ?? 0) + 1;
1047
+ this.records.set(key, structuredClone({
1048
+ ...snapshot,
1049
+ revision
1050
+ }));
1051
+ },
1052
+ release: () => {
1053
+ if (!released) {
1054
+ released = true;
1055
+ this.active.delete(key);
1056
+ }
1057
+ }
1058
+ };
1059
+ }
1060
+ /** Imported display-only transcripts are readable but never represented as complete replay state. */
1061
+ importLegacy(identity, sessionId, history) {
1062
+ const key = harnessSessionKey(identity, sessionId);
1063
+ if (this.active.has(key)) throw new HarnessSessionConflict();
1064
+ this.records.set(key, {
1065
+ history: structuredClone(history),
1066
+ state: {},
1067
+ revision: 0,
1068
+ replayable: false
1069
+ });
1070
+ }
1071
+ };
1072
+ /** Session resources live until explicit closeSession; run resources use their returned lease. */
1073
+ var HarnessResourcePool = class {
1074
+ sessions = /* @__PURE__ */ new Map();
1075
+ pending = /* @__PURE__ */ new Map();
1076
+ async acquire(identity, sessionId, id, scope, initialize) {
1077
+ if (scope === "run" || scope === "host") {
1078
+ const resource = await initialize();
1079
+ if (scope === "host" && resource.ownership !== "host") {
1080
+ try {
1081
+ await resource.dispose?.();
1082
+ } catch {}
1083
+ throw new Error("Host resources must be externally owned");
1084
+ }
1085
+ let released = false;
1086
+ return {
1087
+ value: resource.value,
1088
+ release: async () => {
1089
+ if (!released) {
1090
+ released = true;
1091
+ if (resource.ownership === "runtime") await resource.dispose?.();
1092
+ }
1093
+ }
1094
+ };
1095
+ }
1096
+ const key = harnessSessionKey(identity, sessionId);
1097
+ let resources = this.sessions.get(key);
1098
+ if (!resources) {
1099
+ resources = /* @__PURE__ */ new Map();
1100
+ this.sessions.set(key, resources);
1101
+ }
1102
+ let entry = resources.get(id);
1103
+ if (!entry) {
1104
+ const pendingKey = JSON.stringify([key, id]);
1105
+ let promise = this.pending.get(pendingKey);
1106
+ if (!promise) {
1107
+ promise = initialize();
1108
+ this.pending.set(pendingKey, promise);
1109
+ }
1110
+ try {
1111
+ const resource = await promise;
1112
+ entry = resources.get(id);
1113
+ if (!entry) {
1114
+ entry = {
1115
+ resource,
1116
+ users: 0
1117
+ };
1118
+ resources.set(id, entry);
1119
+ }
1120
+ } finally {
1121
+ this.pending.delete(pendingKey);
1122
+ }
1123
+ }
1124
+ entry.users++;
1125
+ let released = false;
1126
+ return {
1127
+ value: entry.resource.value,
1128
+ release: async () => {
1129
+ if (!released) {
1130
+ released = true;
1131
+ entry.users--;
1132
+ }
1133
+ }
1134
+ };
1135
+ }
1136
+ async closeSession(identity, sessionId) {
1137
+ const key = harnessSessionKey(identity, sessionId);
1138
+ const resources = this.sessions.get(key);
1139
+ if (!resources) return [];
1140
+ if ([...resources.values()].some((entry) => entry.users > 0) || [...this.pending.keys()].some((pending) => JSON.parse(pending)[0] === key)) throw new HarnessSessionConflict();
1141
+ this.sessions.delete(key);
1142
+ const failures = [];
1143
+ for (const [id, entry] of [...resources].reverse()) try {
1144
+ if (entry.resource.ownership === "runtime") await entry.resource.dispose?.();
1145
+ } catch {
1146
+ failures.push(id);
1147
+ }
1148
+ return failures;
1149
+ }
1150
+ };
1151
+ //#endregion
1152
+ //#region src/runtime/driver.ts
1153
+ var HarnessUnsupportedError = class extends Error {
1154
+ operation;
1155
+ code = "unsupported";
1156
+ constructor(operation) {
1157
+ super(`Unsupported harness operation: ${operation}`);
1158
+ this.operation = operation;
1159
+ }
1160
+ };
1161
+ var HarnessBudgetError = class extends Error {
1162
+ code = "budget_exhausted";
1163
+ constructor(message) {
1164
+ super(message);
1165
+ }
1166
+ };
1167
+ const zero = () => ({
1168
+ promptTokens: 0,
1169
+ completionTokens: 0,
1170
+ totalTokens: 0
1171
+ });
1172
+ function narrow(base, selected, label) {
1173
+ if (new Set(selected).size !== selected.length || selected.some((value) => !base.includes(value))) throw new Error(`Cannot widen ${label} grants`);
1174
+ return [...selected];
1175
+ }
1176
+ /** Local lifecycle owner. Custom driver code is trusted; services enforce all intercepted effects. */
1177
+ var HarnessRuntime = class {
1178
+ sessions;
1179
+ resources;
1180
+ config;
1181
+ artifacts = /* @__PURE__ */ new Map();
1182
+ getArtifact(identity, sessionId, id) {
1183
+ const entry = this.artifacts.get(id);
1184
+ if (!entry || entry.owner !== harnessSessionKey(identity, sessionId)) return void 0;
1185
+ return structuredClone(entry.value);
1186
+ }
1187
+ constructor(config) {
1188
+ const definition = config.definition ? createHarnessDefinition(config.definition) : void 0;
1189
+ const bindings = definition ? resolveHarnessRuntime(definition) : {};
1190
+ const driver = config.driver ?? bindings.driver;
1191
+ if (!driver) throw new Error("A trusted execution driver is required");
1192
+ config = {
1193
+ ...bindings,
1194
+ ...config,
1195
+ driver,
1196
+ definition
1197
+ };
1198
+ if (bindings.modelRoles) {
1199
+ config.models = { ...config.models };
1200
+ for (const [role, name] of Object.entries(bindings.modelRoles)) {
1201
+ const binding = config.models[name];
1202
+ if (!binding) throw new Error(`runtime.modelRoles.${role}: missing host model binding ${name}`);
1203
+ config.models = {
1204
+ ...config.models,
1205
+ [role]: binding
1206
+ };
1207
+ }
1208
+ }
1209
+ if (!driver.id || !Number.isInteger(driver.version) || driver.version < 1) throw new Error("Invalid execution driver identity");
1210
+ if (driver.capabilities.durable) throw new HarnessUnsupportedError("durable recovery without a recovery adapter");
1211
+ if (driver.capabilities.controls.includes("replace")) throw new HarnessUnsupportedError("interrupt-and-replace without a driver checkpoint adapter");
1212
+ if (driver.capabilities.policyCoverage !== "local" || !driver.capabilities.controlledExecution) throw new HarnessUnsupportedError("mandatory local execution coverage");
1213
+ for (const value of Object.values(config.budgets ?? {})) if (!Number.isInteger(value) || value < 0) throw new Error("Harness budgets must be nonnegative integers");
1214
+ this.config = {
1215
+ ...config,
1216
+ projectRoot: (0, node_path.resolve)(config.projectRoot ?? process.cwd()),
1217
+ requirements: config.requirements && Object.freeze([...config.requirements]),
1218
+ budgets: { ...config.budgets },
1219
+ models: Object.fromEntries(Object.entries(config.models ?? {}).map(([role, binding]) => [role, Object.freeze({
1220
+ ...binding,
1221
+ options: Object.freeze([...binding.options ?? []])
1222
+ })])),
1223
+ contextPolicy: config.contextPolicy && Object.freeze({
1224
+ id: config.contextPolicy.id,
1225
+ project: config.contextPolicy.project.bind(config.contextPolicy)
1226
+ }),
1227
+ completionPolicy: config.completionPolicy && Object.freeze({
1228
+ id: config.completionPolicy.id,
1229
+ evaluate: config.completionPolicy.evaluate.bind(config.completionPolicy)
1230
+ }),
1231
+ controller: config.controller && Object.freeze({
1232
+ id: config.controller.id,
1233
+ prepareRun: config.controller.prepareRun?.bind(config.controller),
1234
+ prepareStep: config.controller.prepareStep?.bind(config.controller)
1235
+ }),
1236
+ driver: Object.freeze({
1237
+ id: driver.id,
1238
+ version: driver.version,
1239
+ start: driver.start.bind(driver),
1240
+ capabilities: Object.freeze({
1241
+ ...driver.capabilities,
1242
+ controls: Object.freeze([...driver.capabilities.controls])
1243
+ })
1244
+ }),
1245
+ tools: [...config.tools ?? []],
1246
+ grants: {
1247
+ toolIds: [...config.grants.toolIds],
1248
+ modelRoles: [...config.grants.modelRoles],
1249
+ requiredToolIds: [...config.grants.requiredToolIds ?? []]
1250
+ }
1251
+ };
1252
+ this.sessions = config.sessionStore ?? new InMemoryHarnessSessionStore();
1253
+ this.resources = config.resources ?? new HarnessResourcePool();
1254
+ }
1255
+ run(input, options) {
1256
+ return this.start(input, options).result();
1257
+ }
1258
+ stream(input, options) {
1259
+ return this.start(input, options).events();
1260
+ }
1261
+ start(input, options) {
1262
+ if (!options.identity?.tenantId || !options.identity.userId || !options.sessionId) throw new Error("Verified tenant, actor and session identity are required");
1263
+ const config = this.config;
1264
+ if (options.runMode !== void 0 && options.runMode !== "execute" && options.runMode !== "plan") throw new Error("Invalid run mode");
1265
+ const grants = options.grants ? {
1266
+ toolIds: narrow(config.grants.toolIds, options.grants.toolIds, "tool"),
1267
+ modelRoles: narrow(config.grants.modelRoles, options.grants.modelRoles, "model"),
1268
+ requiredToolIds: [.../* @__PURE__ */ new Set([...config.grants.requiredToolIds ?? [], ...options.grants.requiredToolIds ?? []])]
1269
+ } : config.grants;
1270
+ if (grants.requiredToolIds?.some((id) => !grants.toolIds.includes(id))) throw new Error("Required tool omitted by grants");
1271
+ if (options.deadline !== void 0 && !Number.isFinite(options.deadline)) throw new Error("Deadline must be finite");
1272
+ const controller = new AbortController();
1273
+ const runId = (0, node_crypto.randomUUID)();
1274
+ const request = Object.freeze({
1275
+ input: structuredClone(input),
1276
+ identity: Object.freeze({ ...options.identity }),
1277
+ sessionId: options.sessionId,
1278
+ runId,
1279
+ attemptId: (0, node_crypto.randomUUID)(),
1280
+ parentRunId: options.parentRunId,
1281
+ rootRunId: options.rootRunId ?? runId,
1282
+ signal: controller.signal,
1283
+ deadline: options.deadline,
1284
+ grants: Object.freeze({
1285
+ toolIds: Object.freeze([...grants.toolIds]),
1286
+ modelRoles: Object.freeze([...grants.modelRoles]),
1287
+ requiredToolIds: Object.freeze([...grants.requiredToolIds ?? []])
1288
+ }),
1289
+ runMode: options.runMode ?? "execute"
1290
+ });
1291
+ const events = new InMemoryHarnessEventStore({
1292
+ runId,
1293
+ sessionId: request.sessionId,
1294
+ attemptId: request.attemptId,
1295
+ parentRunId: request.parentRunId,
1296
+ rootRunId: request.rootRunId
1297
+ }, config.eventCapacity);
1298
+ const startedAt = Date.now();
1299
+ config.telemetry?.emit("run.start", {
1300
+ runId,
1301
+ agentName: config.driver.id,
1302
+ input: "",
1303
+ sessionId: request.sessionId,
1304
+ tenantId: request.identity.tenantId,
1305
+ userId: request.identity.userId,
1306
+ parentRunId: request.parentRunId,
1307
+ rootRunId: request.rootRunId,
1308
+ attemptId: request.attemptId
1309
+ });
1310
+ const observeControl = async (operation, work) => {
1311
+ const controllerCallId = (0, node_crypto.randomUUID)();
1312
+ const identity = {
1313
+ runId,
1314
+ controllerCallId,
1315
+ operation
1316
+ };
1317
+ config.telemetry?.emit("controller.start", identity);
1318
+ try {
1319
+ const result = await work();
1320
+ controller.signal.throwIfAborted();
1321
+ const decision = result;
1322
+ config.telemetry?.emit("controller.result", {
1323
+ ...identity,
1324
+ decision: decision?.stop ? "stop" : decision?.action ?? "continue",
1325
+ modelRole: decision?.modelRole,
1326
+ activeToolCount: decision?.activeToolIds?.length
1327
+ });
1328
+ return result;
1329
+ } catch (error) {
1330
+ config.telemetry?.emit("controller.error", {
1331
+ ...identity,
1332
+ status: controller.signal.aborted ? "cancelled" : "error"
1333
+ });
1334
+ throw error;
1335
+ }
1336
+ };
1337
+ const observeModel = (provider) => {
1338
+ const identity = {
1339
+ runId,
1340
+ modelCallId: (0, node_crypto.randomUUID)(),
1341
+ providerId: provider.providerId,
1342
+ modelId: provider.modelId
1343
+ };
1344
+ config.telemetry?.emit("model.start", identity);
1345
+ return (complete, amount) => {
1346
+ if (complete) config.telemetry?.emit("model.result", {
1347
+ ...identity,
1348
+ usage: amount,
1349
+ status: "success"
1350
+ });
1351
+ else config.telemetry?.emit("model.error", {
1352
+ ...identity,
1353
+ status: controller.signal.aborted ? "cancelled" : "error",
1354
+ ...amount ? { usage: amount } : {}
1355
+ });
1356
+ };
1357
+ };
1358
+ const generateObserved = async (provider, messages, supplied) => {
1359
+ const end = observeModel(provider);
1360
+ try {
1361
+ const response = await provider.generate(messages, supplied);
1362
+ end(!controller.signal.aborted, response.usage);
1363
+ return response;
1364
+ } catch (error) {
1365
+ end(false);
1366
+ throw error;
1367
+ }
1368
+ };
1369
+ let terminal = false;
1370
+ const inbox = [];
1371
+ const cancel = (reason = "Run cancelled") => {
1372
+ if (!terminal && !controller.signal.aborted) {
1373
+ events.append({
1374
+ type: "control",
1375
+ operation: "cancel",
1376
+ reason
1377
+ });
1378
+ controller.abort(new Error(reason));
1379
+ }
1380
+ };
1381
+ const onAbort = () => cancel("Caller cancelled the run");
1382
+ const callerSignal = options.signal;
1383
+ callerSignal?.addEventListener("abort", onAbort, { once: true });
1384
+ if (callerSignal?.aborted) onAbort();
1385
+ let timer;
1386
+ const scheduleDeadline = () => {
1387
+ if (request.deadline === void 0 || controller.signal.aborted) return;
1388
+ const remaining = request.deadline - Date.now();
1389
+ if (remaining <= 0) cancel("Run deadline exceeded");
1390
+ else timer = setTimeout(scheduleDeadline, Math.min(remaining, 2147483647));
1391
+ };
1392
+ scheduleDeadline();
1393
+ const result = Promise.resolve().then(async () => {
1394
+ let lease;
1395
+ let bound;
1396
+ const releases = [];
1397
+ const usage = zero();
1398
+ let modelCalls = 0;
1399
+ let reservedTokens = 0;
1400
+ const cleanupDiagnostics = [];
1401
+ let toolCalls = 0;
1402
+ let revision = 0;
1403
+ let currentRole = "main";
1404
+ let prepareDefaults = {};
1405
+ let final = {
1406
+ text: "",
1407
+ status: "failed"
1408
+ };
1409
+ let canonical = [];
1410
+ let conversations = {};
1411
+ let state = {};
1412
+ let replayable = true;
1413
+ let committed = false;
1414
+ let acceptingOperations = true;
1415
+ const owned = /* @__PURE__ */ new Set();
1416
+ const streams = /* @__PURE__ */ new Set();
1417
+ const track = (operation) => {
1418
+ owned.add(operation);
1419
+ operation.then(() => owned.delete(operation), () => owned.delete(operation));
1420
+ return operation;
1421
+ };
1422
+ const ensureActive = () => {
1423
+ if (request.deadline !== void 0 && Date.now() >= request.deadline) cancel("Run deadline exceeded");
1424
+ controller.signal.throwIfAborted();
1425
+ if (!acceptingOperations) throw new Error("Run has finished accepting operations");
1426
+ };
1427
+ const activeTools = /* @__PURE__ */ new Map();
1428
+ try {
1429
+ ensureActive();
1430
+ lease = await this.sessions.acquire(request.identity, request.sessionId);
1431
+ ensureActive();
1432
+ const snapshot = lease.read();
1433
+ canonical = snapshot.history;
1434
+ conversations = snapshot.conversations ?? {};
1435
+ state = snapshot.state;
1436
+ replayable = snapshot.replayable;
1437
+ let policy;
1438
+ let services;
1439
+ const ctx = new _agentium_core.RunContext({
1440
+ executionPolicy: {
1441
+ decide: (call, callCtx) => policy.decide(call, callCtx),
1442
+ resolveEffect: (call, callCtx) => policy.resolveEffect?.(call, callCtx) ?? "unknown"
1443
+ },
1444
+ sessionId: request.sessionId,
1445
+ userId: request.identity.userId,
1446
+ tenantId: request.identity.tenantId,
1447
+ runId,
1448
+ signal: controller.signal,
1449
+ runMode: request.runMode,
1450
+ eventBus: new _agentium_core.EventBus(),
1451
+ sessionState: state,
1452
+ metadata: {
1453
+ parentRunId: request.parentRunId,
1454
+ rootRunId: request.rootRunId
1455
+ }
1456
+ });
1457
+ Object.defineProperty(ctx, "executionServices", {
1458
+ get: () => services,
1459
+ configurable: false
1460
+ });
1461
+ policy = {
1462
+ decide: async (call, callCtx) => {
1463
+ ensureActive();
1464
+ if (!request.grants.toolIds.includes(call.toolName) || activeTools.has(callCtx.runId) && !activeTools.get(callCtx.runId).includes(call.toolName)) return {
1465
+ action: "deny",
1466
+ reason: "Tool is outside the effective grants"
1467
+ };
1468
+ if (revision > 0 && !config.allowRevisionEffects) return {
1469
+ action: "deny",
1470
+ reason: "Completion revisions cannot execute effects without host opt-in"
1471
+ };
1472
+ if (toolCalls >= (config.budgets?.maxToolCalls ?? Infinity)) return {
1473
+ action: "deny",
1474
+ reason: "Aggregate tool budget exhausted"
1475
+ };
1476
+ toolCalls++;
1477
+ return (0, _agentium_core.evaluateExecutionPolicy)(config.executionPolicy, call, callCtx);
1478
+ },
1479
+ resolveEffect: config.executionPolicy?.resolveEffect
1480
+ };
1481
+ const resolved = config.definition ? resolveHarness(config.definition, {
1482
+ projectRoot: config.projectRoot ?? process.cwd(),
1483
+ requirements: config.requirements
1484
+ }) : void 0;
1485
+ if (resolved) bound = await bindHarness(resolved, ctx);
1486
+ ensureActive();
1487
+ const tools = [...config.tools ?? [], ...bound?.tools ?? []];
1488
+ if (new Set(tools.map((tool) => tool.name)).size !== tools.length) throw new Error("Duplicate runtime tool bindings");
1489
+ const executor = new _agentium_core.ToolExecutor(tools, {
1490
+ approvalManager: config.approvalManager,
1491
+ executionPolicy: policy
1492
+ });
1493
+ const sources = bound ? await fetchHarnessContext(bound.contextSources, typeof input === "string" ? input : JSON.stringify(input), ctx) : { messages: [] };
1494
+ const entries = sources.messages.map((message) => {
1495
+ const source = JSON.parse(String(message.content));
1496
+ return {
1497
+ id: source.entryId,
1498
+ text: source.text,
1499
+ trust: "source",
1500
+ source: source.source,
1501
+ byteLength: Buffer.byteLength(source.text)
1502
+ };
1503
+ });
1504
+ const middleware = bound?.middleware ?? [];
1505
+ const prompts = (bound?.promptFragments ?? []).map((fragment) => ({
1506
+ role: "system",
1507
+ content: fragment.text
1508
+ }));
1509
+ const prepare = async (provider, messages, supplied = {}, callCtx = ctx) => {
1510
+ ensureActive();
1511
+ if (modelCalls >= (config.budgets?.maxModelCalls ?? Infinity) || usage.totalTokens >= (config.budgets?.maxTokens ?? Infinity)) throw new HarnessBudgetError("Aggregate model/token budget exhausted");
1512
+ const index = modelCalls++;
1513
+ const next = config.controller?.prepareStep ? await observeControl("prepareStep", () => config.controller.prepareStep({
1514
+ index,
1515
+ tools: supplied.tools ?? []
1516
+ }, callCtx)) : void 0;
1517
+ const overrides = {
1518
+ ...prepareDefaults,
1519
+ ...next
1520
+ };
1521
+ if (overrides.stop) throw new HarnessBudgetError(overrides.stop.reason);
1522
+ const role = overrides.modelRole ?? "main";
1523
+ if (!request.grants.modelRoles.includes(role)) throw new Error("Controller selected an ungranted model role");
1524
+ const binding = config.models?.[role];
1525
+ const selected = binding?.provider ?? (role === "main" ? provider : void 0);
1526
+ if (!selected) throw new Error("No host model binding for selected role");
1527
+ const active = overrides.activeToolIds ?? (supplied.tools ?? []).map((tool) => tool.name).filter((id) => request.grants.toolIds.includes(id));
1528
+ narrow(request.grants.toolIds, active, "active tool");
1529
+ if (request.grants.requiredToolIds?.some((id) => !active.includes(id))) throw new Error("Controller omitted a required tool");
1530
+ if (active.some((id) => !(supplied.tools ?? []).some((tool) => tool.name === id))) throw new Error("Controller selected an unavailable tool");
1531
+ for (const key of Object.keys(overrides.options ?? {})) if (!binding?.options?.includes(key)) throw new HarnessUnsupportedError(`model option ${key}`);
1532
+ if (currentRole !== role && messages.some((message) => message.providerExtras && !message.providerExtras.harnessContext)) throw new Error("Cannot switch model roles while opaque provider continuation is present");
1533
+ currentRole = role;
1534
+ const composed = [
1535
+ ...prompts,
1536
+ ...sources.messages,
1537
+ ...messages
1538
+ ];
1539
+ const transformed = await runBeforeModel(middleware, composed, callCtx);
1540
+ const projection = await projectHarnessContext(overrides.contextPolicy ?? config.contextPolicy, transformed, callCtx, entries);
1541
+ ensureActive();
1542
+ const remaining = (config.budgets?.maxTokens ?? Infinity) - usage.totalTokens - reservedTokens;
1543
+ if (remaining <= 0) throw new HarnessBudgetError("Aggregate token budget exhausted or reserved by another call");
1544
+ for (const value of [supplied.maxTokens, overrides.options?.maxTokens]) if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) throw new HarnessUnsupportedError("maxTokens must be a finite positive integer");
1545
+ const options = {
1546
+ ...supplied,
1547
+ ...overrides.options,
1548
+ signal: controller.signal,
1549
+ tools: supplied.tools?.filter((tool) => active.includes(tool.name)),
1550
+ ...Number.isFinite(remaining) ? { maxTokens: Math.min(supplied.maxTokens ?? Infinity, overrides.options?.maxTokens ?? Infinity, remaining) } : {}
1551
+ };
1552
+ activeTools.set(callCtx.runId, active);
1553
+ const reservation = Number.isFinite(remaining) ? options.maxTokens ?? remaining : 0;
1554
+ reservedTokens += reservation;
1555
+ return {
1556
+ provider: selected,
1557
+ messages: projection.messages,
1558
+ options,
1559
+ reservation
1560
+ };
1561
+ };
1562
+ const account = (provider, amount) => {
1563
+ if ([
1564
+ amount.promptTokens,
1565
+ amount.completionTokens,
1566
+ amount.totalTokens
1567
+ ].some((value) => !Number.isFinite(value) || value < 0)) throw new Error("Provider returned invalid token usage");
1568
+ usage.promptTokens += amount.promptTokens;
1569
+ usage.completionTokens += amount.completionTokens;
1570
+ usage.totalTokens += amount.totalTokens;
1571
+ for (const key of [
1572
+ "reasoningTokens",
1573
+ "cachedTokens",
1574
+ "audioInputTokens",
1575
+ "audioOutputTokens"
1576
+ ]) if (amount[key] !== void 0) usage[key] = (usage[key] ?? 0) + amount[key];
1577
+ if (!controller.signal.aborted && !terminal) events.append({
1578
+ type: "model.complete",
1579
+ providerId: provider.providerId,
1580
+ modelId: provider.modelId,
1581
+ usage: {
1582
+ promptTokens: amount.promptTokens,
1583
+ completionTokens: amount.completionTokens,
1584
+ totalTokens: amount.totalTokens
1585
+ }
1586
+ });
1587
+ };
1588
+ services = {
1589
+ ctx,
1590
+ signal: controller.signal,
1591
+ tools,
1592
+ definitionId: config.definition?.kind === "portable" ? config.definition.manifest.id : config.definition?.id,
1593
+ agentConfiguration: {
1594
+ defaults: resolved?.defaults ?? {},
1595
+ limits: resolved?.limits ?? {},
1596
+ projectRoot: config.projectRoot ?? process.cwd()
1597
+ },
1598
+ executionPolicy: policy,
1599
+ approvalManager: config.approvalManager,
1600
+ state,
1601
+ sessionKey: harnessSessionKey(request.identity, request.sessionId),
1602
+ get history() {
1603
+ return structuredClone(canonical);
1604
+ },
1605
+ controlModel: (role, messages, options = {}) => track((async () => {
1606
+ ensureActive();
1607
+ if (!request.grants.modelRoles.includes(role)) throw new Error("Ungranted control model role");
1608
+ const binding = config.models?.[role];
1609
+ if (!binding) throw new Error("Control model requires an explicit host binding");
1610
+ for (const key of Object.keys(options)) if (!["maxTokens", "temperature"].includes(key) || !binding.options?.includes(key)) throw new HarnessUnsupportedError(`control model option ${key}`);
1611
+ if (options.maxTokens !== void 0 && (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0)) throw new HarnessUnsupportedError("maxTokens must be a finite positive integer");
1612
+ if (options.temperature !== void 0 && (!Number.isFinite(options.temperature) || options.temperature < 0)) throw new HarnessUnsupportedError("temperature must be finite and non-negative");
1613
+ const projection = structuredClone(messages);
1614
+ if (projection.some((message) => message.role === "tool" || message.toolCalls?.length || message.providerExtras)) throw new Error("Control model calls require independent plain messages without provider/tool continuation");
1615
+ (0, _agentium_core.validateConversationTransform)(projection, projection);
1616
+ const remaining = (config.budgets?.maxTokens ?? Infinity) - usage.totalTokens - reservedTokens;
1617
+ if (modelCalls >= (config.budgets?.maxModelCalls ?? Infinity) || remaining <= 0) throw new HarnessBudgetError("Aggregate model/token budget exhausted");
1618
+ const maxTokens = Math.min(options.maxTokens ?? Infinity, remaining);
1619
+ const reservation = Number.isFinite(maxTokens) ? maxTokens : 0;
1620
+ modelCalls++;
1621
+ reservedTokens += reservation;
1622
+ try {
1623
+ ensureActive();
1624
+ const response = await generateObserved(binding.provider, projection, {
1625
+ ...options,
1626
+ ...Number.isFinite(maxTokens) ? { maxTokens } : {},
1627
+ signal: controller.signal
1628
+ });
1629
+ account(binding.provider, response.usage);
1630
+ ensureActive();
1631
+ if (response.finishReason !== "stop" || response.message.toolCalls?.length) throw new Error("Control model must return a complete tool-free response");
1632
+ return response;
1633
+ } finally {
1634
+ reservedTokens -= reservation;
1635
+ }
1636
+ })()),
1637
+ model: (provider, messages, options, callCtx) => track((async () => {
1638
+ const call = await prepare(provider, messages, options, callCtx);
1639
+ try {
1640
+ ensureActive();
1641
+ const response = await generateObserved(call.provider, call.messages, call.options);
1642
+ account(call.provider, response.usage);
1643
+ ensureActive();
1644
+ await runAfterModel(middleware, response, callCtx ?? ctx);
1645
+ return response;
1646
+ } finally {
1647
+ reservedTokens -= call.reservation;
1648
+ }
1649
+ })()),
1650
+ streamModel: (provider, messages, options, callCtx) => {
1651
+ let finishLifetime;
1652
+ track(new Promise((resolve) => {
1653
+ finishLifetime = resolve;
1654
+ }));
1655
+ const implementation = (async function* () {
1656
+ const call = await prepare(provider, messages, options, callCtx);
1657
+ const endObservation = observeModel(call.provider);
1658
+ let stream;
1659
+ let succeeded = false;
1660
+ let text = "";
1661
+ let finished = false;
1662
+ let amount = zero();
1663
+ let reason = "stop";
1664
+ let extras;
1665
+ const calls = [];
1666
+ try {
1667
+ ensureActive();
1668
+ stream = call.provider.stream(call.messages, call.options);
1669
+ while (true) {
1670
+ const next = await stream.next();
1671
+ if (!next.done && next.value.type === "finish") {
1672
+ if (finished) throw new Error("Provider emitted duplicate terminal usage");
1673
+ finished = true;
1674
+ reason = next.value.finishReason;
1675
+ extras = next.value.providerExtras;
1676
+ amount = next.value.usage ?? zero();
1677
+ account(call.provider, amount);
1678
+ }
1679
+ ensureActive();
1680
+ if (next.done) break;
1681
+ if (next.value.type === "text") text += next.value.text;
1682
+ else if (next.value.type === "tool_call_start") calls.push({
1683
+ ...next.value.toolCall,
1684
+ args: ""
1685
+ });
1686
+ else if (next.value.type === "tool_call_delta") {
1687
+ const chunk = next.value;
1688
+ const pending = calls.find((item) => item.id === chunk.toolCallId);
1689
+ if (pending) pending.args += chunk.argumentsDelta;
1690
+ }
1691
+ yield next.value;
1692
+ }
1693
+ if (!finished) throw new Error("Provider stream ended without a terminal response");
1694
+ const response = {
1695
+ message: {
1696
+ role: "assistant",
1697
+ content: text || null,
1698
+ ...calls.length ? { toolCalls: calls.map((item) => ({
1699
+ id: item.id,
1700
+ name: item.name,
1701
+ arguments: JSON.parse(item.args)
1702
+ })) } : {},
1703
+ ...extras ? { providerExtras: extras } : {}
1704
+ },
1705
+ usage: amount,
1706
+ finishReason: reason === "tool_calls" || reason === "length" || reason === "content_filter" ? reason : "stop",
1707
+ raw: { streamed: true }
1708
+ };
1709
+ await runAfterModel(middleware, response, callCtx ?? ctx);
1710
+ succeeded = true;
1711
+ } finally {
1712
+ endObservation(succeeded, finished ? amount : void 0);
1713
+ reservedTokens -= call.reservation;
1714
+ await stream?.return(void 0);
1715
+ }
1716
+ })();
1717
+ const finish = () => {
1718
+ streams.delete(iterator);
1719
+ finishLifetime();
1720
+ };
1721
+ const iterator = {
1722
+ [Symbol.asyncIterator]() {
1723
+ return this;
1724
+ },
1725
+ [Symbol.asyncDispose]: async () => {
1726
+ await iterator.return(void 0);
1727
+ },
1728
+ next: (...args) => track(implementation.next(...args)).then((item) => {
1729
+ if (item.done) finish();
1730
+ return item;
1731
+ }, (error) => {
1732
+ finish();
1733
+ throw error;
1734
+ }),
1735
+ return: (value) => track(implementation.return(value)).finally(finish),
1736
+ throw: (error) => track(implementation.throw(error)).finally(finish)
1737
+ };
1738
+ streams.add(iterator);
1739
+ return iterator;
1740
+ },
1741
+ runOwned: (operation) => track((async () => {
1742
+ ensureActive();
1743
+ return operation();
1744
+ })()),
1745
+ observeTool: async (result, callCtx) => {
1746
+ ensureActive();
1747
+ await runAfterTool(middleware, result, callCtx ?? ctx);
1748
+ services.emit({
1749
+ type: "tool.complete",
1750
+ toolName: result.toolName,
1751
+ toolCallId: result.toolCallId,
1752
+ denied: Boolean(result.error)
1753
+ });
1754
+ },
1755
+ dispatch: (call) => track((async () => {
1756
+ ensureActive();
1757
+ const [result] = await executor.executeAll([call], ctx);
1758
+ ensureActive();
1759
+ await runAfterTool(middleware, result, ctx);
1760
+ services.emit({
1761
+ type: "tool.complete",
1762
+ toolName: call.name,
1763
+ toolCallId: call.id,
1764
+ denied: Boolean(result.error)
1765
+ });
1766
+ return result;
1767
+ })()),
1768
+ dispatchEffect: (name, args, execute) => track((async () => {
1769
+ ensureActive();
1770
+ let value;
1771
+ const tool = {
1772
+ name,
1773
+ description: "Trusted deterministic workflow step",
1774
+ parameters: zod_v3.z.object({}).passthrough(),
1775
+ execute: async (validated, callCtx) => {
1776
+ value = await execute(validated, callCtx);
1777
+ return "Completed";
1778
+ }
1779
+ };
1780
+ const [result] = await new _agentium_core.ToolExecutor([tool], {
1781
+ approvalManager: config.approvalManager,
1782
+ executionPolicy: policy
1783
+ }).executeAll([{
1784
+ id: (0, node_crypto.randomUUID)(),
1785
+ name,
1786
+ arguments: args
1787
+ }], ctx);
1788
+ ensureActive();
1789
+ await runAfterTool(middleware, result, ctx);
1790
+ services.emit({
1791
+ type: "tool.complete",
1792
+ toolName: name,
1793
+ toolCallId: result.toolCallId,
1794
+ denied: Boolean(result.error)
1795
+ });
1796
+ if (result.error) throw new Error(result.error);
1797
+ return value;
1798
+ })()),
1799
+ recordConversation: (executionId, messages) => {
1800
+ ensureActive();
1801
+ const combined = [...conversations[executionId] ?? [], ...structuredClone(messages)];
1802
+ (0, _agentium_core.validateConversationTransform)(combined, combined);
1803
+ conversations[executionId] = combined;
1804
+ if (executionId === request.runId) canonical.push(...structuredClone(messages));
1805
+ },
1806
+ append: (messages) => {
1807
+ ensureActive();
1808
+ canonical.push(...structuredClone(messages));
1809
+ },
1810
+ emit: (payload) => {
1811
+ ensureActive();
1812
+ if (Buffer.byteLength(JSON.stringify(payload)) > 65536) throw new Error("Generic event exceeds 64KB; store an artifact reference instead");
1813
+ events.append(payload);
1814
+ },
1815
+ takeInput: () => {
1816
+ ensureActive();
1817
+ const index = inbox.findIndex((item) => item.mode === "steer");
1818
+ return index < 0 ? void 0 : inbox.splice(index, 1)[0];
1819
+ },
1820
+ resource: (id, scope, initialize) => track((async () => {
1821
+ ensureActive();
1822
+ const lease = await this.resources.acquire(request.identity, request.sessionId, id, scope, initialize);
1823
+ releases.push(lease.release);
1824
+ ensureActive();
1825
+ return lease.value;
1826
+ })()),
1827
+ putArtifact: (value) => {
1828
+ ensureActive();
1829
+ const id = (0, node_crypto.randomUUID)();
1830
+ this.artifacts.set(id, {
1831
+ owner: services.sessionKey,
1832
+ value: structuredClone(value)
1833
+ });
1834
+ return id;
1835
+ },
1836
+ getArtifact: (id) => {
1837
+ ensureActive();
1838
+ return this.getArtifact(request.identity, request.sessionId, id);
1839
+ }
1840
+ };
1841
+ prepareDefaults = (config.controller?.prepareRun ? await observeControl("prepareRun", () => config.controller.prepareRun(ctx)) : void 0) ?? {};
1842
+ if (prepareDefaults.stop) throw new HarnessBudgetError(prepareDefaults.stop.reason);
1843
+ events.append({
1844
+ type: "run.started",
1845
+ driverId: config.driver.id
1846
+ });
1847
+ let current = request;
1848
+ while (true) {
1849
+ ensureActive();
1850
+ final = await config.driver.start(current, services);
1851
+ if (streams.size) throw new Error("Driver returned with an unfinished model stream");
1852
+ ensureActive();
1853
+ if (!final || typeof final.text !== "string" || final.status !== void 0 && ![
1854
+ "completed",
1855
+ "failed",
1856
+ "cancelled",
1857
+ "stopped",
1858
+ "awaiting_input"
1859
+ ].includes(final.status)) throw new Error("Driver returned an invalid terminal output");
1860
+ final = structuredClone(final);
1861
+ JSON.stringify(final);
1862
+ if (final.history) {
1863
+ const proposed = structuredClone(final.history);
1864
+ if (canonical.some((message, index) => JSON.stringify(message) !== JSON.stringify(proposed[index]))) throw new Error("Driver replaced immutable canonical history");
1865
+ (0, _agentium_core.validateConversationTransform)(proposed, proposed);
1866
+ canonical = proposed;
1867
+ }
1868
+ if (final.status && final.status !== "completed") break;
1869
+ const decision = config.completionPolicy ? await observeControl("completion", () => config.completionPolicy.evaluate({
1870
+ text: final.text,
1871
+ structured: final.structured,
1872
+ revision
1873
+ }, ctx)) : void 0;
1874
+ if (decision) {
1875
+ services.emit({
1876
+ type: "completion",
1877
+ action: decision.action,
1878
+ reason: decision.reason
1879
+ });
1880
+ if (decision.action === "stop" || decision.action === "await_input") {
1881
+ final = {
1882
+ ...final,
1883
+ status: decision.action === "stop" ? "stopped" : "awaiting_input",
1884
+ reason: {
1885
+ code: decision.action,
1886
+ message: decision.reason
1887
+ }
1888
+ };
1889
+ break;
1890
+ }
1891
+ if (decision.action === "revise") {
1892
+ if (revision >= (config.budgets?.maxRevisions ?? 0)) {
1893
+ final = {
1894
+ ...final,
1895
+ status: "stopped",
1896
+ reason: {
1897
+ code: "revision_limit",
1898
+ message: "Completion revision limit reached"
1899
+ }
1900
+ };
1901
+ break;
1902
+ }
1903
+ revision++;
1904
+ current = {
1905
+ ...request,
1906
+ input: decision.instruction
1907
+ };
1908
+ continue;
1909
+ }
1910
+ }
1911
+ const followUp = inbox.findIndex((item) => item.mode === "follow_up");
1912
+ if (followUp < 0) break;
1913
+ current = {
1914
+ ...request,
1915
+ input: inbox.splice(followUp, 1)[0].input
1916
+ };
1917
+ }
1918
+ const rejected = (await Promise.allSettled([...owned])).find((entry) => entry.status === "rejected");
1919
+ if (rejected?.status === "rejected") throw rejected.reason;
1920
+ ensureActive();
1921
+ lease.commit({
1922
+ history: canonical,
1923
+ conversations,
1924
+ state,
1925
+ replayable
1926
+ });
1927
+ committed = true;
1928
+ } catch (error) {
1929
+ final = {
1930
+ text: "",
1931
+ status: controller.signal.aborted ? "cancelled" : error instanceof HarnessBudgetError ? "stopped" : "failed",
1932
+ reason: {
1933
+ code: controller.signal.aborted ? "cancelled" : error instanceof HarnessBudgetError ? error.code : error?.code ?? "execution_failed",
1934
+ message: controller.signal.aborted ? "Cancellation acknowledged; already started external effects are not rolled back" : error instanceof Error ? error.message : "Execution failed"
1935
+ }
1936
+ };
1937
+ } finally {
1938
+ acceptingOperations = false;
1939
+ await Promise.allSettled([...streams].map((stream) => stream.return(void 0)));
1940
+ await Promise.allSettled([...owned]);
1941
+ if (lease && !committed) try {
1942
+ lease.commit({
1943
+ history: canonical,
1944
+ conversations,
1945
+ state,
1946
+ replayable: false
1947
+ });
1948
+ } catch {
1949
+ cleanupDiagnostics.push("partial_history_commit_failed");
1950
+ }
1951
+ for (const release of releases.reverse()) try {
1952
+ await release();
1953
+ } catch {
1954
+ cleanupDiagnostics.push("resource_release_failed");
1955
+ }
1956
+ try {
1957
+ const diagnostics = await bound?.dispose();
1958
+ cleanupDiagnostics.push(...(diagnostics ?? []).map((item) => item.code));
1959
+ } catch {
1960
+ cleanupDiagnostics.push("binding_release_failed");
1961
+ }
1962
+ try {
1963
+ lease?.release();
1964
+ } catch {
1965
+ cleanupDiagnostics.push("session_release_failed");
1966
+ }
1967
+ if (timer) clearTimeout(timer);
1968
+ callerSignal?.removeEventListener("abort", onAbort);
1969
+ }
1970
+ if (controller.signal.aborted) final = {
1971
+ ...final,
1972
+ status: "cancelled",
1973
+ reason: {
1974
+ code: "cancelled",
1975
+ message: "Cancellation acknowledged"
1976
+ }
1977
+ };
1978
+ const terminalResult = {
1979
+ status: final.status ?? "completed",
1980
+ text: final.text,
1981
+ ...final.structured !== void 0 ? { structured: final.structured } : {},
1982
+ ...final.reason ? { reason: final.reason } : {},
1983
+ ...final.artifacts ? { artifacts: final.artifacts } : {},
1984
+ usage: modelCalls ? usage : final.usage ?? usage,
1985
+ runId,
1986
+ sessionId: request.sessionId,
1987
+ finalCursor: events.cursor + 1
1988
+ };
1989
+ if (cleanupDiagnostics.length) terminalResult.cleanupDiagnostics = cleanupDiagnostics;
1990
+ if (Buffer.byteLength(JSON.stringify(terminalResult)) > 65536) {
1991
+ const id = (0, node_crypto.randomUUID)();
1992
+ this.artifacts.set(id, {
1993
+ owner: harnessSessionKey(request.identity, request.sessionId),
1994
+ value: structuredClone(terminalResult)
1995
+ });
1996
+ terminalResult.text = terminalResult.text.slice(0, 4096);
1997
+ delete terminalResult.structured;
1998
+ terminalResult.artifacts = [{
1999
+ id,
2000
+ mimeType: "application/json"
2001
+ }];
2002
+ if (terminalResult.reason) terminalResult.reason = {
2003
+ code: terminalResult.reason.code.slice(0, 128),
2004
+ message: terminalResult.reason.message.slice(0, 2048)
2005
+ };
2006
+ }
2007
+ terminal = true;
2008
+ events.append({
2009
+ type: "run.terminal",
2010
+ result: terminalResult
2011
+ });
2012
+ config.telemetry?.emit("run.complete", {
2013
+ runId,
2014
+ output: {
2015
+ text: "",
2016
+ toolCalls: [],
2017
+ usage: terminalResult.usage,
2018
+ runId,
2019
+ agentName: config.driver.id,
2020
+ sessionId: request.sessionId,
2021
+ durationMs: Date.now() - startedAt,
2022
+ status: terminalResult.status === "failed" ? "error" : terminalResult.status === "awaiting_input" ? "stopped" : terminalResult.status
2023
+ }
2024
+ });
2025
+ return structuredClone(terminalResult);
2026
+ });
2027
+ return {
2028
+ runId,
2029
+ events: (options) => events.events(options),
2030
+ result: () => result.then((value) => structuredClone(value)),
2031
+ cancel,
2032
+ send: async (input, options) => {
2033
+ if (terminal || controller.signal.aborted) throw new Error("Run is no longer active");
2034
+ if (!config.driver.capabilities.controls.includes(options.mode)) throw new HarnessUnsupportedError(options.mode);
2035
+ if (inbox.length >= 32) throw new Error("Run input queue is full");
2036
+ inbox.push({
2037
+ input: structuredClone(input),
2038
+ mode: options.mode
2039
+ });
2040
+ events.append({
2041
+ type: "control",
2042
+ operation: options.mode
2043
+ });
2044
+ }
2045
+ };
2046
+ }
2047
+ };
2048
+ //#endregion
2049
+ Object.defineProperty(exports, "HarnessBudgetError", {
2050
+ enumerable: true,
2051
+ get: function() {
2052
+ return HarnessBudgetError;
2053
+ }
2054
+ });
2055
+ Object.defineProperty(exports, "HarnessEventGapError", {
2056
+ enumerable: true,
2057
+ get: function() {
2058
+ return HarnessEventGapError;
2059
+ }
2060
+ });
2061
+ Object.defineProperty(exports, "HarnessResourcePool", {
2062
+ enumerable: true,
2063
+ get: function() {
2064
+ return HarnessResourcePool;
2065
+ }
2066
+ });
2067
+ Object.defineProperty(exports, "HarnessRuntime", {
2068
+ enumerable: true,
2069
+ get: function() {
2070
+ return HarnessRuntime;
2071
+ }
2072
+ });
2073
+ Object.defineProperty(exports, "HarnessSessionConflict", {
2074
+ enumerable: true,
2075
+ get: function() {
2076
+ return HarnessSessionConflict;
2077
+ }
2078
+ });
2079
+ Object.defineProperty(exports, "HarnessUnsupportedError", {
2080
+ enumerable: true,
2081
+ get: function() {
2082
+ return HarnessUnsupportedError;
2083
+ }
2084
+ });
2085
+ Object.defineProperty(exports, "InMemoryHarnessEventStore", {
2086
+ enumerable: true,
2087
+ get: function() {
2088
+ return InMemoryHarnessEventStore;
2089
+ }
2090
+ });
2091
+ Object.defineProperty(exports, "InMemoryHarnessSessionStore", {
2092
+ enumerable: true,
2093
+ get: function() {
2094
+ return InMemoryHarnessSessionStore;
2095
+ }
2096
+ });
2097
+ Object.defineProperty(exports, "createHarnessDefinition", {
2098
+ enumerable: true,
2099
+ get: function() {
2100
+ return createHarnessDefinition;
2101
+ }
2102
+ });
2103
+ Object.defineProperty(exports, "describeHarnessDefinition", {
2104
+ enumerable: true,
2105
+ get: function() {
2106
+ return describeHarnessDefinition;
2107
+ }
2108
+ });
2109
+ Object.defineProperty(exports, "exportHarnessManifest", {
2110
+ enumerable: true,
2111
+ get: function() {
2112
+ return exportHarnessManifest;
2113
+ }
2114
+ });
2115
+ Object.defineProperty(exports, "extendHarnessDefinition", {
2116
+ enumerable: true,
2117
+ get: function() {
2118
+ return extendHarnessDefinition;
2119
+ }
2120
+ });
2121
+ Object.defineProperty(exports, "hashHarnessManifest", {
2122
+ enumerable: true,
2123
+ get: function() {
2124
+ return hashHarnessManifest;
2125
+ }
2126
+ });
2127
+ Object.defineProperty(exports, "loadHarnessManifest", {
2128
+ enumerable: true,
2129
+ get: function() {
2130
+ return loadHarnessManifest;
2131
+ }
2132
+ });
2133
+ Object.defineProperty(exports, "resolveHarnessRuntime", {
2134
+ enumerable: true,
2135
+ get: function() {
2136
+ return resolveHarnessRuntime;
2137
+ }
2138
+ });