@nylorun/harness 0.5.0-beta.1 → 0.8.0-beta.1

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 (59) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +27 -108
  3. package/dist/build/agent.d.ts +6 -7
  4. package/dist/build/agent.js +16 -7
  5. package/dist/build/assemble.d.ts +5 -6
  6. package/dist/build/assemble.js +10 -43
  7. package/dist/build/bind-tool.d.ts +2 -3
  8. package/dist/build/bind-tool.js +6 -4
  9. package/dist/build/builder.d.ts +30 -15
  10. package/dist/build/builder.js +89 -38
  11. package/dist/build/helpers.d.ts +2 -3
  12. package/dist/build/helpers.js +0 -1
  13. package/dist/build/manifest.d.ts +2 -4
  14. package/dist/build/manifest.js +2 -8
  15. package/dist/errors.d.ts +1 -1
  16. package/dist/index.d.ts +7 -6
  17. package/dist/index.js +2 -2
  18. package/dist/session/capability-state.d.ts +14 -0
  19. package/dist/session/capability-state.js +67 -0
  20. package/dist/session/input-queue.d.ts +11 -2
  21. package/dist/session/input-queue.js +5 -1
  22. package/dist/session/record.d.ts +11 -0
  23. package/dist/session/record.js +26 -0
  24. package/dist/session/scheduler.d.ts +17 -3
  25. package/dist/session/scheduler.js +181 -48
  26. package/dist/session/seed.d.ts +10 -0
  27. package/dist/session/seed.js +219 -0
  28. package/dist/session/session.d.ts +3 -2
  29. package/dist/session/session.js +15 -3
  30. package/dist/session/state.d.ts +8 -3
  31. package/dist/session/state.js +16 -4
  32. package/dist/session/submission-stream.d.ts +2 -0
  33. package/dist/session/submission-stream.js +11 -1
  34. package/dist/step/model-configuration.d.ts +1 -3
  35. package/dist/step/model-configuration.js +2 -4
  36. package/dist/step/project.js +4 -2
  37. package/dist/step/run.d.ts +6 -1
  38. package/dist/step/run.js +35 -13
  39. package/dist/step/seal.d.ts +6 -2
  40. package/dist/step/seal.js +4 -3
  41. package/dist/step/step-context.d.ts +5 -2
  42. package/dist/step/step-context.js +19 -11
  43. package/dist/turn/plan-runner.d.ts +28 -13
  44. package/dist/turn/plan-runner.js +165 -182
  45. package/dist/turn/runner.d.ts +18 -4
  46. package/dist/turn/runner.js +73 -45
  47. package/dist/types/manifest.d.ts +2 -6
  48. package/dist/types/middleware.d.ts +25 -4
  49. package/dist/types/model.d.ts +3 -2
  50. package/dist/types/session.d.ts +86 -2
  51. package/dist/types/shared.d.ts +65 -9
  52. package/dist/types/tool.d.ts +37 -39
  53. package/dist/utils/immutable.js +16 -2
  54. package/package.json +1 -2
  55. package/dist/build/adapters.d.ts +0 -11
  56. package/dist/build/adapters.js +0 -91
  57. package/docs/loop.md +0 -47
  58. package/docs/model-call-projection.md +0 -112
  59. package/docs/reference.md +0 -122
@@ -1,8 +1,13 @@
1
- import type { InputEvent, SessionSnapshot } from "../types/session.js";
1
+ import type { ActiveExecutionRecord, InputEvent, SessionSnapshot, TranscriptEntry } from "../types/session.js";
2
2
  import type { ModelCandidate } from "../types/model.js";
3
3
  import type { RequiredInteraction, ToolResult } from "../types/tool.js";
4
- export declare function initialState(id: string): SessionSnapshot;
5
- export declare function withStatus(state: SessionSnapshot, status: SessionSnapshot["status"], pendingInteraction?: RequiredInteraction): SessionSnapshot;
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;
6
11
  export declare function beginTurn(state: SessionSnapshot, turnId: string, event?: InputEvent): SessionSnapshot;
7
12
  export declare const commitInput: (state: SessionSnapshot, turnId: string, event: InputEvent) => SessionSnapshot;
