@nylorun/harness 0.5.0-beta.1 → 0.7.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 +45 -0
  2. package/README.md +22 -108
  3. package/dist/build/agent.d.ts +4 -7
  4. package/dist/build/agent.js +12 -7
  5. package/dist/build/assemble.d.ts +2 -6
  6. package/dist/build/assemble.js +4 -47
  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 +5 -8
  10. package/dist/build/builder.js +34 -13
  11. package/dist/build/helpers.d.ts +2 -3
  12. package/dist/build/helpers.js +0 -1
  13. package/dist/build/manifest.d.ts +0 -4
  14. package/dist/build/manifest.js +0 -8
  15. package/dist/errors.d.ts +1 -1
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.js +1 -1
  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 +0 -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,9 +1,10 @@
1
- import type { InputEvent, SessionEvent, SessionSnapshot } from "../types/session.js";
1
+ import type { ActiveExecutionRecord, InputEvent, SessionEvent, SessionRecord, SessionSnapshot } from "../types/session.js";
2
2
  import type { JsonObject, Tripwire } from "../types/shared.js";
3
3
  import type { RequiredInteraction } from "../types/tool.js";
4
4
  import type { LoopAgent } from "../build/agent.js";
5
5
  import type { ObserveEmit } from "../utils/observe.js";
6
6
  import { ToolPlanRunner } from "./plan-runner.js";
