@statelyai/agent 2.0.0-alpha.23 → 2.0.0-alpha.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/ai-sdk.cjs +30 -34
  2. package/dist/ai-sdk.d.cts +27 -31
  3. package/dist/ai-sdk.d.mts +27 -31
  4. package/dist/ai-sdk.mjs +27 -31
  5. package/dist/decision-CM9CYZ6t.cjs +519 -0
  6. package/dist/decision-DzSv1NpD.mjs +453 -0
  7. package/dist/event-log-store-Ba2fsc4E.mjs +576 -0
  8. package/dist/event-log-store-DQmImLYk.cjs +702 -0
  9. package/dist/event-log-store-DvOcs10J.d.cts +338 -0
  10. package/dist/event-log-store-DxyVgnas.d.mts +338 -0
  11. package/dist/index.cjs +429 -4272
  12. package/dist/index.d.cts +104 -1005
  13. package/dist/index.d.mts +104 -1005
  14. package/dist/index.mjs +389 -4202
  15. package/dist/log.cjs +392 -0
  16. package/dist/log.d.cts +32 -0
  17. package/dist/log.d.mts +32 -0
  18. package/dist/log.mjs +371 -0
  19. package/dist/machines.cjs +27 -16
  20. package/dist/machines.d.cts +1 -1
  21. package/dist/machines.d.mts +1 -1
  22. package/dist/machines.mjs +27 -16
  23. package/dist/openai.cjs +16 -15
  24. package/dist/openai.d.cts +9 -9
  25. package/dist/openai.d.mts +9 -9
  26. package/dist/openai.mjs +10 -9
  27. package/dist/otel.d.cts +1 -1
  28. package/dist/otel.d.mts +1 -1
  29. package/dist/{run-agent-Ck7Sa3Tb.d.mts → run-agent-BOfoJg5B.d.cts} +86 -387
  30. package/dist/{run-agent-DWqA8KiI.d.cts → run-agent-CG343kFs.d.mts} +86 -387
  31. package/dist/run-agent-Cb1-LS5P.cjs +1783 -0
  32. package/dist/run-agent-DvDQd4If.mjs +1669 -0
  33. package/dist/{setup-agent-CeO2fARv.mjs → setup-agent-CFW-jPI8.mjs} +9 -20
  34. package/dist/{setup-agent-Dl2VELLi.cjs → setup-agent-CPf7MxnG.cjs} +20 -37
  35. package/dist/testing.cjs +1222 -0
  36. package/dist/testing.d.cts +602 -0
  37. package/dist/testing.d.mts +602 -0
  38. package/dist/testing.mjs +1212 -0
  39. package/dist/{text-logic-DJBK3LbG.d.mts → text-logic-BFEOxyxv.d.cts} +71 -147
  40. package/dist/text-logic-Bl_ZRxDi.mjs +833 -0
  41. package/dist/text-logic-C8xyr6Ag.cjs +1091 -0
  42. package/dist/{text-logic-C7bSnH_B.d.cts → text-logic-hZSiRUo1.d.mts} +71 -147
  43. package/dist/{types-CjHk5OdH.d.cts → types-B1Qs2q3q.d.cts} +2 -1
  44. package/dist/{types-DWA6X9R-.d.mts → types-tA3SdczG.d.mts} +2 -1
  45. package/dist/usage-BdEjFuIi.mjs +5 -0
  46. package/dist/usage-efOQPdf_.cjs +10 -0
  47. package/dist/utils-C1p37X_c.d.mts +209 -0
  48. package/dist/utils-RalRn9sk.d.cts +209 -0
  49. package/package.json +24 -4
  50. package/readme.md +2 -2
  51. package/skills/generate-machine/SKILL.md +4 -6
  52. package/dist/decision-BBZ2jMO1.mjs +0 -1490
  53. package/dist/decision-Cv6SfNWl.cjs +0 -1856
