@statelyai/agent 2.0.0-alpha.25 → 2.0.0-alpha.27
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 +4 -4
- package/dist/ai-sdk.d.cts +6 -6
- package/dist/ai-sdk.d.mts +6 -6
- package/dist/ai-sdk.mjs +4 -4
- package/dist/{decision-CM9CYZ6t.cjs → decision-CmM8-HHz.cjs} +10 -9
- package/dist/{decision-DzSv1NpD.mjs → decision-RQ0iv0oZ.mjs} +10 -9
- package/dist/{event-log-store-Ba2fsc4E.mjs → event-log-store-2pAFiWuc.mjs} +1 -1
- package/dist/{event-log-store-DvOcs10J.d.cts → event-log-store-2xzFmgr2.d.cts} +1 -1
- package/dist/{event-log-store-DxyVgnas.d.mts → event-log-store-BIPOz-di.d.mts} +1 -1
- package/dist/{event-log-store-DQmImLYk.cjs → event-log-store-C-COhIXP.cjs} +1 -1
- package/dist/index.cjs +13 -195
- package/dist/index.d.cts +25 -105
- package/dist/index.d.mts +25 -105
- package/dist/index.mjs +12 -189
- package/dist/log.cjs +1 -1
- package/dist/log.d.cts +1 -1
- package/dist/log.d.mts +1 -1
- package/dist/log.mjs +1 -1
- package/dist/machines.cjs +4 -7
- package/dist/machines.d.cts +5 -7
- package/dist/machines.d.mts +5 -7
- package/dist/machines.mjs +4 -7
- package/dist/openai.cjs +3 -3
- package/dist/openai.d.cts +5 -5
- package/dist/openai.d.mts +5 -5
- package/dist/openai.mjs +3 -3
- package/dist/otel.cjs +2 -2
- package/dist/otel.d.cts +3 -3
- package/dist/otel.d.mts +3 -3
- package/dist/otel.mjs +2 -2
- package/dist/{run-agent-CG343kFs.d.mts → run-agent-ByO-Bxyd.d.cts} +184 -179
- package/dist/{run-agent-BOfoJg5B.d.cts → run-agent-COXXc0RW.d.mts} +184 -179
- package/dist/{run-agent-Cb1-LS5P.cjs → run-agent-Dmf8GV2d.cjs} +539 -737
- package/dist/{run-agent-DvDQd4If.mjs → run-agent-xNTxpt82.mjs} +537 -705
- package/dist/{setup-agent-CFW-jPI8.mjs → setup-agent-BuanDXeh.mjs} +6 -28
- package/dist/{setup-agent-CPf7MxnG.cjs → setup-agent-DV4cTou-.cjs} +6 -28
- package/dist/testing.cjs +285 -170
- package/dist/testing.d.cts +39 -22
- package/dist/testing.d.mts +39 -22
- package/dist/testing.mjs +270 -155
- package/dist/{text-logic-Bl_ZRxDi.mjs → text-logic-CIqHAmBi.mjs} +10 -26
- package/dist/{text-logic-BFEOxyxv.d.cts → text-logic-D-czVPYp.d.mts} +27 -28
- package/dist/{text-logic-hZSiRUo1.d.mts → text-logic-IjfYy9DP.d.cts} +27 -28
- package/dist/{text-logic-C8xyr6Ag.cjs → text-logic-f5F5sCdc.cjs} +9 -43
- package/dist/{types-B1Qs2q3q.d.cts → types-BgNbNKnJ.d.mts} +3 -3
- package/dist/{types-tA3SdczG.d.mts → types-hR5f5YXa.d.cts} +3 -3
- package/dist/{utils-C1p37X_c.d.mts → utils-_q7F-AZu.d.mts} +3 -103
- package/dist/{utils-RalRn9sk.d.cts → utils-o4YRdHJX.d.cts} +3 -103
- package/dist/validate.cjs +0 -8
- package/dist/validate.mjs +0 -8
- package/package.json +5 -4
- package/readme.md +8 -4
- package/schemas/agent-workflow.json +0 -8
package/dist/testing.mjs
CHANGED
|
@@ -1,6 +1,251 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as isDecisionLogic, d as isRecord, l as getAcceptedEvents, s as resolveDecision, t as AgentDecisionExhaustedError } from "./decision-
|
|
3
|
-
import {
|
|
1
|
+
import { b as getRegisteredAgentExecutionOptions, d as isTextLogic, f as normalizeGeneratorResult, j as getStateMeta, u as getCallUsage, v as executorBoundLogics, z as AgentError } from "./text-logic-CIqHAmBi.mjs";
|
|
2
|
+
import { a as isDecisionLogic, d as isRecord, l as getAcceptedEvents, s as resolveDecision, t as AgentDecisionExhaustedError } from "./decision-RQ0iv0oZ.mjs";
|
|
3
|
+
import { f as runToQuiescence, s as createAgentRuntime } from "./run-agent-xNTxpt82.mjs";
|
|
4
|
+
import { initialTransition, transition } from "xstate";
|
|
5
|
+
//#region src/steps.ts
|
|
6
|
+
/**
|
|
7
|
+
* Internal: the pure `(state, event) => (nextState, requests)` view of an
|
|
8
|
+
* agent machine, used by model-free simulation (`simulateAgent`) and tests.
|
|
9
|
+
* An {@link AgentStep} is a snapshot plus the model requests the machine is
|
|
10
|
+
* now waiting on; nothing here executes anything. Hosts use
|
|
11
|
+
* `createAgentRuntime`.
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/** @internal Normalizes current and legacy XState invoke effect shapes. */
|
|
15
|
+
function getInvokeEffectMetadata(action) {
|
|
16
|
+
if (action.type === "@xstate.spawn") return action;
|
|
17
|
+
if (action.type === "xstate.spawnChild") {
|
|
18
|
+
const params = action.params;
|
|
19
|
+
return params ? {
|
|
20
|
+
...params,
|
|
21
|
+
logic: action.logic
|
|
22
|
+
} : void 0;
|
|
23
|
+
}
|
|
24
|
+
if (action.type === "@xstate.start" && typeof action.src === "string") return action;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The invokes a snapshot is still waiting on, lowered to the same spawn-effect
|
|
28
|
+
* shape {@link getAgentRequests} reads. Derived from the snapshot's live
|
|
29
|
+
* children rather than the last transition's actions, so a state that invokes
|
|
30
|
+
* several actors at once (an `invoke: [...]` array, or parallel regions each
|
|
31
|
+
* invoking) keeps reporting the siblings after the first one settles —
|
|
32
|
+
* including when the settled invoke's `onDone` is targetless and starts
|
|
33
|
+
* nothing new.
|
|
34
|
+
*
|
|
35
|
+
* Entries are ordered by invoke id, so a walker that resolves one invoke per
|
|
36
|
+
* step is deterministic.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
function getPendingInvokes(snapshot) {
|
|
41
|
+
const children = snapshot.children ?? {};
|
|
42
|
+
return Object.keys(children).sort().flatMap((id) => {
|
|
43
|
+
const child = children[id];
|
|
44
|
+
const childSnapshot = child?.getSnapshot?.();
|
|
45
|
+
if (!child || childSnapshot?.status !== "active") return [];
|
|
46
|
+
return [{
|
|
47
|
+
type: "@xstate.spawn",
|
|
48
|
+
id,
|
|
49
|
+
src: child.src,
|
|
50
|
+
input: childSnapshot.input,
|
|
51
|
+
logic: child.logic
|
|
52
|
+
}];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Scans a set of executable actions (as returned by xstate's `transition`/
|
|
57
|
+
* `initialTransition`) for spawned `TextLogic`/`DecisionLogic` invokes and
|
|
58
|
+
* lowers each into an {@link AgentStepRequest}. Pass `options.machine` to
|
|
59
|
+
* pre-fill `schemas`/`actors` from the machine's registered `setupAgent`
|
|
60
|
+
* options instead of passing them by hand.
|
|
61
|
+
* `options.snapshot` is required to resolve a decision's candidate events
|
|
62
|
+
* (intersecting declared `allowedEvents` with what's currently legal) — omit
|
|
63
|
+
* it and decision requests report an empty `events` list.
|
|
64
|
+
*
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
function getAgentRequests(actions, { machine, ...options } = {}) {
|
|
68
|
+
if (machine) options = {
|
|
69
|
+
...getRegisteredAgentExecutionOptions(machine),
|
|
70
|
+
...options
|
|
71
|
+
};
|
|
72
|
+
return actions.flatMap((action) => {
|
|
73
|
+
const params = getInvokeEffectMetadata(action);
|
|
74
|
+
if (!params || typeof params.src !== "string") return [];
|
|
75
|
+
if (typeof params.id !== "string" || params.id.length === 0) throw new Error(`Agent invoke '${params.src}' must define a durable string id.`);
|
|
76
|
+
const registeredLogic = isTextLogic(params.logic) || isDecisionLogic(params.logic) ? params.logic : options.actors?.[params.src];
|
|
77
|
+
if (isDecisionLogic(registeredLogic)) {
|
|
78
|
+
const decisionRequest = registeredLogic.request(params.input);
|
|
79
|
+
const allowedEventTypes = registeredLogic.allowedEventTypes?.(params.input);
|
|
80
|
+
const events = options.snapshot ? getAcceptedEvents(options.snapshot, {
|
|
81
|
+
events: options.events,
|
|
82
|
+
schemas: options.schemas,
|
|
83
|
+
eventTypes: allowedEventTypes
|
|
84
|
+
}) : [];
|
|
85
|
+
return [{
|
|
86
|
+
...decisionRequest,
|
|
87
|
+
id: params.id,
|
|
88
|
+
events
|
|
89
|
+
}];
|
|
90
|
+
}
|
|
91
|
+
const textLogic = isTextLogic(registeredLogic) ? registeredLogic : void 0;
|
|
92
|
+
const input = textLogic ? textLogic.request(params.input) : void 0;
|
|
93
|
+
if (!input) return [];
|
|
94
|
+
return [{
|
|
95
|
+
kind: "text",
|
|
96
|
+
id: params.id,
|
|
97
|
+
src: params.src,
|
|
98
|
+
...textLogic ? { mode: textLogic.mode } : {},
|
|
99
|
+
input,
|
|
100
|
+
tools: input.tools ?? {},
|
|
101
|
+
events: []
|
|
102
|
+
}];
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Builds the canonical `xstate.done.actor` event xstate's `transition()`
|
|
107
|
+
* expects to resolve a spawned invoke — including the child session identity
|
|
108
|
+
* that rejects a stale completion from an earlier incarnation.
|
|
109
|
+
*
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
function doneEvent(snapshot, request, output) {
|
|
113
|
+
const id = typeof request === "string" ? request : request.id;
|
|
114
|
+
const child = snapshot.children[id];
|
|
115
|
+
return {
|
|
116
|
+
type: "xstate.done.actor",
|
|
117
|
+
actorId: id,
|
|
118
|
+
...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
|
|
119
|
+
output
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The `xstate.error.actor` counterpart of {@link doneEvent} — the event a
|
|
124
|
+
* failed invoke delivers, which `onError` transitions observe. Carries the
|
|
125
|
+
* same child session identity, so xstate does not discard it as stale.
|
|
126
|
+
*
|
|
127
|
+
* @internal
|
|
128
|
+
*/
|
|
129
|
+
function errorEvent(snapshot, request, error) {
|
|
130
|
+
const id = typeof request === "string" ? request : request.id;
|
|
131
|
+
const child = snapshot.children[id];
|
|
132
|
+
return {
|
|
133
|
+
type: "xstate.error.actor",
|
|
134
|
+
actorId: id,
|
|
135
|
+
...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
|
|
136
|
+
error
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Applies a request's `output` as a done event (or, with `outcome: 'error'`,
|
|
141
|
+
* its `output` as the invoke's error) via `transition(...)`, returning the raw
|
|
142
|
+
* `[snapshot, actions]` tuple. Lower-level than {@link resolveAgentStep} /
|
|
143
|
+
* {@link rejectAgentStep} — those helpers wrap this and also run
|
|
144
|
+
* {@link getAgentRequests} to produce the next {@link AgentStep}.
|
|
145
|
+
*
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
148
|
+
function transitionResult(logic, snapshot, request, output, outcome = "output") {
|
|
149
|
+
const event = outcome === "error" ? errorEvent(snapshot, request, output) : doneEvent(snapshot, request, output);
|
|
150
|
+
const result = transition(logic, snapshot, event);
|
|
151
|
+
applyFinalStateOutput(logic, result[0], event);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Starts a machine and returns its first {@link AgentStep} — the step-path
|
|
156
|
+
* equivalent of `initialTransition` plus request discovery. Begins the
|
|
157
|
+
* durable/per-model-call-checkpoint loop: resolve each `step.requests` entry
|
|
158
|
+
* (via {@link executeAgentRequest} for `kind: 'text'`, or
|
|
159
|
+
* {@link resolveDecision} for `kind: 'decision'`), then advance with
|
|
160
|
+
* {@link resolveAgentStep} or {@link transitionAgentStep}.
|
|
161
|
+
*/
|
|
162
|
+
function initialAgentStep(machine, input, options) {
|
|
163
|
+
const [snapshot, actions] = initialTransition(machine, input);
|
|
164
|
+
return createAgentStep(machine, snapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Applies an externally-sent event (e.g. a decision's chosen event, or a
|
|
168
|
+
* human's reply) and returns the next {@link AgentStep}. Accepts **either**
|
|
169
|
+
* a raw snapshot **or** a prior `AgentStep` as the second argument —
|
|
170
|
+
* `.snapshot` is unwrapped automatically, so callers can thread the whole
|
|
171
|
+
* step object through without manually plucking the snapshot out.
|
|
172
|
+
*/
|
|
173
|
+
function transitionAgentStep(machine, snapshotOrStep, event, options) {
|
|
174
|
+
const [nextSnapshot, actions] = transition(machine, isAgentStep(snapshotOrStep) ? snapshotOrStep.snapshot : snapshotOrStep, event);
|
|
175
|
+
return createAgentStep(machine, nextSnapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Applies a resolved text request's output (a `kind: 'text'`
|
|
179
|
+
* {@link AgentRequest} — not a decision) as a done event and returns the
|
|
180
|
+
* next {@link AgentStep}. For decisions, resolve with `resolveDecision`
|
|
181
|
+
* (which returns a {@link ChosenEvent}) and apply it with
|
|
182
|
+
* {@link transitionAgentStep} instead — a decision has no output value of
|
|
183
|
+
* its own to feed here.
|
|
184
|
+
*/
|
|
185
|
+
function resolveAgentStep(machine, step, request, output, options) {
|
|
186
|
+
const [snapshot, actions] = transitionResult(machine, step.snapshot, request, output);
|
|
187
|
+
return createAgentStep(machine, snapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Rejects a pending invoke — the failure counterpart of
|
|
191
|
+
* {@link resolveAgentStep}. Delivers `error` as the invoke's
|
|
192
|
+
* `xstate.error.actor` event, so the machine takes that invoke's `onError`
|
|
193
|
+
* transition and returns the next {@link AgentStep}. With no `onError` in
|
|
194
|
+
* scope the returned step's snapshot has `status: 'error'`, exactly as a live
|
|
195
|
+
* run's failed invoke does.
|
|
196
|
+
*
|
|
197
|
+
* A durable host replays a failed call with this: the log says the call
|
|
198
|
+
* failed, so the replay rejects the invoke instead of resolving it. Scripted
|
|
199
|
+
* playthroughs use it too — see `simulateAgent`'s `script.errors`.
|
|
200
|
+
*
|
|
201
|
+
* `error` can be any value. An `Error` instance is the usual one, but a plain
|
|
202
|
+
* object (`{ code: 'truncated' }`) reaches the `onError` transition as
|
|
203
|
+
* `event.error` untouched, so a machine that branches on a failure code is
|
|
204
|
+
* testable without constructing a provider's error class.
|
|
205
|
+
*/
|
|
206
|
+
function rejectAgentStep(machine, step, request, error, options) {
|
|
207
|
+
const [snapshot, actions] = transitionResult(machine, step.snapshot, request, error, "error");
|
|
208
|
+
return createAgentStep(machine, snapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
|
|
209
|
+
}
|
|
210
|
+
function createAgentStep(machine, snapshot, actions, options) {
|
|
211
|
+
applyFinalStateOutput(machine, snapshot);
|
|
212
|
+
return {
|
|
213
|
+
snapshot,
|
|
214
|
+
actions,
|
|
215
|
+
requests: getAgentRequests(getPendingInvokes(snapshot), {
|
|
216
|
+
...options,
|
|
217
|
+
snapshot
|
|
218
|
+
}),
|
|
219
|
+
done: snapshot.status === "done"
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function resolveStateValueConfig(config, value) {
|
|
223
|
+
if (typeof value === "string") return config.states?.[value];
|
|
224
|
+
if (!value || typeof value !== "object") return;
|
|
225
|
+
for (const [key, childValue] of Object.entries(value)) {
|
|
226
|
+
const childConfig = config.states?.[key];
|
|
227
|
+
if (!childConfig) continue;
|
|
228
|
+
if (childConfig.type === "final") return childConfig;
|
|
229
|
+
const nested = resolveStateValueConfig(childConfig, childValue);
|
|
230
|
+
if (nested) return nested;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function applyFinalStateOutput(logic, snapshot, event) {
|
|
234
|
+
const machineSnapshot = snapshot;
|
|
235
|
+
if (machineSnapshot.status !== "done" || machineSnapshot.output !== void 0 || !("config" in logic)) return;
|
|
236
|
+
const config = logic.config;
|
|
237
|
+
if (!config) return;
|
|
238
|
+
const output = resolveStateValueConfig(config, machineSnapshot.value)?.output;
|
|
239
|
+
if (output === void 0) return;
|
|
240
|
+
machineSnapshot.output = typeof output === "function" ? output({
|
|
241
|
+
context: machineSnapshot.context,
|
|
242
|
+
event
|
|
243
|
+
}) : output;
|
|
244
|
+
}
|
|
245
|
+
function isAgentStep(value) {
|
|
246
|
+
return !!value && typeof value === "object" && "snapshot" in value && "actions" in value && "requests" in value;
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
4
249
|
//#region src/verify.ts
|
|
5
250
|
const DECIDE_SRC = "agent.decide";
|
|
6
251
|
function normalizeInvokes(invoke) {
|
|
@@ -77,7 +322,7 @@ function checkDirectObjectSrc(ctx) {
|
|
|
77
322
|
code: "direct-object-src",
|
|
78
323
|
severity: "warning",
|
|
79
324
|
path: node.path,
|
|
80
|
-
message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by
|
|
325
|
+
message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by the agent runtime, so they inherit no host executors — call '.withExecutor(...)' on the logic, or register it as a string-keyed actor source (machine.provide({ actors: { name: logic } })) and invoke it by name.`
|
|
81
326
|
});
|
|
82
327
|
}
|
|
83
328
|
return out;
|
|
@@ -811,11 +1056,13 @@ function matcherFor(expected) {
|
|
|
811
1056
|
* @example Trajectory assertions in a test
|
|
812
1057
|
* ```ts
|
|
813
1058
|
* const statePath: unknown[] = [];
|
|
814
|
-
* const result = await
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
1059
|
+
* const result = await runToQuiescence(
|
|
1060
|
+
* createAgentRuntime(machine, {
|
|
1061
|
+
* executors,
|
|
1062
|
+
* onTransition: (snapshot) => statePath.push(snapshot.value),
|
|
1063
|
+
* }),
|
|
1064
|
+
* { input },
|
|
1065
|
+
* );
|
|
819
1066
|
*
|
|
820
1067
|
* const path = matchesTrajectory(statePath, ['prompting', 'drafting', 'sent']);
|
|
821
1068
|
* expect(path.matched, JSON.stringify(path.firstMiss)).toBe(true);
|
|
@@ -869,43 +1116,7 @@ function matchesTrajectory(actual, expected, options = {}) {
|
|
|
869
1116
|
};
|
|
870
1117
|
}
|
|
871
1118
|
//#endregion
|
|
872
|
-
//#region src/
|
|
873
|
-
/**
|
|
874
|
-
* Scripted executors — a deterministic stand-in for a model host.
|
|
875
|
-
*
|
|
876
|
-
* `createScriptedExecutors` builds a full `{ generateText, streamText, decide }`
|
|
877
|
-
* set that plays back canned answers keyed by
|
|
878
|
-
* request name, so `runAgent` (or `provideExecutors`, or a bare
|
|
879
|
-
* `TextLogic.execute`) runs with no API key and no network.
|
|
880
|
-
*
|
|
881
|
-
* One rule: entries for a name are consumed in order, and the last entry
|
|
882
|
-
* repeats forever.
|
|
883
|
-
*
|
|
884
|
-
* @module
|
|
885
|
-
*/
|
|
886
|
-
function toQueues(value) {
|
|
887
|
-
return new Map(Object.entries(value ?? {}).map(([name, entries]) => [name, {
|
|
888
|
-
entries: Array.isArray(entries) ? [...entries] : [entries],
|
|
889
|
-
index: 0
|
|
890
|
-
}]));
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Takes the next entry for `name`, holding on the last entry once the list is
|
|
894
|
-
* spent. `undefined` means the script has no route for this name at all.
|
|
895
|
-
* @internal
|
|
896
|
-
*/
|
|
897
|
-
function takeEntry(queues, name) {
|
|
898
|
-
const queue = queues.get(name) ?? queues.get("*");
|
|
899
|
-
if (!queue || queue.entries.length === 0) return;
|
|
900
|
-
const entry = queue.entries[Math.min(queue.index, queue.entries.length - 1)];
|
|
901
|
-
queue.index++;
|
|
902
|
-
return entry;
|
|
903
|
-
}
|
|
904
|
-
/** @internal */
|
|
905
|
-
function noAnswer(name, queues) {
|
|
906
|
-
const known = [...queues.keys()].join(", ") || "(none)";
|
|
907
|
-
return /* @__PURE__ */ new Error(`No scripted answer for request '${name}'. Known: ${known}`);
|
|
908
|
-
}
|
|
1119
|
+
//#region src/seam.ts
|
|
909
1120
|
/** The only own keys an executor result carries. @internal */
|
|
910
1121
|
const TEXT_RESULT_KEYS = new Set([
|
|
911
1122
|
"result",
|
|
@@ -924,123 +1135,26 @@ function isTextResult(value) {
|
|
|
924
1135
|
return Object.hasOwn(value, "result") && Object.keys(value).every((key) => TEXT_RESULT_KEYS.has(key));
|
|
925
1136
|
}
|
|
926
1137
|
/**
|
|
927
|
-
* Resolves ONE scripted
|
|
1138
|
+
* Resolves ONE scripted entry to an executor result: a function entry is
|
|
928
1139
|
* called with the request, and the value is taken as the executor result only
|
|
929
|
-
* when it is one (see {@link isTextResult}).
|
|
930
|
-
* queues follow the same entry conventions. @internal
|
|
1140
|
+
* when it is one (see {@link isTextResult}). @internal
|
|
931
1141
|
*/
|
|
932
|
-
async function
|
|
1142
|
+
async function resolveSeamScriptEntry(entry, request, info) {
|
|
933
1143
|
const value = typeof entry === "function" ? await entry(request, info) : entry;
|
|
934
1144
|
return isRecord(value) && isTextResult(value) ? value : { result: value };
|
|
935
1145
|
}
|
|
936
|
-
/**
|
|
937
|
-
* Names a pending text request in an error message. Shared with `runSeam`.
|
|
938
|
-
* @internal
|
|
939
|
-
*/
|
|
1146
|
+
/** Names a pending text request in an error message. @internal */
|
|
940
1147
|
function describeText(request) {
|
|
941
1148
|
return request.name ? `'${request.name}' (model '${request.model}')` : `(model '${request.model}')`;
|
|
942
1149
|
}
|
|
943
1150
|
/**
|
|
944
|
-
* Stream semantics with no model: the whole text lands as one chunk.
|
|
945
|
-
* with `runSeam`, whose scripted answers stream the same way. @internal
|
|
1151
|
+
* Stream semantics with no model: the whole text lands as one chunk. @internal
|
|
946
1152
|
*/
|
|
947
1153
|
function emitScriptedChunk(result, info) {
|
|
948
1154
|
const text = isRecord(result) ? result["result"] : void 0;
|
|
949
1155
|
if (typeof text === "string") info?.onChunk?.(text);
|
|
950
1156
|
}
|
|
951
1157
|
/**
|
|
952
|
-
* Executors that replay a script instead of calling a model. Every slot is
|
|
953
|
-
* provided, so any machine binds.
|
|
954
|
-
*
|
|
955
|
-
* Answers are keyed by the request's resolved name — its authored `name`, else
|
|
956
|
-
* the invoke `id` — and `"*"` serves anything unmatched.
|
|
957
|
-
* Entries for a name are consumed in order and the last one repeats forever, so
|
|
958
|
-
* a looping machine needs no extra option. A request the script has no route
|
|
959
|
-
* for throws an ordinary `Error` from inside the executor, which reaches the
|
|
960
|
-
* machine as an actor error.
|
|
961
|
-
*
|
|
962
|
-
* Queues are copied on creation, so the caller's arrays are never mutated and
|
|
963
|
-
* each call builds a fresh, independent playthrough. Entries may be plain
|
|
964
|
-
* values or functions of the request.
|
|
965
|
-
*
|
|
966
|
-
* @example
|
|
967
|
-
* ```ts
|
|
968
|
-
* const result = await runAgent(moderationMachine, {
|
|
969
|
-
* input: { comment: 'honestly this update is terrible', trust: 20 },
|
|
970
|
-
* executors: createScriptedExecutors({
|
|
971
|
-
* decisions: { moderate: [{ type: 'FLAG', reason: 'Borderline tone.' }] },
|
|
972
|
-
* }),
|
|
973
|
-
* });
|
|
974
|
-
* ```
|
|
975
|
-
*
|
|
976
|
-
* @example Dynamic entries
|
|
977
|
-
* ```ts
|
|
978
|
-
* createScriptedExecutors({
|
|
979
|
-
* text: { draft: (request) => `Draft about ${request.prompt}` },
|
|
980
|
-
* decisions: { route: (request) => ({ type: request.events[0]!.type }) },
|
|
981
|
-
* });
|
|
982
|
-
* ```
|
|
983
|
-
*/
|
|
984
|
-
function createScriptedExecutors(script = {}) {
|
|
985
|
-
const decisions = toQueues(script.decisions);
|
|
986
|
-
const text = toQueues(script.text);
|
|
987
|
-
const calls = [];
|
|
988
|
-
const withDefaultUsage = (result) => result.usage || !script.usage ? result : {
|
|
989
|
-
...result,
|
|
990
|
-
usage: script.usage
|
|
991
|
-
};
|
|
992
|
-
const nextText = async (kind, request, info) => {
|
|
993
|
-
const name = request.name ?? "*";
|
|
994
|
-
calls.push({
|
|
995
|
-
kind,
|
|
996
|
-
name,
|
|
997
|
-
input: request.input,
|
|
998
|
-
request
|
|
999
|
-
});
|
|
1000
|
-
const entry = takeEntry(text, name);
|
|
1001
|
-
if (entry === void 0) throw noAnswer(name, text);
|
|
1002
|
-
return withDefaultUsage(await resolveScriptedTextEntry(entry, request, info));
|
|
1003
|
-
};
|
|
1004
|
-
return {
|
|
1005
|
-
calls,
|
|
1006
|
-
generateText: (request, info) => nextText("generateText", request, info),
|
|
1007
|
-
streamText: async (request, info) => {
|
|
1008
|
-
const name = request.name ?? "*";
|
|
1009
|
-
const streamEntry = script.stream?.[name] ?? script.stream?.["*"];
|
|
1010
|
-
if (streamEntry !== void 0) {
|
|
1011
|
-
calls.push({
|
|
1012
|
-
kind: "streamText",
|
|
1013
|
-
name,
|
|
1014
|
-
input: request.input,
|
|
1015
|
-
request
|
|
1016
|
-
});
|
|
1017
|
-
const resolved = typeof streamEntry === "function" ? await streamEntry(request, info) : streamEntry;
|
|
1018
|
-
const chunks = typeof resolved === "string" ? [resolved] : [...resolved];
|
|
1019
|
-
for (const chunk of chunks) info?.onChunk?.(chunk);
|
|
1020
|
-
return withDefaultUsage({ result: chunks.join("") });
|
|
1021
|
-
}
|
|
1022
|
-
const result = await nextText("streamText", request, info);
|
|
1023
|
-
emitScriptedChunk(result, info);
|
|
1024
|
-
return result;
|
|
1025
|
-
},
|
|
1026
|
-
decide: async (request) => {
|
|
1027
|
-
const name = request.name ?? request.id;
|
|
1028
|
-
calls.push({
|
|
1029
|
-
kind: "decide",
|
|
1030
|
-
name,
|
|
1031
|
-
input: request.input,
|
|
1032
|
-
request
|
|
1033
|
-
});
|
|
1034
|
-
const entry = takeEntry(decisions, name);
|
|
1035
|
-
if (entry === void 0) throw noAnswer(name, decisions);
|
|
1036
|
-
const value = typeof entry === "function" ? await entry(request) : entry;
|
|
1037
|
-
return withDefaultUsage(isRecord(value) && typeof value["type"] !== "string" && "event" in value ? value : { event: value });
|
|
1038
|
-
}
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
//#endregion
|
|
1042
|
-
//#region src/seam.ts
|
|
1043
|
-
/**
|
|
1044
1158
|
* The seam's own answer, for scoring: the executor result's `result`.
|
|
1045
1159
|
* @internal
|
|
1046
1160
|
*/
|
|
@@ -1062,8 +1176,8 @@ async function seamOutputOf(result, request) {
|
|
|
1062
1176
|
* @example No API key: the seam is scripted too, so the whole thing runs offline.
|
|
1063
1177
|
* ```ts
|
|
1064
1178
|
* const run = await runSeam(emailDrafter, {
|
|
1065
|
-
* scripts: {
|
|
1066
|
-
* seam: { request: '
|
|
1179
|
+
* scripts: { writeFollowUps: [followUps], emailDrafter: [draft] },
|
|
1180
|
+
* seam: { request: 'writeFollowUps' },
|
|
1067
1181
|
* respond: ({ state }) => (state === 'prompting' ? { type: 'PROMPT_SUBMITTED', prompt } : null),
|
|
1068
1182
|
* });
|
|
1069
1183
|
*
|
|
@@ -1099,7 +1213,7 @@ async function runSeam(machine, options) {
|
|
|
1099
1213
|
const takeScriptedSlot = async (request, info) => {
|
|
1100
1214
|
const queue = queues.get(queueKeyOf(request));
|
|
1101
1215
|
if (!queue?.length) return;
|
|
1102
|
-
return
|
|
1216
|
+
return resolveSeamScriptEntry(options.repeatLast && queue.length === 1 ? queue[0] : queue.shift(), request, info);
|
|
1103
1217
|
};
|
|
1104
1218
|
const scriptedAnswer = async (request, info) => {
|
|
1105
1219
|
const scripted = await takeScriptedSlot(request, info);
|
|
@@ -1156,9 +1270,7 @@ async function runSeam(machine, options) {
|
|
|
1156
1270
|
let result;
|
|
1157
1271
|
const maxTurns = options.maxTurns ?? 12;
|
|
1158
1272
|
for (let turn = 0; turn <= maxTurns; turn++) {
|
|
1159
|
-
result = await
|
|
1160
|
-
...snapshot ? { snapshot } : { input: options.input },
|
|
1161
|
-
...event ? { event } : {},
|
|
1273
|
+
result = await runToQuiescence(createAgentRuntime(machine, {
|
|
1162
1274
|
...options.actors ? { actors: options.actors } : {},
|
|
1163
1275
|
executors,
|
|
1164
1276
|
onTransition: (next, causedBy) => {
|
|
@@ -1167,6 +1279,9 @@ async function runSeam(machine, options) {
|
|
|
1167
1279
|
liveEvents = events.length;
|
|
1168
1280
|
statePath.push(next.value);
|
|
1169
1281
|
}
|
|
1282
|
+
}), {
|
|
1283
|
+
...snapshot ? { snapshot } : { input: options.input },
|
|
1284
|
+
...event ? { event } : {}
|
|
1170
1285
|
});
|
|
1171
1286
|
if (result.status !== "idle" || turn === maxTurns) break;
|
|
1172
1287
|
const next = options.respond?.({
|
|
@@ -1209,4 +1324,4 @@ async function runSeam(machine, options) {
|
|
|
1209
1324
|
};
|
|
1210
1325
|
}
|
|
1211
1326
|
//#endregion
|
|
1212
|
-
export { AgentLintError, AgentUnknownStateError, assertAgentMachine, canReach,
|
|
1327
|
+
export { AgentLintError, AgentUnknownStateError, assertAgentMachine, canReach, explorePaths, lintAgentMachine, matchesTrajectory, runSeam, simulateAgent };
|
|
@@ -125,7 +125,7 @@ function findNonSerializableContextPaths(context, limit = 5) {
|
|
|
125
125
|
* their prompts or executors hash identically; adding/removing/retargeting a
|
|
126
126
|
* state or transition changes the hash.
|
|
127
127
|
*
|
|
128
|
-
* Used by {@link
|
|
128
|
+
* Used by {@link createAgentRuntime} to stamp settled snapshots with a `version` and to
|
|
129
129
|
* detect a structurally-edited machine on resume. It is a change detector, not
|
|
130
130
|
* a cryptographic digest — collisions are possible but unlikely for real
|
|
131
131
|
* configs. Declare `createMachine({ version })` to pin an explicit version instead.
|
|
@@ -136,7 +136,7 @@ function getMachineStructuralHash(machine) {
|
|
|
136
136
|
/**
|
|
137
137
|
* The version a machine is stamped with everywhere: an explicitly declared
|
|
138
138
|
* `createMachine({ version })` when present, else its
|
|
139
|
-
* {@link getMachineStructuralHash}. Single source of truth so `
|
|
139
|
+
* {@link getMachineStructuralHash}. Single source of truth so `createAgentRuntime`,
|
|
140
140
|
* `provideExecutors`/`traceTransitions` trace envelopes agree.
|
|
141
141
|
* @internal
|
|
142
142
|
*/
|
|
@@ -327,7 +327,7 @@ function validateSchemaSync(schema, value) {
|
|
|
327
327
|
* Registered execution options, keyed on BOTH the machine object and its root
|
|
328
328
|
* `config` object. `config` is shared by reference across `machine.provide(...)`
|
|
329
329
|
* (unlike the machine itself), so the `config` key is what lets a machine
|
|
330
|
-
* rebound by `
|
|
330
|
+
* rebound by `createAgentRuntime` — or by any host `.provide(...)` — still resolve the
|
|
331
331
|
* schemas/actors `setupAgent` registered. Always write through
|
|
332
332
|
* {@link setAgentExecutionOptions} and read through
|
|
333
333
|
* {@link getAgentExecutionOptions}.
|
|
@@ -347,7 +347,7 @@ function setAgentExecutionOptions(machine, options) {
|
|
|
347
347
|
}
|
|
348
348
|
/**
|
|
349
349
|
* Reads the options registered for `machine`, falling back to the root `config`
|
|
350
|
-
* key so a `.provide(...)`-rebound machine (e.g. the one behind a `
|
|
350
|
+
* key so a `.provide(...)`-rebound machine (e.g. the one behind a `runToQuiescence`
|
|
351
351
|
* result snapshot) resolves the same pack.
|
|
352
352
|
*/
|
|
353
353
|
function getAgentExecutionOptions(machine) {
|
|
@@ -357,24 +357,9 @@ function getAgentExecutionOptions(machine) {
|
|
|
357
357
|
const config = machineConfigKey(machine);
|
|
358
358
|
return config ? agentExecutionOptions.get(config) : void 0;
|
|
359
359
|
}
|
|
360
|
-
/**
|
|
361
|
-
* Machine-carried wait-state predicates, keyed on the machine's root `config`
|
|
362
|
-
* object. `config` is shared by reference across `machine.provide(...)` (unlike
|
|
363
|
-
* the machine object itself), so a predicate registered here travels with the
|
|
364
|
-
* machine through `.provide` — which is why it is keyed on `config`, not the
|
|
365
|
-
* machine. Set by `setupAgent({ isIdle })` in `createMachine` and by
|
|
366
|
-
* `setupAgent.fromConfig` (its `isIdle` option or the config's
|
|
367
|
-
* `idleTags`), read by `runAgent` before its structural default.
|
|
368
|
-
*/
|
|
369
|
-
const machineIdlePredicates = /* @__PURE__ */ new WeakMap();
|
|
370
|
-
/** Reads the {@link machineIdlePredicates} predicate carried by `machine` (via its root `config`), if any. */
|
|
371
|
-
function getMachineIdlePredicate(machine) {
|
|
372
|
-
const config = machine.config;
|
|
373
|
-
return config ? machineIdlePredicates.get(config) : void 0;
|
|
374
|
-
}
|
|
375
360
|
const unboundPlaceholderLogics = /* @__PURE__ */ new WeakSet();
|
|
376
361
|
/** Text/decision logics created WITH their own executor (withExecutor or the
|
|
377
|
-
* factory's second arg) — these are runnable as-is, so
|
|
362
|
+
* factory's second arg) — these are runnable as-is, so the runtime's bind check
|
|
378
363
|
* must not reject them as direct-object invoke srcs. */
|
|
379
364
|
const executorBoundLogics = /* @__PURE__ */ new WeakSet();
|
|
380
365
|
function missingActor(src) {
|
|
@@ -408,7 +393,7 @@ const AGENT_FINISH_REASONS = new Set([
|
|
|
408
393
|
/**
|
|
409
394
|
* Reads a settled call's {@link AgentFinishReason} off a RAW executor result's
|
|
410
395
|
* `finishReason` field — the finish-reason sibling of {@link getCallUsage},
|
|
411
|
-
* applied by
|
|
396
|
+
* applied by the runtime before it puts the reason on the `request.end` trace.
|
|
412
397
|
* A provider's `'error'` normalizes to `'other'`; anything else the union does
|
|
413
398
|
* not name (an un-normalized provider string, a missing field) returns
|
|
414
399
|
* `undefined`.
|
|
@@ -430,7 +415,7 @@ const AGENT_USAGE_TOKEN_FIELDS = [
|
|
|
430
415
|
/**
|
|
431
416
|
* Reads a settled call's per-call {@link AgentCallUsage} off a RAW executor
|
|
432
417
|
* result's `usage` field, keeping only finite numbers — the same normalization
|
|
433
|
-
*
|
|
418
|
+
* the runtime applies before it delivers `'@agent.usage'`. Returns `undefined`
|
|
434
419
|
* when the result reports no usage at all. Works for our `{ result, usage }`
|
|
435
420
|
* shape, for a raw Vercel AI SDK result (its `LanguageModelUsage` carries
|
|
436
421
|
* the same flat field names), and for any custom executor that follows the
|
|
@@ -585,8 +570,7 @@ function resolveTextLogicValue(value, args) {
|
|
|
585
570
|
* result under `actors:` and invoke it by name (equivalent to what
|
|
586
571
|
* `setupAgent({ requests })` builds internally for each request entry). Pass
|
|
587
572
|
* `execute` here, or bind it later with `.withExecutor(...)`, a runtime
|
|
588
|
-
* adapter's `machine.provide(...)`, or `
|
|
589
|
-
* `streamText` options.
|
|
573
|
+
* adapter's `machine.provide(...)`, or `createAgentRuntime`'s `executors`.
|
|
590
574
|
*
|
|
591
575
|
* @example
|
|
592
576
|
* ```ts
|
|
@@ -639,7 +623,7 @@ function createTextLogic(config, execute) {
|
|
|
639
623
|
};
|
|
640
624
|
const logic = createAsyncLogic({ run: async ({ input, signal, system, self }, enq) => {
|
|
641
625
|
const resolvedRequest = request(input);
|
|
642
|
-
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
|
|
626
|
+
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 createAgentRuntime/provideExecutors, or execute the XState effect in your host.");
|
|
643
627
|
const result = await execute({
|
|
644
628
|
input,
|
|
645
629
|
request: resolvedRequest,
|
|
@@ -830,4 +814,4 @@ function invalidGeneratorResult(id) {
|
|
|
830
814
|
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.`);
|
|
831
815
|
}
|
|
832
816
|
//#endregion
|
|
833
|
-
export {
|
|
817
|
+
export { getMessageText as A, AgentTruncatedError as B, setAgentExecutionOptions as C, getJsonSchema as D, findNonSerializableContextPaths as E, systemMessage as F, toolMessage as I, userMessage as L, getStatePath as M, isStandardSchema as N, getJsonSchemaSync as O, resolveMachineVersion as P, validateSchemaSync as R, missingActor as S, djb2Hex as T, responseMessagesOf as _, bindRequestExecutor as a, getRegisteredAgentExecutionOptions as b, getAgentOutputMode as c, isTextLogic as d, normalizeGeneratorResult as f, resolveTextLogicValue as g, providerOutputSchema as h, STREAM_TEXT_ACTOR as i, getStateMeta as j, getMachineStructuralHash as k, getCallFinishReason as l, parseProviderOutput as m, DECIDE_ACTOR as n, builtinTextActors as o, parseOutput as p, GENERATE_TEXT_ACTOR as r, createTextLogic as s, AGENT_USAGE_TOKEN_FIELDS as t, getCallUsage as u, executorBoundLogics as v, assistantMessage as w, isUnboundPlaceholder as x, getAgentExecutionOptions as y, AgentError as z };
|