7
+ import type { CapabilityStateRegistry } from "../session/capability-state.js";
7
8
  export interface PendingTurn {
8
9
  readonly plan: ToolPlanRunner;
9
10
  readonly turnId: string;
@@ -21,6 +22,12 @@ export type TurnProgress = {
21
22
  readonly state: SessionSnapshot;
22
23
  readonly pending: PendingTurn;
23
24
  readonly interaction: RequiredInteraction;
25
+ } | {
26
+ readonly kind: "deferred";
27
+ readonly state: SessionSnapshot;
28
+ readonly turnId: string;
29
+ readonly stepId: string;
30
+ readonly active: ActiveExecutionRecord;
24
31
  } | {
25
32
  readonly kind: "tripwire";
26
33
  readonly state: SessionSnapshot;
@@ -30,14 +37,18 @@ export type TurnProgress = {
30
37
  };
31
38
  export interface TurnRunContext {
32
39
  readonly signal: AbortSignal;
40
+ readonly states: CapabilityStateRegistry;
33
41
  readonly observe: ObserveEmit;
34
42
  readonly assertCurrent: () => void;
35
43
  readonly onPlanActive: (pending: PendingTurn | undefined) => void;
36
- readonly onState: (state: SessionSnapshot) => void;
44
+ readonly commit: (state: SessionSnapshot, transition: SessionRecord["transition"], active?: ActiveExecutionRecord) => Promise<SessionSnapshot>;
37
45
  readonly onConversation: (event: SessionEvent) => void;
38
- readonly claimInterrupts: (turnId: string) => readonly InputEvent[];
46
+ readonly claimInterrupts: (state: SessionSnapshot, turnId: string) => Promise<{
47
+ readonly state: SessionSnapshot;
48
+ readonly arrivals: readonly InputEvent[];
49
+ }>;
39
50
  }
40
- /** Advances one Turn until it finalizes, trips a policy, or pauses for an interaction. */
51
+ /** Advances one Turn until it finalizes, trips policy, or reaches a waiting barrier. */
41
52
  export declare class TurnRunner {
42
53
  private readonly agent;
43
54
  private readonly sessionId;
@@ -47,7 +58,10 @@ export declare class TurnRunner {
47
58
  readonly context?: JsonObject;
48
59
  }>);
49
60
  start(state: SessionSnapshot, event: InputEvent, context: TurnRunContext): Promise<TurnProgress>;
61
+ continue(state: SessionSnapshot, context: TurnRunContext): Promise<TurnProgress>;
50
62
  resume(state: SessionSnapshot, pending: PendingTurn, event: InputEvent, context: TurnRunContext): Promise<TurnProgress>;
51
63
  private advance;
64
+ private waitForInteraction;
65
+ private waitForDeferred;
52
66
  private runPlan;
53
67
  }
@@ -1,9 +1,9 @@
1
- import { beginTurn, commitCandidate, commitFinal, commitInput, commitToolResults, } from "../session/state.js";
1
+ import { beginTurn, commitCandidate, commitFinal, commitToolResults, withStatus, } from "../session/state.js";
2
2
  import { runStep } from "../step/run.js";
3
3
  import { createId } from "../utils/ids.js";
4
4
  import { copyJson } from "../utils/immutable.js";
5
5
  import { ToolPlanRunner } from "./plan-runner.js";
6
- /** Advances one Turn until it finalizes, trips a policy, or pauses for an interaction. */
6
+ /** Advances one Turn until it finalizes, trips policy, or reaches a waiting barrier. */
7
7
  export class TurnRunner {
8
8
  agent;
9
9
  sessionId;
@@ -15,19 +15,27 @@ export class TurnRunner {
15
15
  }
16
16
  async start(state, event, context) {
17
17
  const turnId = createId("turn");
18
- const started = beginTurn(state, turnId, event);
19
- context.onState(started);
18
+ state = await context.commit(beginTurn(state, turnId, event), "input");
20
19
  context.onConversation({ type: "input", event, turnId });
21
- return this.advance(started, turnId, 1, [event], [], context);
20
+ return this.advance(state, turnId, 1, [event], [], context);
21
+ }
22
+ async continue(state, context) {
23
+ const turnId = createId("turn");
24
+ return this.advance(beginTurn(state, turnId), turnId, 1, [], [], context);
22
25
  }
23
26
  async resume(state, pending, event, context) {
27
+ const resume = pending.plan.interactionResume(event);
28
+ const active = pending.plan.activeInteractionRecord(pending.turnId, pending.stepId, resume);
29
+ state = await context.commit(withStatus(state, "running", undefined, active), "input", active);
24
30
  context.onConversation({ type: "input", event, turnId: pending.turnId });
25
31
  const progress = await this.runPlan(pending, context, event);
26
32
  if (progress.kind === "interaction-required")
27
- return { kind: "interaction-required", state, pending, interaction: progress.interaction };
28
- const afterTools = commitToolResults(state, pending.turnId, pending.stepId, progress.results);
29
- context.onState(afterTools);
30
- const claimed = claimAndRecord(afterTools, pending.turnId, context);
33
+ return this.waitForInteraction(state, pending, progress.interaction, context);
34
+ if (progress.kind === "deferred")
35
+ return this.waitForDeferred(state, pending, pending.plan.activeToolsRecord(pending.turnId, pending.stepId), context);
36
+ state = await context.commit(commitToolResults(state, pending.turnId, pending.stepId, progress.results), "tool-results");
37
+ pending.plan.publishSettlements(context.observe);
38
+ const claimed = await context.claimInterrupts(state, pending.turnId);
31
39
  return this.advance(claimed.state, pending.turnId, pending.stepNumber + 1, claimed.arrivals, progress.results, context);
32
40
  }
33
41
  async advance(initialState, turnId, firstStep, arrivals, initialResults, context) {
@@ -49,80 +57,100 @@ export class TurnRunner {
49
57
  toolResults,
50
58
  signal: context.signal,
51
59
  session: this.session,
60
+ states: context.states,
61
+ recordModelRequested: (next, active) => context.commit(next, "model-requested", active),
52
62
  });
53
63
  context.assertCurrent();
64
+ state = run.state;
65
+ const pending = run.output.kind === "tools"
66
+ ? {
67
+ plan: new ToolPlanRunner(run.output.plan),
68
+ turnId,
69
+ stepId,
70
+ stepNumber,
71
+ }
72
+ : undefined;
54
73
  if (run.candidate) {
55
- state = commitCandidate(state, turnId, stepId, run.candidate);
56
- context.onState(state);
74
+ const active = pending?.plan.activeToolsRecord(turnId, stepId);
75
+ state = await context.commit(commitCandidate(state, turnId, stepId, run.candidate), "candidate", active);
76
+ context.observe({
77
+ type: "model.completed",
78
+ turnId,
79
+ stepId,
80
+ ...(run.requestedModelId === undefined ? {} : { requestedModelId: run.requestedModelId }),
81
+ attributes: run.candidate,
82
+ });
57
83
  context.onConversation({ type: "candidate", turnId, stepId, candidate: run.candidate });
58
84
  }
59
85
  if (run.output.kind === "tripwire")
60
86
  return { kind: "tripwire", state, turnId, stepId, tripwire: run.output.tripwire };
61
- if (run.output.kind === "final")
62
- return {
63
- kind: "final",
64
- state: commitFinal(state, turnId, stepId, run.output.output),
65
- turnId,
66
- stepId,
67
- output: run.output.output,
68
- };
69
- const plan = run.output.plan;
87
+ if (run.output.kind === "deferred-model") {
88
+ state = await context.commit(withStatus(state, "waiting", undefined, run.output.active), "waiting", run.output.active);
89
+ return { kind: "deferred", state, turnId, stepId, active: run.output.active };
90
+ }
91
+ if (run.output.kind === "final") {
92
+ state = await context.commit(withStatus(commitFinal(state, turnId, stepId, run.output.output), "idle"), "final");
93
+ return { kind: "final", state, turnId, stepId, output: run.output.output };
94
+ }
95
+ const toolPlan = run.output.plan;
70
96
  context.observe(() => ({
71
97
  type: "tool.sealed",
72
98
  turnId,
73
99
  stepId,
74
100
  attributes: {
75
- executable: Object.freeze(plan.executable.map((entry) => Object.freeze({
101
+ executable: Object.freeze(toolPlan.executable.map((entry) => Object.freeze({
76
102
  callId: entry.call.callId,
77
103
  toolName: entry.call.toolName,
78
104
  args: copyJson(entry.call.args),
79
- executeWith: entry.call.executeWith,
80
105
  invocationId: entry.invocationId,
81
- ...(entry.preflight === undefined ? {} : { preflight: entry.preflight }),
106
+ owner: entry.owner,
82
107
  ...(entry.interaction === undefined
83
108
  ? {}
84
109
  : { interaction: copyJson(entry.interaction) }),
85
110
  }))),
86
- immediate: copyJson(plan.immediateResults),
111
+ immediate: copyJson(toolPlan.immediateResults),
87
112
  },
88
113
  }));
89
- const pending = {
90
- plan: new ToolPlanRunner(run.output.plan),
91
- turnId,
92
- stepId,
93
- stepNumber,
94
- };
95
114
  const progress = await this.runPlan(pending, context);
96
115
  if (progress.kind === "interaction-required")
97
- return { kind: "interaction-required", state, pending, interaction: progress.interaction };
98
- state = commitToolResults(state, turnId, stepId, progress.results);
99
- context.onState(state);
100
- const claimed = claimAndRecord(state, turnId, context);
116
+ return this.waitForInteraction(state, pending, progress.interaction, context);
117
+ if (progress.kind === "deferred")
118
+ return this.waitForDeferred(state, pending, pending.plan.activeToolsRecord(turnId, stepId), context);
119
+ state = await context.commit(commitToolResults(state, turnId, stepId, progress.results), "tool-results");
120
+ pending.plan.publishSettlements(context.observe);
121
+ const claimed = await context.claimInterrupts(state, turnId);
101
122
  state = claimed.state;
102
123
  stepArrivals = claimed.arrivals;
103
124
  toolResults = progress.results;
104
125
  }
105
126
  }
127
+ async waitForInteraction(state, pending, interaction, context) {
128
+ const active = pending.plan.activeInteractionRecord(pending.turnId, pending.stepId);
129
+ state = await context.commit(withStatus(state, "waiting", interaction, active), "waiting", active);
130
+ pending.plan.publishSettlements(context.observe);
131
+ return { kind: "interaction-required", state, pending, interaction };
132
+ }
133
+ async waitForDeferred(state, pending, active, context) {
134
+ state = await context.commit(withStatus(state, "waiting", undefined, active), "waiting", active);
135
+ pending.plan.publishSettlements(context.observe);
136
+ return {
137
+ kind: "deferred",
138
+ state,
139
+ turnId: pending.turnId,
140
+ stepId: pending.stepId,
141
+ active,
142
+ };
143
+ }
106
144
  async runPlan(pending, context, resume) {
107
145
  context.onPlanActive(pending);
108
146
  const progress = await pending.plan.run({
109
- adapters: this.agent.adapters,
110
147
  signal: context.signal,
111
148
  observe: context.observe,
112
149
  ids: { sessionId: this.sessionId, turnId: pending.turnId, stepId: pending.stepId },
150
+ states: context.states,
113
151
  }, resume);
114
152
  context.assertCurrent();
115
153
  context.onPlanActive(undefined);
116
154
  return progress;
117
155
  }
118
156
  }
119
- function claimAndRecord(state, turnId, context) {
120
- const arrivals = context.claimInterrupts(turnId);
121
- if (arrivals.length === 0)
122
- return { state, arrivals };
123
- let next = state;
124
- for (const event of arrivals)
125
- next = commitInput(next, turnId, event);
126
- context.onState(next);
127
- return { state: next, arrivals };
128
- }
@@ -1,15 +1,9 @@
1
1
  import type { BoundMiddleware } from "./middleware.js";
2
- import type { ModelDirective } from "./model.js";
3
2
  import type { BuildDiagnostic } from "./shared.js";
4
3
  export interface AgentManifest {
5
4
  readonly middleware: readonly {
6
5
  readonly id: string;
7
6
  }[];
8
- readonly model?: ModelDirective;
9
- readonly adapters: readonly {
10
- readonly id: string;
11
- readonly maxConcurrentCalls?: number;
12
- }[];
13
7
  }
14
8
  export type BuildResult<Agent> = {
15
9
  readonly ok: true;
@@ -1,5 +1,5 @@
1
1
  import type { ContextMutationOptions, ModelCandidate, ModelDirective, ModelToolCall, ModelConfigurationMutationOptions } from "./model.js";
2
- import type { InputEvent, TranscriptEntry } from "./session.js";
2
+ import type { InputEvent, SessionIdentity, TranscriptEntry } from "./session.js";
3
3
  import type { ContextItem, JsonObject, Tripwire } from "./shared.js";
4
4
  import type { Interaction, ToolDefinition, ToolResult } from "./tool.js";
5
5
  export interface StepInput {
@@ -18,7 +18,7 @@ export interface StepInput {
18
18
  readonly toolResults: readonly ToolResult[];
19
19
  readonly transcript: readonly TranscriptEntry[];
20
20
  }
21
- export interface StepRequest {
21
+ interface StepRequestBase {
22
22
  /** Opaque identity of the Session that owns this Step. */
23
23
  readonly sessionId: string;
24
24
  /** Opaque identity of the Turn that owns this Step. */
@@ -49,17 +49,38 @@ export interface StepRequest {
49
49
  };
50
50
  tripwire(error: Tripwire): StepResponse;
51
51
  }
52
+ export type StepRequest<State = never> = StepRequestBase & ([State] extends [never] ? object : {
53
+ readonly state: Promise<State>;
54
+ });
52
55
  export interface StepResponse {
53
56
  candidate(): Readonly<ModelCandidate> | undefined;
54
57
  toolCalls(): readonly ModelToolCall[];
55
58
  replace(candidate: ModelCandidate): void;
56
59
  deny(callId: string, reason: string): void;
57
60
  requireInteraction(callId: string, interaction: Interaction): void;
58
- requirePreflight(callId: string, kind: "sandbox" | "validation"): void;
59
61
  tripwire(error: Tripwire): StepResponse;
60
62
  }
61
- export type StepMiddleware = (request: StepRequest, next: () => Promise<StepResponse>) => Promise<StepResponse>;
63
+ export type StepMiddleware<State = never> = (request: StepRequest<State>, next: () => Promise<StepResponse>) => Promise<StepResponse>;
64
+ export interface CapabilityState<State> {
65
+ create(session: SessionIdentity, signal: AbortSignal): State | Promise<State>;
66
+ dispose?(value: State): void | Promise<void>;
67
+ }
68
+ export type CapabilityItems<Item> = readonly Item[] | Readonly<{
69
+ readonly slot: string;
70
+ readonly items: readonly Item[];
71
+ }>;
72
+ /** A named capability that may supply static model surface and one typed middleware handler. */
73
+ export interface CapabilityDeclaration<State = never> {
74
+ readonly id: string;
75
+ readonly tools?: CapabilityItems<ToolDefinition<ToolDefinition["parameters"], State>>;
76
+ readonly instructions?: CapabilityItems<string>;
77
+ readonly model?: ModelDirective;
78
+ readonly state?: CapabilityState<State>;
79
+ readonly middleware?: StepMiddleware<State>;
80
+ }
62
81
  export interface BoundMiddleware {
63
82
  readonly id: string;
64
83
  readonly handle: StepMiddleware;
84
+ readonly state?: CapabilityState<unknown>;
65
85
  }
86
+ export {};
@@ -1,4 +1,4 @@
1
- import type { ContextItem, JsonObject } from "./shared.js";
1
+ import type { ContextItem, DeferredOutcome, JsonObject } from "./shared.js";
2
2
  import type { InputEvent, TranscriptEntry } from "./session.js";
3
3
  import type { BoundToolDefinition, ToolResult } from "./tool.js";
4
4
  export interface ModelToolCall {
@@ -161,6 +161,7 @@ export interface ModelCall {
161
161
  }
162
162
  export interface ModelAdapterContext {
163
163
  readonly request: ModelRequest;
164
+ readonly invocationId: string;
164
165
  readonly signal: AbortSignal;
165
166
  }
166
- export type ModelAdapter = (call: ModelCall, context: ModelAdapterContext) => Promise<ModelCandidate | string>;
167
+ export type ModelAdapter = (call: ModelCall, context: ModelAdapterContext) => Promise<ModelCandidate | string | DeferredOutcome>;
@@ -1,6 +1,6 @@
1
- import type { ModelCandidate } from "./model.js";
1
+ import type { ModelCall, ModelCandidate } from "./model.js";
2
2
  import type { JsonObject, JsonValue, Observer, Tripwire } from "./shared.js";
3
- import type { RequiredInteraction, ToolResult } from "./tool.js";
3
+ import type { RequiredInteraction, ToolExecutionResume, ToolOwner, ToolResult } from "./tool.js";
4
4
  export type InputEvent = {
5
5
  readonly kind: "user-message";
6
6
  readonly text: string;
@@ -39,6 +39,10 @@ export type SessionEvent = {
39
39
  readonly type: "tripwire";
40
40
  readonly tripwire: Tripwire;
41
41
  readonly turnId: string;
42
+ } | {
43
+ readonly type: "execution.deferred";
44
+ readonly active: ActiveExecutionRecord;
45
+ readonly turnId: string;
42
46
  } | {
43
47
  readonly type: "input.queued";
44
48
  readonly inputId: string;
@@ -67,7 +71,23 @@ export interface SessionOptions {
67
71
  readonly id?: string;
68
72
  readonly userId?: string;
69
73
  readonly context?: JsonObject;
74
+ readonly seed?: never;
75
+ readonly recorder?: SessionRecorder;
76
+ }
77
+ /** Stable host-owned facts available while a capability creates session-local state. */
78
+ export interface SessionIdentity {
79
+ readonly id: string;
80
+ readonly userId?: string;
81
+ readonly context?: JsonObject;
82
+ }
83
+ export interface SeededSessionOptions {
84
+ readonly seed: SessionSeed;
85
+ readonly recorder?: SessionRecorder;
86
+ readonly id?: never;
87
+ readonly userId?: never;
88
+ readonly context?: never;
70
89
  }
90
+ export type SessionRunOptions = SessionOptions | SeededSessionOptions;
71
91
  export interface InputOptions {
72
92
  readonly signal?: AbortSignal;
73
93
  }
@@ -108,14 +128,78 @@ export interface SessionSnapshot {
108
128
  readonly id: string;
109
129
  readonly status: "idle" | "running" | "waiting" | "stopped";
110
130
  readonly turnCount: number;
131
+ readonly revision: number;
111
132
  readonly transcript: readonly TranscriptEntry[];
112
133
  readonly pendingInteraction?: RequiredInteraction;
134
+ readonly active?: ActiveExecutionRecord;
135
+ }
136
+ export interface SessionSeed {
137
+ readonly id?: string;
138
+ readonly userId?: string;
139
+ readonly context?: JsonObject;
140
+ readonly turnCount?: number;
141
+ readonly revision?: number;
142
+ readonly transcript: readonly TranscriptEntry[];
143
+ }
144
+ export interface ActiveModelExecutionRecord {
145
+ readonly kind: "model";
146
+ readonly turnId: string;
147
+ readonly stepId: string;
148
+ readonly invocationId: string;
149
+ readonly call: ModelCall;
150
+ readonly token?: JsonValue;
151
+ }
152
+ export interface ActiveToolCallRecord {
153
+ readonly callId: string;
154
+ readonly toolName: string;
155
+ readonly args: JsonValue;
156
+ readonly invocationId: string;
157
+ readonly owner: ToolOwner;
158
+ readonly status: "pending" | "settled" | "deferred";
159
+ readonly result?: ToolResult;
160
+ readonly token?: JsonValue;
161
+ }
162
+ export interface ActiveToolsExecutionRecord {
163
+ readonly kind: "tools";
164
+ readonly turnId: string;
165
+ readonly stepId: string;
166
+ readonly calls: readonly ActiveToolCallRecord[];
167
+ }
168
+ export interface ActiveInteractionExecutionRecord {
169
+ readonly kind: "interaction";
170
+ readonly turnId: string;
171
+ readonly stepId: string;
172
+ readonly callId: string;
173
+ readonly toolName: string;
174
+ readonly interaction: RequiredInteraction;
175
+ readonly token?: JsonValue;
176
+ readonly resume?: ToolExecutionResume;
177
+ readonly tools: ActiveToolsExecutionRecord;
178
+ }
179
+ export type ActiveExecutionRecord = ActiveModelExecutionRecord | ActiveToolsExecutionRecord | ActiveInteractionExecutionRecord;
180
+ export interface SessionRecord {
181
+ readonly version: 1;
182
+ readonly revision: number;
183
+ readonly transition: "input" | "model-requested" | "candidate" | "tool-results" | "waiting" | "final" | "stopped";
184
+ readonly session: {
185
+ readonly id: string;
186
+ readonly userId?: string;
187
+ readonly context?: JsonObject;
188
+ readonly turnCount: number;
189
+ readonly status: SessionSnapshot["status"];
190
+ };
191
+ readonly transcript: readonly TranscriptEntry[];
192
+ readonly active?: ActiveExecutionRecord;
193
+ }
194
+ export interface SessionRecorder {
195
+ record(value: SessionRecord): Promise<void>;
113
196
  }
114
197
  export interface Session {
115
198
  readonly id: string;
116
199
  readonly state: SessionSnapshot;
117
200
  input(event: SessionInput, options?: InputOptions): InputHandle;
118
201
  interrupt(event: MessageInput, options?: InputOptions): InputHandle;
202
+ continue(options?: InputOptions): InputHandle;
119
203
  stream(): AsyncIterable<SessionEvent>;
120
204
  observe(listener: Observer): () => void;
121
205
  stop(reason?: string): Promise<void>;
@@ -6,6 +6,10 @@ export type JsonValue = JsonPrimitive | JsonObject | readonly JsonValue[];
6
6
  export type JsonObject = {
7
7
  readonly [key: string]: JsonValue;
8
8
  };
9
+ export interface DeferredOutcome {
10
+ readonly kind: "deferred";
11
+ readonly token?: JsonValue;
12
+ }
9
13
  export interface BuildDiagnostic {
10
14
  readonly code: string;
11
15
  readonly message: string;
@@ -25,7 +29,10 @@ export interface Tripwire {
25
29
  export interface ObserveToolSnapshot {
26
30
  readonly name: string;
27
31
  readonly description?: string;
28
- readonly executeWith: string;
32
+ readonly owner: {
33
+ readonly middlewareId: string;
34
+ readonly slot: string;
35
+ };
29
36
  readonly parameters: {
30
37
  readonly jsonSchema: JsonObject;
31
38
  };
@@ -38,9 +45,11 @@ export interface ObserveSealedCall {
38
45
  readonly callId: string;
39
46
  readonly toolName: string;
40
47
  readonly args: JsonValue;
41
- readonly executeWith: string;
42
48
  readonly invocationId: string;
43
- readonly preflight?: "sandbox" | "validation";
49
+ readonly owner: {
50
+ readonly middlewareId: string;
51
+ readonly slot: string;
52
+ };
44
53
  readonly interaction?: RequiredInteraction;
45
54
  }
46
55
  export interface ObserveModelRequested {
@@ -54,6 +63,25 @@ export interface ObserveModelRequested {
54
63
  export type ObserveEvent = {
55
64
  readonly type: "session.stopped";
56
65
  readonly reason: string;
66
+ } | {
67
+ readonly type: "capability.state.dispose.failed";
68
+ readonly capabilityId: string;
69
+ readonly attributes: {
70
+ readonly message: string;
71
+ };
72
+ } | {
73
+ readonly type: "session.seeded";
74
+ readonly revision: number;
75
+ readonly transcriptEntries: number;
76
+ } | {
77
+ readonly type: "session.continued";
78
+ readonly inputId?: string;
79
+ } | {
80
+ readonly type: "session.record.failed";
81
+ readonly code: string;
82
+ readonly attributes: {
83
+ readonly message: string;
84
+ };
57
85
  } | {
58
86
  readonly type: "input.received";
59
87
  readonly inputId: string;
@@ -83,6 +111,15 @@ export type ObserveEvent = {
83
111
  readonly inputId?: string;
84
112
  readonly requestedModelId?: string;
85
113
  readonly attributes: ModelCandidate;
114
+ } | {
115
+ readonly type: "model.deferred";
116
+ readonly turnId: string;
117
+ readonly stepId: string;
118
+ readonly inputId?: string;
119
+ readonly invocationId: string;
120
+ readonly attributes: {
121
+ readonly token?: JsonValue;
122
+ };
86
123
  } | {
87
124
  readonly type: "step.started";
88
125
  readonly turnId: string;
@@ -122,28 +159,47 @@ export type ObserveEvent = {
122
159
  readonly immediate: readonly ToolResult[];
123
160
  };
124
161
  } | {
125
- readonly type: "adapter.preflight.started" | "adapter.started";
162
+ readonly type: "tool.started";
163
+ readonly sessionId: string;
126
164
  readonly turnId: string;
127
165
  readonly stepId: string;
128
166
  readonly inputId?: string;
129
- readonly adapterId: string;
130
167
  readonly toolName: string;
131
168
  readonly callId: string;
132
- readonly invocationId?: string;
169
+ readonly invocationId: string;
170
+ readonly middlewareId: string;
171
+ readonly slot: string;
133
172
  readonly attributes: {
134
173
  readonly args: JsonValue;
135
174
  };
136
175
  } | {
137
- readonly type: "adapter.preflight.completed" | "adapter.completed";
176
+ readonly type: "tool.completed";
177
+ readonly sessionId: string;
138
178
  readonly turnId: string;
139
179
  readonly stepId: string;
140
180
  readonly inputId?: string;
141
- readonly adapterId: string;
142
181
  readonly toolName: string;
143
182
  readonly callId: string;
183
+ readonly invocationId: string;
184
+ readonly middlewareId: string;
185
+ readonly slot: string;
144
186
  readonly outcome: string;
145
187
  readonly code?: string;
146
188
  readonly attributes?: ToolResult;
189
+ } | {
190
+ readonly type: "tool.deferred";
191
+ readonly sessionId: string;
192
+ readonly turnId: string;
193
+ readonly stepId: string;
194
+ readonly inputId?: string;
195
+ readonly toolName: string;
196
+ readonly callId: string;
197
+ readonly invocationId: string;
198
+ readonly middlewareId: string;
199
+ readonly slot: string;
200
+ readonly attributes: {
201
+ readonly token?: JsonValue;
202
+ };
147
203
  } | {
148
204
  readonly type: "turn.completed";
149
205
  readonly turnId: string;
@@ -161,7 +217,7 @@ export type ObserveEvent = {
161
217
  readonly kind: string;
162
218
  readonly callId?: string;
163
219
  readonly toolName?: string;
164
- readonly phase?: "interaction" | "preflight" | "execute";
220
+ readonly phase?: "interaction" | "execute";
165
221
  readonly attributes: {
166
222
  readonly prompt: string;
167
223
  readonly metadata?: JsonObject;