@nylorun/harness 0.11.1-beta → 0.13.0-beta

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 (131) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +124 -5
  3. package/dist/definition/agent-definition.d.ts +11 -0
  4. package/dist/definition/agent-definition.js +12 -0
  5. package/dist/definition/assemble.d.ts +22 -0
  6. package/dist/{build → definition}/assemble.js +6 -7
  7. package/dist/definition/bind-agent.d.ts +9 -0
  8. package/dist/definition/bind-agent.js +21 -0
  9. package/dist/{build → definition}/bind-tool.d.ts +2 -1
  10. package/dist/{build → definition}/bind-tool.js +1 -0
  11. package/dist/definition/bound.d.ts +16 -0
  12. package/dist/definition/builder.d.ts +30 -0
  13. package/dist/definition/builder.js +94 -0
  14. package/dist/definition/declaration.d.ts +3 -0
  15. package/dist/definition/declaration.js +53 -0
  16. package/dist/{build → definition}/helpers.d.ts +1 -1
  17. package/dist/{build → definition}/manifest.d.ts +3 -1
  18. package/dist/definition/manifest.js +45 -0
  19. package/dist/{session → definition}/output-contract.d.ts +2 -2
  20. package/dist/{session → definition}/output-contract.js +1 -1
  21. package/dist/definition/registry.d.ts +10 -0
  22. package/dist/definition/registry.js +48 -0
  23. package/dist/{build → definition}/schema.d.ts +4 -4
  24. package/dist/errors.d.ts +1 -1
  25. package/dist/execution/dispatch.d.ts +2 -0
  26. package/dist/execution/dispatch.js +164 -0
  27. package/dist/execution/initial-state.d.ts +3 -0
  28. package/dist/execution/initial-state.js +14 -0
  29. package/dist/execution/invocation.d.ts +21 -0
  30. package/dist/execution/invocation.js +32 -0
  31. package/dist/{model → execution/model}/adapters.d.ts +2 -2
  32. package/dist/{model → execution/model}/adapters.js +2 -2
  33. package/dist/{model → execution/model}/normalize.d.ts +2 -2
  34. package/dist/{model → execution/model}/normalize.js +2 -2
  35. package/dist/{model → execution/model}/prepared.d.ts +3 -3
  36. package/dist/{step → execution/model}/project.d.ts +1 -1
  37. package/dist/{step → execution/model}/project.js +2 -10
  38. package/dist/execution/observe.d.ts +17 -0
  39. package/dist/execution/observe.js +53 -0
  40. package/dist/execution/options.d.ts +10 -0
  41. package/dist/execution/options.js +49 -0
  42. package/dist/execution/resume.d.ts +4 -0
  43. package/dist/execution/resume.js +52 -0
  44. package/dist/execution/run.d.ts +5 -0
  45. package/dist/execution/run.js +256 -0
  46. package/dist/execution/state.d.ts +8 -0
  47. package/dist/execution/state.js +208 -0
  48. package/dist/{step → execution/step}/canonicalize.d.ts +2 -2
  49. package/dist/{step → execution/step}/canonicalize.js +2 -2
  50. package/dist/{step → execution/step}/compose.d.ts +3 -2
  51. package/dist/{step → execution/step}/compose.js +5 -5
  52. package/dist/{step → execution/step}/context-draft.d.ts +3 -5
  53. package/dist/{step → execution/step}/context-draft.js +5 -22
  54. package/dist/execution/step/describe.d.ts +9 -0
  55. package/dist/execution/step/describe.js +22 -0
  56. package/dist/{step → execution/step}/model-configuration.d.ts +4 -3
  57. package/dist/{step → execution/step}/model-configuration.js +3 -3
  58. package/dist/execution/step/resolve.d.ts +11 -0
  59. package/dist/{step → execution/step}/resolve.js +5 -3
  60. package/dist/execution/step/run.d.ts +29 -0
  61. package/dist/{step → execution/step}/run.js +19 -53
  62. package/dist/execution/step/runtime.d.ts +32 -0
  63. package/dist/execution/step/runtime.js +1 -0
  64. package/dist/{step → execution/step}/seal.d.ts +13 -8
  65. package/dist/{step → execution/step}/seal.js +8 -6
  66. package/dist/{step → execution/step}/slot-assembly.d.ts +4 -4
  67. package/dist/{step → execution/step}/slot-assembly.js +4 -4
  68. package/dist/{step → execution/step}/step-context.d.ts +11 -12
  69. package/dist/{step → execution/step}/step-context.js +5 -27
  70. package/dist/execution/tool-result.d.ts +4 -0
  71. package/dist/execution/tool-result.js +30 -0
  72. package/dist/execution/transcript.d.ts +2 -0
  73. package/dist/{session/seed.js → execution/transcript.js} +9 -39
  74. package/dist/index.d.ts +15 -12
  75. package/dist/index.js +5 -5
  76. package/dist/types/agent.d.ts +9 -0
  77. package/dist/types/agent.js +1 -0
  78. package/dist/types/execution.d.ts +97 -0
  79. package/dist/types/execution.js +1 -0
  80. package/dist/types/manifest.d.ts +16 -13
  81. package/dist/types/middleware.d.ts +11 -40
  82. package/dist/types/model.d.ts +10 -10
  83. package/dist/types/observe.d.ts +165 -0
  84. package/dist/types/observe.js +1 -0
  85. package/dist/types/shared.d.ts +1 -225
  86. package/dist/types/tool.d.ts +21 -19
  87. package/dist/types/transcript.d.ts +65 -0
  88. package/dist/types/transcript.js +1 -0
  89. package/dist/utils/canonical.d.ts +1 -0
  90. package/dist/utils/canonical.js +10 -0
  91. package/dist/utils/immutable.d.ts +1 -0
  92. package/dist/utils/immutable.js +8 -0
  93. package/package.json +5 -5
  94. package/dist/build/agent.d.ts +0 -18
  95. package/dist/build/agent.js +0 -37
  96. package/dist/build/assemble.d.ts +0 -8
  97. package/dist/build/builder.d.ts +0 -46
  98. package/dist/build/builder.js +0 -151
  99. package/dist/build/manifest.js +0 -20
  100. package/dist/session/capability-state.d.ts +0 -14
  101. package/dist/session/capability-state.js +0 -67
  102. package/dist/session/event-log.d.ts +0 -12
  103. package/dist/session/event-log.js +0 -63
  104. package/dist/session/input-queue.d.ts +0 -41
  105. package/dist/session/input-queue.js +0 -94
  106. package/dist/session/record.d.ts +0 -11
  107. package/dist/session/record.js +0 -26
  108. package/dist/session/scheduler.d.ts +0 -62
  109. package/dist/session/scheduler.js +0 -485
  110. package/dist/session/seed.d.ts +0 -10
  111. package/dist/session/session.d.ts +0 -18
  112. package/dist/session/session.js +0 -103
  113. package/dist/session/state.d.ts +0 -15
  114. package/dist/session/state.js +0 -48
  115. package/dist/session/submission-stream.d.ts +0 -16
  116. package/dist/session/submission-stream.js +0 -46
  117. package/dist/step/resolve.d.ts +0 -10
  118. package/dist/step/run.d.ts +0 -33
  119. package/dist/turn/plan-runner.d.ts +0 -70
  120. package/dist/turn/plan-runner.js +0 -388
  121. package/dist/turn/runner.d.ts +0 -69
  122. package/dist/turn/runner.js +0 -158
  123. package/dist/types/session.d.ts +0 -227
  124. package/dist/utils/maps.d.ts +0 -1
  125. package/dist/utils/maps.js +0 -37
  126. package/dist/utils/observe.d.ts +0 -8
  127. package/dist/utils/observe.js +0 -29
  128. /package/dist/{types/session.js → definition/bound.js} +0 -0
  129. /package/dist/{build → definition}/helpers.js +0 -0
  130. /package/dist/{build → definition}/schema.js +0 -0
  131. /package/dist/{model → execution/model}/prepared.js +0 -0
