@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.
- package/CHANGELOG.md +63 -0
- package/README.md +27 -108
- package/dist/build/agent.d.ts +6 -7
- package/dist/build/agent.js +16 -7
- package/dist/build/assemble.d.ts +5 -6
- package/dist/build/assemble.js +10 -43
- package/dist/build/bind-tool.d.ts +2 -3
- package/dist/build/bind-tool.js +6 -4
- package/dist/build/builder.d.ts +30 -15
- package/dist/build/builder.js +89 -38
- package/dist/build/helpers.d.ts +2 -3
- package/dist/build/helpers.js +0 -1
- package/dist/build/manifest.d.ts +2 -4
- package/dist/build/manifest.js +2 -8
- package/dist/errors.d.ts +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +2 -2
- package/dist/session/capability-state.d.ts +14 -0
- package/dist/session/capability-state.js +67 -0
- package/dist/session/input-queue.d.ts +11 -2
- package/dist/session/input-queue.js +5 -1
- package/dist/session/record.d.ts +11 -0
- package/dist/session/record.js +26 -0
- package/dist/session/scheduler.d.ts +17 -3
- package/dist/session/scheduler.js +181 -48
- package/dist/session/seed.d.ts +10 -0
- package/dist/session/seed.js +219 -0
- package/dist/session/session.d.ts +3 -2
- package/dist/session/session.js +15 -3
- package/dist/session/state.d.ts +8 -3
- package/dist/session/state.js +16 -4
- package/dist/session/submission-stream.d.ts +2 -0
- package/dist/session/submission-stream.js +11 -1
- package/dist/step/model-configuration.d.ts +1 -3
- package/dist/step/model-configuration.js +2 -4
- package/dist/step/project.js +4 -2
- package/dist/step/run.d.ts +6 -1
- package/dist/step/run.js +35 -13
- package/dist/step/seal.d.ts +6 -2
- package/dist/step/seal.js +4 -3
- package/dist/step/step-context.d.ts +5 -2
- package/dist/step/step-context.js +19 -11
- package/dist/turn/plan-runner.d.ts +28 -13
- package/dist/turn/plan-runner.js +165 -182
- package/dist/turn/runner.d.ts +18 -4
- package/dist/turn/runner.js +73 -45
- package/dist/types/manifest.d.ts +2 -6
- package/dist/types/middleware.d.ts +25 -4
- package/dist/types/model.d.ts +3 -2
- package/dist/types/session.d.ts +86 -2
- package/dist/types/shared.d.ts +65 -9
- package/dist/types/tool.d.ts +37 -39
- package/dist/utils/immutable.js +16 -2
- package/package.json +1 -2
- package/dist/build/adapters.d.ts +0 -11
- package/dist/build/adapters.js +0 -91
- package/docs/loop.md +0 -47
- package/docs/model-call-projection.md +0 -112
- package/docs/reference.md +0 -122
package/dist/turn/plan-runner.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { HarnessError, isHarnessError } from "../errors.js";
|
|
2
2
|
import { createId } from "../utils/ids.js";
|
|
3
3
|
import { copyJson, copyJsonObject } from "../utils/immutable.js";
|
|
4
|
-
/** Owns
|
|
4
|
+
/** Owns one sealed plan's deterministic interaction and concurrent execution progress. */
|
|
5
5
|
export class ToolPlanRunner {
|
|
6
6
|
plan;
|
|
7
7
|
results;
|
|
8
|
+
deferred = new Map();
|
|
8
9
|
resumes = new Map();
|
|
9
|
-
phase = "interaction";
|
|
10
|
-
index = 0;
|
|
11
10
|
pending = [];
|
|
11
|
+
settlementEvents = [];
|
|
12
|
+
interactionIndex = 0;
|
|
12
13
|
executeStarted = false;
|
|
13
14
|
resumeExecute;
|
|
14
15
|
constructor(plan) {
|
|
@@ -23,65 +24,104 @@ export class ToolPlanRunner {
|
|
|
23
24
|
}
|
|
24
25
|
get interactionCallId() {
|
|
25
26
|
const pending = this.pending[0];
|
|
26
|
-
|
|
27
|
-
return undefined;
|
|
28
|
-
return this.plan.executable[pending.index]?.call.callId;
|
|
27
|
+
return pending ? this.plan.executable[pending.index]?.call.callId : undefined;
|
|
29
28
|
}
|
|
30
29
|
get interactionToolName() {
|
|
31
30
|
const pending = this.pending[0];
|
|
32
|
-
|
|
33
|
-
return undefined;
|
|
34
|
-
return this.plan.executable[pending.index]?.call.toolName;
|
|
35
|
-
}
|
|
36
|
-
get interactionPhase() {
|
|
37
|
-
return this.pending[0]?.phase;
|
|
31
|
+
return pending ? this.plan.executable[pending.index]?.call.toolName : undefined;
|
|
38
32
|
}
|
|
39
33
|
cancelledResults(reason) {
|
|
40
34
|
return this.resultsInOrder("tool.cancelled", reason);
|
|
41
35
|
}
|
|
36
|
+
/** Publishes settlement facts only after their authoritative state was recorded. */
|
|
37
|
+
publishSettlements(observe) {
|
|
38
|
+
for (const event of this.settlementEvents.splice(0))
|
|
39
|
+
observe(event);
|
|
40
|
+
}
|
|
41
|
+
activeToolsRecord(turnId, stepId) {
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
kind: "tools",
|
|
44
|
+
turnId,
|
|
45
|
+
stepId,
|
|
46
|
+
calls: Object.freeze(this.plan.executable.map((entry) => {
|
|
47
|
+
const result = this.results.get(entry.call.callId);
|
|
48
|
+
const deferred = this.deferred.get(entry.call.callId);
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
callId: entry.call.callId,
|
|
51
|
+
toolName: entry.call.toolName,
|
|
52
|
+
args: copyJson(entry.call.args),
|
|
53
|
+
invocationId: entry.invocationId,
|
|
54
|
+
owner: entry.owner,
|
|
55
|
+
status: result
|
|
56
|
+
? "settled"
|
|
57
|
+
: deferred
|
|
58
|
+
? "deferred"
|
|
59
|
+
: "pending",
|
|
60
|
+
...(result ? { result } : {}),
|
|
61
|
+
...(deferred?.token === undefined ? {} : { token: deferred.token }),
|
|
62
|
+
});
|
|
63
|
+
})),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
activeInteractionRecord(turnId, stepId, resume) {
|
|
67
|
+
const pending = this.pending[0];
|
|
68
|
+
const entry = pending ? this.plan.executable[pending.index] : undefined;
|
|
69
|
+
if (!pending || !entry)
|
|
70
|
+
throw new HarnessError("interaction.invalid", "Missing pending interaction state");
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
kind: "interaction",
|
|
73
|
+
turnId,
|
|
74
|
+
stepId,
|
|
75
|
+
callId: entry.call.callId,
|
|
76
|
+
toolName: entry.call.toolName,
|
|
77
|
+
interaction: pending.interaction,
|
|
78
|
+
...(pending.token === undefined ? {} : { token: pending.token }),
|
|
79
|
+
...(resume === undefined ? {} : { resume }),
|
|
80
|
+
tools: this.activeToolsRecord(turnId, stepId),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
interactionResume(event) {
|
|
84
|
+
const pending = this.pending[0];
|
|
85
|
+
if (!pending)
|
|
86
|
+
throw new HarnessError("interaction.invalid", "Missing pending interaction state");
|
|
87
|
+
return resumeValue(event, pending);
|
|
88
|
+
}
|
|
42
89
|
async run(context, resume) {
|
|
43
90
|
this.acceptResume(resume);
|
|
44
|
-
while (
|
|
91
|
+
while (this.interactionIndex < this.plan.executable.length) {
|
|
45
92
|
this.throwIfAborted(context.signal);
|
|
46
|
-
|
|
47
|
-
return this.runExecute(context);
|
|
48
|
-
if (this.index >= this.plan.executable.length) {
|
|
49
|
-
if (this.phase === "interaction") {
|
|
50
|
-
this.phase = "preflight";
|
|
51
|
-
this.index = 0;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (this.phase === "preflight") {
|
|
55
|
-
this.phase = "execute";
|
|
56
|
-
this.index = 0;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
return { kind: "completed", results: this.orderedResults() };
|
|
60
|
-
}
|
|
61
|
-
const entry = this.plan.executable[this.index];
|
|
93
|
+
const entry = this.plan.executable[this.interactionIndex];
|
|
62
94
|
if (this.results.has(entry.call.callId)) {
|
|
63
|
-
this.
|
|
95
|
+
this.interactionIndex += 1;
|
|
64
96
|
continue;
|
|
65
97
|
}
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
return this.requireInteraction({
|
|
72
|
-
phase: this.phase,
|
|
73
|
-
index: this.index,
|
|
74
|
-
interaction: entry.interaction,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
const adapter = context.adapters.require(entry.call.executeWith);
|
|
78
|
-
if (this.phase === "preflight") {
|
|
79
|
-
const progress = await this.preflight(entry, adapter, context);
|
|
80
|
-
if (progress)
|
|
81
|
-
return progress;
|
|
98
|
+
if (entry.interaction) {
|
|
99
|
+
this.enqueueInteraction({ index: this.interactionIndex, interaction: entry.interaction });
|
|
100
|
+
return this.interactionRequired();
|
|
82
101
|
}
|
|
83
|
-
this.
|
|
102
|
+
this.interactionIndex += 1;
|
|
84
103
|
}
|
|
104
|
+
if (this.resumeExecute) {
|
|
105
|
+
const entry = this.resumeExecute;
|
|
106
|
+
this.resumeExecute = undefined;
|
|
107
|
+
await this.executeBatch([entry], context);
|
|
108
|
+
}
|
|
109
|
+
else if (!this.executeStarted) {
|
|
110
|
+
this.executeStarted = true;
|
|
111
|
+
await this.executeBatch(this.plan.executable.filter((entry) => !this.results.has(entry.call.callId)), context);
|
|
112
|
+
}
|
|
113
|
+
if (this.pending.length)
|
|
114
|
+
return this.interactionRequired();
|
|
115
|
+
if (this.deferred.size)
|
|
116
|
+
return {
|
|
117
|
+
kind: "deferred",
|
|
118
|
+
settled: this.orderedSettledResults(),
|
|
119
|
+
deferred: Object.freeze(this.plan.executable.flatMap((entry) => {
|
|
120
|
+
const value = this.deferred.get(entry.call.callId);
|
|
121
|
+
return value ? [value] : [];
|
|
122
|
+
})),
|
|
123
|
+
};
|
|
124
|
+
return { kind: "completed", results: this.orderedResults() };
|
|
85
125
|
}
|
|
86
126
|
acceptResume(resume) {
|
|
87
127
|
const pending = this.pending.shift();
|
|
@@ -98,162 +138,100 @@ export class ToolPlanRunner {
|
|
|
98
138
|
kind: "denied",
|
|
99
139
|
reason: "Approval rejected",
|
|
100
140
|
}));
|
|
101
|
-
this.index
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (pending.phase === "interaction") {
|
|
105
|
-
this.resumes.set(entry.call.callId, value);
|
|
106
|
-
this.index += 1;
|
|
141
|
+
this.interactionIndex = Math.max(this.interactionIndex, pending.index + 1);
|
|
107
142
|
return;
|
|
108
143
|
}
|
|
109
|
-
this.resumes.set(
|
|
110
|
-
if (
|
|
144
|
+
this.resumes.set(entry.call.callId, value);
|
|
145
|
+
if (this.executeStarted)
|
|
111
146
|
this.resumeExecute = entry;
|
|
147
|
+
else
|
|
148
|
+
this.interactionIndex = Math.max(this.interactionIndex, pending.index + 1);
|
|
112
149
|
}
|
|
113
|
-
async
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
150
|
+
async executeBatch(entries, context) {
|
|
151
|
+
const settled = await Promise.allSettled(entries.map((entry) => this.execute(entry, context)));
|
|
152
|
+
if (context.signal.aborted)
|
|
153
|
+
throw context.signal.reason;
|
|
154
|
+
for (const outcome of settled) {
|
|
155
|
+
if (outcome.status === "rejected")
|
|
156
|
+
throw outcome.reason;
|
|
157
|
+
if (outcome.value)
|
|
158
|
+
this.enqueueInteraction(outcome.value);
|
|
122
159
|
}
|
|
123
|
-
return this.pending.length
|
|
124
|
-
? this.interactionRequired()
|
|
125
|
-
: { kind: "completed", results: this.orderedResults() };
|
|
126
160
|
}
|
|
127
|
-
async
|
|
128
|
-
if (!entry.preflight)
|
|
129
|
-
return undefined;
|
|
130
|
-
if (!adapter.preflight) {
|
|
131
|
-
this.results.set(entry.call.callId, failed(entry, "tool.preflight-missing", `Adapter '${entry.call.executeWith}' does not implement preflight`));
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
161
|
+
async execute(entry, context) {
|
|
134
162
|
context.observe({
|
|
135
|
-
type: "
|
|
163
|
+
type: "tool.started",
|
|
164
|
+
sessionId: context.ids.sessionId,
|
|
136
165
|
turnId: context.ids.turnId,
|
|
137
166
|
stepId: context.ids.stepId,
|
|
138
|
-
adapterId: adapter.id,
|
|
139
167
|
toolName: entry.call.toolName,
|
|
140
168
|
callId: entry.call.callId,
|
|
141
169
|
invocationId: entry.invocationId,
|
|
170
|
+
middlewareId: entry.owner.middlewareId,
|
|
171
|
+
slot: entry.owner.slot,
|
|
142
172
|
attributes: { args: copyJson(entry.call.args) },
|
|
143
173
|
});
|
|
144
174
|
try {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
invocationId: entry.invocationId,
|
|
148
|
-
signal: context.signal,
|
|
149
|
-
resume: this.takeResume("preflight", entry.call.callId),
|
|
150
|
-
});
|
|
151
|
-
this.throwIfAborted(context.signal);
|
|
152
|
-
if (outcome.kind === "interaction-required")
|
|
153
|
-
return this.requireInteraction(interactionBarrier(this.phase, this.index, outcome));
|
|
154
|
-
if (outcome.kind !== "completed")
|
|
155
|
-
this.results.set(entry.call.callId, resultFrom(entry, outcome));
|
|
156
|
-
context.observe({
|
|
157
|
-
type: "adapter.preflight.completed",
|
|
175
|
+
const toolContext = {
|
|
176
|
+
sessionId: context.ids.sessionId,
|
|
158
177
|
turnId: context.ids.turnId,
|
|
159
178
|
stepId: context.ids.stepId,
|
|
160
|
-
adapterId: adapter.id,
|
|
161
|
-
toolName: entry.call.toolName,
|
|
162
179
|
callId: entry.call.callId,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
180
|
+
invocationId: entry.invocationId,
|
|
181
|
+
signal: context.signal,
|
|
182
|
+
resume: this.takeResume(entry.call.callId),
|
|
183
|
+
};
|
|
184
|
+
if (context.states?.has(entry.owner.middlewareId))
|
|
185
|
+
Object.defineProperty(toolContext, "state", {
|
|
186
|
+
enumerable: true,
|
|
187
|
+
get: () => context.states.get(entry.owner.middlewareId),
|
|
188
|
+
});
|
|
189
|
+
const outcome = await entry.execute(entry.call.args, Object.freeze(toolContext));
|
|
168
190
|
this.throwIfAborted(context.signal);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
adapterId: adapter.id,
|
|
176
|
-
toolName: entry.call.toolName,
|
|
177
|
-
callId: entry.call.callId,
|
|
178
|
-
outcome: "failed",
|
|
179
|
-
...adapterCompleted(result),
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
async executeBatch(entries, context) {
|
|
185
|
-
const settled = await Promise.allSettled(entries.map((entry) => this.execute(entry, context)));
|
|
186
|
-
if (context.signal.aborted)
|
|
187
|
-
throw context.signal.reason;
|
|
188
|
-
for (const outcome of settled) {
|
|
189
|
-
if (outcome.status === "rejected")
|
|
190
|
-
throw outcome.reason;
|
|
191
|
-
if (outcome.value)
|
|
192
|
-
this.enqueueInteraction(outcome.value);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
async execute(entry, context) {
|
|
196
|
-
const adapter = context.adapters.require(entry.call.executeWith);
|
|
197
|
-
try {
|
|
198
|
-
return await context.adapters.execute(adapter.id, context.signal, async () => {
|
|
199
|
-
context.observe({
|
|
200
|
-
type: "adapter.started",
|
|
201
|
-
turnId: context.ids.turnId,
|
|
202
|
-
stepId: context.ids.stepId,
|
|
203
|
-
adapterId: adapter.id,
|
|
204
|
-
toolName: entry.call.toolName,
|
|
191
|
+
if (!outcome || typeof outcome !== "object" || typeof outcome.kind !== "string")
|
|
192
|
+
throw new HarnessError("tool.invalid-tool-result", "Tool returned an invalid outcome");
|
|
193
|
+
if (outcome.kind === "interaction-required")
|
|
194
|
+
return interactionBarrier(this.plan.executable.indexOf(entry), outcome);
|
|
195
|
+
if (outcome.kind === "deferred") {
|
|
196
|
+
const value = Object.freeze({
|
|
205
197
|
callId: entry.call.callId,
|
|
198
|
+
toolName: entry.call.toolName,
|
|
199
|
+
args: copyJson(entry.call.args),
|
|
206
200
|
invocationId: entry.invocationId,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const outcome = await adapter.execute(entry.call, {
|
|
210
|
-
invocationId: entry.invocationId,
|
|
211
|
-
signal: context.signal,
|
|
212
|
-
resume: this.takeResume("execute", entry.call.callId),
|
|
201
|
+
owner: entry.owner,
|
|
202
|
+
...(outcome.token === undefined ? {} : { token: copyJson(outcome.token) }),
|
|
213
203
|
});
|
|
214
|
-
this.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
context.observe({
|
|
219
|
-
type: "adapter.completed",
|
|
204
|
+
this.deferred.set(entry.call.callId, value);
|
|
205
|
+
this.settlementEvents.push({
|
|
206
|
+
type: "tool.deferred",
|
|
207
|
+
sessionId: context.ids.sessionId,
|
|
220
208
|
turnId: context.ids.turnId,
|
|
221
209
|
stepId: context.ids.stepId,
|
|
222
|
-
adapterId: adapter.id,
|
|
223
210
|
toolName: entry.call.toolName,
|
|
224
211
|
callId: entry.call.callId,
|
|
225
|
-
|
|
226
|
-
|
|
212
|
+
invocationId: entry.invocationId,
|
|
213
|
+
middlewareId: entry.owner.middlewareId,
|
|
214
|
+
slot: entry.owner.slot,
|
|
215
|
+
attributes: outcome.token === undefined ? {} : { token: copyJson(outcome.token) },
|
|
227
216
|
});
|
|
228
217
|
return undefined;
|
|
229
|
-
}
|
|
218
|
+
}
|
|
219
|
+
const result = resultFrom(entry, outcome);
|
|
220
|
+
this.results.set(entry.call.callId, result);
|
|
221
|
+
this.settlementEvents.push(completedEvent(entry, context, result));
|
|
230
222
|
}
|
|
231
223
|
catch (error) {
|
|
232
224
|
this.throwIfAborted(context.signal);
|
|
233
225
|
const result = failed(entry, isHarnessError(error) ? error.code : "tool.execution-failed", error);
|
|
234
226
|
this.results.set(entry.call.callId, result);
|
|
235
|
-
|
|
236
|
-
type: "adapter.completed",
|
|
237
|
-
turnId: context.ids.turnId,
|
|
238
|
-
stepId: context.ids.stepId,
|
|
239
|
-
adapterId: adapter.id,
|
|
240
|
-
toolName: entry.call.toolName,
|
|
241
|
-
callId: entry.call.callId,
|
|
242
|
-
outcome: "failed",
|
|
243
|
-
...adapterCompleted(result),
|
|
244
|
-
});
|
|
227
|
+
this.settlementEvents.push(completedEvent(entry, context, result));
|
|
245
228
|
}
|
|
246
229
|
return undefined;
|
|
247
230
|
}
|
|
248
|
-
takeResume(
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return resume;
|
|
253
|
-
}
|
|
254
|
-
requireInteraction(pending) {
|
|
255
|
-
this.enqueueInteraction(pending);
|
|
256
|
-
return this.interactionRequired();
|
|
231
|
+
takeResume(callId) {
|
|
232
|
+
const value = this.resumes.get(callId);
|
|
233
|
+
this.resumes.delete(callId);
|
|
234
|
+
return value;
|
|
257
235
|
}
|
|
258
236
|
enqueueInteraction(pending) {
|
|
259
237
|
const position = this.pending.findIndex((item) => item.index > pending.index);
|
|
@@ -271,24 +249,23 @@ export class ToolPlanRunner {
|
|
|
271
249
|
orderedResults() {
|
|
272
250
|
return this.resultsInOrder("tool.missing-result", "Tool call did not produce a result");
|
|
273
251
|
}
|
|
252
|
+
orderedSettledResults() {
|
|
253
|
+
return Object.freeze(this.plan.order.flatMap(({ callId }) => {
|
|
254
|
+
const result = this.results.get(callId);
|
|
255
|
+
return result ? [result] : [];
|
|
256
|
+
}));
|
|
257
|
+
}
|
|
274
258
|
resultsInOrder(code, message) {
|
|
275
259
|
return Object.freeze(this.plan.order.map(({ callId, toolName }) => this.results.get(callId) ??
|
|
276
|
-
Object.freeze({
|
|
277
|
-
callId,
|
|
278
|
-
toolName,
|
|
279
|
-
kind: "failed",
|
|
280
|
-
code,
|
|
281
|
-
message,
|
|
282
|
-
})));
|
|
260
|
+
Object.freeze({ kind: "failed", callId, toolName, code, message })));
|
|
283
261
|
}
|
|
284
262
|
throwIfAborted(signal) {
|
|
285
263
|
if (signal.aborted)
|
|
286
264
|
throw signal.reason;
|
|
287
265
|
}
|
|
288
266
|
}
|
|
289
|
-
function interactionBarrier(
|
|
267
|
+
function interactionBarrier(index, outcome) {
|
|
290
268
|
return {
|
|
291
|
-
phase,
|
|
292
269
|
index,
|
|
293
270
|
interaction: normalizeInteraction(outcome.interaction),
|
|
294
271
|
...(outcome.token === undefined ? {} : { token: copyJson(outcome.token) }),
|
|
@@ -347,15 +324,21 @@ function resultFrom(entry, outcome) {
|
|
|
347
324
|
code: outcome.code,
|
|
348
325
|
message: outcome.message,
|
|
349
326
|
});
|
|
350
|
-
default:
|
|
351
|
-
throw new HarnessError("adapter.invalid-outcome", "Tool adapter returned an invalid outcome");
|
|
352
327
|
}
|
|
353
328
|
}
|
|
354
|
-
function
|
|
355
|
-
if (!result)
|
|
356
|
-
return {};
|
|
329
|
+
function completedEvent(entry, context, result) {
|
|
357
330
|
return {
|
|
358
|
-
|
|
331
|
+
type: "tool.completed",
|
|
332
|
+
sessionId: context.ids.sessionId,
|
|
333
|
+
turnId: context.ids.turnId,
|
|
334
|
+
stepId: context.ids.stepId,
|
|
335
|
+
toolName: entry.call.toolName,
|
|
336
|
+
callId: entry.call.callId,
|
|
337
|
+
invocationId: entry.invocationId,
|
|
338
|
+
middlewareId: entry.owner.middlewareId,
|
|
339
|
+
slot: entry.owner.slot,
|
|
340
|
+
outcome: result.kind,
|
|
341
|
+
...(result.kind === "failed" ? { code: result.code } : {}),
|
|
359
342
|
attributes: copyJson(result),
|
|
360
343
|
};
|
|
361
344
|
}
|
package/dist/turn/runner.d.ts
CHANGED
|
@@ -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
|
|
44
|
+
readonly commit: (state: SessionSnapshot, transition: SessionRecord["transition"], active?: ActiveExecutionRecord) => Promise<SessionSnapshot>;
|
|
37
45
|
readonly onConversation: (event: SessionEvent) => void;
|
|
38
|
-
readonly claimInterrupts: (turnId: string) =>
|
|
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
|
|
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
|
}
|