@nylorun/harness 0.12.0-beta → 0.15.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.
- package/CHANGELOG.md +71 -0
- package/HOST_CONTRACT.md +27 -0
- package/README.md +86 -5
- package/dist/compatibility.d.ts +3 -0
- package/dist/compatibility.js +3 -0
- package/dist/definition/agent-definition.d.ts +19 -0
- package/dist/definition/agent-definition.js +20 -0
- package/dist/definition/compatibility.d.ts +11 -0
- package/dist/definition/compatibility.js +30 -0
- package/dist/definition/registry.d.ts +10 -0
- package/dist/definition/registry.js +49 -0
- package/dist/index.d.ts +4 -15
- package/dist/index.js +1 -6
- package/dist/loop/dispatch.d.ts +2 -0
- package/dist/loop/dispatch.js +327 -0
- package/dist/loop/host-suspension.d.ts +6 -0
- package/dist/loop/host-suspension.js +10 -0
- package/dist/loop/initial-state.d.ts +13 -0
- package/dist/loop/initial-state.js +30 -0
- package/dist/loop/invocation.d.ts +24 -0
- package/dist/loop/invocation.js +37 -0
- package/dist/{model → loop/model}/adapters.d.ts +4 -2
- package/dist/{model → loop/model}/adapters.js +3 -2
- package/dist/{model → loop/model}/normalize.d.ts +2 -2
- package/dist/{model → loop/model}/normalize.js +2 -2
- package/dist/{model → loop/model}/prepared.d.ts +3 -3
- package/dist/{step → loop/model}/project.d.ts +1 -1
- package/dist/{step → loop/model}/project.js +2 -10
- package/dist/loop/observe.d.ts +17 -0
- package/dist/loop/observe.js +53 -0
- package/dist/loop/options.d.ts +11 -0
- package/dist/loop/options.js +69 -0
- package/dist/loop/resume.d.ts +4 -0
- package/dist/loop/resume.js +52 -0
- package/dist/loop/run.d.ts +5 -0
- package/dist/loop/run.js +264 -0
- package/dist/loop/state.d.ts +8 -0
- package/dist/loop/state.js +218 -0
- package/dist/{step → loop/step}/canonicalize.d.ts +2 -2
- package/dist/{step → loop/step}/canonicalize.js +2 -2
- package/dist/{step → loop/step}/compose.d.ts +3 -2
- package/dist/{step → loop/step}/compose.js +8 -5
- package/dist/loop/step/context-draft.d.ts +8 -0
- package/dist/{step → loop/step}/context-draft.js +5 -22
- package/dist/loop/step/describe.d.ts +9 -0
- package/dist/loop/step/describe.js +22 -0
- package/dist/loop/step/dynamics.d.ts +29 -0
- package/dist/loop/step/dynamics.js +147 -0
- package/dist/{step → loop/step}/model-configuration.d.ts +4 -3
- package/dist/{step → loop/step}/model-configuration.js +3 -3
- package/dist/loop/step/resolve.d.ts +11 -0
- package/dist/{step → loop/step}/resolve.js +5 -3
- package/dist/loop/step/run.d.ts +30 -0
- package/dist/{step → loop/step}/run.js +104 -53
- package/dist/loop/step/runtime.d.ts +40 -0
- package/dist/{step → loop/step}/seal.d.ts +13 -8
- package/dist/{step → loop/step}/seal.js +8 -6
- package/dist/{step → loop/step}/slot-assembly.d.ts +4 -4
- package/dist/{step → loop/step}/slot-assembly.js +4 -4
- package/dist/{step → loop/step}/step-context.d.ts +11 -12
- package/dist/{step → loop/step}/step-context.js +5 -27
- package/dist/loop/tool-result.d.ts +4 -0
- package/dist/loop/tool-result.js +30 -0
- package/dist/loop/transcript.d.ts +2 -0
- package/dist/{session/seed.js → loop/transcript.js} +11 -41
- package/dist/loop/waits.d.ts +50 -0
- package/dist/loop/waits.js +11 -0
- package/dist/run/durable.d.ts +71 -0
- package/dist/run/durable.js +164 -0
- package/dist/run/index.d.ts +33 -0
- package/dist/run/index.js +38 -0
- package/dist/types/client.d.ts +88 -0
- package/dist/types/execution.d.ts +114 -0
- package/dist/utils/ids.d.ts +1 -0
- package/dist/utils/ids.js +11 -1
- package/package.json +18 -6
- package/dist/build/agent.d.ts +0 -17
- package/dist/build/agent.js +0 -32
- package/dist/build/assemble.d.ts +0 -7
- package/dist/build/assemble.js +0 -41
- package/dist/build/bind-tool.d.ts +0 -3
- package/dist/build/bind-tool.js +0 -18
- package/dist/build/builder.d.ts +0 -38
- package/dist/build/builder.js +0 -136
- package/dist/build/helpers.d.ts +0 -6
- package/dist/build/helpers.js +0 -4
- package/dist/build/manifest.d.ts +0 -7
- package/dist/build/manifest.js +0 -20
- package/dist/build/schema.d.ts +0 -14
- package/dist/build/schema.js +0 -205
- package/dist/errors.d.ts +0 -17
- package/dist/errors.js +0 -17
- package/dist/session/capability-state.d.ts +0 -14
- package/dist/session/capability-state.js +0 -67
- package/dist/session/event-log.d.ts +0 -12
- package/dist/session/event-log.js +0 -63
- package/dist/session/input-queue.d.ts +0 -41
- package/dist/session/input-queue.js +0 -94
- package/dist/session/output-contract.d.ts +0 -6
- package/dist/session/output-contract.js +0 -12
- package/dist/session/record.d.ts +0 -11
- package/dist/session/record.js +0 -26
- package/dist/session/scheduler.d.ts +0 -62
- package/dist/session/scheduler.js +0 -475
- package/dist/session/seed.d.ts +0 -10
- package/dist/session/session.d.ts +0 -17
- package/dist/session/session.js +0 -101
- package/dist/session/state.d.ts +0 -15
- package/dist/session/state.js +0 -48
- package/dist/session/submission-stream.d.ts +0 -16
- package/dist/session/submission-stream.js +0 -46
- package/dist/step/context-draft.d.ts +0 -10
- package/dist/step/resolve.d.ts +0 -10
- package/dist/step/run.d.ts +0 -33
- package/dist/turn/plan-runner.d.ts +0 -72
- package/dist/turn/plan-runner.js +0 -389
- package/dist/turn/runner.d.ts +0 -69
- package/dist/turn/runner.js +0 -159
- package/dist/types/manifest.d.ts +0 -19
- package/dist/types/middleware.d.ts +0 -95
- package/dist/types/model.d.ts +0 -184
- package/dist/types/session.d.ts +0 -230
- package/dist/types/session.js +0 -1
- package/dist/types/shared.d.ts +0 -249
- package/dist/types/shared.js +0 -1
- package/dist/types/tool.d.ts +0 -139
- package/dist/types/tool.js +0 -1
- package/dist/utils/immutable.d.ts +0 -5
- package/dist/utils/immutable.js +0 -68
- package/dist/utils/maps.d.ts +0 -1
- package/dist/utils/maps.js +0 -37
- package/dist/utils/observe.d.ts +0 -3
- package/dist/utils/observe.js +0 -13
- /package/dist/{model → loop/model}/prepared.js +0 -0
- /package/dist/{types/manifest.js → loop/step/runtime.js} +0 -0
- /package/dist/types/{middleware.js → client.js} +0 -0
- /package/dist/types/{model.js → execution.js} +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { HostSuspension } from "./host-suspension.js";
|
|
2
|
+
import { HarnessError, isHarnessError } from "@nylorun/core/define";
|
|
3
|
+
import { isToolError } from "@nylorun/core/define";
|
|
4
|
+
import { createId } from "../utils/ids.js";
|
|
5
|
+
import { copyJson } from "@nylorun/core/define";
|
|
6
|
+
import { createSessionStateBag } from "./initial-state.js";
|
|
7
|
+
import { toolResult } from "./tool-result.js";
|
|
8
|
+
import { isWaitSignal, WaitSignal } from "./waits.js";
|
|
9
|
+
export async function dispatchPlan(invocation) {
|
|
10
|
+
const { signal, definitions, options, observe, record } = invocation;
|
|
11
|
+
let plan = invocation.state.plan;
|
|
12
|
+
if (plan.calls.some((call) => call.status === "interaction" && call.interactionPhase === "before")) {
|
|
13
|
+
for (const call of plan.calls)
|
|
14
|
+
if (call.status === "interaction")
|
|
15
|
+
observe({
|
|
16
|
+
type: "interaction.required",
|
|
17
|
+
turnId: plan.turnId,
|
|
18
|
+
stepId: plan.stepId,
|
|
19
|
+
interactionId: call.interaction.id,
|
|
20
|
+
kind: call.interaction.kind,
|
|
21
|
+
callId: call.callId,
|
|
22
|
+
toolName: call.toolName,
|
|
23
|
+
phase: "interaction",
|
|
24
|
+
attributes: {
|
|
25
|
+
prompt: call.interaction.prompt,
|
|
26
|
+
...(call.interaction.metadata ? { metadata: call.interaction.metadata } : {}),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
return "paused";
|
|
30
|
+
}
|
|
31
|
+
signal.throwIfAborted();
|
|
32
|
+
const pending = plan.calls.filter((call) => call.status === "pending" || call.status === "active");
|
|
33
|
+
const redelivering = plan.calls.some((call) => call.status === "active");
|
|
34
|
+
if (pending.length) {
|
|
35
|
+
invocation.state = {
|
|
36
|
+
...invocation.state,
|
|
37
|
+
plan: {
|
|
38
|
+
...plan,
|
|
39
|
+
calls: plan.calls.map((call) => call.status === "pending" || call.status === "active"
|
|
40
|
+
? { ...call, status: "active" }
|
|
41
|
+
: call),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
await record();
|
|
45
|
+
signal.throwIfAborted();
|
|
46
|
+
const sessionBag = invocation.sessionBag;
|
|
47
|
+
const stateApi = createSessionStateBag(sessionBag);
|
|
48
|
+
const settled = await settleDispatch(pending.map(async (call) => {
|
|
49
|
+
const definition = definitions.get(call.invocationId);
|
|
50
|
+
const eventIds = {
|
|
51
|
+
executionId: invocation.state.executionId,
|
|
52
|
+
turnId: plan.turnId,
|
|
53
|
+
stepId: plan.stepId,
|
|
54
|
+
callId: call.callId,
|
|
55
|
+
invocationId: call.invocationId,
|
|
56
|
+
toolName: call.toolName,
|
|
57
|
+
middlewareId: call.reference.capabilityId,
|
|
58
|
+
slot: definition.owner.slot,
|
|
59
|
+
};
|
|
60
|
+
observe({ type: "tool.started", ...eventIds, attributes: { args: call.args } });
|
|
61
|
+
let outcome;
|
|
62
|
+
try {
|
|
63
|
+
signal.throwIfAborted();
|
|
64
|
+
if (definition.approval && !call.resume) {
|
|
65
|
+
const decision = await definition.approval(call.args);
|
|
66
|
+
const prompt = typeof decision === "string"
|
|
67
|
+
? decision
|
|
68
|
+
: decision === true
|
|
69
|
+
? `Approve ${call.toolName}?`
|
|
70
|
+
: undefined;
|
|
71
|
+
if (prompt) {
|
|
72
|
+
return copyJson({
|
|
73
|
+
...call,
|
|
74
|
+
status: "interaction",
|
|
75
|
+
interaction: {
|
|
76
|
+
kind: "approval",
|
|
77
|
+
prompt,
|
|
78
|
+
id: createId("interaction"),
|
|
79
|
+
},
|
|
80
|
+
interactionPhase: "before",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const stepMemos = new Map();
|
|
85
|
+
if (call.token && typeof call.token === "object" && !Array.isArray(call.token)) {
|
|
86
|
+
const memos = call.token.steps;
|
|
87
|
+
if (memos)
|
|
88
|
+
for (const [key, value] of Object.entries(memos))
|
|
89
|
+
stepMemos.set(key, value);
|
|
90
|
+
}
|
|
91
|
+
const raw = await definition.execute(call.args, {
|
|
92
|
+
executionId: invocation.state.executionId,
|
|
93
|
+
turnId: plan.turnId,
|
|
94
|
+
stepId: plan.stepId,
|
|
95
|
+
callId: call.callId,
|
|
96
|
+
invocationId: call.invocationId,
|
|
97
|
+
signal,
|
|
98
|
+
info: options.info,
|
|
99
|
+
onModelCall: options.onModelCall,
|
|
100
|
+
idempotencyKey: call.callId,
|
|
101
|
+
...(redelivering || call.status === "active" ? { redelivery: true } : {}),
|
|
102
|
+
state: stateApi,
|
|
103
|
+
session: { id: invocation.state.executionId },
|
|
104
|
+
progress(message, data) {
|
|
105
|
+
observe({
|
|
106
|
+
type: "tool.progress",
|
|
107
|
+
...eventIds,
|
|
108
|
+
attributes: {
|
|
109
|
+
message,
|
|
110
|
+
...(data === undefined ? {} : { data }),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
async ask(prompt, opts) {
|
|
115
|
+
if (call.resume?.kind === "response")
|
|
116
|
+
return call.resume.value;
|
|
117
|
+
throw new WaitSignal({
|
|
118
|
+
kind: "interaction-required",
|
|
119
|
+
interaction: {
|
|
120
|
+
kind: "response",
|
|
121
|
+
prompt,
|
|
122
|
+
...(opts ? { metadata: opts } : {}),
|
|
123
|
+
},
|
|
124
|
+
wait: { kind: "ask", waitId: createId("wait") },
|
|
125
|
+
token: { steps: Object.fromEntries(stepMemos) },
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
async approve(prompt) {
|
|
129
|
+
if (call.resume?.kind === "approval")
|
|
130
|
+
return Boolean(call.resume.approved);
|
|
131
|
+
throw new WaitSignal({
|
|
132
|
+
kind: "interaction-required",
|
|
133
|
+
interaction: { kind: "approval", prompt },
|
|
134
|
+
wait: { kind: "approve", waitId: createId("wait") },
|
|
135
|
+
token: { steps: Object.fromEntries(stepMemos) },
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
async sleep(duration) {
|
|
139
|
+
throw new WaitSignal({
|
|
140
|
+
kind: "deferred",
|
|
141
|
+
wait: { kind: "sleep", waitId: createId("wait") },
|
|
142
|
+
token: { steps: Object.fromEntries(stepMemos), duration },
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
async waitFor(event, opts) {
|
|
146
|
+
throw new WaitSignal({
|
|
147
|
+
kind: "deferred",
|
|
148
|
+
wait: { kind: "waitFor", waitId: createId("wait"), name: event },
|
|
149
|
+
token: { steps: Object.fromEntries(stepMemos), event, ...(opts ?? {}) },
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
async step(name, fn) {
|
|
153
|
+
if (stepMemos.has(name))
|
|
154
|
+
return stepMemos.get(name);
|
|
155
|
+
const value = await fn();
|
|
156
|
+
stepMemos.set(name, value);
|
|
157
|
+
return value;
|
|
158
|
+
},
|
|
159
|
+
...(call.resume ? { resume: call.resume } : {}),
|
|
160
|
+
});
|
|
161
|
+
outcome = normalizeOutcome(raw);
|
|
162
|
+
if (!outcome || typeof outcome !== "object")
|
|
163
|
+
throw new HarnessError("tool.invalid-tool-result", "Tool returned an invalid outcome");
|
|
164
|
+
if (outcome.kind === "interaction-required") {
|
|
165
|
+
if (!["approval", "response"].includes(outcome.interaction.kind) ||
|
|
166
|
+
typeof outcome.interaction.prompt !== "string")
|
|
167
|
+
throw new HarnessError("tool.invalid-tool-result", "Tool returned an invalid interaction");
|
|
168
|
+
return copyJson({
|
|
169
|
+
...call,
|
|
170
|
+
status: "interaction",
|
|
171
|
+
interaction: {
|
|
172
|
+
...outcome.interaction,
|
|
173
|
+
id: outcome.interaction.id ?? createId("interaction"),
|
|
174
|
+
},
|
|
175
|
+
interactionPhase: "execute",
|
|
176
|
+
...(outcome.token === undefined ? {} : { token: outcome.token }),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (outcome.kind === "deferred") {
|
|
180
|
+
observe({
|
|
181
|
+
type: "tool.deferred",
|
|
182
|
+
...eventIds,
|
|
183
|
+
attributes: outcome.token === undefined ? {} : { token: outcome.token },
|
|
184
|
+
});
|
|
185
|
+
return copyJson({
|
|
186
|
+
...call,
|
|
187
|
+
status: "deferred",
|
|
188
|
+
...(outcome.token === undefined ? {} : { token: outcome.token }),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
const result = toolResult(call, definition, outcome);
|
|
192
|
+
observe({
|
|
193
|
+
type: "tool.completed",
|
|
194
|
+
...eventIds,
|
|
195
|
+
outcome: result.kind,
|
|
196
|
+
attributes: result,
|
|
197
|
+
});
|
|
198
|
+
return copyJson({ ...call, status: "settled", result });
|
|
199
|
+
}
|
|
200
|
+
catch (cause) {
|
|
201
|
+
if (cause instanceof HostSuspension)
|
|
202
|
+
throw cause;
|
|
203
|
+
if (isWaitSignal(cause)) {
|
|
204
|
+
if (cause.outcome.kind === "interaction-required") {
|
|
205
|
+
return copyJson({
|
|
206
|
+
...call,
|
|
207
|
+
status: "interaction",
|
|
208
|
+
interaction: {
|
|
209
|
+
...cause.outcome.interaction,
|
|
210
|
+
id: createId("interaction"),
|
|
211
|
+
},
|
|
212
|
+
interactionPhase: "execute",
|
|
213
|
+
wait: cause.outcome.wait,
|
|
214
|
+
...(cause.outcome.token === undefined ? {} : { token: cause.outcome.token }),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
observe({
|
|
218
|
+
type: "tool.deferred",
|
|
219
|
+
...eventIds,
|
|
220
|
+
attributes: cause.outcome.token === undefined ? {} : { token: cause.outcome.token },
|
|
221
|
+
});
|
|
222
|
+
return copyJson({
|
|
223
|
+
...call,
|
|
224
|
+
status: "deferred",
|
|
225
|
+
wait: cause.outcome.wait,
|
|
226
|
+
...(cause.outcome.token === undefined ? {} : { token: cause.outcome.token }),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (isToolError(cause)) {
|
|
230
|
+
const result = {
|
|
231
|
+
callId: call.callId,
|
|
232
|
+
toolName: call.toolName,
|
|
233
|
+
kind: "failed",
|
|
234
|
+
code: cause.code,
|
|
235
|
+
message: cause.message,
|
|
236
|
+
};
|
|
237
|
+
observe({
|
|
238
|
+
type: "tool.completed",
|
|
239
|
+
...eventIds,
|
|
240
|
+
outcome: result.kind,
|
|
241
|
+
attributes: result,
|
|
242
|
+
});
|
|
243
|
+
return copyJson({ ...call, status: "settled", result });
|
|
244
|
+
}
|
|
245
|
+
const result = {
|
|
246
|
+
callId: call.callId,
|
|
247
|
+
toolName: call.toolName,
|
|
248
|
+
kind: "failed",
|
|
249
|
+
code: signal.aborted
|
|
250
|
+
? "tool.cancelled"
|
|
251
|
+
: isHarnessError(cause)
|
|
252
|
+
? cause.code
|
|
253
|
+
: "tool.execution-failed",
|
|
254
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
255
|
+
};
|
|
256
|
+
observe({
|
|
257
|
+
type: "tool.completed",
|
|
258
|
+
...eventIds,
|
|
259
|
+
outcome: result.kind,
|
|
260
|
+
attributes: result,
|
|
261
|
+
});
|
|
262
|
+
return copyJson({ ...call, status: "settled", result });
|
|
263
|
+
}
|
|
264
|
+
}));
|
|
265
|
+
const byId = new Map(settled.map((call) => [call.invocationId, call]));
|
|
266
|
+
plan = { ...plan, calls: plan.calls.map((call) => byId.get(call.invocationId) ?? call) };
|
|
267
|
+
invocation.state = {
|
|
268
|
+
...invocation.state,
|
|
269
|
+
plan,
|
|
270
|
+
};
|
|
271
|
+
await record();
|
|
272
|
+
}
|
|
273
|
+
signal.throwIfAborted();
|
|
274
|
+
if (plan.calls.some((call) => call.status === "interaction" || call.status === "deferred")) {
|
|
275
|
+
for (const call of plan.calls)
|
|
276
|
+
if (call.status === "interaction")
|
|
277
|
+
observe({
|
|
278
|
+
type: "interaction.required",
|
|
279
|
+
turnId: plan.turnId,
|
|
280
|
+
stepId: plan.stepId,
|
|
281
|
+
interactionId: call.interaction.id,
|
|
282
|
+
kind: call.interaction.kind,
|
|
283
|
+
callId: call.callId,
|
|
284
|
+
toolName: call.toolName,
|
|
285
|
+
phase: call.interactionPhase === "before" ? "interaction" : "execute",
|
|
286
|
+
attributes: {
|
|
287
|
+
prompt: call.interaction.prompt,
|
|
288
|
+
...(call.interaction.metadata ? { metadata: call.interaction.metadata } : {}),
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
return "paused";
|
|
292
|
+
}
|
|
293
|
+
const results = [
|
|
294
|
+
...plan.immediate,
|
|
295
|
+
...plan.calls.flatMap((call) => (call.result ? [call.result] : [])),
|
|
296
|
+
];
|
|
297
|
+
const ordered = plan.order.map((item) => results.find((result) => result.callId === item.callId && result.toolName === item.toolName));
|
|
298
|
+
const { plan: _, ...rest } = invocation.state;
|
|
299
|
+
invocation.state = {
|
|
300
|
+
...rest,
|
|
301
|
+
transcript: [
|
|
302
|
+
...invocation.state.transcript,
|
|
303
|
+
{ kind: "tool-results", turnId: plan.turnId, stepId: plan.stepId, results: ordered },
|
|
304
|
+
],
|
|
305
|
+
};
|
|
306
|
+
await record();
|
|
307
|
+
}
|
|
308
|
+
function normalizeOutcome(raw) {
|
|
309
|
+
if (raw && typeof raw === "object" && "kind" in raw) {
|
|
310
|
+
const kind = raw.kind;
|
|
311
|
+
if (kind === "completed" ||
|
|
312
|
+
kind === "denied" ||
|
|
313
|
+
kind === "failed" ||
|
|
314
|
+
kind === "interaction-required" ||
|
|
315
|
+
kind === "deferred")
|
|
316
|
+
return raw;
|
|
317
|
+
}
|
|
318
|
+
return { kind: "completed", output: raw };
|
|
319
|
+
}
|
|
320
|
+
// Persist all parallel action intents before handing control back to a durable host.
|
|
321
|
+
async function settleDispatch(promises) {
|
|
322
|
+
const results = await Promise.allSettled(promises);
|
|
323
|
+
const failure = results.find((r) => r.status === "rejected");
|
|
324
|
+
if (failure)
|
|
325
|
+
throw failure.reason;
|
|
326
|
+
return results.map((r) => r.value);
|
|
327
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Control transfer to a durable host, never a model/tool failure. */
|
|
2
|
+
export class HostSuspension extends Error {
|
|
3
|
+
effectId;
|
|
4
|
+
status;
|
|
5
|
+
constructor(effectId, status) {
|
|
6
|
+
super(`Host suspended at ${effectId}`);
|
|
7
|
+
this.effectId = effectId;
|
|
8
|
+
this.status = status;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentDefinition } from "../definition/agent-definition.js";
|
|
2
|
+
import type { ExecutionState } from "../types/execution.js";
|
|
3
|
+
import type { JsonObject, JsonValue } from "@nylorun/core/define";
|
|
4
|
+
export declare function initializeExecutionState(agent: AgentDefinition, options?: {
|
|
5
|
+
readonly executionId?: string;
|
|
6
|
+
readonly state?: JsonObject;
|
|
7
|
+
}): ExecutionState;
|
|
8
|
+
/** Mutable session-memory bag backed by Invocation / ExecutionState.state. */
|
|
9
|
+
export declare function createSessionStateBag(bag: Record<string, JsonValue>): {
|
|
10
|
+
get(key: string): JsonValue | undefined;
|
|
11
|
+
set(key: string, value: JsonValue): void;
|
|
12
|
+
entries(): Readonly<Record<string, JsonValue>>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { copyJson } from "@nylorun/core/define";
|
|
2
|
+
import { createId } from "../utils/ids.js";
|
|
3
|
+
export function initializeExecutionState(agent, options = {}) {
|
|
4
|
+
return copyJson({
|
|
5
|
+
version: 1,
|
|
6
|
+
agentId: agent.id,
|
|
7
|
+
manifestHash: agent.hash,
|
|
8
|
+
executionId: options.executionId ?? createId("execution"),
|
|
9
|
+
revision: 0,
|
|
10
|
+
turnCount: 0,
|
|
11
|
+
transcript: [],
|
|
12
|
+
status: "ready",
|
|
13
|
+
state: options.state ?? {},
|
|
14
|
+
...(agent.output ? { outputContract: agent.output.schema.jsonSchema } : {}),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** Mutable session-memory bag backed by Invocation / ExecutionState.state. */
|
|
18
|
+
export function createSessionStateBag(bag) {
|
|
19
|
+
return {
|
|
20
|
+
get(key) {
|
|
21
|
+
return bag[key];
|
|
22
|
+
},
|
|
23
|
+
set(key, value) {
|
|
24
|
+
bag[key] = value;
|
|
25
|
+
},
|
|
26
|
+
entries() {
|
|
27
|
+
return { ...bag };
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AgentDefinition } from "../definition/agent-definition.js";
|
|
2
|
+
import type { BoundToolDefinition } from "@nylorun/core/define";
|
|
3
|
+
import type { ExecutionState, RunOptions } from "../types/execution.js";
|
|
4
|
+
import type { ObserveEvent } from "@nylorun/core/define";
|
|
5
|
+
import type { JsonValue } from "@nylorun/core/define";
|
|
6
|
+
import { type ExecutionOutcomeEvent, type ObserveEmit } from "./observe.js";
|
|
7
|
+
export interface Invocation {
|
|
8
|
+
readonly agent: AgentDefinition;
|
|
9
|
+
readonly options: RunOptions<any>;
|
|
10
|
+
readonly signal: AbortSignal;
|
|
11
|
+
readonly definitions: Map<string, BoundToolDefinition>;
|
|
12
|
+
state: ExecutionState;
|
|
13
|
+
/** Mutable durable session memory for this invocation. */
|
|
14
|
+
sessionBag: Record<string, JsonValue>;
|
|
15
|
+
observe: ObserveEmit;
|
|
16
|
+
emit(event: ObserveEvent | ExecutionOutcomeEvent): void;
|
|
17
|
+
record(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createInvocation(input: {
|
|
20
|
+
readonly agent: AgentDefinition;
|
|
21
|
+
readonly options: RunOptions<any>;
|
|
22
|
+
readonly state: ExecutionState;
|
|
23
|
+
readonly definitions: Map<string, BoundToolDefinition>;
|
|
24
|
+
}): Invocation;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { HarnessError } from "@nylorun/core/define";
|
|
2
|
+
import { copyJson } from "@nylorun/core/define";
|
|
3
|
+
import { createId } from "../utils/ids.js";
|
|
4
|
+
import { createObservationSink } from "./observe.js";
|
|
5
|
+
export function createInvocation(input) {
|
|
6
|
+
const invocation = {
|
|
7
|
+
agent: input.agent,
|
|
8
|
+
options: input.options,
|
|
9
|
+
signal: input.options.signal ?? new AbortController().signal,
|
|
10
|
+
definitions: input.definitions,
|
|
11
|
+
state: input.state,
|
|
12
|
+
sessionBag: { ...(input.state.state ?? {}) },
|
|
13
|
+
};
|
|
14
|
+
const sink = createObservationSink({
|
|
15
|
+
listener: input.options.onEvent,
|
|
16
|
+
executionId: () => invocation.state.executionId,
|
|
17
|
+
runId: createId("run"),
|
|
18
|
+
});
|
|
19
|
+
invocation.observe = sink.observe;
|
|
20
|
+
invocation.emit = sink.emit;
|
|
21
|
+
invocation.record = async () => {
|
|
22
|
+
invocation.state = copyJson({
|
|
23
|
+
...invocation.state,
|
|
24
|
+
state: { ...invocation.sessionBag },
|
|
25
|
+
revision: invocation.state.revision + 1,
|
|
26
|
+
});
|
|
27
|
+
if (!input.options.record)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
await input.options.record(copyJson(invocation.state));
|
|
31
|
+
}
|
|
32
|
+
catch (cause) {
|
|
33
|
+
throw new HarnessError("execution.record-failed", "Recording failed. Execution stopped; reconcile any external effects before retrying.", { cause });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return invocation;
|
|
37
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { ModelAdapter, ModelAdapterContext, ModelCall, ModelCandidate } from "
|
|
2
|
-
import type { JsonObject } from "
|
|
1
|
+
import type { ModelAdapter, ModelAdapterContext, ModelCall, ModelCandidate } from "@nylorun/core/define";
|
|
2
|
+
import type { JsonObject } from "@nylorun/core/define";
|
|
3
|
+
import { preparedModel } from "./prepared.js";
|
|
4
|
+
export { preparedModel };
|
|
3
5
|
export type ChatCompletionsMessage = {
|
|
4
6
|
readonly role: "system";
|
|
5
7
|
readonly content: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { HarnessError } from "
|
|
2
|
-
import { copyJsonObject } from "
|
|
1
|
+
import { HarnessError } from "@nylorun/core/define";
|
|
2
|
+
import { copyJsonObject } from "@nylorun/core/define";
|
|
3
3
|
import { preparedModel } from "./prepared.js";
|
|
4
|
+
export { preparedModel };
|
|
4
5
|
/** Translate a Harness call to the OpenAI Chat Completions request shape. */
|
|
5
6
|
export function toChatCompletions(call) {
|
|
6
7
|
const messages = withoutProviderReasoning(call.prompt).map((item) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { HarnessError } from "
|
|
2
|
-
import type { ModelCandidate, ModelDirective, ModelOutputBlock } from "
|
|
1
|
+
import { HarnessError } from "@nylorun/core/define";
|
|
2
|
+
import type { ModelCandidate, ModelDirective, ModelOutputBlock } from "@nylorun/core/define";
|
|
3
3
|
export declare function normalizeDirective(value: unknown): ModelDirective | HarnessError;
|
|
4
4
|
export declare function sameDirective(left: ModelDirective, right: ModelDirective): boolean;
|
|
5
5
|
export declare function textFromOutput(output: readonly ModelOutputBlock[]): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { HarnessError, isHarnessError } from "
|
|
2
|
-
import { assertJson, copyJson, copyJsonObject } from "
|
|
1
|
+
import { HarnessError, isHarnessError } from "@nylorun/core/define";
|
|
2
|
+
import { assertJson, copyJson, copyJsonObject } from "@nylorun/core/define";
|
|
3
3
|
const FINISH_REASONS = new Set([
|
|
4
4
|
"stop",
|
|
5
5
|
"length",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ModelAdapter, ModelAdapterContext, ModelCandidate, ModelCall } from "
|
|
2
|
-
import type {
|
|
1
|
+
import type { ModelAdapter, ModelAdapterContext, ModelCandidate, ModelCall } from "@nylorun/core/define";
|
|
2
|
+
import type { JsonValue } from "@nylorun/core/define";
|
|
3
3
|
export interface PreparedModelOptions<Wire> {
|
|
4
4
|
readonly adapter: string;
|
|
5
5
|
prepare(call: ModelCall, context: ModelAdapterContext): Promise<{
|
|
@@ -7,7 +7,7 @@ export interface PreparedModelOptions<Wire> {
|
|
|
7
7
|
readonly observed: JsonValue;
|
|
8
8
|
}>;
|
|
9
9
|
send(request: Wire, call: ModelCall, context: ModelAdapterContext): Promise<unknown>;
|
|
10
|
-
decode(response: unknown, call: ModelCall): ModelCandidate | string
|
|
10
|
+
decode(response: unknown, call: ModelCall): ModelCandidate | string;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Builds a ModelAdapter that reports the provider request derived from Harness's canonical call.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ModelCall, ModelRequest } from "
|
|
1
|
+
import type { ModelCall, ModelRequest } from "@nylorun/core/define";
|
|
2
2
|
export declare function projectModelCall(request: ModelRequest): ModelCall;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copyJson } from "
|
|
1
|
+
import { copyJson, freezeGraph } from "@nylorun/core/define";
|
|
2
2
|
export function projectModelCall(request) {
|
|
3
3
|
return freezeGraph({
|
|
4
4
|
prompt: Object.freeze([
|
|
@@ -13,7 +13,7 @@ export function projectModelCall(request) {
|
|
|
13
13
|
}))),
|
|
14
14
|
...(request.model === undefined ? {} : { model: copyJson(request.model) }),
|
|
15
15
|
...(request.outputSchema === undefined ? {} : { outputSchema: copyJson(request.outputSchema) }),
|
|
16
|
-
|
|
16
|
+
executionId: request.executionId,
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
function projectInstructions(instructions) {
|
|
@@ -121,11 +121,3 @@ function freezeItem(item) {
|
|
|
121
121
|
content: Object.freeze(item.content.map((part) => Object.freeze(part))),
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
-
function freezeGraph(value, seen = new WeakSet()) {
|
|
125
|
-
if (!value || typeof value !== "object" || seen.has(value))
|
|
126
|
-
return value;
|
|
127
|
-
seen.add(value);
|
|
128
|
-
for (const item of Object.values(value))
|
|
129
|
-
freezeGraph(item, seen);
|
|
130
|
-
return Object.freeze(value);
|
|
131
|
-
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExecutionEvent } from "../types/execution.js";
|
|
2
|
+
import type { ObserveEvent, Observer } from "@nylorun/core/define";
|
|
3
|
+
import type { JsonValue } from "@nylorun/core/define";
|
|
4
|
+
export type ObserveEmit = (event: ObserveEvent | (() => ObserveEvent)) => void;
|
|
5
|
+
export type ExecutionOutcomeEvent = {
|
|
6
|
+
type: "execution.completed" | "execution.paused" | "execution.cancelled" | "execution.failed" | "observation.failed";
|
|
7
|
+
attributes?: Record<string, JsonValue>;
|
|
8
|
+
};
|
|
9
|
+
export declare function createObservationSink(input: {
|
|
10
|
+
readonly listener?: (event: ExecutionEvent) => void | Promise<void>;
|
|
11
|
+
readonly executionId: () => string;
|
|
12
|
+
readonly runId: string;
|
|
13
|
+
}): {
|
|
14
|
+
readonly observe: ObserveEmit;
|
|
15
|
+
readonly emit: (event: ObserveEvent | ExecutionOutcomeEvent) => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function emitObserve(listener: Observer | undefined, event: ObserveEvent | (() => ObserveEvent)): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { copyJson } from "@nylorun/core/define";
|
|
2
|
+
export function createObservationSink(input) {
|
|
3
|
+
let sequence = 0;
|
|
4
|
+
let reportingFailure = false;
|
|
5
|
+
const emit = (event) => {
|
|
6
|
+
if (!input.listener)
|
|
7
|
+
return;
|
|
8
|
+
const value = copyJson({
|
|
9
|
+
...event,
|
|
10
|
+
executionId: input.executionId(),
|
|
11
|
+
runId: input.runId,
|
|
12
|
+
sequence: ++sequence,
|
|
13
|
+
});
|
|
14
|
+
const failed = () => {
|
|
15
|
+
if (reportingFailure || event.type === "observation.failed")
|
|
16
|
+
return;
|
|
17
|
+
reportingFailure = true;
|
|
18
|
+
emit({
|
|
19
|
+
type: "observation.failed",
|
|
20
|
+
attributes: { message: "Execution observer failed; execution is continuing" },
|
|
21
|
+
});
|
|
22
|
+
reportingFailure = false;
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
const result = input.listener(value);
|
|
26
|
+
if (result)
|
|
27
|
+
void Promise.resolve(result).catch(failed);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
failed();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
emit,
|
|
35
|
+
observe(source) {
|
|
36
|
+
if (!input.listener)
|
|
37
|
+
return;
|
|
38
|
+
emit(typeof source === "function" ? source() : source);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function emitObserve(listener, event) {
|
|
43
|
+
if (!listener)
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
const result = listener(typeof event === "function" ? event() : event);
|
|
47
|
+
if (result)
|
|
48
|
+
void Promise.resolve(result).catch(() => undefined);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Observation is deliberately fail-open for ObserveEvent-only callers.
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentDefinition } from "../definition/agent-definition.js";
|
|
2
|
+
import type { BoundToolDefinition } from "@nylorun/core/define";
|
|
3
|
+
import type { ExecutionState, RunOptions } from "../types/execution.js";
|
|
4
|
+
import { normalizeInput } from "./state.js";
|
|
5
|
+
export declare function validateRunOptions(options: RunOptions<any>): void;
|
|
6
|
+
export declare function openExecution(agent: AgentDefinition, options: RunOptions<any>): {
|
|
7
|
+
readonly state: ExecutionState;
|
|
8
|
+
readonly input: ReturnType<typeof normalizeInput>;
|
|
9
|
+
readonly definitions: Map<string, BoundToolDefinition>;
|
|
10
|
+
readonly redelivery: boolean;
|
|
11
|
+
};
|