@statelyai/agent 2.0.0-alpha.24 → 2.0.0-alpha.26
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/dist/ai-sdk.cjs +30 -34
- package/dist/ai-sdk.d.cts +27 -31
- package/dist/ai-sdk.d.mts +27 -31
- package/dist/ai-sdk.mjs +27 -31
- package/dist/decision-CM9CYZ6t.cjs +519 -0
- package/dist/decision-DzSv1NpD.mjs +453 -0
- package/dist/event-log-store-Ba2fsc4E.mjs +576 -0
- package/dist/event-log-store-DQmImLYk.cjs +702 -0
- package/dist/event-log-store-DvOcs10J.d.cts +338 -0
- package/dist/event-log-store-DxyVgnas.d.mts +338 -0
- package/dist/index.cjs +429 -4272
- package/dist/index.d.cts +104 -1005
- package/dist/index.d.mts +104 -1005
- package/dist/index.mjs +389 -4202
- package/dist/log.cjs +392 -0
- package/dist/log.d.cts +32 -0
- package/dist/log.d.mts +32 -0
- package/dist/log.mjs +371 -0
- package/dist/machines.cjs +27 -16
- package/dist/machines.d.cts +1 -1
- package/dist/machines.d.mts +1 -1
- package/dist/machines.mjs +27 -16
- package/dist/openai.cjs +16 -15
- package/dist/openai.d.cts +9 -9
- package/dist/openai.d.mts +9 -9
- package/dist/openai.mjs +10 -9
- package/dist/otel.d.cts +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/{run-agent-Ck7Sa3Tb.d.mts → run-agent-BOfoJg5B.d.cts} +86 -387
- package/dist/{run-agent-DWqA8KiI.d.cts → run-agent-CG343kFs.d.mts} +86 -387
- package/dist/run-agent-Cb1-LS5P.cjs +1783 -0
- package/dist/run-agent-DvDQd4If.mjs +1669 -0
- package/dist/{setup-agent-CeO2fARv.mjs → setup-agent-CFW-jPI8.mjs} +9 -20
- package/dist/{setup-agent-Dl2VELLi.cjs → setup-agent-CPf7MxnG.cjs} +20 -37
- package/dist/testing.cjs +1088 -0
- package/dist/testing.d.cts +617 -0
- package/dist/testing.d.mts +617 -0
- package/dist/testing.mjs +1079 -0
- package/dist/{text-logic-DJBK3LbG.d.mts → text-logic-BFEOxyxv.d.cts} +71 -147
- package/dist/text-logic-Bl_ZRxDi.mjs +833 -0
- package/dist/text-logic-C8xyr6Ag.cjs +1091 -0
- package/dist/{text-logic-C7bSnH_B.d.cts → text-logic-hZSiRUo1.d.mts} +71 -147
- package/dist/{types-CjHk5OdH.d.cts → types-B1Qs2q3q.d.cts} +2 -1
- package/dist/{types-DWA6X9R-.d.mts → types-tA3SdczG.d.mts} +2 -1
- package/dist/usage-BdEjFuIi.mjs +5 -0
- package/dist/usage-efOQPdf_.cjs +10 -0
- package/dist/utils-C2DPqomn.d.mts +109 -0
- package/dist/utils-ceStNCOh.d.cts +109 -0
- package/package.json +26 -5
- package/readme.md +5 -3
- package/skills/generate-machine/SKILL.md +4 -6
- package/dist/decision-BBZ2jMO1.mjs +0 -1490
- package/dist/decision-Cv6SfNWl.cjs +0 -1856
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
require("./validate.cjs");
|
|
2
|
+
let xstate = require("xstate");
|
|
3
|
+
//#region src/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Base class for every error this package throws. Carries a stable, kebab-case
|
|
6
|
+
* `code` so hosts can branch on the failure without `instanceof` (useful across
|
|
7
|
+
* bundle/realm boundaries and after serialization).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* catch (error) {
|
|
12
|
+
* if (error instanceof AgentError && error.code === 'agent-idle') { ... }
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
var AgentError = class extends Error {
|
|
17
|
+
code;
|
|
18
|
+
constructor(code, message, options) {
|
|
19
|
+
super(message, options);
|
|
20
|
+
this.name = "AgentError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* A model call ran out of output tokens before it produced anything usable.
|
|
26
|
+
* Core never throws this: it is the adapter's signal that a `'length'` finish
|
|
27
|
+
* reason left nothing to return — a structured request whose JSON never
|
|
28
|
+
* closed, say. A text request that WAS cut off still returns its text, with
|
|
29
|
+
* `finishReason: 'length'`.
|
|
30
|
+
*
|
|
31
|
+
* The code is `'truncated'`, so an invoke's `onError` can branch on it without
|
|
32
|
+
* `instanceof`:
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* onError: [
|
|
37
|
+
* { guard: ({ event }) => event.error.code === 'truncated', target: 'retryShorter' },
|
|
38
|
+
* { target: 'failed' },
|
|
39
|
+
* ]
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
var AgentTruncatedError = class extends AgentError {
|
|
43
|
+
/** The request that was cut off — `AgentTextRequest.name`. */
|
|
44
|
+
requestName;
|
|
45
|
+
/** The durable invoke id of the request, when the host knows it. */
|
|
46
|
+
requestId;
|
|
47
|
+
/** Whatever the model did produce before it hit the limit, if anything. */
|
|
48
|
+
partialOutput;
|
|
49
|
+
constructor(message, options) {
|
|
50
|
+
super("truncated", message, { cause: options.cause });
|
|
51
|
+
this.name = "AgentTruncatedError";
|
|
52
|
+
this.requestName = options.requestName;
|
|
53
|
+
if (options.requestId !== void 0) this.requestId = options.requestId;
|
|
54
|
+
if (options.partialOutput !== void 0) this.partialOutput = options.partialOutput;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/utils.ts
|
|
59
|
+
/** Returns the human-readable text carried by a message, ignoring non-text parts. */
|
|
60
|
+
function getMessageText(message) {
|
|
61
|
+
if (typeof message.content === "string") return message.content;
|
|
62
|
+
const text = [];
|
|
63
|
+
for (const part of message.content) {
|
|
64
|
+
if (part.type === "text") {
|
|
65
|
+
text.push(part.text);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (part.type !== "tool-result") continue;
|
|
69
|
+
if (part.output.type === "text" || part.output.type === "error-text") text.push(part.output.value);
|
|
70
|
+
else if (part.output.type === "content") text.push(...part.output.value.flatMap((item) => item.type === "text" ? [item.text] : []));
|
|
71
|
+
}
|
|
72
|
+
return text.join("\n");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Walks a context value and returns the dot-paths of the first few values that
|
|
76
|
+
* would NOT survive a JSON persist/resume round-trip (persist with XState's
|
|
77
|
+
* `actor.getPersistedSnapshot()`, resume with `createActor(machine, { snapshot })`):
|
|
78
|
+
* `Date`, `Map`, `Set`, `RegExp`, functions, `undefined`, `bigint`, class
|
|
79
|
+
* instances (non-plain objects), and circular references. Plain objects,
|
|
80
|
+
* arrays, and JSON primitives are walked/allowed. Returns `[]` for a
|
|
81
|
+
* fully-JSON-safe value. Cheap and bounded (stops after `limit` findings) —
|
|
82
|
+
* intended for a dev-only warning at the moment persistence matters.
|
|
83
|
+
*/
|
|
84
|
+
function findNonSerializableContextPaths(context, limit = 5) {
|
|
85
|
+
const paths = [];
|
|
86
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
87
|
+
const walk = (value, path) => {
|
|
88
|
+
if (paths.length >= limit) return;
|
|
89
|
+
if (value === null) return;
|
|
90
|
+
const type = typeof value;
|
|
91
|
+
if (type === "string" || type === "number" || type === "boolean") return;
|
|
92
|
+
if (type === "undefined" || type === "bigint" || type === "function" || type === "symbol") {
|
|
93
|
+
paths.push(`${path} (${type})`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const obj = value;
|
|
97
|
+
if (seen.has(obj)) {
|
|
98
|
+
paths.push(`${path} (circular)`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
seen.add(obj);
|
|
103
|
+
value.forEach((item, index) => walk(item, `${path}[${index}]`));
|
|
104
|
+
seen.delete(obj);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const proto = Object.getPrototypeOf(value);
|
|
108
|
+
if (proto === Object.prototype || proto === null) {
|
|
109
|
+
seen.add(obj);
|
|
110
|
+
for (const [key, item] of Object.entries(value)) walk(item, `${path}.${key}`);
|
|
111
|
+
seen.delete(obj);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const ctorName = value.constructor?.name ?? "object";
|
|
115
|
+
paths.push(`${path} (${ctorName})`);
|
|
116
|
+
};
|
|
117
|
+
walk(context, "context");
|
|
118
|
+
return paths;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A stable, dependency-free structural fingerprint of a machine — a short hex
|
|
122
|
+
* `djb2` hash over its **structural** config only: state ids/nesting, transition
|
|
123
|
+
* event types and targets, invoke `src`s, `initial`, and any other serializable
|
|
124
|
+
* config fields. Function values (context/output builders, prompts, inline
|
|
125
|
+
* guards/actions) are excluded entirely, so two machines that differ only in
|
|
126
|
+
* their prompts or executors hash identically; adding/removing/retargeting a
|
|
127
|
+
* state or transition changes the hash.
|
|
128
|
+
*
|
|
129
|
+
* Used by {@link runAgent} to stamp settled snapshots with a `version` and to
|
|
130
|
+
* detect a structurally-edited machine on resume. It is a change detector, not
|
|
131
|
+
* a cryptographic digest — collisions are possible but unlikely for real
|
|
132
|
+
* configs. Declare `createMachine({ version })` to pin an explicit version instead.
|
|
133
|
+
*/
|
|
134
|
+
function getMachineStructuralHash(machine) {
|
|
135
|
+
return djb2Hex(stableStructuralString(machine.config));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The version a machine is stamped with everywhere: an explicitly declared
|
|
139
|
+
* `createMachine({ version })` when present, else its
|
|
140
|
+
* {@link getMachineStructuralHash}. Single source of truth so `runAgent`,
|
|
141
|
+
* `provideExecutors`/`traceTransitions` trace envelopes agree.
|
|
142
|
+
* @internal
|
|
143
|
+
*/
|
|
144
|
+
function resolveMachineVersion(machine) {
|
|
145
|
+
return machine.version ?? getMachineStructuralHash(machine);
|
|
146
|
+
}
|
|
147
|
+
function stableStructuralString(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
148
|
+
if (value === null) return "null";
|
|
149
|
+
const type = typeof value;
|
|
150
|
+
if (type === "function" || type === "undefined" || type === "symbol") return "";
|
|
151
|
+
if (type === "string") return JSON.stringify(value);
|
|
152
|
+
if (type === "number" || type === "boolean") return String(value);
|
|
153
|
+
if (type === "bigint") return `${value}n`;
|
|
154
|
+
const obj = value;
|
|
155
|
+
if (seen.has(obj)) return "\"[circular]\"";
|
|
156
|
+
seen.add(obj);
|
|
157
|
+
let out;
|
|
158
|
+
if (Array.isArray(value)) out = `[${value.map((item) => stableStructuralString(item, seen)).join(",")}]`;
|
|
159
|
+
else {
|
|
160
|
+
const parts = [];
|
|
161
|
+
for (const key of Object.keys(obj).sort()) {
|
|
162
|
+
const child = obj[key];
|
|
163
|
+
const childType = typeof child;
|
|
164
|
+
if (childType === "function" || childType === "undefined" || childType === "symbol") continue;
|
|
165
|
+
parts.push(`${JSON.stringify(key)}:${stableStructuralString(child, seen)}`);
|
|
166
|
+
}
|
|
167
|
+
out = `{${parts.join(",")}}`;
|
|
168
|
+
}
|
|
169
|
+
seen.delete(obj);
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* djb2 → unsigned 32-bit → 8-char hex. No dependencies. A change detector, not
|
|
174
|
+
* a cryptographic digest.
|
|
175
|
+
*
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
178
|
+
function djb2Hex(input) {
|
|
179
|
+
let hash = 5381;
|
|
180
|
+
for (let i = 0; i < input.length; i++) hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
|
|
181
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
182
|
+
}
|
|
183
|
+
/** Builds a {@link UserMessage} from a string or multimodal content parts. */
|
|
184
|
+
function userMessage(content) {
|
|
185
|
+
return {
|
|
186
|
+
role: "user",
|
|
187
|
+
content
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/** Builds an {@link AssistantMessage} from a string or content parts (text, files, tool calls/results). */
|
|
191
|
+
function assistantMessage(content) {
|
|
192
|
+
return {
|
|
193
|
+
role: "assistant",
|
|
194
|
+
content
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** Builds a {@link SystemMessage}. */
|
|
198
|
+
function systemMessage(content) {
|
|
199
|
+
return {
|
|
200
|
+
role: "system",
|
|
201
|
+
content
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/** Builds a {@link ToolMessage} from one or more tool-result parts. */
|
|
205
|
+
function toolMessage(content) {
|
|
206
|
+
return {
|
|
207
|
+
role: "tool",
|
|
208
|
+
content
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Returns the merged `meta` of a snapshot's active state(s) — the typed
|
|
213
|
+
* replacement for the `Object.values(snapshot.getMeta())[0]` dance.
|
|
214
|
+
*
|
|
215
|
+
* `snapshot.getMeta()` is keyed by state id; a leaf machine has one active
|
|
216
|
+
* state, but parallel/nested machines can have several. This shallow-merges
|
|
217
|
+
* every active state's meta into one object and returns `{}` when no active
|
|
218
|
+
* state declares meta.
|
|
219
|
+
*
|
|
220
|
+
* Merge order is fixed and does not depend on XState's internal node order:
|
|
221
|
+
* entries are sorted by structural depth (the state node's distance from the
|
|
222
|
+
* root, regardless of custom `id` strings), then by state id lexicographically,
|
|
223
|
+
* and merged in that order. So a deeper state's key wins over an ancestor's,
|
|
224
|
+
* and between equal-depth parallel siblings the later state id alphabetically
|
|
225
|
+
* wins.
|
|
226
|
+
*
|
|
227
|
+
* The return type is recovered from the snapshot's own `getMeta()` type, so a
|
|
228
|
+
* schema-typed machine (`setupAgent({ meta })`) yields the meta schema's
|
|
229
|
+
* output type. Pass an explicit `TMeta` to override when the snapshot is
|
|
230
|
+
* untyped (e.g. `AnyMachineSnapshot`).
|
|
231
|
+
*
|
|
232
|
+
* @example HITL: read the current state's interaction protocol off an idle
|
|
233
|
+
* snapshot to render for a human.
|
|
234
|
+
* ```ts
|
|
235
|
+
* const { interaction } = getStateMeta(result.snapshot);
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
function getStateMeta(snapshot) {
|
|
239
|
+
const nodes = snapshot.nodes;
|
|
240
|
+
const depthById = new Map(nodes?.map((node) => [node.id, node.path.length]));
|
|
241
|
+
const depth = (id) => depthById.get(id) ?? id.split(".").length;
|
|
242
|
+
const entries = Object.entries(snapshot.getMeta()).filter((entry) => entry[1] != null).sort(([a], [b]) => depth(a) - depth(b) || (a < b ? -1 : a > b ? 1 : 0));
|
|
243
|
+
return Object.assign({}, ...entries.map(([, meta]) => meta));
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Renders a snapshot's state value as one deterministic string, so a log line,
|
|
247
|
+
* a progress field, or a test assertion reads the same on every run.
|
|
248
|
+
*
|
|
249
|
+
* - An atomic state is its own name: `'writing'`.
|
|
250
|
+
* - A nested state joins with dots: `{ review: { editing: 'draft' } }` →
|
|
251
|
+
* `'review.editing.draft'`.
|
|
252
|
+
* - A parallel state brackets its regions, sorted by region name, so key order
|
|
253
|
+
* never leaks into the string: `{ p: { left: 'x', right: { a: 'b' } } }` →
|
|
254
|
+
* `'p:{left.x,right.a.b}'`.
|
|
255
|
+
*
|
|
256
|
+
* Prefer this over `String(snapshot.value)`, which renders every non-atomic
|
|
257
|
+
* value as `[object Object]`.
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* onTransition: (snapshot) => console.log(getStatePath(snapshot));
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
function getStatePath(snapshot) {
|
|
265
|
+
return serializeStateValue(typeof snapshot === "object" && snapshot !== null && "value" in snapshot && ("status" in snapshot || typeof snapshot.matches === "function") ? snapshot.value : snapshot);
|
|
266
|
+
}
|
|
267
|
+
function serializeStateValue(value) {
|
|
268
|
+
if (typeof value === "string") return value;
|
|
269
|
+
const entries = Object.entries(value).map(([key, child]) => {
|
|
270
|
+
const rendered = serializeStateValue(child);
|
|
271
|
+
if (!rendered) return [key, key];
|
|
272
|
+
return [key, `${key}${rendered.startsWith("{") ? ":" : "."}${rendered}`];
|
|
273
|
+
}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
274
|
+
if (entries.length === 0) return "";
|
|
275
|
+
if (entries.length === 1) return entries[0][1];
|
|
276
|
+
return `{${entries.map(([, rendered]) => rendered).join(",")}}`;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Structural guard for a {@link StandardSchemaV1}: `true` when `value` carries
|
|
280
|
+
* the `~standard` marker. Used to tell an already-schema'd tool `inputSchema`
|
|
281
|
+
* (a Zod/Valibot/… schema) apart from an SDK-specific schema wrapper that core
|
|
282
|
+
* can't read directly — see the `ai-sdk` tool pass-through.
|
|
283
|
+
*/
|
|
284
|
+
function isStandardSchema(value) {
|
|
285
|
+
return typeof value === "object" && value !== null && "~standard" in value;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Pulls the JSON Schema off a {@link StandardSchemaV1} via its optional
|
|
289
|
+
* `~standard.jsonSchema.input()` extension (implemented by e.g. Zod v4's
|
|
290
|
+
* `z.toJSONSchema`), awaiting it when the producer is async. Returns
|
|
291
|
+
* `undefined` when the schema doesn't expose the extension. Use this to build
|
|
292
|
+
* a provider request's `response_format`/tool `parameters` from a schema.
|
|
293
|
+
*/
|
|
294
|
+
async function getJsonSchema(schema) {
|
|
295
|
+
const jsonSchemaFn = schema?.["~standard"].jsonSchema?.input;
|
|
296
|
+
if (!jsonSchemaFn) return;
|
|
297
|
+
const result = jsonSchemaFn();
|
|
298
|
+
return result instanceof Promise ? await result : result;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Synchronous variant of {@link getJsonSchema}, for call sites that can't
|
|
302
|
+
* await (building tool/event descriptors inline). An async JSON Schema
|
|
303
|
+
* producer is treated as absent (returns `undefined`) — in practice Zod's
|
|
304
|
+
* `z.toJSONSchema` resolves synchronously.
|
|
305
|
+
*/
|
|
306
|
+
function getJsonSchemaSync(schema) {
|
|
307
|
+
const jsonSchemaFn = schema?.["~standard"].jsonSchema?.input;
|
|
308
|
+
if (!jsonSchemaFn) return;
|
|
309
|
+
const result = jsonSchemaFn();
|
|
310
|
+
return result instanceof Promise ? void 0 : result;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Validates `value` against a {@link StandardSchemaV1}, synchronously.
|
|
314
|
+
* Throws if the schema's `validate` returns a `Promise` (async validation is
|
|
315
|
+
* not supported anywhere in this library) or if validation reports issues —
|
|
316
|
+
* in which case the thrown `Error.message` joins every issue message with
|
|
317
|
+
* `', '`.
|
|
318
|
+
*/
|
|
319
|
+
function validateSchemaSync(schema, value) {
|
|
320
|
+
const result = schema["~standard"].validate(value);
|
|
321
|
+
if (result instanceof Promise) throw new Error("Async schema validation is not supported.");
|
|
322
|
+
if (result.issues) throw new Error(result.issues.map((issue) => issue.message).join(", "));
|
|
323
|
+
return result.value;
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/internal/registry.ts
|
|
327
|
+
/**
|
|
328
|
+
* Registered execution options, keyed on BOTH the machine object and its root
|
|
329
|
+
* `config` object. `config` is shared by reference across `machine.provide(...)`
|
|
330
|
+
* (unlike the machine itself), so the `config` key is what lets a machine
|
|
331
|
+
* rebound by `runAgent` — or by any host `.provide(...)` — still resolve the
|
|
332
|
+
* schemas/actors `setupAgent` registered. Always write through
|
|
333
|
+
* {@link setAgentExecutionOptions} and read through
|
|
334
|
+
* {@link getAgentExecutionOptions}.
|
|
335
|
+
*/
|
|
336
|
+
const agentExecutionOptions = /* @__PURE__ */ new WeakMap();
|
|
337
|
+
/** The root `config` object a machine shares with every `.provide(...)` of itself. */
|
|
338
|
+
function machineConfigKey(machine) {
|
|
339
|
+
const config = machine?.config;
|
|
340
|
+
return config && typeof config === "object" ? config : void 0;
|
|
341
|
+
}
|
|
342
|
+
/** Registers `options` for `machine` and for every `.provide(...)` of it. */
|
|
343
|
+
function setAgentExecutionOptions(machine, options) {
|
|
344
|
+
if (!machine || typeof machine !== "object") return;
|
|
345
|
+
agentExecutionOptions.set(machine, options);
|
|
346
|
+
const config = machineConfigKey(machine);
|
|
347
|
+
if (config) agentExecutionOptions.set(config, options);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Reads the options registered for `machine`, falling back to the root `config`
|
|
351
|
+
* key so a `.provide(...)`-rebound machine (e.g. the one behind a `runAgent`
|
|
352
|
+
* result snapshot) resolves the same pack.
|
|
353
|
+
*/
|
|
354
|
+
function getAgentExecutionOptions(machine) {
|
|
355
|
+
if (!machine || typeof machine !== "object") return void 0;
|
|
356
|
+
const direct = agentExecutionOptions.get(machine);
|
|
357
|
+
if (direct) return direct;
|
|
358
|
+
const config = machineConfigKey(machine);
|
|
359
|
+
return config ? agentExecutionOptions.get(config) : void 0;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Machine-carried wait-state predicates, keyed on the machine's root `config`
|
|
363
|
+
* object. `config` is shared by reference across `machine.provide(...)` (unlike
|
|
364
|
+
* the machine object itself), so a predicate registered here travels with the
|
|
365
|
+
* machine through `.provide` — which is why it is keyed on `config`, not the
|
|
366
|
+
* machine. Set by `setupAgent({ isIdle })` in `createMachine` and by
|
|
367
|
+
* `setupAgent.fromConfig` (its `isIdle` option or the config's
|
|
368
|
+
* `idleTags`), read by `runAgent` before its structural default.
|
|
369
|
+
*/
|
|
370
|
+
const machineIdlePredicates = /* @__PURE__ */ new WeakMap();
|
|
371
|
+
/** Reads the {@link machineIdlePredicates} predicate carried by `machine` (via its root `config`), if any. */
|
|
372
|
+
function getMachineIdlePredicate(machine) {
|
|
373
|
+
const config = machine.config;
|
|
374
|
+
return config ? machineIdlePredicates.get(config) : void 0;
|
|
375
|
+
}
|
|
376
|
+
const unboundPlaceholderLogics = /* @__PURE__ */ new WeakSet();
|
|
377
|
+
/** Text/decision logics created WITH their own executor (withExecutor or the
|
|
378
|
+
* factory's second arg) — these are runnable as-is, so runAgent's bind check
|
|
379
|
+
* must not reject them as direct-object invoke srcs. */
|
|
380
|
+
const executorBoundLogics = /* @__PURE__ */ new WeakSet();
|
|
381
|
+
function missingActor(src) {
|
|
382
|
+
const logic = (0, xstate.createAsyncLogic)({ run: async () => {
|
|
383
|
+
throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }).`);
|
|
384
|
+
} });
|
|
385
|
+
unboundPlaceholderLogics.add(logic);
|
|
386
|
+
return logic;
|
|
387
|
+
}
|
|
388
|
+
function isUnboundPlaceholder(logic) {
|
|
389
|
+
return !!logic && typeof logic === "object" && unboundPlaceholderLogics.has(logic);
|
|
390
|
+
}
|
|
391
|
+
function getRegisteredAgentExecutionOptions(machine, options) {
|
|
392
|
+
return {
|
|
393
|
+
...getAgentExecutionOptions(machine),
|
|
394
|
+
...options
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/text-logic.ts
|
|
399
|
+
const GENERATE_TEXT_ACTOR = "agent.generateText";
|
|
400
|
+
const STREAM_TEXT_ACTOR = "agent.streamText";
|
|
401
|
+
const DECIDE_ACTOR = "agent.decide";
|
|
402
|
+
const AGENT_FINISH_REASONS = new Set([
|
|
403
|
+
"stop",
|
|
404
|
+
"length",
|
|
405
|
+
"tool-calls",
|
|
406
|
+
"content-filter",
|
|
407
|
+
"other"
|
|
408
|
+
]);
|
|
409
|
+
/**
|
|
410
|
+
* Reads a settled call's {@link AgentFinishReason} off a RAW executor result's
|
|
411
|
+
* `finishReason` field — the finish-reason sibling of {@link getCallUsage},
|
|
412
|
+
* applied by `runAgent` before it puts the reason on the `request.end` trace.
|
|
413
|
+
* A provider's `'error'` normalizes to `'other'`; anything else the union does
|
|
414
|
+
* not name (an un-normalized provider string, a missing field) returns
|
|
415
|
+
* `undefined`.
|
|
416
|
+
*/
|
|
417
|
+
function getCallFinishReason(raw) {
|
|
418
|
+
const value = raw?.finishReason;
|
|
419
|
+
if (typeof value !== "string") return;
|
|
420
|
+
if (value === "error") return "other";
|
|
421
|
+
return AGENT_FINISH_REASONS.has(value) ? value : void 0;
|
|
422
|
+
}
|
|
423
|
+
/** The token fields {@link AgentUsage} aggregates. @internal */
|
|
424
|
+
const AGENT_USAGE_TOKEN_FIELDS = [
|
|
425
|
+
"inputTokens",
|
|
426
|
+
"outputTokens",
|
|
427
|
+
"totalTokens",
|
|
428
|
+
"reasoningTokens",
|
|
429
|
+
"cachedInputTokens"
|
|
430
|
+
];
|
|
431
|
+
/**
|
|
432
|
+
* Reads a settled call's per-call {@link AgentCallUsage} off a RAW executor
|
|
433
|
+
* result's `usage` field, keeping only finite numbers — the same normalization
|
|
434
|
+
* `runAgent` applies before it delivers `'@agent.usage'`. Returns `undefined`
|
|
435
|
+
* when the result reports no usage at all. Works for our `{ result, usage }`
|
|
436
|
+
* shape, for a raw Vercel AI SDK result (its `LanguageModelUsage` carries
|
|
437
|
+
* the same flat field names), and for any custom executor that follows the
|
|
438
|
+
* shape.
|
|
439
|
+
*
|
|
440
|
+
* The seam for the step-loop path, where the host holds the raw result itself:
|
|
441
|
+
*
|
|
442
|
+
* ```ts
|
|
443
|
+
* const { result, messages } = await executeAgentRequest(effect, executors);
|
|
444
|
+
* const usage = getCallUsage(raw);
|
|
445
|
+
* if (usage) append({ type: AGENT_USAGE_EVENT_TYPE, usage }); // journal + transition, like any event
|
|
446
|
+
* append(effect.toDoneEvent(output));
|
|
447
|
+
* ```
|
|
448
|
+
*
|
|
449
|
+
* See "Token usage on this path" in docs/steps.md for the full loop.
|
|
450
|
+
*/
|
|
451
|
+
function getCallUsage(raw) {
|
|
452
|
+
const usage = raw?.usage;
|
|
453
|
+
if (!usage || typeof usage !== "object") return;
|
|
454
|
+
let out;
|
|
455
|
+
for (const field of AGENT_USAGE_TOKEN_FIELDS) {
|
|
456
|
+
const value = usage[field];
|
|
457
|
+
if (typeof value === "number" && Number.isFinite(value)) (out ??= {})[field] = value;
|
|
458
|
+
}
|
|
459
|
+
return out;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* The response messages off an executor result, or none. Fields
|
|
463
|
+
* holding `undefined` are dropped on the way in: SDK message objects often
|
|
464
|
+
* carry optional slots as explicit `undefined` (the AI SDK's
|
|
465
|
+
* `providerOptions`, for one), and the messages now travel inside the
|
|
466
|
+
* invoke's done event, which the event log requires to be strict JSON.
|
|
467
|
+
* @internal
|
|
468
|
+
*/
|
|
469
|
+
function responseMessagesOf(raw) {
|
|
470
|
+
const messages = raw?.messages;
|
|
471
|
+
return Array.isArray(messages) ? dropUndefined(messages) : [];
|
|
472
|
+
}
|
|
473
|
+
function dropUndefined(value) {
|
|
474
|
+
if (Array.isArray(value)) return value.map(dropUndefined);
|
|
475
|
+
if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
476
|
+
const out = {};
|
|
477
|
+
for (const [key, entry] of Object.entries(value)) if (entry !== void 0) out[key] = dropUndefined(entry);
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
function textRequestSourceIssue(request) {
|
|
483
|
+
const hasPrompt = typeof request.prompt === "string" && request.prompt.length > 0;
|
|
484
|
+
const hasMessages = Array.isArray(request.messages) && request.messages.length > 0;
|
|
485
|
+
const label = request.name ? ` '${request.name}'` : "";
|
|
486
|
+
if (hasPrompt && hasMessages) return `Agent text request${label} has both a non-empty \`prompt\` and \`messages\` — provide exactly one so the model has a single input source.`;
|
|
487
|
+
if (!hasPrompt && !hasMessages) return `Agent text request${label} has neither a non-empty \`prompt\` nor \`messages\` — provide at least one so the model has something to respond to.`;
|
|
488
|
+
}
|
|
489
|
+
const agentTextInputSchema = { "~standard": {
|
|
490
|
+
version: 1,
|
|
491
|
+
vendor: "statelyai-agent",
|
|
492
|
+
validate(value) {
|
|
493
|
+
if (!value || typeof value !== "object") return { issues: [{ message: "Expected agent text input object" }] };
|
|
494
|
+
const request = value;
|
|
495
|
+
if (typeof request.model !== "string") return { issues: [{ message: "Expected agent text input with a string `model`" }] };
|
|
496
|
+
const issue = textRequestSourceIssue(request);
|
|
497
|
+
if (issue) return { issues: [{ message: issue }] };
|
|
498
|
+
return { value: request };
|
|
499
|
+
}
|
|
500
|
+
} };
|
|
501
|
+
const passthroughSchema = { "~standard": {
|
|
502
|
+
version: 1,
|
|
503
|
+
vendor: "statelyai-agent",
|
|
504
|
+
validate(value) {
|
|
505
|
+
return { value };
|
|
506
|
+
}
|
|
507
|
+
} };
|
|
508
|
+
const unknownOutputSchema = passthroughSchema;
|
|
509
|
+
const stringOutputSchema = { "~standard": {
|
|
510
|
+
version: 1,
|
|
511
|
+
vendor: "statelyai-agent",
|
|
512
|
+
validate(value) {
|
|
513
|
+
return typeof value === "string" ? { value } : { issues: [{ message: "Expected string output" }] };
|
|
514
|
+
}
|
|
515
|
+
} };
|
|
516
|
+
const noInputSchema = passthroughSchema;
|
|
517
|
+
function createBuiltinTextActor(src, mode, outputSchema) {
|
|
518
|
+
const logic = (0, xstate.createAsyncLogic)({ run: async () => {
|
|
519
|
+
throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }) or execute the returned agent request with executeAgentRequest(...).`);
|
|
520
|
+
} });
|
|
521
|
+
return Object.assign(logic, {
|
|
522
|
+
kind: "statelyai.textLogic",
|
|
523
|
+
mode,
|
|
524
|
+
schemas: {
|
|
525
|
+
input: agentTextInputSchema,
|
|
526
|
+
output: outputSchema
|
|
527
|
+
},
|
|
528
|
+
request(input) {
|
|
529
|
+
return validateSchemaSync(agentTextInputSchema, input);
|
|
530
|
+
},
|
|
531
|
+
async execute(input, executors) {
|
|
532
|
+
const { result: output, raw } = await executeAgentTextRequest(mode, src, validateSchemaSync(agentTextInputSchema, input), executors);
|
|
533
|
+
return {
|
|
534
|
+
result: validateSchemaSync(outputSchema, output),
|
|
535
|
+
messages: responseMessagesOf(raw)
|
|
536
|
+
};
|
|
537
|
+
},
|
|
538
|
+
withExecutor(execute) {
|
|
539
|
+
return createTextLogic({
|
|
540
|
+
mode,
|
|
541
|
+
schemas: {
|
|
542
|
+
input: agentTextInputSchema,
|
|
543
|
+
output: outputSchema
|
|
544
|
+
},
|
|
545
|
+
name: ({ input }) => input.name,
|
|
546
|
+
model: ({ input }) => input.model,
|
|
547
|
+
system: ({ input }) => input.system,
|
|
548
|
+
prompt: ({ input }) => input.prompt,
|
|
549
|
+
messages: ({ input }) => input.messages,
|
|
550
|
+
tools: ({ input }) => input.tools,
|
|
551
|
+
toolChoice: ({ input }) => input.toolChoice,
|
|
552
|
+
includeReasoning: ({ input }) => input.includeReasoning,
|
|
553
|
+
temperature: ({ input }) => input.temperature,
|
|
554
|
+
maxOutputTokens: ({ input }) => input.maxOutputTokens,
|
|
555
|
+
topP: ({ input }) => input.topP,
|
|
556
|
+
topK: ({ input }) => input.topK,
|
|
557
|
+
seed: ({ input }) => input.seed,
|
|
558
|
+
stopSequences: ({ input }) => input.stopSequences,
|
|
559
|
+
maxSteps: ({ input }) => input.maxSteps,
|
|
560
|
+
metadata: ({ input }) => input.metadata
|
|
561
|
+
}, execute);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
/** The unbound `agent.generateText`/`agent.streamText` builtins registered by setupAgent. @internal */
|
|
566
|
+
const builtinTextActors = {
|
|
567
|
+
[GENERATE_TEXT_ACTOR]: createBuiltinTextActor(GENERATE_TEXT_ACTOR, "generate", unknownOutputSchema),
|
|
568
|
+
[STREAM_TEXT_ACTOR]: createBuiltinTextActor(STREAM_TEXT_ACTOR, "stream", stringOutputSchema)
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
571
|
+
* Validates a raw model/executor output against `schema`, returning the
|
|
572
|
+
* parsed value. Thin wrapper over {@link validateSchemaSync} for parsing a
|
|
573
|
+
* text request's structured output outside of `TextLogic.execute`/
|
|
574
|
+
* `executeAgentRequest` (e.g. a custom host loop).
|
|
575
|
+
*/
|
|
576
|
+
function parseOutput(schema, output) {
|
|
577
|
+
return validateSchemaSync(schema, output);
|
|
578
|
+
}
|
|
579
|
+
/** Resolves a `ResolveTextLogicValue` (calls it if it's a function, else returns it as-is). @internal */
|
|
580
|
+
function resolveTextLogicValue(value, args) {
|
|
581
|
+
return typeof value === "function" ? value(args) : value;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Creates reusable, standalone {@link TextLogic}: an actor that, when run,
|
|
585
|
+
* resolves typed input to typed output via a model call. Register the
|
|
586
|
+
* result under `actors:` and invoke it by name (equivalent to what
|
|
587
|
+
* `setupAgent({ requests })` builds internally for each request entry). Pass
|
|
588
|
+
* `execute` here, or bind it later with `.withExecutor(...)`, a runtime
|
|
589
|
+
* adapter's `machine.provide(...)`, or `runAgent`'s `generateText`/
|
|
590
|
+
* `streamText` options.
|
|
591
|
+
*
|
|
592
|
+
* @example
|
|
593
|
+
* ```ts
|
|
594
|
+
* export const tellJoke = createTextLogic({
|
|
595
|
+
* mode: 'stream',
|
|
596
|
+
* schemas: { input: z.object({ topic: z.string() }), output: z.string() },
|
|
597
|
+
* model: 'openai/gpt-5.4-mini',
|
|
598
|
+
* system: 'You tell short, punchy jokes.',
|
|
599
|
+
* prompt: ({ input }) => `Tell a joke about ${input.topic}.`,
|
|
600
|
+
* });
|
|
601
|
+
* ```
|
|
602
|
+
*/
|
|
603
|
+
function createTextLogic(config, execute) {
|
|
604
|
+
const schemas = {
|
|
605
|
+
input: config.schemas?.input ?? noInputSchema,
|
|
606
|
+
output: config.schemas?.output ?? stringOutputSchema
|
|
607
|
+
};
|
|
608
|
+
const request = (input) => {
|
|
609
|
+
const parsedInput = validateSchemaSync(schemas.input, input);
|
|
610
|
+
const args = { input: parsedInput };
|
|
611
|
+
const name = resolveTextLogicValue(config.name, args);
|
|
612
|
+
const prompt = resolveTextLogicValue(config.prompt, args);
|
|
613
|
+
const messages = resolveTextLogicValue(config.messages, args);
|
|
614
|
+
const sourceIssue = textRequestSourceIssue({
|
|
615
|
+
name,
|
|
616
|
+
prompt,
|
|
617
|
+
messages
|
|
618
|
+
});
|
|
619
|
+
if (sourceIssue) throw new Error(sourceIssue);
|
|
620
|
+
return {
|
|
621
|
+
name,
|
|
622
|
+
input: parsedInput,
|
|
623
|
+
model: resolveTextLogicValue(config.model, args),
|
|
624
|
+
system: resolveTextLogicValue(config.system, args),
|
|
625
|
+
prompt,
|
|
626
|
+
messages,
|
|
627
|
+
tools: resolveTextLogicValue(config.tools, args),
|
|
628
|
+
toolChoice: resolveTextLogicValue(config.toolChoice, args),
|
|
629
|
+
outputSchema: schemas.output,
|
|
630
|
+
includeReasoning: resolveTextLogicValue(config.includeReasoning, args),
|
|
631
|
+
temperature: resolveTextLogicValue(config.temperature, args),
|
|
632
|
+
maxOutputTokens: resolveTextLogicValue(config.maxOutputTokens, args),
|
|
633
|
+
topP: resolveTextLogicValue(config.topP, args),
|
|
634
|
+
topK: resolveTextLogicValue(config.topK, args),
|
|
635
|
+
seed: resolveTextLogicValue(config.seed, args),
|
|
636
|
+
stopSequences: resolveTextLogicValue(config.stopSequences, args),
|
|
637
|
+
maxSteps: resolveTextLogicValue(config.maxSteps, args),
|
|
638
|
+
metadata: resolveTextLogicValue(config.metadata, args)
|
|
639
|
+
};
|
|
640
|
+
};
|
|
641
|
+
const logic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal, system, self }, enq) => {
|
|
642
|
+
const resolvedRequest = request(input);
|
|
643
|
+
if (!execute) throw new Error("Text logic has no host execution. Pass an executor as the second argument to createTextLogic(...), provide a runtime adapter, or bind it through runAgent/provideExecutors, or execute the XState effect in your host.");
|
|
644
|
+
const result = await execute({
|
|
645
|
+
input,
|
|
646
|
+
request: resolvedRequest,
|
|
647
|
+
signal,
|
|
648
|
+
system,
|
|
649
|
+
self,
|
|
650
|
+
emit: enq.emit
|
|
651
|
+
});
|
|
652
|
+
const selfId = self?.id;
|
|
653
|
+
const output = await normalizeGeneratorResult(result, typeof selfId === "string" ? selfId : "text logic");
|
|
654
|
+
return {
|
|
655
|
+
result: validateSchemaSync(schemas.output, output),
|
|
656
|
+
messages: responseMessagesOf(result)
|
|
657
|
+
};
|
|
658
|
+
} });
|
|
659
|
+
const textLogic = Object.assign(logic, {
|
|
660
|
+
kind: "statelyai.textLogic",
|
|
661
|
+
mode: config.mode ?? "generate",
|
|
662
|
+
schemas,
|
|
663
|
+
request,
|
|
664
|
+
async execute(input, executors) {
|
|
665
|
+
const { result: output, raw } = await executeAgentTextRequest(config.mode ?? "generate", "textLogic", request(input), executors);
|
|
666
|
+
return {
|
|
667
|
+
result: validateSchemaSync(schemas.output, output),
|
|
668
|
+
messages: responseMessagesOf(raw)
|
|
669
|
+
};
|
|
670
|
+
},
|
|
671
|
+
withExecutor(nextExecute) {
|
|
672
|
+
return createTextLogic(config, nextExecute);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
if (execute) executorBoundLogics.add(textLogic);
|
|
676
|
+
return textLogic;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Binds a child machine's {@link TextLogic} to a raw
|
|
680
|
+
* {@link AgentRequestExecutor} (the `generateText`/`streamText` shape hosts
|
|
681
|
+
* implement). Encapsulates the `withExecutor` idiom child agents repeat:
|
|
682
|
+
* default the request's `tools` to `{}`, forward the actor `signal`, call the
|
|
683
|
+
* executor, and return its `{ result }`. Use this to share ONE
|
|
684
|
+
* executor across a parent and its nested children.
|
|
685
|
+
*
|
|
686
|
+
* @example
|
|
687
|
+
* ```ts
|
|
688
|
+
* childMachine.provide({
|
|
689
|
+
* actors: {
|
|
690
|
+
* researchTopic: bindRequestExecutor(setup.requests.researchTopic, generateText),
|
|
691
|
+
* },
|
|
692
|
+
* });
|
|
693
|
+
* ```
|
|
694
|
+
*/
|
|
695
|
+
function bindRequestExecutor(logic, executor, info) {
|
|
696
|
+
return logic.withExecutor(async ({ request, signal }) => {
|
|
697
|
+
const { result, messages } = await executor({
|
|
698
|
+
...request,
|
|
699
|
+
tools: request.tools ?? {}
|
|
700
|
+
}, {
|
|
701
|
+
signal,
|
|
702
|
+
onChunk: info?.onChunk
|
|
703
|
+
});
|
|
704
|
+
return {
|
|
705
|
+
result,
|
|
706
|
+
messages
|
|
707
|
+
};
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
/** Type guard: true for any actor logic built by createTextLogic (checks the `kind` marker). @internal */
|
|
711
|
+
function isTextLogic(value) {
|
|
712
|
+
return !!value && typeof value === "object" && value.kind === "statelyai.textLogic" && typeof value.request === "function";
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Classifies a text request's output schema as `'structured'` (its JSON
|
|
716
|
+
* Schema is `type: 'object'`, `type: 'array'`, or a top-level union/
|
|
717
|
+
* composition — `anyOf`/`oneOf`/`allOf`, which a bare `z.union`/
|
|
718
|
+
* `z.discriminatedUnion` emits with no top-level `type`) or `'text'`
|
|
719
|
+
* (anything else, including no schema). Reads the schema's
|
|
720
|
+
* `~standard.jsonSchema.input()` extension — schemas without it are treated
|
|
721
|
+
* as `'text'`.
|
|
722
|
+
*/
|
|
723
|
+
function getAgentOutputMode(schema) {
|
|
724
|
+
const jsonSchema = getJsonSchemaSync(schema);
|
|
725
|
+
if (!jsonSchema) return "text";
|
|
726
|
+
if (jsonSchema.type === "object" || jsonSchema.type === "array") return "structured";
|
|
727
|
+
if (jsonSchema.type === void 0 && ("anyOf" in jsonSchema || "oneOf" in jsonSchema || "allOf" in jsonSchema)) return "structured";
|
|
728
|
+
return "text";
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Builds the schema every structured request is sent to the provider as: a
|
|
732
|
+
* root object `{ result: <inner> }` wrapping the declared schema, plus — when
|
|
733
|
+
* `options.reasoning` is `true` — an optional string `reasoning` property listed
|
|
734
|
+
* BEFORE `result` (property order nudges the model to reason first). This is THE
|
|
735
|
+
* wire contract for structured output: a root object is universally accepted as
|
|
736
|
+
* a provider response schema, unlike a bare union/array root that many providers
|
|
737
|
+
* reject.
|
|
738
|
+
*
|
|
739
|
+
* The returned {@link StandardSchemaV1} validates the `{ reasoning?, result }`
|
|
740
|
+
* object (validating `result` through the original schema, capturing a string
|
|
741
|
+
* `reasoning` when present) and exposes the wrapped JSON Schema. An executor
|
|
742
|
+
* returns the parsed `{ result, reasoning? }` as-is: `result` is what the
|
|
743
|
+
* machine validates against the declared schema, and `reasoning` stays on the
|
|
744
|
+
* raw executor result, never in machine context/output.
|
|
745
|
+
*/
|
|
746
|
+
function providerOutputSchema(inner, options = {}) {
|
|
747
|
+
const includeReasoning = options.reasoning === true;
|
|
748
|
+
const buildJson = (innerJson) => ({
|
|
749
|
+
type: "object",
|
|
750
|
+
properties: {
|
|
751
|
+
...includeReasoning ? { reasoning: { type: "string" } } : {},
|
|
752
|
+
result: innerJson ?? {}
|
|
753
|
+
},
|
|
754
|
+
required: ["result"],
|
|
755
|
+
additionalProperties: false
|
|
756
|
+
});
|
|
757
|
+
return { "~standard": {
|
|
758
|
+
version: 1,
|
|
759
|
+
vendor: "statelyai-agent",
|
|
760
|
+
validate(value) {
|
|
761
|
+
if (!value || typeof value !== "object" || !("result" in value)) return { issues: [{ message: "Expected a { result } object" }] };
|
|
762
|
+
const innerResult = inner["~standard"].validate(value.result);
|
|
763
|
+
if (innerResult instanceof Promise) throw new Error("Async schema validation is not supported.");
|
|
764
|
+
if (innerResult.issues) return innerResult;
|
|
765
|
+
const parsed = { result: innerResult.value };
|
|
766
|
+
const reasoning = value.reasoning;
|
|
767
|
+
if (typeof reasoning === "string") parsed.reasoning = reasoning;
|
|
768
|
+
return { value: parsed };
|
|
769
|
+
},
|
|
770
|
+
jsonSchema: { input: () => {
|
|
771
|
+
const innerJson = inner["~standard"].jsonSchema?.input?.();
|
|
772
|
+
return innerJson instanceof Promise ? innerJson.then(buildJson) : buildJson(innerJson);
|
|
773
|
+
} }
|
|
774
|
+
} };
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Validates a raw provider value against the provider output schema for
|
|
778
|
+
* `request` and returns the unwrapped `{ result, reasoning? }` — the checked
|
|
779
|
+
* replacement for `raw as ProviderStructuredOutput` in hand-written hosts.
|
|
780
|
+
* Pair with {@link providerOutputSchema} (which produced the schema the
|
|
781
|
+
* provider was asked to satisfy).
|
|
782
|
+
*/
|
|
783
|
+
function parseProviderOutput(request, value) {
|
|
784
|
+
if (!request.outputSchema) throw new Error("parseProviderOutput: the request declares no outputSchema.");
|
|
785
|
+
return validateSchemaSync(providerOutputSchema(request.outputSchema, { reasoning: request.includeReasoning }), value);
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Merges request-declared and call-site `tools`, dispatches to the
|
|
789
|
+
* `mode`-appropriate executor (`generateText`/`streamText`), and normalizes
|
|
790
|
+
* the raw result via {@link normalizeGeneratorResult}. Shared by
|
|
791
|
+
* `TextLogic.execute`, `executeAgentRequest`, and the `agent.generateText`/
|
|
792
|
+
* `agent.streamText` builtins. Throws if no executor is registered for
|
|
793
|
+
* `mode`.
|
|
794
|
+
*
|
|
795
|
+
* @internal
|
|
796
|
+
*/
|
|
797
|
+
async function executeAgentTextRequest(mode, id, input, executors, tools = {}, info) {
|
|
798
|
+
const request = {
|
|
799
|
+
...input,
|
|
800
|
+
name: input.name ?? id,
|
|
801
|
+
tools: {
|
|
802
|
+
...input.tools,
|
|
803
|
+
...tools
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
const sourceIssue = textRequestSourceIssue(request);
|
|
807
|
+
if (sourceIssue) throw new Error(sourceIssue);
|
|
808
|
+
const executor = mode === "stream" ? executors.streamText : executors.generateText;
|
|
809
|
+
if (!executor) throw new Error(`No executor provided for ${mode === "stream" ? "stream" : "generate"} request '${id}'.`);
|
|
810
|
+
const raw = await executor(request, info);
|
|
811
|
+
return {
|
|
812
|
+
result: await normalizeGeneratorResult(raw, id),
|
|
813
|
+
raw
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Unwraps an executor result into the request's final value: awaits and
|
|
818
|
+
* returns `result` from the `{ result }` {@link AgentRequestExecutorResult}.
|
|
819
|
+
* Anything else is a runtime error naming `id`. This is
|
|
820
|
+
* generator-result unwrapping only — decision results are extracted separately
|
|
821
|
+
* by `resolveDecision`.
|
|
822
|
+
*
|
|
823
|
+
* @internal
|
|
824
|
+
*/
|
|
825
|
+
async function normalizeGeneratorResult(result, id = "text request") {
|
|
826
|
+
const resolved = await result;
|
|
827
|
+
if (!resolved || typeof resolved !== "object" || !("result" in resolved)) throw invalidGeneratorResult(id);
|
|
828
|
+
return await resolved.result;
|
|
829
|
+
}
|
|
830
|
+
function invalidGeneratorResult(id) {
|
|
831
|
+
return /* @__PURE__ */ new Error(`Executor for '${id}' returned an invalid result: executors must return { result } (the text string or structured object as \`result\`, plus optional \`messages\`, \`usage\` and other passthrough fields). Wrap a raw SDK result with an adapter such as createAiSdkExecutors.`);
|
|
832
|
+
}
|
|
833
|
+
//#endregion
|
|
834
|
+
Object.defineProperty(exports, "AGENT_USAGE_TOKEN_FIELDS", {
|
|
835
|
+
enumerable: true,
|
|
836
|
+
get: function() {
|
|
837
|
+
return AGENT_USAGE_TOKEN_FIELDS;
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
Object.defineProperty(exports, "AgentError", {
|
|
841
|
+
enumerable: true,
|
|
842
|
+
get: function() {
|
|
843
|
+
return AgentError;
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
Object.defineProperty(exports, "AgentTruncatedError", {
|
|
847
|
+
enumerable: true,
|
|
848
|
+
get: function() {
|
|
849
|
+
return AgentTruncatedError;
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
Object.defineProperty(exports, "DECIDE_ACTOR", {
|
|
853
|
+
enumerable: true,
|
|
854
|
+
get: function() {
|
|
855
|
+
return DECIDE_ACTOR;
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
Object.defineProperty(exports, "GENERATE_TEXT_ACTOR", {
|
|
859
|
+
enumerable: true,
|
|
860
|
+
get: function() {
|
|
861
|
+
return GENERATE_TEXT_ACTOR;
|
|
862
|
+
}
|
|
863
|
+
});
|
|
864
|
+
Object.defineProperty(exports, "STREAM_TEXT_ACTOR", {
|
|
865
|
+
enumerable: true,
|
|
866
|
+
get: function() {
|
|
867
|
+
return STREAM_TEXT_ACTOR;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
Object.defineProperty(exports, "assistantMessage", {
|
|
871
|
+
enumerable: true,
|
|
872
|
+
get: function() {
|
|
873
|
+
return assistantMessage;
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
Object.defineProperty(exports, "bindRequestExecutor", {
|
|
877
|
+
enumerable: true,
|
|
878
|
+
get: function() {
|
|
879
|
+
return bindRequestExecutor;
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
Object.defineProperty(exports, "builtinTextActors", {
|
|
883
|
+
enumerable: true,
|
|
884
|
+
get: function() {
|
|
885
|
+
return builtinTextActors;
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
Object.defineProperty(exports, "createTextLogic", {
|
|
889
|
+
enumerable: true,
|
|
890
|
+
get: function() {
|
|
891
|
+
return createTextLogic;
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
Object.defineProperty(exports, "djb2Hex", {
|
|
895
|
+
enumerable: true,
|
|
896
|
+
get: function() {
|
|
897
|
+
return djb2Hex;
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
Object.defineProperty(exports, "executeAgentTextRequest", {
|
|
901
|
+
enumerable: true,
|
|
902
|
+
get: function() {
|
|
903
|
+
return executeAgentTextRequest;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
Object.defineProperty(exports, "executorBoundLogics", {
|
|
907
|
+
enumerable: true,
|
|
908
|
+
get: function() {
|
|
909
|
+
return executorBoundLogics;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
Object.defineProperty(exports, "findNonSerializableContextPaths", {
|
|
913
|
+
enumerable: true,
|
|
914
|
+
get: function() {
|
|
915
|
+
return findNonSerializableContextPaths;
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
Object.defineProperty(exports, "getAgentExecutionOptions", {
|
|
919
|
+
enumerable: true,
|
|
920
|
+
get: function() {
|
|
921
|
+
return getAgentExecutionOptions;
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
Object.defineProperty(exports, "getAgentOutputMode", {
|
|
925
|
+
enumerable: true,
|
|
926
|
+
get: function() {
|
|
927
|
+
return getAgentOutputMode;
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
Object.defineProperty(exports, "getCallFinishReason", {
|
|
931
|
+
enumerable: true,
|
|
932
|
+
get: function() {
|
|
933
|
+
return getCallFinishReason;
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
Object.defineProperty(exports, "getCallUsage", {
|
|
937
|
+
enumerable: true,
|
|
938
|
+
get: function() {
|
|
939
|
+
return getCallUsage;
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
Object.defineProperty(exports, "getJsonSchema", {
|
|
943
|
+
enumerable: true,
|
|
944
|
+
get: function() {
|
|
945
|
+
return getJsonSchema;
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
Object.defineProperty(exports, "getJsonSchemaSync", {
|
|
949
|
+
enumerable: true,
|
|
950
|
+
get: function() {
|
|
951
|
+
return getJsonSchemaSync;
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
Object.defineProperty(exports, "getMachineIdlePredicate", {
|
|
955
|
+
enumerable: true,
|
|
956
|
+
get: function() {
|
|
957
|
+
return getMachineIdlePredicate;
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
Object.defineProperty(exports, "getMachineStructuralHash", {
|
|
961
|
+
enumerable: true,
|
|
962
|
+
get: function() {
|
|
963
|
+
return getMachineStructuralHash;
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
Object.defineProperty(exports, "getMessageText", {
|
|
967
|
+
enumerable: true,
|
|
968
|
+
get: function() {
|
|
969
|
+
return getMessageText;
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
Object.defineProperty(exports, "getRegisteredAgentExecutionOptions", {
|
|
973
|
+
enumerable: true,
|
|
974
|
+
get: function() {
|
|
975
|
+
return getRegisteredAgentExecutionOptions;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
Object.defineProperty(exports, "getStateMeta", {
|
|
979
|
+
enumerable: true,
|
|
980
|
+
get: function() {
|
|
981
|
+
return getStateMeta;
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
Object.defineProperty(exports, "getStatePath", {
|
|
985
|
+
enumerable: true,
|
|
986
|
+
get: function() {
|
|
987
|
+
return getStatePath;
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
Object.defineProperty(exports, "isStandardSchema", {
|
|
991
|
+
enumerable: true,
|
|
992
|
+
get: function() {
|
|
993
|
+
return isStandardSchema;
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
Object.defineProperty(exports, "isTextLogic", {
|
|
997
|
+
enumerable: true,
|
|
998
|
+
get: function() {
|
|
999
|
+
return isTextLogic;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
Object.defineProperty(exports, "isUnboundPlaceholder", {
|
|
1003
|
+
enumerable: true,
|
|
1004
|
+
get: function() {
|
|
1005
|
+
return isUnboundPlaceholder;
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
Object.defineProperty(exports, "machineIdlePredicates", {
|
|
1009
|
+
enumerable: true,
|
|
1010
|
+
get: function() {
|
|
1011
|
+
return machineIdlePredicates;
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
Object.defineProperty(exports, "missingActor", {
|
|
1015
|
+
enumerable: true,
|
|
1016
|
+
get: function() {
|
|
1017
|
+
return missingActor;
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
Object.defineProperty(exports, "normalizeGeneratorResult", {
|
|
1021
|
+
enumerable: true,
|
|
1022
|
+
get: function() {
|
|
1023
|
+
return normalizeGeneratorResult;
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
Object.defineProperty(exports, "parseOutput", {
|
|
1027
|
+
enumerable: true,
|
|
1028
|
+
get: function() {
|
|
1029
|
+
return parseOutput;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
Object.defineProperty(exports, "parseProviderOutput", {
|
|
1033
|
+
enumerable: true,
|
|
1034
|
+
get: function() {
|
|
1035
|
+
return parseProviderOutput;
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
Object.defineProperty(exports, "providerOutputSchema", {
|
|
1039
|
+
enumerable: true,
|
|
1040
|
+
get: function() {
|
|
1041
|
+
return providerOutputSchema;
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
Object.defineProperty(exports, "resolveMachineVersion", {
|
|
1045
|
+
enumerable: true,
|
|
1046
|
+
get: function() {
|
|
1047
|
+
return resolveMachineVersion;
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
Object.defineProperty(exports, "resolveTextLogicValue", {
|
|
1051
|
+
enumerable: true,
|
|
1052
|
+
get: function() {
|
|
1053
|
+
return resolveTextLogicValue;
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
Object.defineProperty(exports, "responseMessagesOf", {
|
|
1057
|
+
enumerable: true,
|
|
1058
|
+
get: function() {
|
|
1059
|
+
return responseMessagesOf;
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
Object.defineProperty(exports, "setAgentExecutionOptions", {
|
|
1063
|
+
enumerable: true,
|
|
1064
|
+
get: function() {
|
|
1065
|
+
return setAgentExecutionOptions;
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
Object.defineProperty(exports, "systemMessage", {
|
|
1069
|
+
enumerable: true,
|
|
1070
|
+
get: function() {
|
|
1071
|
+
return systemMessage;
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
Object.defineProperty(exports, "toolMessage", {
|
|
1075
|
+
enumerable: true,
|
|
1076
|
+
get: function() {
|
|
1077
|
+
return toolMessage;
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
Object.defineProperty(exports, "userMessage", {
|
|
1081
|
+
enumerable: true,
|
|
1082
|
+
get: function() {
|
|
1083
|
+
return userMessage;
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
Object.defineProperty(exports, "validateSchemaSync", {
|
|
1087
|
+
enumerable: true,
|
|
1088
|
+
get: function() {
|
|
1089
|
+
return validateSchemaSync;
|
|
1090
|
+
}
|
|
1091
|
+
});
|