@@ -0,0 +1,209 @@
1
+ import { C as UserMessage, S as ToolResultPart, _ as StandardSchemaV1, b as ToolCallPart, f as FilePart, l as AssistantMessage, n as AgentMessage, p as ImagePart, u as ChosenEvent, v as SystemMessage, x as ToolMessage, y as TextPart } from "./types-B1Qs2q3q.cjs";
2
+ import { M as AgentDecisionRequest, c as AgentRequestExecutorInfo, f as AgentTextRequest, t as AgentCallUsage, u as AgentRequestExecutors } from "./text-logic-BFEOxyxv.cjs";
3
+ import { AnyMachineSnapshot, AnyStateMachine, StateValue } from "xstate";
4
+
5
+ //#region src/scripted-executors.d.ts
6
+ /**
7
+ * A scripted `decide` answer: either the {@link ChosenEvent} itself (an object
8
+ * with a string `type`), or the executor result `{ event, reason?, usage? }`
9
+ * when the entry also reports a `reason` or token `usage`.
10
+ */
11
+ type ScriptedDecisionValue = ChosenEvent | {
12
+ event: ChosenEvent;
13
+ reason?: string;
14
+ usage?: AgentCallUsage;
15
+ };
16
+ /**
17
+ * One scripted `decide` answer, or a function of the
18
+ * {@link AgentDecisionRequest} returning one (for choices that depend on the
19
+ * prompt, the candidate `events`, or the prior failed `attempts`).
20
+ */
21
+ type ScriptedDecisionEntry = ScriptedDecisionValue | ((request: AgentDecisionRequest) => ScriptedDecisionValue | PromiseLike<ScriptedDecisionValue>);
22
+ /**
23
+ * One scripted text answer: the request's output value (a string, or the
24
+ * object a structured request declares), or a function of the
25
+ * {@link AgentTextRequest} returning one.
26
+ *
27
+ * An entry is taken as the executor result (instead of the value itself)
28
+ * only when its OWN keys are a `result` plus, optionally, `messages`/`usage`/
29
+ * `raw` — that is how an entry reports token `usage`. Anything else, including
30
+ * an object that merely happens to have a `result` key alongside its own data
31
+ * (`{ result: 'draft', confidence: 0.9 }`), is the output value. For a
32
+ * structured request whose declared output is exactly `{ result }` (or
33
+ * `{ result, usage }`), wrap it once more: `{ result: { result: '…' } }`.
34
+ */
35
+ type ScriptedTextEntry = ((request: AgentTextRequest, info?: AgentRequestExecutorInfo) => unknown) | string | number | boolean | null | object;
36
+ /**
37
+ * A request-name keyed script. The key is the request's resolved `name` — the
38
+ * authored name, else the invoke `id`. `"*"` is the fallback route.
39
+ */
40
+ type ScriptedByName<TEntry> = Record<string, TEntry | TEntry[]>;
41
+ /** One scripted stream call: chunks, or a function producing chunks. */
42
+ type ScriptedStreamEntry = string | readonly string[] | ((request: AgentTextRequest, info?: AgentRequestExecutorInfo) => string | readonly string[] | PromiseLike<string | readonly string[]>);
43
+ /** A call observed by {@link createScriptedExecutors}. */
44
+ interface ScriptedExecutorCall {
45
+ kind: "generateText" | "streamText" | "decide";
46
+ name: string;
47
+ input: unknown;
48
+ request: AgentTextRequest | AgentDecisionRequest;
49
+ }
50
+ /**
51
+ * The script {@link createScriptedExecutors} plays back. Every channel is
52
+ * keyed by request name and consumed in order; the last entry for a name
53
+ * repeats forever.
54
+ */
55
+ interface ScriptedExecutorsScript {
56
+ /** Answers for `decide` requests, keyed by request name. */
57
+ decisions?: ScriptedByName<ScriptedDecisionEntry>;
58
+ /** Answers for generate requests, keyed by request name. */
59
+ text?: ScriptedByName<ScriptedTextEntry>;
60
+ /** Stream chunks keyed by request name. */
61
+ stream?: Record<string, ScriptedStreamEntry>;
62
+ /** Default usage attached when an entry does not provide its own. */
63
+ usage?: AgentCallUsage;
64
+ }
65
+ /** What {@link createScriptedExecutors} returns: the full executor set plus the calls it observed. */
66
+ type ScriptedExecutors = Required<AgentRequestExecutors> & {
67
+ calls: ScriptedExecutorCall[];
68
+ };
69
+ /**
70
+ * Executors that replay a script instead of calling a model. Every slot is
71
+ * provided, so any machine binds.
72
+ *
73
+ * Answers are keyed by the request's resolved name — its authored `name`, else
74
+ * the invoke `id` — and `"*"` serves anything unmatched.
75
+ * Entries for a name are consumed in order and the last one repeats forever, so
76
+ * a looping machine needs no extra option. A request the script has no route
77
+ * for throws an ordinary `Error` from inside the executor, which reaches the
78
+ * machine as an actor error.
79
+ *
80
+ * Queues are copied on creation, so the caller's arrays are never mutated and
81
+ * each call builds a fresh, independent playthrough. Entries may be plain
82
+ * values or functions of the request.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const result = await runAgent(moderationMachine, {
87
+ * input: { comment: 'honestly this update is terrible', trust: 20 },
88
+ * executors: createScriptedExecutors({
89
+ * decisions: { moderate: [{ type: 'FLAG', reason: 'Borderline tone.' }] },
90
+ * }),
91
+ * });
92
+ * ```
93
+ *
94
+ * @example Dynamic entries
95
+ * ```ts
96
+ * createScriptedExecutors({
97
+ * text: { draft: (request) => `Draft about ${request.prompt}` },
98
+ * decisions: { route: (request) => ({ type: request.events[0]!.type }) },
99
+ * });
100
+ * ```
101
+ */
102
+ declare function createScriptedExecutors(script?: ScriptedExecutorsScript): ScriptedExecutors;
103
+ //#endregion
104
+ //#region src/utils.d.ts
105
+ /** Returns the human-readable text carried by a message, ignoring non-text parts. */
106
+ declare function getMessageText(message: AgentMessage): string;
107
+ /**
108
+ * A stable, dependency-free structural fingerprint of a machine — a short hex
109
+ * `djb2` hash over its **structural** config only: state ids/nesting, transition
110
+ * event types and targets, invoke `src`s, `initial`, and any other serializable
111
+ * config fields. Function values (context/output builders, prompts, inline
112
+ * guards/actions) are excluded entirely, so two machines that differ only in
113
+ * their prompts or executors hash identically; adding/removing/retargeting a
114
+ * state or transition changes the hash.
115
+ *
116
+ * Used by {@link runAgent} to stamp settled snapshots with a `version` and to
117
+ * detect a structurally-edited machine on resume. It is a change detector, not
118
+ * a cryptographic digest — collisions are possible but unlikely for real
119
+ * configs. Declare `createMachine({ version })` to pin an explicit version instead.
120
+ */
121
+ declare function getMachineStructuralHash(machine: AnyStateMachine): string;
122
+ /** Builds a {@link UserMessage} from a string or multimodal content parts. */
123
+ declare function userMessage(content: string | Array<TextPart | ImagePart | FilePart>): UserMessage;
124
+ /** Builds an {@link AssistantMessage} from a string or content parts (text, files, tool calls/results). */
125
+ declare function assistantMessage(content: string | Array<TextPart | FilePart | ToolCallPart | ToolResultPart>): AssistantMessage;
126
+ /** Builds a {@link SystemMessage}. */
127
+ declare function systemMessage(content: string): SystemMessage;
128
+ /** Builds a {@link ToolMessage} from one or more tool-result parts. */
129
+ declare function toolMessage(content: Array<ToolResultPart>): ToolMessage;
130
+ type MetaOfSnapshot<TSnapshot extends {
131
+ getMeta(): Record<string, unknown>;
132
+ }> = NonNullable<ReturnType<TSnapshot["getMeta"]>[keyof ReturnType<TSnapshot["getMeta"]>]>;
133
+ /**
134
+ * Returns the merged `meta` of a snapshot's active state(s) — the typed
135
+ * replacement for the `Object.values(snapshot.getMeta())[0]` dance.
136
+ *
137
+ * `snapshot.getMeta()` is keyed by state id; a leaf machine has one active
138
+ * state, but parallel/nested machines can have several. This shallow-merges
139
+ * every active state's meta into one object and returns `{}` when no active
140
+ * state declares meta.
141
+ *
142
+ * Merge order is fixed and does not depend on XState's internal node order:
143
+ * entries are sorted by structural depth (the state node's distance from the
144
+ * root, regardless of custom `id` strings), then by state id lexicographically,
145
+ * and merged in that order. So a deeper state's key wins over an ancestor's,
146
+ * and between equal-depth parallel siblings the later state id alphabetically
147
+ * wins.
148
+ *
149
+ * The return type is recovered from the snapshot's own `getMeta()` type, so a
150
+ * schema-typed machine (`setupAgent({ meta })`) yields the meta schema's
151
+ * output type. Pass an explicit `TMeta` to override when the snapshot is
152
+ * untyped (e.g. `AnyMachineSnapshot`).
153
+ *
154
+ * @example HITL: read the current state's interaction protocol off an idle
155
+ * snapshot to render for a human.
156
+ * ```ts
157
+ * const { interaction } = getStateMeta(result.snapshot);
158
+ * ```
159
+ */
160
+ declare function getStateMeta<TSnapshot extends {
161
+ getMeta(): Record<string, unknown>;
162
+ } = AnyMachineSnapshot, TMeta = MetaOfSnapshot<TSnapshot>>(snapshot: TSnapshot): Partial<TMeta>;
163
+ /**
164
+ * Renders a snapshot's state value as one deterministic string, so a log line,
165
+ * a progress field, or a test assertion reads the same on every run.
166
+ *
167
+ * - An atomic state is its own name: `'writing'`.
168
+ * - A nested state joins with dots: `{ review: { editing: 'draft' } }` →
169
+ * `'review.editing.draft'`.
170
+ * - A parallel state brackets its regions, sorted by region name, so key order
171
+ * never leaks into the string: `{ p: { left: 'x', right: { a: 'b' } } }` →
172
+ * `'p:{left.x,right.a.b}'`.
173
+ *
174
+ * Prefer this over `String(snapshot.value)`, which renders every non-atomic
175
+ * value as `[object Object]`.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * onTransition: (snapshot) => console.log(getStatePath(snapshot));
180
+ * ```
181
+ */
182
+ declare function getStatePath(snapshot: {
183
+ value: StateValue;
184
+ status?: string;
185
+ } | StateValue): string;
186
+ /**
187
+ * Structural guard for a {@link StandardSchemaV1}: `true` when `value` carries
188
+ * the `~standard` marker. Used to tell an already-schema'd tool `inputSchema`
189
+ * (a Zod/Valibot/… schema) apart from an SDK-specific schema wrapper that core
190
+ * can't read directly — see the `ai-sdk` tool pass-through.
191
+ */
192
+ declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
193
+ /**
194
+ * Pulls the JSON Schema off a {@link StandardSchemaV1} via its optional
195
+ * `~standard.jsonSchema.input()` extension (implemented by e.g. Zod v4's
196
+ * `z.toJSONSchema`), awaiting it when the producer is async. Returns
197
+ * `undefined` when the schema doesn't expose the extension. Use this to build
198
+ * a provider request's `response_format`/tool `parameters` from a schema.
199
+ */
200
+ declare function getJsonSchema(schema?: StandardSchemaV1): Promise<Record<string, unknown> | undefined>;
201
+ /**
202
+ * Synchronous variant of {@link getJsonSchema}, for call sites that can't
203
+ * await (building tool/event descriptors inline). An async JSON Schema
204
+ * producer is treated as absent (returns `undefined`) — in practice Zod's
205
+ * `z.toJSONSchema` resolves synchronously.
206
+ */
207
+ declare function getJsonSchemaSync(schema?: StandardSchemaV1): Record<string, unknown> | undefined;
208
+ //#endregion
209
+ export { ScriptedExecutorsScript as _, getMachineStructuralHash as a, getStatePath as c, toolMessage as d, userMessage as f, ScriptedExecutors as g, ScriptedDecisionValue as h, getJsonSchemaSync as i, isStandardSchema as l, ScriptedDecisionEntry as m, assistantMessage as n, getMessageText as o, ScriptedByName as p, getJsonSchema as r, getStateMeta as s, MetaOfSnapshot as t, systemMessage as u, ScriptedTextEntry as v, createScriptedExecutors as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statelyai/agent",
3
- "version": "2.0.0-alpha.23",
3
+ "version": "2.0.0-alpha.25",
4
4
  "description": "Make invalid agent actions impossible. Agent logic as state machines: deterministic, inspectable, resumable, runs anywhere.",
5
5
  "keywords": [
6
6
  "agent",
@@ -48,6 +48,16 @@
48
48
  "default": "./dist/ai-sdk.cjs"
49
49
  }
50
50
  },