8
13
  export declare const commitCandidate: (state: SessionSnapshot, turnId: string, stepId: string, candidate: ModelCandidate) => SessionSnapshot;
@@ -1,15 +1,26 @@
1
- export function initialState(id) {
2
- return Object.freeze({ id, status: "idle", turnCount: 0, transcript: Object.freeze([]) });
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
+ });
3
9
  }
4
- export function withStatus(state, status, pendingInteraction) {
10
+ export function withStatus(state, status, pendingInteraction, active) {
5
11
  return Object.freeze({
6
12
  id: state.id,
7
13
  status,
8
14
  turnCount: state.turnCount,
15
+ revision: state.revision,
9
16
  transcript: state.transcript,
10
17
  ...(pendingInteraction ? { pendingInteraction } : {}),
18
+ ...(active ? { active } : {}),
11
19
  });
12
20
  }
21
+ export function withRevision(state, revision) {
22
+ return Object.freeze({ ...state, revision });
23
+ }
13
24
  export function beginTurn(state, turnId, event) {
14
25
  const transcript = event
15
26
  ? [
@@ -20,7 +31,8 @@ export function beginTurn(state, turnId, event) {
20
31
  return Object.freeze({
21
32
  id: state.id,
22
33
  status: "running",
23
- turnCount: state.turnCount + (event ? 1 : 0),
34
+ turnCount: state.turnCount + 1,
35
+ revision: state.revision,
24
36
  transcript: Object.freeze(transcript),
25
37
  });
26
38
  }
@@ -3,11 +3,13 @@ export declare class SubmissionStream implements InputHandle {
3
3
  readonly inputId: string;
4
4
  readonly completed: Promise<InputCompletion>;
5
5
  private resolveCompletion;
6
+ private rejectCompletion;
6
7
  private readonly events;
7
8
  private readonly cleanups;
8
9
  private done;
9
10
  constructor(inputId: string);
10
11
  emit(event: SessionEvent): void;
11
12
  finish(status: InputCompletion["status"]): void;
13
+ fail(error: unknown): void;
12
14
  onFinish(cleanup: () => void): void;
13
15
  }
@@ -2,13 +2,15 @@ export class SubmissionStream {
2
2
  inputId;
3
3
  completed;
4
4
  resolveCompletion;
5
+ rejectCompletion;
5
6
  events = [];
6
7
  cleanups = [];
7
8
  done = false;
8
9
  constructor(inputId) {
9
10
  this.inputId = inputId;
10
- this.completed = new Promise((resolve) => {
11
+ this.completed = new Promise((resolve, reject) => {
11
12
  this.resolveCompletion = resolve;
13
+ this.rejectCompletion = reject;
12
14
  });
13
15
  }
14
16
  emit(event) {
@@ -27,6 +29,14 @@ export class SubmissionStream {
27
29
  events: Object.freeze([...this.events]),
28
30
  }));
29
31
  }
32
+ fail(error) {
33
+ if (this.done)
34
+ return;
35
+ this.done = true;
36
+ while (this.cleanups.length)
37
+ this.cleanups.pop()();
38
+ this.rejectCompletion(error);
39
+ }
30
40
  onFinish(cleanup) {
31
41
  if (this.done)
32
42
  cleanup();
@@ -1,11 +1,9 @@
1
1
  import type { ModelConfigurationMutationOptions, ModelConfigurationSnapshot, ModelDirective } from "../types/model.js";
2
2
  import type { ToolDefinition } from "../types/tool.js";
3
- import type { AdapterRegistry } from "../build/adapters.js";
4
3
  /** Per-step, middleware-owned model configuration draft. It never persists past the call. */
5
4
  export declare class ModelConfigurationDraft {
6
5
  #private;
7
- private readonly adapters;
8
- constructor(adapters: AdapterRegistry, directive?: ModelDirective);
6
+ constructor(directive?: ModelDirective);
9
7
  setInstructions(middlewareId: string, middlewareOrder: number, slot: string, items: readonly string[], options?: ModelConfigurationMutationOptions): void;
10
8
  setTools(middlewareId: string, middlewareOrder: number, slot: string, tools: readonly ToolDefinition[], options?: ModelConfigurationMutationOptions): void;
11
9
  select(middlewareId: string, middlewareOrder: number, directive: ModelDirective, options?: Omit<ModelConfigurationMutationOptions, "order">): void;
@@ -6,12 +6,10 @@ import { copyJson } from "../utils/immutable.js";
6
6
  import { checkedReason, slotOwner, SlotDraft } from "./slot-assembly.js";
7
7
  /** Per-step, middleware-owned model configuration draft. It never persists past the call. */
8
8
  export class ModelConfigurationDraft {
9
- adapters;
10
9
  #instructions = new SlotDraft();
11
10
  #tools = new SlotDraft();
12
11
  #model;
13
- constructor(adapters, directive) {
14
- this.adapters = adapters;
12
+ constructor(directive) {
15
13
  if (directive !== undefined)
16
14
  this.#model = Object.freeze({ directive: checkedDirective(directive) });
17
15
  }
@@ -38,7 +36,7 @@ export class ModelConfigurationDraft {
38
36
  middlewareId,
39
37
  middlewareOrder,
40
38
  slot,
41
- value: Object.freeze(tools.map((tool) => bindTool(tool, this.adapters))),
39
+ value: Object.freeze(tools.map((tool) => bindTool(tool, { middlewareId, slot }))),
42
40
  order: options?.order,
43
41
  reason: options?.reason,
44
42
  invalidSlot: "configuration.invalid-slot",
@@ -81,8 +81,10 @@ function projectToolResult(result) {
81
81
  }
82
82
  function toolResultPayload(result) {
83
83
  if (result.kind === "completed")
84
- return result.output ?? null;
85
- return { kind: result.kind, reason: result.reason ?? result.message ?? result.code ?? "failed" };
84
+ return result.output;
85
+ if (result.kind === "denied")
86
+ return { kind: result.kind, reason: result.reason };
87
+ return { kind: result.kind, code: result.code, message: result.message };
86
88
  }
87
89
  function textPart(text) {
88
90
  return Object.freeze({ type: "text", text });
@@ -1,12 +1,15 @@
1
1
  import type { ModelCandidate } from "../types/model.js";
2
- import type { InputEvent, SessionSnapshot } from "../types/session.js";
2
+ import type { ActiveModelExecutionRecord, InputEvent, SessionSnapshot } from "../types/session.js";
3
3
  import type { LoopAgent } from "../build/agent.js";
4
4
  import type { ObserveEmit } from "../utils/observe.js";
5
5
  import { type SealedStepOutput } from "./seal.js";
6
+ import type { CapabilityStateRegistry } from "../session/capability-state.js";
6
7
  export interface StepRunResult {
7
8
  readonly stepId: string;
9
+ readonly state: SessionSnapshot;
8
10
  readonly candidate?: ModelCandidate;
9
11
  readonly output: SealedStepOutput;
12
+ readonly requestedModelId?: string;
10
13
  }
11
14
  export declare function runStep(input: {
12
15
  agent: LoopAgent;
@@ -24,4 +27,6 @@ export declare function runStep(input: {
24
27
  readonly userId?: string;
25
28
  readonly context?: import("../types/shared.js").JsonObject;
26
29
  }>;
30
+ states: CapabilityStateRegistry;
31
+ recordModelRequested(state: SessionSnapshot, active: ActiveModelExecutionRecord): Promise<SessionSnapshot>;
27
32
  }): Promise<StepRunResult>;
package/dist/step/run.js CHANGED
@@ -8,7 +8,10 @@ import { projectModelCall } from "./project.js";
8
8
  import { resolveModelRequest } from "./resolve.js";
9
9
  import { sealStep } from "./seal.js";
10
10
  import { ModelConfigurationDraft } from "./model-configuration.js";
11
+ import { createId } from "../utils/ids.js";
11
12
  export async function runStep(input) {
13
+ let state = input.state;
14
+ let requestedModelId;
12
15
  const stepInput = Object.freeze({
13
16
  sessionId: input.sessionId,
14
17
  turnId: input.turnId,
@@ -20,9 +23,9 @@ export async function runStep(input) {
20
23
  toolResults: Object.freeze([...input.toolResults]),
21
24
  transcript: Object.freeze([...input.state.transcript]),
22
25
  });
23
- const configuration = new ModelConfigurationDraft(input.agent.adapters, input.agent.directive);
26
+ const configuration = new ModelConfigurationDraft();
24
27
  const runtimeContext = new ContextDraft(input.session.context);
25
- const context = new StepContext(stepInput, input.observe, configuration, runtimeContext);
28
+ const context = new StepContext(stepInput, input.observe, configuration, runtimeContext, input.states);
26
29
  input.observe(() => ({
27
30
  type: "step.started",
28
31
  turnId: input.turnId,
@@ -50,7 +53,17 @@ export async function runStep(input) {
50
53
  arrivals: input.arrivals,
51
54
  toolResults: input.toolResults,
52
55
  });
56
+ requestedModelId = request.model?.id;
53
57
  const call = projectModelCall(request);
58
+ const invocationId = createId("invocation");
59
+ const active = Object.freeze({
60
+ kind: "model",
61
+ turnId: input.turnId,
62
+ stepId: input.stepId,
63
+ invocationId,
64
+ call,
65
+ });
66
+ state = await input.recordModelRequested(state, active);
54
67
  input.observe(() => ({
55
68
  type: "model.requested",
56
69
  turnId: input.turnId,
@@ -63,22 +76,22 @@ export async function runStep(input) {
63
76
  }),
64
77
  }));
65
78
  try {
66
- const minted = context.mintFromModel(normalizeCandidate(await input.agent.invoke(call, {
79
+ const outcome = await input.agent.invoke(call, {
67
80
  request,
81
+ invocationId,
68
82
  signal: input.signal,
69
- })));
83
+ });
70
84
  if (input.signal.aborted)
71
85
  throw input.signal.reason;
86
+ if (isDeferred(outcome))
87
+ return context.deferModel(Object.freeze({
88
+ ...active,
89
+ ...(outcome.token === undefined ? {} : { token: copyJson(outcome.token) }),
90
+ }));
91
+ const minted = context.mintFromModel(normalizeCandidate(outcome));
72
92
  const candidate = context.currentCandidate;
73
93
  if (!candidate)
74
94
  throw new HarnessError("model.candidate-missing", "Model candidate missing after mint");
75
- input.observe(() => ({
76
- type: "model.completed",
77
- turnId: input.turnId,
78
- stepId: input.stepId,
79
- ...(request.model?.id === undefined ? {} : { requestedModelId: request.model.id }),
80
- attributes: candidate,
81
- }));
82
95
  return minted;
83
96
  }
84
97
  catch (error) {
@@ -92,7 +105,16 @@ export async function runStep(input) {
92
105
  }, input.observe);
93
106
  const output = sealStep(context);
94
107
  const candidate = output.kind === "tools" ? output.plan.candidate : context.currentCandidate;
95
- return Object.freeze({ stepId: input.stepId, ...(candidate ? { candidate } : {}), output });
108
+ return Object.freeze({
109
+ stepId: input.stepId,
110
+ state,
111
+ ...(candidate ? { candidate } : {}),
112
+ ...(requestedModelId === undefined ? {} : { requestedModelId }),
113
+ output,
114
+ });
115
+ }
116
+ function isDeferred(value) {
117
+ return (typeof value === "object" && value !== null && "kind" in value && value.kind === "deferred");
96
118
  }
97
119
  function snapshotStepStart(input) {
98
120
  const session = Object.freeze({
@@ -121,7 +143,7 @@ function snapshotTools(tools) {
121
143
  return Object.freeze(tools.map((tool) => Object.freeze({
122
144
  name: tool.name,
123
145
  ...(tool.description === undefined ? {} : { description: tool.description }),
124
- executeWith: tool.executeWith,
146
+ owner: tool.owner,
125
147
  parameters: Object.freeze({ jsonSchema: copyJson(tool.parameters.jsonSchema) }),
126
148
  })));
127
149
  }
@@ -1,12 +1,13 @@
1
1
  import type { ModelCandidate } from "../types/model.js";
2
2
  import type { Tripwire } from "../types/shared.js";
3
- import type { RequiredInteraction, SealedToolCall, ToolResult } from "../types/tool.js";
3
+ import type { BoundToolDefinition, RequiredInteraction, SealedToolCall, ToolOwner, ToolResult } from "../types/tool.js";
4
4
  import type { StepContext } from "./step-context.js";
5
5
  export interface ExecutablePlanEntry {
6
6
  readonly call: SealedToolCall;
7
7
  readonly invocationId: string;
8
+ readonly owner: ToolOwner;
9
+ readonly execute: BoundToolDefinition["execute"];
8
10
  readonly interaction?: RequiredInteraction;
9
- readonly preflight?: "sandbox" | "validation";
10
11
  }
11
12
  export interface InternalToolPlan {
12
13
  readonly candidate: ModelCandidate;
@@ -23,6 +24,9 @@ export type SealedStepOutput = {
23
24
  } | {
24
25
  readonly kind: "final";
25
26
  readonly output: string;
27
+ } | {
28
+ readonly kind: "deferred-model";
29
+ readonly active: import("../types/session.js").ActiveModelExecutionRecord;
26
30
  } | {
27
31
  readonly kind: "tools";
28
32
  readonly plan: InternalToolPlan;
package/dist/step/seal.js CHANGED
@@ -7,6 +7,8 @@ const failed = (callId, toolName, code, message) => Object.freeze({ kind: "faile
7
7
  export function sealStep(context) {
8
8
  if (context.currentTripwire)
9
9
  return Object.freeze({ kind: "tripwire", tripwire: context.currentTripwire });
10
+ if (context.currentModelDeferred)
11
+ return Object.freeze({ kind: "deferred-model", active: context.currentModelDeferred });
10
12
  const calls = context.canonicalCalls();
11
13
  if (!calls.length)
12
14
  return Object.freeze({
@@ -75,7 +77,6 @@ function sealCall(context, catalog, candidate) {
75
77
  const interaction = requested
76
78
  ? Object.freeze({ ...requested, id: requested.id ?? createId("interaction") })
77
79
  : undefined;
78
- const preflight = context.preflightFor(candidate.id);
79
80
  return {
80
81
  order,
81
82
  canonical,
@@ -84,11 +85,11 @@ function sealCall(context, catalog, candidate) {
84
85
  callId: candidate.id,
85
86
  toolName: candidate.name,
86
87
  args,
87
- executeWith: tool.executeWith,
88
88
  }),
89
89
  invocationId: createId("invocation"),
90
+ owner: tool.owner,
91
+ execute: tool.execute,
90
92
  ...(interaction ? { interaction } : {}),
91
- ...(preflight ? { preflight } : {}),
92
93
  }),
93
94
  };
94
95
  }
@@ -1,19 +1,22 @@
1
1
  import type { ContextSnapshot, ModelCandidate, ModelDirective, ModelConfigurationSnapshot } from "../types/model.js";
2
2
  import type { StepInput, StepRequest, StepResponse } from "../types/middleware.js";
3
3
  import type { Tripwire } from "../types/shared.js";
4
+ import type { ActiveModelExecutionRecord } from "../types/session.js";
4
5
  import type { ObserveEmit } from "../utils/observe.js";
5
6
  import type { BoundToolDefinition, Interaction } from "../types/tool.js";
6
7
  import { type CanonicalCall } from "./canonicalize.js";
7
8
  import { ContextDraft } from "./context-draft.js";
8
9
  import { ModelConfigurationDraft } from "./model-configuration.js";
10
+ import type { CapabilityStateRegistry } from "../session/capability-state.js";
9
11
  export declare function isBrandedResponse(value: unknown): value is StepResponse;
10
12
  /** Mutable Step state. Middleware receives leased request views and one branded response. */
11
13
  export declare class StepContext {
12
14
  #private;
13
15
  readonly input: Readonly<StepInput>;
14
- constructor(input: Readonly<StepInput>, observe: ObserveEmit, configuration: ModelConfigurationDraft, context: ContextDraft);
16
+ constructor(input: Readonly<StepInput>, observe: ObserveEmit, configuration: ModelConfigurationDraft, context: ContextDraft, states?: CapabilityStateRegistry);
15
17
  get currentTripwire(): Tripwire | undefined;
16
18
  get currentCandidate(): Readonly<ModelCandidate> | undefined;
19
+ get currentModelDeferred(): ActiveModelExecutionRecord | undefined;
17
20
  get selectedDirective(): ModelDirective | undefined;
18
21
  get instructions(): readonly string[];
19
22
  contextSnapshot(): ContextSnapshot;
@@ -24,9 +27,9 @@ export declare class StepContext {
24
27
  sealContext(snapshot: ContextSnapshot): void;
25
28
  denialFor(callId: string): string | undefined;
26
29
  interactionFor(callId: string): Interaction | undefined;
27
- preflightFor(callId: string): "sandbox" | "validation" | undefined;
28
30
  canonicalCalls(): readonly CanonicalCall[];
29
31
  mintFromModel(candidate: Readonly<ModelCandidate>): StepResponse;
32
+ deferModel(active: ActiveModelExecutionRecord): StepResponse;
30
33
  tripwire(error: Tripwire): StepResponse;
31
34
  seal(): void;
32
35
  requestFacade(middlewareId: string, middlewareOrder: number): {
@@ -13,22 +13,24 @@ export class StepContext {
13
13
  #observe;
14
14
  #context;
15
15
  #configuration;
16
+ #states;
16
17
  #denials = new Map();
17
18
  #interactions = new Map();
18
- #preflights = new Map();
19
19
  #identities = new Map();
20
20
  #canonical = Object.freeze([]);
21
21
  #candidate;
22
22
  #tripwire;
23
+ #modelDeferred;
23
24
  #response;
24
25
  #sealed = false;
25
26
  #configurationSnapshot;
26
27
  #contextSnapshot;
27
- constructor(input, observe, configuration, context) {
28
+ constructor(input, observe, configuration, context, states) {
28
29
  this.input = input;
29
30
  this.#observe = observe;
30
31
  this.#configuration = configuration;
31
32
  this.#context = context;
33
+ this.#states = states;
32
34
  }
33
35
  get currentTripwire() {
34
36
  return this.#tripwire;
@@ -36,6 +38,9 @@ export class StepContext {
36
38
  get currentCandidate() {
37
39
  return this.#candidate;
38
40
  }
41
+ get currentModelDeferred() {
42
+ return this.#modelDeferred;
43
+ }
39
44
  get selectedDirective() {
40
45
  return this.configurationSnapshot().model;
41
46
  }
@@ -66,9 +71,6 @@ export class StepContext {
66
71
  interactionFor(callId) {
67
72
  return this.#interactions.get(callId);
68
73
  }
69
- preflightFor(callId) {
70
- return this.#preflights.get(callId);
71
- }
72
74
  canonicalCalls() {
73
75
  return this.#canonical;
74
76
  }
@@ -81,6 +83,10 @@ export class StepContext {
81
83
  this.#identities.set(call.id, identityKey(call.name, call.args));
82
84
  return this.#ensureResponse();
83
85
  }
86
+ deferModel(active) {
87
+ this.#modelDeferred = active;
88
+ return this.#ensureResponse();
89
+ }
84
90
  tripwire(error) {
85
91
  this.#tripwire ??= Object.freeze({ ...error });
86
92
  return this.#ensureResponse();
@@ -131,7 +137,7 @@ export class StepContext {
131
137
  });
132
138
  }
133
139
  });
134
- const value = Object.freeze({
140
+ const request = {
135
141
  sessionId: this.input.sessionId,
136
142
  turnId: this.input.turnId,
137
143
  stepId: this.input.stepId,
@@ -164,7 +170,13 @@ export class StepContext {
164
170
  });
165
171
  return minted;
166
172
  },
167
- });
173
+ };
174
+ if (this.#states?.has(middlewareId))
175
+ Object.defineProperty(request, "state", {
176
+ enumerable: true,
177
+ get: () => this.#states.get(middlewareId),
178
+ });
179
+ const value = Object.freeze(request);
168
180
  return Object.freeze({
169
181
  value,
170
182
  revokeMutators: () => {
@@ -187,10 +199,6 @@ export class StepContext {
187
199
  if (!this.#interactions.has(callId))
188
200
  this.#interactions.set(callId, freezeGraph({ ...interaction }));
189
201
  },
190
- requirePreflight: (callId, kind) => {
191
- if (!this.#preflights.has(callId))
192
- this.#preflights.set(callId, kind);
193
- },
194
202
  tripwire: (error) => {
195
203
  this.#tripwire ??= Object.freeze({ ...error });
196
204
  return value;
@@ -1,18 +1,30 @@
1
- import type { AdapterRegistry } from "../build/adapters.js";
2
1
  import type { ObserveEmit } from "../utils/observe.js";
3
2
  import type { InputEvent } from "../types/session.js";
4
- import type { RequiredInteraction, ToolResult } from "../types/tool.js";
5
- import type { InternalToolPlan } from "../step/seal.js";
6
- type PlanPhase = "interaction" | "preflight" | "execute";
3
+ import type { ActiveInteractionExecutionRecord, ActiveToolsExecutionRecord } from "../types/session.js";
4
+ import type { RequiredInteraction, ToolExecutionResume, ToolResult } from "../types/tool.js";
5
+ import type { JsonValue } from "../types/shared.js";
6
+ import type { CapabilityStateRegistry } from "../session/capability-state.js";
7
+ import type { ExecutablePlanEntry, InternalToolPlan } from "../step/seal.js";
8
+ export interface DeferredToolCall {
9
+ readonly callId: string;
10
+ readonly toolName: string;
11
+ readonly args: JsonValue;
12
+ readonly invocationId: string;
13
+ readonly owner: ExecutablePlanEntry["owner"];
14
+ readonly token?: JsonValue;
15
+ }
7
16
  export type ToolPlanProgress = {
8
17
  readonly kind: "completed";
9
18
  readonly results: readonly ToolResult[];
10
19
  } | {
11
20
  readonly kind: "interaction-required";
12
21
  readonly interaction: RequiredInteraction;
22
+ } | {
23
+ readonly kind: "deferred";
24
+ readonly settled: readonly ToolResult[];
25
+ readonly deferred: readonly DeferredToolCall[];
13
26
  };
14
27
  export interface ToolPlanRunContext {
15
- readonly adapters: AdapterRegistry;
16
28
  readonly signal: AbortSignal;
17
29
  readonly observe: ObserveEmit;
18
30
  readonly ids: {
@@ -20,15 +32,17 @@ export interface ToolPlanRunContext {
20
32
  readonly turnId: string;
21
33
  readonly stepId: string;
22
34
  };
35
+ readonly states?: CapabilityStateRegistry;
23
36
  }
24
- /** Owns the mutable progress of one sealed tool plan across interaction resumes. */
37
+ /** Owns one sealed plan's deterministic interaction and concurrent execution progress. */
25
38
  export declare class ToolPlanRunner {
26
39
  private readonly plan;
27
40
  private readonly results;
41
+ private readonly deferred;
28
42
  private readonly resumes;
29
- private phase;
30
- private index;
31
43
  private readonly pending;
44
+ private readonly settlementEvents;
45
+ private interactionIndex;
32
46
  private executeStarted;
33
47
  private resumeExecute?;
34
48
  constructor(plan: InternalToolPlan);
@@ -36,20 +50,21 @@ export declare class ToolPlanRunner {
36
50
  get interactionKind(): RequiredInteraction["kind"] | undefined;
37
51
  get interactionCallId(): string | undefined;
38
52
  get interactionToolName(): string | undefined;
39
- get interactionPhase(): PlanPhase | undefined;
40
53
  cancelledResults(reason: string): readonly ToolResult[];
54
+ /** Publishes settlement facts only after their authoritative state was recorded. */
55
+ publishSettlements(observe: ObserveEmit): void;
56
+ activeToolsRecord(turnId: string, stepId: string): ActiveToolsExecutionRecord;
57
+ activeInteractionRecord(turnId: string, stepId: string, resume?: ToolExecutionResume): ActiveInteractionExecutionRecord;
58
+ interactionResume(event: InputEvent): ToolExecutionResume;
41
59
  run(context: ToolPlanRunContext, resume?: InputEvent): Promise<ToolPlanProgress>;
42
60
  private acceptResume;
43
- private runExecute;
44
- private preflight;
45
61
  private executeBatch;
46
62
  private execute;
47
63
  private takeResume;
48
- private requireInteraction;
49
64
  private enqueueInteraction;
50
65
  private interactionRequired;
51
66
  private orderedResults;
67
+ private orderedSettledResults;
52
68
  private resultsInOrder;
53
69
  private throwIfAborted;
54
70
  }
55
- export {};