@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.
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 +1088 -0
  36. package/dist/testing.d.cts +617 -0
  37. package/dist/testing.d.mts +617 -0
  38. package/dist/testing.mjs +1079 -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-C2DPqomn.d.mts +109 -0
  48. package/dist/utils-ceStNCOh.d.cts +109 -0
  49. package/package.json +26 -5
  50. package/readme.md +5 -3
  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,109 @@
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, v as SystemMessage, x as ToolMessage, y as TextPart } from "./types-B1Qs2q3q.cjs";
2
+ import { AnyMachineSnapshot, AnyStateMachine, StateValue } from "xstate";
3
+
4
+ //#region src/utils.d.ts
5
+ /** Returns the human-readable text carried by a message, ignoring non-text parts. */
6
+ declare function getMessageText(message: AgentMessage): string;
7
+ /**
8
+ * A stable, dependency-free structural fingerprint of a machine — a short hex
9
+ * `djb2` hash over its **structural** config only: state ids/nesting, transition
10
+ * event types and targets, invoke `src`s, `initial`, and any other serializable
11
+ * config fields. Function values (context/output builders, prompts, inline
12
+ * guards/actions) are excluded entirely, so two machines that differ only in
13
+ * their prompts or executors hash identically; adding/removing/retargeting a
14
+ * state or transition changes the hash.
15
+ *
16
+ * Used by {@link runAgent} to stamp settled snapshots with a `version` and to
17
+ * detect a structurally-edited machine on resume. It is a change detector, not
18
+ * a cryptographic digest — collisions are possible but unlikely for real
19
+ * configs. Declare `createMachine({ version })` to pin an explicit version instead.
20
+ */
21
+ declare function getMachineStructuralHash(machine: AnyStateMachine): string;
22
+ /** Builds a {@link UserMessage} from a string or multimodal content parts. */
23
+ declare function userMessage(content: string | Array<TextPart | ImagePart | FilePart>): UserMessage;
24
+ /** Builds an {@link AssistantMessage} from a string or content parts (text, files, tool calls/results). */
25
+ declare function assistantMessage(content: string | Array<TextPart | FilePart | ToolCallPart | ToolResultPart>): AssistantMessage;
26
+ /** Builds a {@link SystemMessage}. */
27
+ declare function systemMessage(content: string): SystemMessage;
28
+ /** Builds a {@link ToolMessage} from one or more tool-result parts. */
29
+ declare function toolMessage(content: Array<ToolResultPart>): ToolMessage;
30
+ type MetaOfSnapshot<TSnapshot extends {
31
+ getMeta(): Record<string, unknown>;
32
+ }> = NonNullable<ReturnType<TSnapshot["getMeta"]>[keyof ReturnType<TSnapshot["getMeta"]>]>;
33
+ /**
34
+ * Returns the merged `meta` of a snapshot's active state(s) — the typed
35
+ * replacement for the `Object.values(snapshot.getMeta())[0]` dance.
36
+ *
37
+ * `snapshot.getMeta()` is keyed by state id; a leaf machine has one active
38
+ * state, but parallel/nested machines can have several. This shallow-merges
39
+ * every active state's meta into one object and returns `{}` when no active
40
+ * state declares meta.
41
+ *
42
+ * Merge order is fixed and does not depend on XState's internal node order:
43
+ * entries are sorted by structural depth (the state node's distance from the
44
+ * root, regardless of custom `id` strings), then by state id lexicographically,
45
+ * and merged in that order. So a deeper state's key wins over an ancestor's,
46
+ * and between equal-depth parallel siblings the later state id alphabetically
47
+ * wins.
48
+ *
49
+ * The return type is recovered from the snapshot's own `getMeta()` type, so a
50
+ * schema-typed machine (`setupAgent({ meta })`) yields the meta schema's
51
+ * output type. Pass an explicit `TMeta` to override when the snapshot is
52
+ * untyped (e.g. `AnyMachineSnapshot`).
53
+ *
54
+ * @example HITL: read the current state's interaction protocol off an idle
55
+ * snapshot to render for a human.
56
+ * ```ts
57
+ * const { interaction } = getStateMeta(result.snapshot);
58
+ * ```
59
+ */
60
+ declare function getStateMeta<TSnapshot extends {
61
+ getMeta(): Record<string, unknown>;
62
+ } = AnyMachineSnapshot, TMeta = MetaOfSnapshot<TSnapshot>>(snapshot: TSnapshot): Partial<TMeta>;
63
+ /**
64
+ * Renders a snapshot's state value as one deterministic string, so a log line,
65
+ * a progress field, or a test assertion reads the same on every run.
66
+ *
67
+ * - An atomic state is its own name: `'writing'`.
68
+ * - A nested state joins with dots: `{ review: { editing: 'draft' } }` →
69
+ * `'review.editing.draft'`.
70
+ * - A parallel state brackets its regions, sorted by region name, so key order
71
+ * never leaks into the string: `{ p: { left: 'x', right: { a: 'b' } } }` →
72
+ * `'p:{left.x,right.a.b}'`.
73
+ *
74
+ * Prefer this over `String(snapshot.value)`, which renders every non-atomic
75
+ * value as `[object Object]`.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * onTransition: (snapshot) => console.log(getStatePath(snapshot));
80
+ * ```
81
+ */
82
+ declare function getStatePath(snapshot: {
83
+ value: StateValue;
84
+ status?: string;
85
+ } | StateValue): string;
86
+ /**
87
+ * Structural guard for a {@link StandardSchemaV1}: `true` when `value` carries
88
+ * the `~standard` marker. Used to tell an already-schema'd tool `inputSchema`
89
+ * (a Zod/Valibot/… schema) apart from an SDK-specific schema wrapper that core
90
+ * can't read directly — see the `ai-sdk` tool pass-through.
91
+ */
92
+ declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
93
+ /**
94
+ * Pulls the JSON Schema off a {@link StandardSchemaV1} via its optional
95
+ * `~standard.jsonSchema.input()` extension (implemented by e.g. Zod v4's
96
+ * `z.toJSONSchema`), awaiting it when the producer is async. Returns
97
+ * `undefined` when the schema doesn't expose the extension. Use this to build
98
+ * a provider request's `response_format`/tool `parameters` from a schema.
99
+ */
100
+ declare function getJsonSchema(schema?: StandardSchemaV1): Promise<Record<string, unknown> | undefined>;
101
+ /**
102
+ * Synchronous variant of {@link getJsonSchema}, for call sites that can't
103
+ * await (building tool/event descriptors inline). An async JSON Schema
104
+ * producer is treated as absent (returns `undefined`) — in practice Zod's
105
+ * `z.toJSONSchema` resolves synchronously.
106
+ */
107
+ declare function getJsonSchemaSync(schema?: StandardSchemaV1): Record<string, unknown> | undefined;
108
+ //#endregion
109
+ export { getMachineStructuralHash as a, getStatePath as c, toolMessage as d, userMessage as f, getJsonSchemaSync as i, isStandardSchema as l, assistantMessage as n, getMessageText as o, getJsonSchema as r, getStateMeta as s, MetaOfSnapshot as t, systemMessage as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statelyai/agent",
3
- "version": "2.0.0-alpha.24",
3
+ "version": "2.0.0-alpha.26",
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",
@@ -95,6 +115,7 @@
95
115
  },