51
+ "./log": {
52
+ "import": {
53
+ "types": "./dist/log.d.mts",
54
+ "default": "./dist/log.mjs"
55
+ },
56
+ "require": {
57
+ "types": "./dist/log.d.cts",
58
+ "default": "./dist/log.cjs"
59
+ }
60
+ },
51
61
  "./machines": {
52
62
  "import": {
53
63
  "types": "./dist/machines.d.mts",
@@ -78,6 +88,16 @@
78
88
  "default": "./dist/otel.cjs"
79
89
  }
80
90
  },
91
+ "./testing": {
92
+ "import": {
93
+ "types": "./dist/testing.d.mts",
94
+ "default": "./dist/testing.mjs"
95
+ },
96
+ "require": {
97
+ "types": "./dist/testing.d.cts",
98
+ "default": "./dist/testing.cjs"
99
+ }
100
+ },
81
101
  "./validate": {
82
102
  "import": {
83
103
  "types": "./dist/validate.d.mts",
@@ -123,7 +143,7 @@
123
143
  "typescript": "^5.9.3",
124
144
  "valibot": "^1.4.2",
125
145
  "vitest": "^4.1.11",
126
- "xstate": "6.0.0-alpha.48",
146
+ "xstate": "6.0.0-alpha.57",
127
147
  "zod": "^4.4.3"
128
148
  },
129
149
  "peerDependencies": {
@@ -131,7 +151,7 @@
131
151
  "ai": "^7.0.0",
132
152
  "ajv": "^8.20.0",
133
153
  "openai": ">=5.0.0 <8",
134
- "xstate": ">=6.0.0-alpha.46 <6.0.0"
154
+ "xstate": ">=6.0.0-alpha.57 <6.0.0"
135
155
  },
136
156
  "peerDependenciesMeta": {
137
157
  "@opentelemetry/api": {
@@ -168,7 +188,7 @@
168
188
  "check:dts": "tsc -p fixtures/dts-consumer/tsconfig.json",
169
189
  "test": "vitest",
170
190
  "test:live": "LIVE_TESTS=1 vitest --run",
171
- "test:ci": "vitest --run && pnpm run test:cloudflare && pnpm run build && pnpm --filter @statelyai/agent-demo test",
191
+ "test:ci": "vitest --run && pnpm run build && pnpm run test:cloudflare && pnpm --filter @statelyai/agent-demo test",
172
192
  "changeset": "changeset",
173
193
  "release": "changeset publish",
174
194
  "version": "changeset version"
package/readme.md CHANGED
@@ -139,9 +139,9 @@ const liveResult = await runAgent(refundMachine, {
139
139
  });
140
140
  ```
141
141
 
142
- For a machine with several requests, `createScriptedExecutors` holds ordered answers keyed by request name. See [Evals](docs/evals.md#scripted-executors).
142
+ For a machine with several requests, `createScriptedExecutors` from `@statelyai/agent/testing` holds ordered answers keyed by request name. See [Evals](docs/evals.md#scripted-executors).
143
143
 
144
- Passing a `defineModels` registry to `setupAgent({ models })` types the machine's model refs and supplies the AI SDK executor by default; explicit `executors` override it. Core does not import the AI SDK. See [Hosts and executors](docs/hosts.md).
144
+ Passing the same `models` map to `setupAgent({ models })` types the machine's model refs. Executors are always explicit, and core does not import the AI SDK. See [Hosts and executors](docs/hosts.md).
145
145
 
146
146
  ## Architecture
147
147
 
@@ -27,7 +27,7 @@ Rules the runtime enforces. Each one maps to a build error or a lint diagnostic:
27
27
  - A model call is either a named `requests` entry invoked with `src: "<requestName>"`, or an inline invoke with `src: "agent.decide"` plus an `allowedEvents` list.
28
28
  - A state invoking `agent.decide` MUST handle every allowed event in its `on`.
29
29
  - An `agent.decide` invoke has no `onDone` (a decision produces no output). Use `onError` for the retries-exhausted path.
30
- - A request invoke reads its result via `onDone.assign` from `"{{ event.output.<field> }}"`.
30
+ - A request invoke resolves with `{ result, messages }`, so `onDone.assign` reads its result from `"{{ event.output.result.<field> }}"` (`"{{ event.output.result }}"` for a plain-text request). A non-request `actors` invoke reads `"{{ event.output.<field> }}"` directly.
31
31
  - Every path must reach a `"type": "final"` state, and each final state needs an `output` when `schemas.output` is declared.
32
32
  - Do not invent guard or action names. Only `"{{ }}"` guards, `assign`, and `emit` exist unless the host tells you which named guards/actions it implements.
33
33
  - Model refs are strings (`"openai/gpt-5.4-mini"`); the host resolves them.
@@ -113,7 +113,7 @@ Reference config — decision, text request, idle human step, one final state:
113
113
  "input": { "ticket": "{{ context.ticket }}" },
114
114
  "onDone": {
115
115
  "target": "awaitingApproval",
116
- "assign": { "reply": "{{ event.output.reply }}" }
116
+ "assign": { "reply": "{{ event.output.result.reply }}" }
117
117
  }
118
118
  }
119
119
  },
@@ -191,8 +191,7 @@ Lowering is itself a gate: it throws on an unresolved named guard/action and on
191
191
  ## 5. Lint
192
192
 
193
193
  ```ts
194
- import { lintAgentMachine } from "@statelyai/agent";
195
-
194
+ import { lintAgentMachine } from "@statelyai/agent/testing";
196
195
  const diagnostics = lintAgentMachine(machine);
197
196
  lintAgentMachine(machine, { throw: true }); // throws AgentLintError on error-severity findings
198
197
  ```
@@ -206,8 +205,7 @@ The one that bites most often is `decide-without-events`: an `allowedEvents` lis
206
205
  Lint is structural. A dry run proves a path actually settles. No API key needed.
207
206
 
208
207
  ```ts
209
- import { simulateAgent } from "@statelyai/agent";
210
-
208
+ import { simulateAgent } from "@statelyai/agent/testing";
211
209
  const dryRun = await simulateAgent(machine, {
212
210
  input: { ticket: "" },
213
211
  script: {