@@ -1,485 +0,0 @@
1
- import { HarnessError } from "../errors.js";
2
- import { createId } from "../utils/ids.js";
3
- import { createObserverRegistry } from "../utils/observe.js";
4
- import { InputQueue, isInteractionReply, snapshotWork, watchInputAbort, } from "./input-queue.js";
5
- import { SessionEventLog } from "./event-log.js";
6
- import { SubmissionStream } from "./submission-stream.js";
7
- import { commitInput, commitToolResults, initialState, withStatus } from "./state.js";
8
- import {} from "../build/agent.js";
9
- import { TurnRunner } from "../turn/runner.js";
10
- import { sessionRecord } from "./record.js";
11
- import { CapabilityStateRegistry } from "./capability-state.js";
12
- /** Coordinates one active turn at a time; TurnRunner owns the turn's internal state machine. */
13
- export class SessionScheduler {
14
- id;
15
- events = new SessionEventLog();
16
- snapshotValue;
17
- queue = new InputQueue();
18
- sessionController = new AbortController();
19
- activeController;
20
- activeSubmission;
21
- activeWork;
22
- pending;
23
- inFlightPlan;
24
- running = false;
25
- stopping = false;
26
- stopped = false;
27
- suspended = false;
28
- generation = 0;
29
- stopPromise;
30
- recordFailure;
31
- observers = createObserverRegistry();
32
- turns;
33
- states;
34
- constructor(id, agent, session, options = {}) {
35
- this.id = id;
36
- this.snapshotValue = initialState(id, options.seed);
37
- this.turns = new TurnRunner(agent, id, session);
38
- this.session = session;
39
- this.recorder = options.recorder;
40
- this.states = new CapabilityStateRegistry(agent.middleware, Object.freeze({ id, ...session }), (event) => this.emitObserve(event));
41
- if (options.seed) {
42
- const event = Object.freeze({
43
- type: "session.seeded",
44
- revision: options.seed.revision,
45
- transcriptEntries: options.seed.transcript.length,
46
- });
47
- // Construction precedes public observer registration. Defer this live-only fact
48
- // by one microtask so callers can subscribe immediately after run({ seed }).
49
- queueMicrotask(() => {
50
- if (!this.stopped)
51
- this.emitObserve(event);
52
- });
53
- }
54
- }
55
- session;
56
- recorder;
57
- get snapshot() {
58
- return this.snapshotValue;
59
- }
60
- observe(listener) {
61
- if (this.stopped)
62
- return () => undefined;
63
- return this.observers.observe(listener);
64
- }
65
- submit(event, options) {
66
- return this.submitWork(event, options);
67
- }
68
- continue(options) {
69
- return this.submitWork({ kind: "continue" }, options);
70
- }
71
- submitWork(event, options) {
72
- const stream = new SubmissionStream(createId("input"));
73
- const submission = {
74
- event: snapshotWork(event),
75
- options,
76
- stream,
77
- cancelled: false,
78
- };
79
- if (this.stopped)
80
- return this.finishStopped(stream);
81
- if (options?.signal?.aborted)
82
- return this.finishCancelled(stream, cancellationMessage(options.signal));
83
- if (event.kind !== "continue" && isInteractionReply(event)) {
84
- if (!this.enqueueReply(submission, event))
85
- return stream;
86
- }
87
- else
88
- this.enqueueOrdinaryInput(submission);
89
- this.watchAbort(submission);
90
- this.emitObserve({
91
- type: "input.received",
92
- inputId: stream.inputId,
93
- kind: event.kind,
94
- });
95
- this.pump();
96
- return stream;
97
- }
98
- stop(reason = "Session stopped") {
99
- if (this.stopPromise)
100
- return this.stopPromise;
101
- this.stopping = true;
102
- this.generation += 1;
103
- const stopError = new HarnessError("session.stale-result", reason);
104
- this.sessionController.abort(stopError);
105
- this.activeController?.abort(stopError);
106
- this.states.abort(stopError);
107
- const activeWork = this.activeWork;
108
- const activeStream = this.activeSubmission?.stream;
109
- const pending = this.pending;
110
- this.pending = undefined;
111
- this.stopPromise = (async () => {
112
- if (activeWork)
113
- await activeWork;
114
- if (pending && !this.recordFailure)
115
- await this.commitCancelledPlan(pending, reason);
116
- const stopped = await this.commit(withStatus(this.snapshotValue, "stopped"), "stopped");
117
- this.snapshotValue = stopped;
118
- this.stopped = true;
119
- this.stopping = false;
120
- const event = { type: "session.stopped", sessionId: this.id };
121
- this.events.emit(event);
122
- this.events.finish();
123
- if (activeStream) {
124
- activeStream.emit(event);
125
- activeStream.finish("stopped");
126
- }
127
- for (const item of this.queue.drain()) {
128
- item.stream.emit(event);
129
- item.stream.finish("stopped");
130
- }
131
- this.emitObserve({ type: "session.stopped", reason });
132
- await this.states.shutdown(stopError);
133
- this.observers.clear();
134
- })();
135
- return this.stopPromise;
136
- }
137
- enqueueReply(submission, reply) {
138
- if (!this.pending) {
139
- this.reject(submission.stream, "No interaction is pending");
140
- return false;
141
- }
142
- const expected = this.pending.plan.interactionId;
143
- if (expected !== reply.interactionId) {
144
- this.reject(submission.stream, "Interaction id does not match the pending interaction");
145
- return false;
146
- }
147
- if ((this.pending.plan.interactionKind === "approval") !== (reply.kind === "approve")) {
148
- this.reject(submission.stream, "Interaction input kind does not match");
149
- return false;
150
- }
151
- this.queue.add(submission, true);
152
- return true;
153
- }
154
- enqueueOrdinaryInput(submission) {
155
- if (this.running || this.pending || this.queue.size) {
156
- this.publish(submission.stream, {
157
- type: "input.queued",
158
- inputId: submission.stream.inputId,
159
- });
160
- this.emitObserve({
161
- type: "input.queued",
162
- inputId: submission.stream.inputId,
163
- });
164
- }
165
- this.queue.add(submission);
166
- }
167
- watchAbort(submission) {
168
- watchInputAbort(submission, {
169
- isActive: () => this.activeSubmission === submission,
170
- abortActive: (reason) => this.activeController?.abort(reason),
171
- cancelQueued: () => {
172
- const signal = submission.options?.signal;
173
- if (signal && this.queue.remove(submission))
174
- this.finishCancelled(submission.stream, cancellationMessage(signal));
175
- },
176
- });
177
- }
178
- reject(stream, reason) {
179
- this.publish(stream, { type: "input.rejected", inputId: stream.inputId, reason });
180
- this.emitObserve({
181
- type: "input.rejected",
182
- inputId: stream.inputId,
183
- reason,
184
- });
185
- stream.finish("rejected");
186
- return stream;
187
- }
188
- finishCancelled(stream, reason) {
189
- this.publish(stream, { type: "input.cancelled", inputId: stream.inputId, reason });
190
- this.emitObserve({
191
- type: "input.cancelled",
192
- inputId: stream.inputId,
193
- reason,
194
- });
195
- stream.finish("cancelled");
196
- return stream;
197
- }
198
- finishStopped(stream) {
199
- this.publish(stream, { type: "session.stopped", sessionId: this.id });
200
- stream.finish("stopped");
201
- return stream;
202
- }
203
- publish(stream, event) {
204
- stream.emit(event);
205
- this.events.emit(event);
206
- }
207
- pump() {
208
- if (this.running || this.stopping || this.stopped || this.suspended)
209
- return;
210
- const next = this.queue.take(Boolean(this.pending));
211
- if (!next)
212
- return;
213
- if (next.cancelled) {
214
- this.pump();
215
- return;
216
- }
217
- // This Scheduler is the sole serializing point: only one turn can mutate a Session at once.
218
- this.running = true;
219
- this.activeSubmission = next;
220
- this.activeController = new AbortController();
221
- const generation = ++this.generation;
222
- const signal = AbortSignal.any([
223
- this.sessionController.signal,
224
- this.activeController.signal,
225
- ...(next.options?.signal ? [next.options.signal] : []),
226
- ]);
227
- const work = this.run(next, signal, generation).finally(() => {
228
- this.running = false;
229
- this.activeSubmission = undefined;
230
- this.activeController = undefined;
231
- if (this.activeWork === work)
232
- this.activeWork = undefined;
233
- this.pump();
234
- });
235
- this.activeWork = work;
236
- void work;
237
- }
238
- async run(submission, signal, generation) {
239
- try {
240
- const context = {
241
- signal,
242
- states: this.states,
243
- observe: ((event) => this.emitObserve(() => withInputId(typeof event === "function" ? event() : event, submission.stream.inputId))),
244
- assertCurrent: () => this.assertCurrent(generation, signal),
245
- onPlanActive: (plan) => {
246
- this.inFlightPlan = plan;
247
- },
248
- commit: (state, transition, active) => this.commit(state, transition, active).then((committed) => {
249
- if (transition === "model-requested" && submission.event.kind === "continue")
250
- this.emitObserve({ type: "session.continued", inputId: submission.stream.inputId });
251
- return committed;
252
- }),
253
- onConversation: (event) => this.publish(submission.stream, event),
254
- claimInterrupts: (state, turnId) => this.claimInterrupts(state, turnId),
255
- };
256
- const outcome = this.pending
257
- ? await this.resumeTurn(submission, context)
258
- : submission.event.kind === "continue"
259
- ? await this.turns.continue(this.snapshotValue, context)
260
- : await this.turns.start(this.snapshotValue, submission.event, context, submission.options?.output);
261
- this.applyTurnOutcome(submission.stream, outcome);
262
- }
263
- catch (error) {
264
- if (this.inFlightPlan && !this.recordFailure) {
265
- try {
266
- await this.commitCancelledPlan(this.inFlightPlan, message(error));
267
- }
268
- catch {
269
- if (this.recordFailure)
270
- return;
271
- }
272
- this.inFlightPlan = undefined;
273
- }
274
- if (this.recordFailure || this.stopping)
275
- return;
276
- if (this.stopped)
277
- this.finishStopped(submission.stream);
278
- else {
279
- if (!this.pending)
280
- this.snapshotValue = withStatus(this.snapshotValue, "idle");
281
- this.finishCancelled(submission.stream, message(error));
282
- }
283
- }
284
- }
285
- async resumeTurn(submission, context) {
286
- const pending = this.pending;
287
- this.pending = undefined;
288
- if (submission.event.kind === "continue")
289
- throw new HarnessError("interaction.uncorrelated-resume", "Continue cannot answer an interaction");
290
- return this.turns.resume(withStatus(this.snapshotValue, "running"), pending, submission.event, context);
291
- }
292
- applyTurnOutcome(stream, outcome) {
293
- switch (outcome.kind) {
294
- case "final":
295
- this.snapshotValue = outcome.state;
296
- this.publish(stream, { type: "final", output: outcome.output, turnId: outcome.turnId });
297
- this.emitObserve({
298
- type: "turn.completed",
299
- turnId: outcome.turnId,
300
- stepId: outcome.stepId,
301
- inputId: stream.inputId,
302
- attributes: { output: outcome.output },
303
- });
304
- stream.finish("completed");
305
- return;
306
- case "interaction-required":
307
- this.pending = outcome.pending;
308
- this.snapshotValue = outcome.state;
309
- this.emitObserve({
310
- type: "interaction.required",
311
- turnId: outcome.pending.turnId,
312
- stepId: outcome.pending.stepId,
313
- inputId: stream.inputId,
314
- interactionId: outcome.interaction.id,
315
- kind: outcome.interaction.kind,
316
- ...(outcome.pending.plan.interactionCallId === undefined
317
- ? {}
318
- : { callId: outcome.pending.plan.interactionCallId }),
319
- ...(outcome.pending.plan.interactionToolName === undefined
320
- ? {}
321
- : { toolName: outcome.pending.plan.interactionToolName }),
322
- phase: "interaction",
323
- attributes: {
324
- prompt: outcome.interaction.prompt,
325
- ...(outcome.interaction.metadata === undefined
326
- ? {}
327
- : { metadata: outcome.interaction.metadata }),
328
- },
329
- });
330
- this.publish(stream, {
331
- type: "interaction.required",
332
- interaction: outcome.interaction,
333
- turnId: outcome.pending.turnId,
334
- });
335
- stream.finish("waiting");
336
- return;
337
- case "deferred":
338
- this.suspended = true;
339
- this.snapshotValue = outcome.state;
340
- if (outcome.active.kind === "model")
341
- this.emitObserve({
342
- type: "model.deferred",
343
- turnId: outcome.turnId,
344
- stepId: outcome.stepId,
345
- inputId: stream.inputId,
346
- invocationId: outcome.active.invocationId,
347
- attributes: outcome.active.token === undefined ? {} : { token: outcome.active.token },
348
- });
349
- this.publish(stream, {
350
- type: "execution.deferred",
351
- active: outcome.active,
352
- turnId: outcome.turnId,
353
- });
354
- stream.finish("waiting");
355
- return;
356
- case "tripwire":
357
- this.snapshotValue = outcome.state;
358
- this.publish(stream, {
359
- type: "tripwire",
360
- tripwire: outcome.tripwire,
361
- turnId: outcome.turnId,
362
- });
363
- this.emitObserve({
364
- type: "tripwire",
365
- turnId: outcome.turnId,
366
- inputId: stream.inputId,
367
- ...(outcome.stepId ? { stepId: outcome.stepId } : {}),
368
- code: outcome.tripwire.code,
369
- scope: outcome.tripwire.scope ?? "step",
370
- attributes: { message: outcome.tripwire.message },
371
- });
372
- if (outcome.tripwire.scope === "session")
373
- void this.stop("Session policy tripwire");
374
- else
375
- this.snapshotValue = withStatus(this.snapshotValue, "idle");
376
- stream.finish("completed");
377
- }
378
- }
379
- emitObserve(event) {
380
- this.observers.emit(event);
381
- }
382
- async commitCancelledPlan(pending, reason) {
383
- this.snapshotValue = await this.commit(commitToolResults(this.snapshotValue, pending.turnId, pending.stepId, pending.plan.cancelledResults(reason)), "tool-results");
384
- }
385
- async claimInterrupts(state, turnId) {
386
- const claimed = this.queue.takeInterrupts();
387
- const events = [];
388
- let nextState = state;
389
- for (const item of claimed) {
390
- nextState = await this.commit(commitInput(nextState, turnId, item.event), "input");
391
- const conversation = Object.freeze({
392
- type: "input",
393
- event: item.event,
394
- turnId,
395
- });
396
- this.events.emit(conversation);
397
- this.activeSubmission?.stream.emit(conversation);
398
- item.stream.emit(conversation);
399
- item.stream.finish("completed");
400
- events.push(item.event);
401
- }
402
- return Object.freeze({ state: nextState, arrivals: Object.freeze(events) });
403
- }
404
- async commit(state, transition, active) {
405
- this.assertRecordable();
406
- const revision = this.snapshotValue.revision + 1;
407
- const { active: _oldActive, ...rest } = state;
408
- const next = Object.freeze({
409
- ...rest,
410
- revision,
411
- ...(state.pendingInteraction === undefined
412
- ? {}
413
- : { pendingInteraction: state.pendingInteraction }),
414
- ...(active === undefined ? {} : { active }),
415
- });
416
- if (this.recorder) {
417
- const value = sessionRecord({ state: next, transition, session: this.session, active });
418
- try {
419
- await this.recorder.record(value);
420
- }
421
- catch (cause) {
422
- const error = new HarnessError("session.record-failed", "Session recorder failed", {
423
- cause,
424
- });
425
- this.failRecording(error);
426
- throw error;
427
- }
428
- }
429
- this.snapshotValue = next;
430
- return next;
431
- }
432
- assertRecordable() {
433
- if (this.recordFailure)
434
- throw this.recordFailure;
435
- if (this.stopped && !this.stopping)
436
- throw new HarnessError("session.stale-result", "Stopped Session cannot record state");
437
- }
438
- failRecording(error) {
439
- if (this.recordFailure)
440
- return;
441
- this.recordFailure = error;
442
- this.stopping = false;
443
- this.stopped = true;
444
- this.generation += 1;
445
- this.sessionController.abort(error);
446
- this.activeController?.abort(error);
447
- void this.states.shutdown(error);
448
- this.pending = undefined;
449
- this.inFlightPlan = undefined;
450
- this.snapshotValue = withStatus(this.snapshotValue, "stopped");
451
- this.emitObserve({
452
- type: "session.record.failed",
453
- code: error.code,
454
- attributes: { message: error.message },
455
- });
456
- const event = { type: "session.stopped", sessionId: this.id };
457
- this.events.emit(event);
458
- this.events.finish();
459
- this.activeSubmission?.stream.emit(event);
460
- this.activeSubmission?.stream.fail(error);
461
- for (const item of this.queue.drain()) {
462
- item.stream.emit(event);
463
- item.stream.finish("stopped");
464
- }
465
- }
466
- assertCurrent(generation, signal) {
467
- // An abort-ignoring model or tool may resolve late; it must never re-enter this Session.
468
- if (this.stopped || generation !== this.generation || signal.aborted)
469
- throw (signal.reason ??
470
- new HarnessError("session.stale-result", "Stale Session result quarantined"));
471
- }
472
- }
473
- function withInputId(event, inputId) {
474
- return "turnId" in event ? { ...event, inputId } : event;
475
- }
476
- function message(error) {
477
- return error instanceof Error ? error.message : String(error);
478
- }
479
- function cancellationMessage(signal) {
480
- return signal.reason instanceof Error
481
- ? signal.reason.message
482
- : signal.reason
483
- ? String(signal.reason)
484
- : "Input cancelled";
485
- }
@@ -1,10 +0,0 @@
1
- import type { SessionSeed, TranscriptEntry } from "../types/session.js";
2
- export interface NormalizedSessionSeed {
3
- readonly id?: string;
4
- readonly userId?: string;
5
- readonly context?: import("../types/shared.js").JsonObject;
6
- readonly turnCount: number;
7
- readonly revision: number;
8
- readonly transcript: readonly TranscriptEntry[];
9
- }
10
- export declare function normalizeSessionSeed(seed: SessionSeed): NormalizedSessionSeed;
@@ -1,18 +0,0 @@
1
- import type { InputHandle, InputOptions, MessageInput, Session, SessionEvent, SessionRunOptions, SessionSnapshot, SessionInput } from "../types/session.js";
2
- import type { ToolSchemaSource } from "../types/tool.js";
3
- import type { JsonValue, Observer } from "../types/shared.js";
4
- import type { LoopAgent } from "../build/agent.js";
5
- export declare class LiveSession implements Session {
6
- readonly id: string;
7
- private readonly scheduler;
8
- constructor(id: string, agent: LoopAgent, options: SessionRunOptions);
9
- get state(): SessionSnapshot;
10
- input(event: SessionInput, options?: InputOptions & {
11
- readonly outputSchema?: ToolSchemaSource;
12
- }): InputHandle<any>;
13
- interrupt(event: MessageInput, options?: InputOptions): InputHandle<any>;
14
- continue(options?: InputOptions): InputHandle<any>;
15
- stream(): AsyncIterable<SessionEvent<JsonValue>>;
16
- observe(listener: Observer): () => void;
17
- stop(reason?: string): Promise<void>;
18
- }
@@ -1,103 +0,0 @@
1
- import { HarnessError } from "../errors.js";
2
- import { SessionScheduler } from "./scheduler.js";
3
- import { copyJson, copyJsonObject } from "../utils/immutable.js";
4
- import { normalizeSessionSeed } from "./seed.js";
5
- import { bindOutputContract } from "./output-contract.js";
6
- export class LiveSession {
7
- id;
8
- scheduler;
9
- constructor(id, agent, options) {
10
- this.id = id;
11
- const seed = "seed" in options && options.seed ? normalizeSessionSeed(options.seed) : undefined;
12
- const userId = seed?.userId ?? ("userId" in options ? options.userId : undefined);
13
- const suppliedContext = seed?.context ?? ("context" in options ? options.context : undefined);
14
- const session = Object.freeze({
15
- ...(userId === undefined ? {} : { userId }),
16
- ...(suppliedContext === undefined
17
- ? {}
18
- : { context: copyJsonObject(suppliedContext, "session context") }),
19
- });
20
- this.scheduler = new SessionScheduler(id, agent, session, {
21
- ...(seed === undefined ? {} : { seed }),
22
- ...(options.recorder === undefined ? {} : { recorder: options.recorder }),
23
- });
24
- }
25
- get state() {
26
- return this.scheduler.snapshot;
27
- }
28
- input(event, options) {
29
- const output = options?.outputSchema === undefined ? undefined : bindOutputContract(options.outputSchema);
30
- if (output && isInteractionReply(event))
31
- throw new HarnessError("output.invalid-schema", "Interaction replies cannot define a turn outputSchema");
32
- return this.scheduler.submit(normalizeInput(event), {
33
- ...(options?.signal === undefined ? {} : { signal: options.signal }),
34
- ...(output === undefined ? {} : { output }),
35
- });
36
- }
37
- interrupt(event, options) {
38
- return this.scheduler.submit(normalizeMessage("interrupt", event), options);
39
- }
40
- continue(options) {
41
- return this.scheduler.continue(options);
42
- }
43
- stream() {
44
- return this.scheduler.events;
45
- }
46
- observe(listener) {
47
- return this.scheduler.observe(listener);
48
- }
49
- stop(reason) {
50
- return this.scheduler.stop(reason);
51
- }
52
- }
53
- function isInteractionReply(value) {
54
- return (typeof value === "object" &&
55
- "kind" in value &&
56
- (value.kind === "approve" || value.kind === "respond"));
57
- }
58
- function normalizeMessage(kind, value) {
59
- if (typeof value === "string")
60
- return { kind, text: value };
61
- if ("content" in value)
62
- return {
63
- kind,
64
- content: snapshotContent(value.content),
65
- ...(value.metadata === undefined
66
- ? {}
67
- : { metadata: copyJsonObject(value.metadata, "input metadata") }),
68
- };
69
- return {
70
- kind,
71
- text: value.text,
72
- ...(value.metadata === undefined
73
- ? {}
74
- : { metadata: copyJsonObject(value.metadata, "input metadata") }),
75
- };
76
- }
77
- function normalizeInput(input) {
78
- if (typeof input === "string")
79
- return { kind: "user-message", text: input };
80
- if (isInteractionReply(input))
81
- return input;
82
- return normalizeMessage("user-message", input);
83
- }
84
- function snapshotContent(value) {
85
- if (!Array.isArray(value))
86
- throw new HarnessError("input.invalid-content", "Input content must be an array");
87
- return Object.freeze(value.map((part, index) => {
88
- if (!part || typeof part !== "object")
89
- throw new HarnessError("input.invalid-content", `Input content part ${index} must be an object`);
90
- if (part.type === "text") {
91
- if (typeof part.text !== "string")
92
- throw new HarnessError("input.invalid-content", `Input text part ${index} must contain text`);
93
- return Object.freeze({ type: "text", text: part.text });
94
- }
95
- if (part.type !== "media" || typeof part.mediaType !== "string" || part.mediaType === "")
96
- throw new HarnessError("input.invalid-content", `Input media part ${index} must contain a non-empty mediaType`);
97
- return Object.freeze({
98
- type: "media",
99
- mediaType: part.mediaType,
100
- reference: copyJson(part.reference),
101
- });
102
- }));
103
- }
@@ -1,15 +0,0 @@
1
- import type { ActiveExecutionRecord, InputEvent, SessionSnapshot, TranscriptEntry } from "../types/session.js";
2
- import type { ModelCandidate } from "../types/model.js";
3
- import type { RequiredInteraction, ToolResult } from "../types/tool.js";
4
- export declare function initialState(id: string, input?: {
5
- readonly turnCount?: number;
6
- readonly revision?: number;
7
- readonly transcript?: readonly TranscriptEntry[];
8
- }): SessionSnapshot;
9
- export declare function withStatus(state: SessionSnapshot, status: SessionSnapshot["status"], pendingInteraction?: RequiredInteraction, active?: ActiveExecutionRecord): SessionSnapshot;
10
- export declare function withRevision(state: SessionSnapshot, revision: number): SessionSnapshot;
11
- export declare function beginTurn(state: SessionSnapshot, turnId: string, event?: InputEvent): SessionSnapshot;
12
- export declare const commitInput: (state: SessionSnapshot, turnId: string, event: InputEvent) => SessionSnapshot;
13
- export declare const commitCandidate: (state: SessionSnapshot, turnId: string, stepId: string, candidate: ModelCandidate) => SessionSnapshot;
14
- export declare const commitToolResults: (state: SessionSnapshot, turnId: string, stepId: string, results: readonly ToolResult[]) => SessionSnapshot;
15
- export declare const commitFinal: (state: SessionSnapshot, turnId: string, stepId: string, output: import("../types/shared.js").JsonValue) => SessionSnapshot;
@@ -1,48 +0,0 @@
1
- export function initialState(id, input = {}) {
2
- return Object.freeze({
3
- id,
4
- status: "idle",
5
- turnCount: input.turnCount ?? 0,
6
- revision: input.revision ?? 0,
7
- transcript: Object.freeze([...(input.transcript ?? [])]),
8
- });
9
- }
10
- export function withStatus(state, status, pendingInteraction, active) {
11
- return Object.freeze({
12
- id: state.id,
13
- status,
14
- turnCount: state.turnCount,
15
- revision: state.revision,
16
- transcript: state.transcript,
17
- ...(pendingInteraction ? { pendingInteraction } : {}),
18
- ...(active ? { active } : {}),
19
- });
20
- }
21
- export function withRevision(state, revision) {
22
- return Object.freeze({ ...state, revision });
23
- }
24
- export function beginTurn(state, turnId, event) {
25
- const transcript = event
26
- ? [
27
- ...state.transcript,
28
- Object.freeze({ kind: "input", turnId, event: Object.freeze({ ...event }) }),
29
- ]
30
- : [...state.transcript];
31
- return Object.freeze({
32
- id: state.id,
33
- status: "running",
34
- turnCount: state.turnCount + 1,
35
- revision: state.revision,
36
- transcript: Object.freeze(transcript),
37
- });
38
- }
39
- function append(state, entry) {
40
- return Object.freeze({
41
- ...state,
42
- transcript: Object.freeze([...state.transcript, Object.freeze(entry)]),
43
- });
44
- }
45
- export const commitInput = (state, turnId, event) => append(state, { kind: "input", turnId, event: Object.freeze({ ...event }) });
46
- export const commitCandidate = (state, turnId, stepId, candidate) => append(state, { kind: "candidate", turnId, stepId, candidate });
47
- export const commitToolResults = (state, turnId, stepId, results) => append(state, { kind: "tool-results", turnId, stepId, results: Object.freeze([...results]) });
48
- export const commitFinal = (state, turnId, stepId, output) => append(state, { kind: "final", turnId, stepId, output });