96
116
  "devDependencies": {
97
117
  "@ai-sdk/openai": "^4.0.0",
118
+ "@ai-sdk/typesafe-ai": "3.0.6",
98
119
  "@anthropic-ai/sdk": "^0.120.0",
99
120
  "@changesets/changelog-github": "^0.5.2",
100
121
  "@changesets/cli": "^2.31.1",
@@ -108,7 +129,7 @@
108
129
  "@opentelemetry/sdk-trace-base": "^2.10.0",
109
130
  "@types/node": "^20.19.43",
110
131
  "@types/react": "^19.2.18",
111
- "ai": "^7.0.0",
132
+ "ai": "^7.0.113",
112
133
  "ajv": "^8.20.0",
113
134
  "braintrust": "^3.28.0",
114
135
  "dotenv": "^16.6.1",
@@ -123,7 +144,7 @@
123
144
  "typescript": "^5.9.3",
124
145
  "valibot": "^1.4.2",
125
146
  "vitest": "^4.1.11",
126
- "xstate": "6.0.0-alpha.48",
147
+ "xstate": "6.0.0-alpha.57",
127
148
  "zod": "^4.4.3"
128
149
  },
129
150
  "peerDependencies": {
@@ -131,7 +152,7 @@
131
152
  "ai": "^7.0.0",
132
153
  "ajv": "^8.20.0",
133
154
  "openai": ">=5.0.0 <8",
134
- "xstate": ">=6.0.0-alpha.46 <6.0.0"
155
+ "xstate": ">=6.0.0-alpha.57 <6.0.0"
135
156
  },
136
157
  "peerDependenciesMeta": {
137
158
  "@opentelemetry/api": {
@@ -168,7 +189,7 @@
168
189
  "check:dts": "tsc -p fixtures/dts-consumer/tsconfig.json",
169
190
  "test": "vitest",
170
191
  "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",
192
+ "test:ci": "vitest --run && pnpm run build && pnpm run test:cloudflare && pnpm --filter @statelyai/agent-demo test",
172
193
  "changeset": "changeset",
173
194
  "release": "changeset publish",
174
195
  "version": "changeset version"
package/readme.md CHANGED
@@ -40,6 +40,8 @@ For the optional raw OpenAI SDK executor (`@statelyai/agent/openai`):
40
40
  pnpm add openai
41
41
  ```
42
42
 
43
+ Classification, grading, relevance, and yes/no checks are judgments: call the AI SDK's `experimental_evaluate` with `@ai-sdk/typesafe-ai` (`pnpm add ai @ai-sdk/typesafe-ai`) from an ordinary actor; see [Judgments with the AI SDK](docs/judgments.md).
44
+
43
45
  Requirements:
44
46
 
45
47
  - Node 22.18 or newer, and XState v6 alpha.46 or newer.
@@ -139,9 +141,9 @@ const liveResult = await runAgent(refundMachine, {
139
141
  });
140
142
  ```
141
143
 
142
- For a machine with several requests, `createScriptedExecutors` holds ordered answers keyed by request name. See [Evals](docs/evals.md#scripted-executors).
144
+ For a machine with several requests, the executor routes on `request.name`, or `MockLanguageModelV3` from `ai/test` stands in for the provider behind `createAiSdkExecutors`. See [Evals](docs/evals.md#testing-without-a-provider).
143
145
 
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).
146
+ 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
147
 
146
148
  ## Architecture
147
149
 
@@ -155,7 +157,7 @@ flowchart LR
155
157
  R -->|event or output| M
156
158
  ```
157
159
 
158
- The machine never talks to a model directly, so swapping `createAiSdkExecutors` for `createScriptedExecutors` (or your own functions) changes nothing about the agent.
160
+ The machine never talks to a model directly, so swapping `createAiSdkExecutors` for your own functions changes nothing about the agent.
159
161
 
160
162
  The example above has one model decision and two final outcomes. Real machines add approval states, retries, parallel work, child agents, and long-running waits without changing how control flow is represented.
161
163
 
@@ -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: {