@statelyai/agent 2.0.0-alpha.26 → 2.0.0-alpha.28
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 +276 -27
- package/dist/testing.d.cts +17 -15
- package/dist/testing.d.mts +17 -15
- package/dist/testing.mjs +260 -12
- 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-C2DPqomn.d.mts → utils-_q7F-AZu.d.mts} +2 -2
- package/dist/{utils-ceStNCOh.d.cts → utils-o4YRdHJX.d.cts} +2 -2
- package/dist/validate.cjs +0 -8
- package/dist/validate.mjs +0 -8
- package/package.json +3 -3
- package/readme.md +26 -18
- package/schemas/agent-workflow.json +0 -8
- package/skills/generate-machine/SKILL.md +6 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as createDecideActor } from "./decision-
|
|
1
|
+
import { C as setAgentExecutionOptions, R as validateSchemaSync, S as missingActor, n as DECIDE_ACTOR, o as builtinTextActors, s as createTextLogic, y as getAgentExecutionOptions } from "./text-logic-CIqHAmBi.mjs";
|
|
2
|
+
import { n as createDecideActor } from "./decision-RQ0iv0oZ.mjs";
|
|
3
3
|
import { t as AGENT_USAGE_EVENT_TYPE } from "./usage-BdEjFuIi.mjs";
|
|
4
4
|
import { createMachineFromConfig, setup } from "xstate";
|
|
5
5
|
//#region src/workflow-config.ts
|
|
@@ -273,22 +273,6 @@ function translateWorkflowConfig(config, translation) {
|
|
|
273
273
|
if (json.output === void 0 && finalOutputs.length === 1) json.output = finalOutputs[0];
|
|
274
274
|
return json;
|
|
275
275
|
}
|
|
276
|
-
function collectDeclaredStateTags(states, tags = /* @__PURE__ */ new Set()) {
|
|
277
|
-
for (const state of Object.values(states ?? {})) {
|
|
278
|
-
for (const tag of state.tags ?? []) tags.add(tag);
|
|
279
|
-
collectDeclaredStateTags(state.states, tags);
|
|
280
|
-
}
|
|
281
|
-
return tags;
|
|
282
|
-
}
|
|
283
|
-
function resolveIdlePredicate(config, options) {
|
|
284
|
-
if (options.isIdle) return options.isIdle;
|
|
285
|
-
const idleTags = config.idleTags ?? [];
|
|
286
|
-
if (!idleTags.length) return;
|
|
287
|
-
const declaredTags = collectDeclaredStateTags(config.states);
|
|
288
|
-
const unknown = idleTags.filter((tag) => !declaredTags.has(tag));
|
|
289
|
-
if (unknown.length) throw new Error(`setupAgent.fromConfig: 'idleTags' lists ${unknown.map((tag) => `'${tag}'`).join(", ")}, which no state declares in its 'tags'. Tag the waiting state(s) — e.g. "tags": ["${unknown[0]}"] — or remove the entry.`);
|
|
290
|
-
return (snapshot) => idleTags.some((tag) => snapshot.hasTag(tag));
|
|
291
|
-
}
|
|
292
276
|
function setupAgentFromConfig(config, options) {
|
|
293
277
|
if (!options || typeof options.compileSchema !== "function") throw new Error("setupAgent.fromConfig(...) requires a 'compileSchema' option: { compileSchema: (jsonSchema, name) => StandardSchemaV1 }. Bring your own JSON Schema engine (Ajv, @cfworker/json-schema, a compiled-Zod-from-JSON-Schema pipeline, ...). Core intentionally ships no JSON Schema engine.");
|
|
294
278
|
const { compileSchema } = options;
|
|
@@ -310,16 +294,14 @@ function setupAgentFromConfig(config, options) {
|
|
|
310
294
|
event: args.event
|
|
311
295
|
}))])),
|
|
312
296
|
...options.actions ? { actions: options.actions } : {},
|
|
313
|
-
actors
|
|
297
|
+
actors,
|
|
314
298
|
evaluators: { [AGENT_EXPRESSION_LANG]: createWorkflowConfigEvaluator(schemas.context) }
|
|
315
|
-
})
|
|
299
|
+
});
|
|
316
300
|
setAgentExecutionOptions(machine, {
|
|
317
301
|
schemas,
|
|
318
302
|
actors,
|
|
319
303
|
models: {}
|
|
320
304
|
});
|
|
321
|
-
const isIdle = resolveIdlePredicate(config, options);
|
|
322
|
-
if (isIdle && machine.config) machineIdlePredicates.set(machine.config, isIdle);
|
|
323
305
|
return {
|
|
324
306
|
machine,
|
|
325
307
|
schemas
|
|
@@ -388,7 +370,7 @@ const agentUsageEventSchema = { "~standard": {
|
|
|
388
370
|
* Adds the reserved `'@agent.usage'` schema to an authored event map. A
|
|
389
371
|
* user-declared entry under that key is rejected: the `@agent.*` namespace
|
|
390
372
|
* belongs to the library (same rule as the reserved `agent.*` actor keys), and
|
|
391
|
-
* a custom payload schema would silently disagree with what
|
|
373
|
+
* a custom payload schema would silently disagree with what the runtime
|
|
392
374
|
* delivers.
|
|
393
375
|
*/
|
|
394
376
|
function withAgentUsageEventSchema(events) {
|
|
@@ -423,7 +405,7 @@ function createAgentSchemas(schemas) {
|
|
|
423
405
|
/**
|
|
424
406
|
* Schema-first `setup(...)` for agent machines — the standard entry point
|
|
425
407
|
* for authoring a machine (the blueprint) that this library then runs (via
|
|
426
|
-
* {@link
|
|
408
|
+
* {@link createAgentRuntime}) against host-supplied model/decision
|
|
427
409
|
* executors. Context, events, machine input, machine output, and
|
|
428
410
|
* state/transition meta are all standard schemas — no `{} as Type` casts —
|
|
429
411
|
* and are retained on `result.schemas` for runtime validation. Also
|
|
@@ -496,10 +478,6 @@ function setupAgent(config) {
|
|
|
496
478
|
assertStateSchemaKeysExist(config.states, machineConfig?.states);
|
|
497
479
|
const machine = createBaseMachine(withRootOutputFromSingleFinal(machineConfig));
|
|
498
480
|
setAgentExecutionOptions(machine, machineOptions);
|
|
499
|
-
if (config.isIdle) {
|
|
500
|
-
const rootConfig = machine.config;
|
|
501
|
-
if (rootConfig) machineIdlePredicates.set(rootConfig, config.isIdle);
|
|
502
|
-
}
|
|
503
481
|
return machine;
|
|
504
482
|
},
|
|
505
483
|
schemas,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_text_logic = require("./text-logic-
|
|
2
|
-
const require_decision = require("./decision-
|
|
1
|
+
const require_text_logic = require("./text-logic-f5F5sCdc.cjs");
|
|
2
|
+
const require_decision = require("./decision-CmM8-HHz.cjs");
|
|
3
3
|
const require_usage = require("./usage-efOQPdf_.cjs");
|
|
4
4
|
require("./validate.cjs");
|
|
5
5
|
let xstate = require("xstate");
|
|
@@ -274,22 +274,6 @@ function translateWorkflowConfig(config, translation) {
|
|
|
274
274
|
if (json.output === void 0 && finalOutputs.length === 1) json.output = finalOutputs[0];
|
|
275
275
|
return json;
|
|
276
276
|
}
|
|
277
|
-
function collectDeclaredStateTags(states, tags = /* @__PURE__ */ new Set()) {
|
|
278
|
-
for (const state of Object.values(states ?? {})) {
|
|
279
|
-
for (const tag of state.tags ?? []) tags.add(tag);
|
|
280
|
-
collectDeclaredStateTags(state.states, tags);
|
|
281
|
-
}
|
|
282
|
-
return tags;
|
|
283
|
-
}
|
|
284
|
-
function resolveIdlePredicate(config, options) {
|
|
285
|
-
if (options.isIdle) return options.isIdle;
|
|
286
|
-
const idleTags = config.idleTags ?? [];
|
|
287
|
-
if (!idleTags.length) return;
|
|
288
|
-
const declaredTags = collectDeclaredStateTags(config.states);
|
|
289
|
-
const unknown = idleTags.filter((tag) => !declaredTags.has(tag));
|
|
290
|
-
if (unknown.length) throw new Error(`setupAgent.fromConfig: 'idleTags' lists ${unknown.map((tag) => `'${tag}'`).join(", ")}, which no state declares in its 'tags'. Tag the waiting state(s) — e.g. "tags": ["${unknown[0]}"] — or remove the entry.`);
|
|
291
|
-
return (snapshot) => idleTags.some((tag) => snapshot.hasTag(tag));
|
|
292
|
-
}
|
|
293
277
|
function setupAgentFromConfig(config, options) {
|
|
294
278
|
if (!options || typeof options.compileSchema !== "function") throw new Error("setupAgent.fromConfig(...) requires a 'compileSchema' option: { compileSchema: (jsonSchema, name) => StandardSchemaV1 }. Bring your own JSON Schema engine (Ajv, @cfworker/json-schema, a compiled-Zod-from-JSON-Schema pipeline, ...). Core intentionally ships no JSON Schema engine.");
|
|
295
279
|
const { compileSchema } = options;
|
|
@@ -311,16 +295,14 @@ function setupAgentFromConfig(config, options) {
|
|
|
311
295
|
event: args.event
|
|
312
296
|
}))])),
|
|
313
297
|
...options.actions ? { actions: options.actions } : {},
|
|
314
|
-
actors
|
|
298
|
+
actors,
|
|
315
299
|
evaluators: { [AGENT_EXPRESSION_LANG]: createWorkflowConfigEvaluator(schemas.context) }
|
|
316
|
-
})
|
|
300
|
+
});
|
|
317
301
|
require_text_logic.setAgentExecutionOptions(machine, {
|
|
318
302
|
schemas,
|
|
319
303
|
actors,
|
|
320
304
|
models: {}
|
|
321
305
|
});
|
|
322
|
-
const isIdle = resolveIdlePredicate(config, options);
|
|
323
|
-
if (isIdle && machine.config) require_text_logic.machineIdlePredicates.set(machine.config, isIdle);
|
|
324
306
|
return {
|
|
325
307
|
machine,
|
|
326
308
|
schemas
|
|
@@ -389,7 +371,7 @@ const agentUsageEventSchema = { "~standard": {
|
|
|
389
371
|
* Adds the reserved `'@agent.usage'` schema to an authored event map. A
|
|
390
372
|
* user-declared entry under that key is rejected: the `@agent.*` namespace
|
|
391
373
|
* belongs to the library (same rule as the reserved `agent.*` actor keys), and
|
|
392
|
-
* a custom payload schema would silently disagree with what
|
|
374
|
+
* a custom payload schema would silently disagree with what the runtime
|
|
393
375
|
* delivers.
|
|
394
376
|
*/
|
|
395
377
|
function withAgentUsageEventSchema(events) {
|
|
@@ -424,7 +406,7 @@ function createAgentSchemas(schemas) {
|
|
|
424
406
|
/**
|
|
425
407
|
* Schema-first `setup(...)` for agent machines — the standard entry point
|
|
426
408
|
* for authoring a machine (the blueprint) that this library then runs (via
|
|
427
|
-
* {@link
|
|
409
|
+
* {@link createAgentRuntime}) against host-supplied model/decision
|
|
428
410
|
* executors. Context, events, machine input, machine output, and
|
|
429
411
|
* state/transition meta are all standard schemas — no `{} as Type` casts —
|
|
430
412
|
* and are retained on `result.schemas` for runtime validation. Also
|
|
@@ -497,10 +479,6 @@ function setupAgent(config) {
|
|
|
497
479
|
assertStateSchemaKeysExist(config.states, machineConfig?.states);
|
|
498
480
|
const machine = createBaseMachine(withRootOutputFromSingleFinal(machineConfig));
|
|
499
481
|
require_text_logic.setAgentExecutionOptions(machine, machineOptions);
|
|
500
|
-
if (config.isIdle) {
|
|
501
|
-
const rootConfig = machine.config;
|
|
502
|
-
if (rootConfig) require_text_logic.machineIdlePredicates.set(rootConfig, config.isIdle);
|
|
503
|
-
}
|
|
504
482
|
return machine;
|
|
505
483
|
},
|
|
506
484
|
schemas,
|
package/dist/testing.cjs
CHANGED
|
@@ -1,7 +1,253 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_text_logic = require("./text-logic-
|
|
3
|
-
const require_decision = require("./decision-
|
|
4
|
-
const require_run_agent = require("./run-agent-
|
|
2
|
+
const require_text_logic = require("./text-logic-f5F5sCdc.cjs");
|
|
3
|
+
const require_decision = require("./decision-CmM8-HHz.cjs");
|
|
4
|
+
const require_run_agent = require("./run-agent-Dmf8GV2d.cjs");
|
|
5
|
+
require("./validate.cjs");
|
|
6
|
+
let xstate = require("xstate");
|
|
7
|
+
//#region src/steps.ts
|
|
8
|
+
/**
|
|
9
|
+
* Internal: the pure `(state, event) => (nextState, requests)` view of an
|
|
10
|
+
* agent machine, used by model-free simulation (`simulateAgent`) and tests.
|
|
11
|
+
* An {@link AgentStep} is a snapshot plus the model requests the machine is
|
|
12
|
+
* now waiting on; nothing here executes anything. Hosts use
|
|
13
|
+
* `createAgentRuntime`.
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** @internal Normalizes current and legacy XState invoke effect shapes. */
|
|
17
|
+
function getInvokeEffectMetadata(action) {
|
|
18
|
+
if (action.type === "@xstate.spawn") return action;
|
|
19
|
+
if (action.type === "xstate.spawnChild") {
|
|
20
|
+
const params = action.params;
|
|
21
|
+
return params ? {
|
|
22
|
+
...params,
|
|
23
|
+
logic: action.logic
|
|
24
|
+
} : void 0;
|
|
25
|
+
}
|
|
26
|
+
if (action.type === "@xstate.start" && typeof action.src === "string") return action;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The invokes a snapshot is still waiting on, lowered to the same spawn-effect
|
|
30
|
+
* shape {@link getAgentRequests} reads. Derived from the snapshot's live
|
|
31
|
+
* children rather than the last transition's actions, so a state that invokes
|
|
32
|
+
* several actors at once (an `invoke: [...]` array, or parallel regions each
|
|
33
|
+
* invoking) keeps reporting the siblings after the first one settles —
|
|
34
|
+
* including when the settled invoke's `onDone` is targetless and starts
|
|
35
|
+
* nothing new.
|
|
36
|
+
*
|
|
37
|
+
* Entries are ordered by invoke id, so a walker that resolves one invoke per
|
|
38
|
+
* step is deterministic.
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
function getPendingInvokes(snapshot) {
|
|
43
|
+
const children = snapshot.children ?? {};
|
|
44
|
+
return Object.keys(children).sort().flatMap((id) => {
|
|
45
|
+
const child = children[id];
|
|
46
|
+
const childSnapshot = child?.getSnapshot?.();
|
|
47
|
+
if (!child || childSnapshot?.status !== "active") return [];
|
|
48
|
+
return [{
|
|
49
|
+
type: "@xstate.spawn",
|
|
50
|
+
id,
|
|
51
|
+
src: child.src,
|
|
52
|
+
input: childSnapshot.input,
|
|
53
|
+
logic: child.logic
|
|
54
|
+
}];
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Scans a set of executable actions (as returned by xstate's `transition`/
|
|
59
|
+
* `initialTransition`) for spawned `TextLogic`/`DecisionLogic` invokes and
|
|
60
|
+
* lowers each into an {@link AgentStepRequest}. Pass `options.machine` to
|
|
61
|
+
* pre-fill `schemas`/`actors` from the machine's registered `setupAgent`
|
|
62
|
+
* options instead of passing them by hand.
|
|
63
|
+
* `options.snapshot` is required to resolve a decision's candidate events
|
|
64
|
+
* (intersecting declared `allowedEvents` with what's currently legal) — omit
|
|
65
|
+
* it and decision requests report an empty `events` list.
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
function getAgentRequests(actions, { machine, ...options } = {}) {
|
|
70
|
+
if (machine) options = {
|
|
71
|
+
...require_text_logic.getRegisteredAgentExecutionOptions(machine),
|
|
72
|
+
...options
|
|
73
|
+
};
|
|
74
|
+
return actions.flatMap((action) => {
|
|
75
|
+
const params = getInvokeEffectMetadata(action);
|
|
76
|
+
if (!params || typeof params.src !== "string") return [];
|
|
77
|
+
if (typeof params.id !== "string" || params.id.length === 0) throw new Error(`Agent invoke '${params.src}' must define a durable string id.`);
|
|
78
|
+
const registeredLogic = require_text_logic.isTextLogic(params.logic) || require_decision.isDecisionLogic(params.logic) ? params.logic : options.actors?.[params.src];
|
|
79
|
+
if (require_decision.isDecisionLogic(registeredLogic)) {
|
|
80
|
+
const decisionRequest = registeredLogic.request(params.input);
|
|
81
|
+
const allowedEventTypes = registeredLogic.allowedEventTypes?.(params.input);
|
|
82
|
+
const events = options.snapshot ? require_decision.getAcceptedEvents(options.snapshot, {
|
|
83
|
+
events: options.events,
|
|
84
|
+
schemas: options.schemas,
|
|
85
|
+
eventTypes: allowedEventTypes
|
|
86
|
+
}) : [];
|
|
87
|
+
return [{
|
|
88
|
+
...decisionRequest,
|
|
89
|
+
id: params.id,
|
|
90
|
+
events
|
|
91
|
+
}];
|
|
92
|
+
}
|
|
93
|
+
const textLogic = require_text_logic.isTextLogic(registeredLogic) ? registeredLogic : void 0;
|
|
94
|
+
const input = textLogic ? textLogic.request(params.input) : void 0;
|
|
95
|
+
if (!input) return [];
|
|
96
|
+
return [{
|
|
97
|
+
kind: "text",
|
|
98
|
+
id: params.id,
|
|
99
|
+
src: params.src,
|
|
100
|
+
...textLogic ? { mode: textLogic.mode } : {},
|
|
101
|
+
input,
|
|
102
|
+
tools: input.tools ?? {},
|
|
103
|
+
events: []
|
|
104
|
+
}];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Builds the canonical `xstate.done.actor` event xstate's `transition()`
|
|
109
|
+
* expects to resolve a spawned invoke — including the child session identity
|
|
110
|
+
* that rejects a stale completion from an earlier incarnation.
|
|
111
|
+
*
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
function doneEvent(snapshot, request, output) {
|
|
115
|
+
const id = typeof request === "string" ? request : request.id;
|
|
116
|
+
const child = snapshot.children[id];
|
|
117
|
+
return {
|
|
118
|
+
type: "xstate.done.actor",
|
|
119
|
+
actorId: id,
|
|
120
|
+
...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
|
|
121
|
+
output
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The `xstate.error.actor` counterpart of {@link doneEvent} — the event a
|
|
126
|
+
* failed invoke delivers, which `onError` transitions observe. Carries the
|
|
127
|
+
* same child session identity, so xstate does not discard it as stale.
|
|
128
|
+
*
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
function errorEvent(snapshot, request, error) {
|
|
132
|
+
const id = typeof request === "string" ? request : request.id;
|
|
133
|
+
const child = snapshot.children[id];
|
|
134
|
+
return {
|
|
135
|
+
type: "xstate.error.actor",
|
|
136
|
+
actorId: id,
|
|
137
|
+
...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
|
|
138
|
+
error
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Applies a request's `output` as a done event (or, with `outcome: 'error'`,
|
|
143
|
+
* its `output` as the invoke's error) via `transition(...)`, returning the raw
|
|
144
|
+
* `[snapshot, actions]` tuple. Lower-level than {@link resolveAgentStep} /
|
|
145
|
+
* {@link rejectAgentStep} — those helpers wrap this and also run
|
|
146
|
+
* {@link getAgentRequests} to produce the next {@link AgentStep}.
|
|
147
|
+
*
|
|
148
|
+
* @internal
|
|
149
|
+
*/
|
|
150
|
+
function transitionResult(logic, snapshot, request, output, outcome = "output") {
|
|
151
|
+
const event = outcome === "error" ? errorEvent(snapshot, request, output) : doneEvent(snapshot, request, output);
|
|
152
|
+
const result = (0, xstate.transition)(logic, snapshot, event);
|
|
153
|
+
applyFinalStateOutput(logic, result[0], event);
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Starts a machine and returns its first {@link AgentStep} — the step-path
|
|
158
|
+
* equivalent of `initialTransition` plus request discovery. Begins the
|
|
159
|
+
* durable/per-model-call-checkpoint loop: resolve each `step.requests` entry
|
|
160
|
+
* (via {@link executeAgentRequest} for `kind: 'text'`, or
|
|
161
|
+
* {@link resolveDecision} for `kind: 'decision'`), then advance with
|
|
162
|
+
* {@link resolveAgentStep} or {@link transitionAgentStep}.
|
|
163
|
+
*/
|
|
164
|
+
function initialAgentStep(machine, input, options) {
|
|
165
|
+
const [snapshot, actions] = (0, xstate.initialTransition)(machine, input);
|
|
166
|
+
return createAgentStep(machine, snapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Applies an externally-sent event (e.g. a decision's chosen event, or a
|
|
170
|
+
* human's reply) and returns the next {@link AgentStep}. Accepts **either**
|
|
171
|
+
* a raw snapshot **or** a prior `AgentStep` as the second argument —
|
|
172
|
+
* `.snapshot` is unwrapped automatically, so callers can thread the whole
|
|
173
|
+
* step object through without manually plucking the snapshot out.
|
|
174
|
+
*/
|
|
175
|
+
function transitionAgentStep(machine, snapshotOrStep, event, options) {
|
|
176
|
+
const [nextSnapshot, actions] = (0, xstate.transition)(machine, isAgentStep(snapshotOrStep) ? snapshotOrStep.snapshot : snapshotOrStep, event);
|
|
177
|
+
return createAgentStep(machine, nextSnapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Applies a resolved text request's output (a `kind: 'text'`
|
|
181
|
+
* {@link AgentRequest} — not a decision) as a done event and returns the
|
|
182
|
+
* next {@link AgentStep}. For decisions, resolve with `resolveDecision`
|
|
183
|
+
* (which returns a {@link ChosenEvent}) and apply it with
|
|
184
|
+
* {@link transitionAgentStep} instead — a decision has no output value of
|
|
185
|
+
* its own to feed here.
|
|
186
|
+
*/
|
|
187
|
+
function resolveAgentStep(machine, step, request, output, options) {
|
|
188
|
+
const [snapshot, actions] = transitionResult(machine, step.snapshot, request, output);
|
|
189
|
+
return createAgentStep(machine, snapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Rejects a pending invoke — the failure counterpart of
|
|
193
|
+
* {@link resolveAgentStep}. Delivers `error` as the invoke's
|
|
194
|
+
* `xstate.error.actor` event, so the machine takes that invoke's `onError`
|
|
195
|
+
* transition and returns the next {@link AgentStep}. With no `onError` in
|
|
196
|
+
* scope the returned step's snapshot has `status: 'error'`, exactly as a live
|
|
197
|
+
* run's failed invoke does.
|
|
198
|
+
*
|
|
199
|
+
* A durable host replays a failed call with this: the log says the call
|
|
200
|
+
* failed, so the replay rejects the invoke instead of resolving it. Scripted
|
|
201
|
+
* playthroughs use it too — see `simulateAgent`'s `script.errors`.
|
|
202
|
+
*
|
|
203
|
+
* `error` can be any value. An `Error` instance is the usual one, but a plain
|
|
204
|
+
* object (`{ code: 'truncated' }`) reaches the `onError` transition as
|
|
205
|
+
* `event.error` untouched, so a machine that branches on a failure code is
|
|
206
|
+
* testable without constructing a provider's error class.
|
|
207
|
+
*/
|
|
208
|
+
function rejectAgentStep(machine, step, request, error, options) {
|
|
209
|
+
const [snapshot, actions] = transitionResult(machine, step.snapshot, request, error, "error");
|
|
210
|
+
return createAgentStep(machine, snapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
|
|
211
|
+
}
|
|
212
|
+
function createAgentStep(machine, snapshot, actions, options) {
|
|
213
|
+
applyFinalStateOutput(machine, snapshot);
|
|
214
|
+
return {
|
|
215
|
+
snapshot,
|
|
216
|
+
actions,
|
|
217
|
+
requests: getAgentRequests(getPendingInvokes(snapshot), {
|
|
218
|
+
...options,
|
|
219
|
+
snapshot
|
|
220
|
+
}),
|
|
221
|
+
done: snapshot.status === "done"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function resolveStateValueConfig(config, value) {
|
|
225
|
+
if (typeof value === "string") return config.states?.[value];
|
|
226
|
+
if (!value || typeof value !== "object") return;
|
|
227
|
+
for (const [key, childValue] of Object.entries(value)) {
|
|
228
|
+
const childConfig = config.states?.[key];
|
|
229
|
+
if (!childConfig) continue;
|
|
230
|
+
if (childConfig.type === "final") return childConfig;
|
|
231
|
+
const nested = resolveStateValueConfig(childConfig, childValue);
|
|
232
|
+
if (nested) return nested;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function applyFinalStateOutput(logic, snapshot, event) {
|
|
236
|
+
const machineSnapshot = snapshot;
|
|
237
|
+
if (machineSnapshot.status !== "done" || machineSnapshot.output !== void 0 || !("config" in logic)) return;
|
|
238
|
+
const config = logic.config;
|
|
239
|
+
if (!config) return;
|
|
240
|
+
const output = resolveStateValueConfig(config, machineSnapshot.value)?.output;
|
|
241
|
+
if (output === void 0) return;
|
|
242
|
+
machineSnapshot.output = typeof output === "function" ? output({
|
|
243
|
+
context: machineSnapshot.context,
|
|
244
|
+
event
|
|
245
|
+
}) : output;
|
|
246
|
+
}
|
|
247
|
+
function isAgentStep(value) {
|
|
248
|
+
return !!value && typeof value === "object" && "snapshot" in value && "actions" in value && "requests" in value;
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
5
251
|
//#region src/verify.ts
|
|
6
252
|
const DECIDE_SRC = "agent.decide";
|
|
7
253
|
function normalizeInvokes(invoke) {
|
|
@@ -78,7 +324,7 @@ function checkDirectObjectSrc(ctx) {
|
|
|
78
324
|
code: "direct-object-src",
|
|
79
325
|
severity: "warning",
|
|
80
326
|
path: node.path,
|
|
81
|
-
message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by
|
|
327
|
+
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.`
|
|
82
328
|
});
|
|
83
329
|
}
|
|
84
330
|
return out;
|
|
@@ -192,7 +438,7 @@ function assertAgentMachine(machine, options = {}) {
|
|
|
192
438
|
}
|
|
193
439
|
function pendingInvokes(step) {
|
|
194
440
|
const out = [];
|
|
195
|
-
for (const metadata of
|
|
441
|
+
for (const metadata of getPendingInvokes(step.snapshot)) if (typeof metadata.src === "string") out.push({
|
|
196
442
|
id: metadata.id,
|
|
197
443
|
src: metadata.src
|
|
198
444
|
});
|
|
@@ -252,7 +498,7 @@ async function simulateAgent(machine, options) {
|
|
|
252
498
|
errors: mapValues(options.script.errors ?? {}, (arr) => [...arr]),
|
|
253
499
|
events: [...options.script.events ?? []]
|
|
254
500
|
};
|
|
255
|
-
let step =
|
|
501
|
+
let step = initialAgentStep(machine, options.input);
|
|
256
502
|
const trail = [{ state: step.snapshot.value }];
|
|
257
503
|
for (let i = 0; i < maxSteps; i++) {
|
|
258
504
|
if (step.done) return {
|
|
@@ -290,7 +536,7 @@ async function simulateAgent(machine, options) {
|
|
|
290
536
|
}
|
|
291
537
|
const taken = takeFromQueue(script.text, request.src);
|
|
292
538
|
if (!taken.found) throw scriptDryError("text", request.src, request.id);
|
|
293
|
-
step =
|
|
539
|
+
step = resolveAgentStep(machine, step, request, asTextResult(taken.value));
|
|
294
540
|
trail.push({
|
|
295
541
|
state: step.snapshot.value,
|
|
296
542
|
resolvedRequest: {
|
|
@@ -315,7 +561,7 @@ async function simulateAgent(machine, options) {
|
|
|
315
561
|
}
|
|
316
562
|
const taken = takeFromQueue(script.invokes, invoke.src);
|
|
317
563
|
if (!taken.found) throw scriptDryError("invoke", invoke.src, invoke.id);
|
|
318
|
-
step =
|
|
564
|
+
step = resolveAgentStep(machine, step, invoke.id, taken.value);
|
|
319
565
|
trail.push({
|
|
320
566
|
state: step.snapshot.value,
|
|
321
567
|
resolvedRequest: {
|
|
@@ -331,7 +577,7 @@ async function simulateAgent(machine, options) {
|
|
|
331
577
|
if (external.length > 0) {
|
|
332
578
|
const event = external.shift();
|
|
333
579
|
if (!step.snapshot.can(event)) throw new Error(`simulateAgent: scripted external event '${event.type}' cannot be taken in state ${JSON.stringify(step.snapshot.value)} (no handler, or its guard rejected it). A live run's send would be silently dropped here; fix the script's \`events\` queue.`);
|
|
334
|
-
step =
|
|
580
|
+
step = transitionAgentStep(machine, step, event);
|
|
335
581
|
trail.push({
|
|
336
582
|
state: step.snapshot.value,
|
|
337
583
|
appliedEvent: event,
|
|
@@ -356,7 +602,7 @@ function queueKey(channel, src, id) {
|
|
|
356
602
|
}
|
|
357
603
|
function rejectScriptedRequest(machine, step, request, error, entry) {
|
|
358
604
|
const id = typeof request === "string" ? request : request.id;
|
|
359
|
-
const next =
|
|
605
|
+
const next = rejectAgentStep(machine, step, request, error);
|
|
360
606
|
if (next.snapshot.status === "error") throw unhandledScriptedError(error, entry.kind, entry.src, id);
|
|
361
607
|
entry.trail.push({
|
|
362
608
|
state: next.snapshot.value,
|
|
@@ -394,7 +640,7 @@ async function applyScriptedDecision(machine, step, request, decisionSrc, invoke
|
|
|
394
640
|
});
|
|
395
641
|
} catch (error) {
|
|
396
642
|
if (!(error instanceof require_decision.AgentDecisionExhaustedError)) throw error;
|
|
397
|
-
const next =
|
|
643
|
+
const next = rejectAgentStep(machine, step, request.id, error);
|
|
398
644
|
if (next.snapshot.status === "error") throw error;
|
|
399
645
|
trail.push({
|
|
400
646
|
state: next.snapshot.value,
|
|
@@ -402,7 +648,7 @@ async function applyScriptedDecision(machine, step, request, decisionSrc, invoke
|
|
|
402
648
|
});
|
|
403
649
|
return next;
|
|
404
650
|
}
|
|
405
|
-
const next =
|
|
651
|
+
const next = transitionAgentStep(machine, step, chosen);
|
|
406
652
|
const rejected = dequeued.slice(0, -1);
|
|
407
653
|
trail.push({
|
|
408
654
|
state: next.snapshot.value,
|
|
@@ -412,7 +658,7 @@ async function applyScriptedDecision(machine, step, request, decisionSrc, invoke
|
|
|
412
658
|
return next;
|
|
413
659
|
}
|
|
414
660
|
function findInvokeMetadata(step, id) {
|
|
415
|
-
for (const metadata of
|
|
661
|
+
for (const metadata of getPendingInvokes(step.snapshot)) {
|
|
416
662
|
if (metadata.id !== id) continue;
|
|
417
663
|
return {
|
|
418
664
|
...typeof metadata.src === "string" ? { src: metadata.src } : {},
|
|
@@ -472,7 +718,7 @@ async function explore(machine, options, stopWhen) {
|
|
|
472
718
|
reachedValues.push(snapshot.value);
|
|
473
719
|
}
|
|
474
720
|
};
|
|
475
|
-
const initial =
|
|
721
|
+
const initial = initialAgentStep(machine, options.input);
|
|
476
722
|
recordState(initial.snapshot);
|
|
477
723
|
if (stopWhen?.(initial.snapshot)) witness = [];
|
|
478
724
|
const advance = (step) => {
|
|
@@ -493,7 +739,7 @@ async function explore(machine, options, stopWhen) {
|
|
|
493
739
|
step: current,
|
|
494
740
|
blockedSrc: request.src
|
|
495
741
|
};
|
|
496
|
-
current =
|
|
742
|
+
current = resolveAgentStep(machine, current, request, asTextResult(textScript[request.src]));
|
|
497
743
|
recordState(current.snapshot);
|
|
498
744
|
continue;
|
|
499
745
|
}
|
|
@@ -520,7 +766,7 @@ async function explore(machine, options, stopWhen) {
|
|
|
520
766
|
step: current,
|
|
521
767
|
blockedSrc: invoke.src
|
|
522
768
|
};
|
|
523
|
-
current =
|
|
769
|
+
current = resolveAgentStep(machine, current, invoke.id, invokeOutputs[invoke.src]);
|
|
524
770
|
recordState(current.snapshot);
|
|
525
771
|
continue;
|
|
526
772
|
}
|
|
@@ -551,12 +797,12 @@ async function explore(machine, options, stopWhen) {
|
|
|
551
797
|
unexplored.push(`max-depth: stopped at path [${path.map((e) => e.type).join(", ")}]`);
|
|
552
798
|
return;
|
|
553
799
|
}
|
|
554
|
-
const failed =
|
|
800
|
+
const failed = rejectAgentStep(machine, settled, fork.id, errorScript[fork.src]);
|
|
555
801
|
recordState(failed.snapshot);
|
|
556
802
|
await visit(failed, [...path, { type: `xstate.error.actor.${fork.id}` }], depth + 1);
|
|
557
803
|
if (!fork.isDecision) {
|
|
558
804
|
if (fork.hasOutput) {
|
|
559
|
-
const resolved =
|
|
805
|
+
const resolved = resolveAgentStep(machine, settled, fork.id, fork.output);
|
|
560
806
|
recordState(resolved.snapshot);
|
|
561
807
|
await visit(resolved, path, depth + 1);
|
|
562
808
|
}
|
|
@@ -625,7 +871,7 @@ async function explore(machine, options, stopWhen) {
|
|
|
625
871
|
prunedByGuard++;
|
|
626
872
|
continue;
|
|
627
873
|
}
|
|
628
|
-
const next =
|
|
874
|
+
const next = transitionAgentStep(machine, settled, event);
|
|
629
875
|
recordState(next.snapshot);
|
|
630
876
|
await visit(next, [...path, event], depth + 1);
|
|
631
877
|
}
|
|
@@ -812,11 +1058,13 @@ function matcherFor(expected) {
|
|
|
812
1058
|
* @example Trajectory assertions in a test
|
|
813
1059
|
* ```ts
|
|
814
1060
|
* const statePath: unknown[] = [];
|
|
815
|
-
* const result = await
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
*
|
|
1061
|
+
* const result = await runToQuiescence(
|
|
1062
|
+
* createAgentRuntime(machine, {
|
|
1063
|
+
* executors,
|
|
1064
|
+
* onTransition: (snapshot) => statePath.push(snapshot.value),
|
|
1065
|
+
* }),
|
|
1066
|
+
* { input },
|
|
1067
|
+
* );
|
|
820
1068
|
*
|
|
821
1069
|
* const path = matchesTrajectory(statePath, ['prompting', 'drafting', 'sent']);
|
|
822
1070
|
* expect(path.matched, JSON.stringify(path.firstMiss)).toBe(true);
|
|
@@ -1024,9 +1272,7 @@ async function runSeam(machine, options) {
|
|
|
1024
1272
|
let result;
|
|
1025
1273
|
const maxTurns = options.maxTurns ?? 12;
|
|
1026
1274
|
for (let turn = 0; turn <= maxTurns; turn++) {
|
|
1027
|
-
result = await require_run_agent.
|
|
1028
|
-
...snapshot ? { snapshot } : { input: options.input },
|
|
1029
|
-
...event ? { event } : {},
|
|
1275
|
+
result = await require_run_agent.runToQuiescence(require_run_agent.createAgentRuntime(machine, {
|
|
1030
1276
|
...options.actors ? { actors: options.actors } : {},
|
|
1031
1277
|
executors,
|
|
1032
1278
|
onTransition: (next, causedBy) => {
|
|
@@ -1035,6 +1281,9 @@ async function runSeam(machine, options) {
|
|
|
1035
1281
|
liveEvents = events.length;
|
|
1036
1282
|
statePath.push(next.value);
|
|
1037
1283
|
}
|
|
1284
|
+
}), {
|
|
1285
|
+
...snapshot ? { snapshot } : { input: options.input },
|
|
1286
|
+
...event ? { event } : {}
|
|
1038
1287
|
});
|
|
1039
1288
|
if (result.status !== "idle" || turn === maxTurns) break;
|
|
1040
1289
|
const next = options.respond?.({
|
package/dist/testing.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { u as ChosenEvent } from "./types-
|
|
2
|
-
import { Z as AgentError, c as AgentRequestExecutorInfo, f as AgentTextRequest, s as AgentRequestExecutor, t as AgentCallUsage, u as AgentRequestExecutors } from "./text-logic-
|
|
3
|
-
import { d as
|
|
4
|
-
import { t as MetaOfSnapshot } from "./utils-
|
|
1
|
+
import { u as ChosenEvent } from "./types-hR5f5YXa.cjs";
|
|
2
|
+
import { Z as AgentError, c as AgentRequestExecutorInfo, f as AgentTextRequest, s as AgentRequestExecutor, t as AgentCallUsage, u as AgentRequestExecutors } from "./text-logic-IjfYy9DP.cjs";
|
|
3
|
+
import { c as AgentRunResult, d as AgentRuntimeOptions, o as AgentRunInit } from "./run-agent-ByO-Bxyd.cjs";
|
|
4
|
+
import { t as MetaOfSnapshot } from "./utils-o4YRdHJX.cjs";
|
|
5
5
|
import { AnyMachineSnapshot, AnyStateMachine, EventFromLogic, InputFrom, SnapshotFrom, StateValue } from "xstate";
|
|
6
6
|
|
|
7
7
|
//#region src/verify.d.ts
|
|
@@ -415,11 +415,13 @@ interface TrajectoryMatch {
|
|
|
415
415
|
* @example Trajectory assertions in a test
|
|
416
416
|
* ```ts
|
|
417
417
|
* const statePath: unknown[] = [];
|
|
418
|
-
* const result = await
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
418
|
+
* const result = await runToQuiescence(
|
|
419
|
+
* createAgentRuntime(machine, {
|
|
420
|
+
* executors,
|
|
421
|
+
* onTransition: (snapshot) => statePath.push(snapshot.value),
|
|
422
|
+
* }),
|
|
423
|
+
* { input },
|
|
424
|
+
* );
|
|
423
425
|
*
|
|
424
426
|
* const path = matchesTrajectory(statePath, ['prompting', 'drafting', 'sent']);
|
|
425
427
|
* expect(path.matched, JSON.stringify(path.firstMiss)).toBe(true);
|
|
@@ -480,7 +482,7 @@ interface SeamTurn<TMachine extends AnyStateMachine> {
|
|
|
480
482
|
/** 0-based index of this pause within the run. */
|
|
481
483
|
turn: number;
|
|
482
484
|
/** The `idle` result that produced the pause. */
|
|
483
|
-
result:
|
|
485
|
+
result: AgentRunResult<TMachine>;
|
|
484
486
|
}
|
|
485
487
|
/**
|
|
486
488
|
* One text call in a {@link runSeam} run's ledger, in call order — the receipt
|
|
@@ -511,7 +513,7 @@ interface SeamSlice {
|
|
|
511
513
|
}
|
|
512
514
|
/** Options for {@link runSeam}. */
|
|
513
515
|
interface RunSeamOptions<TMachine extends AnyStateMachine> {
|
|
514
|
-
/** Machine input, passed straight through to `
|
|
516
|
+
/** Machine input, passed straight through to `runToQuiescence`. */
|
|
515
517
|
input?: InputFrom<TMachine>;
|
|
516
518
|
/**
|
|
517
519
|
* The call plan: scripted answers per key, consumed in order. A key is a
|
|
@@ -554,15 +556,15 @@ interface RunSeamOptions<TMachine extends AnyStateMachine> {
|
|
|
554
556
|
* Text slots are always owned by the routing.
|
|
555
557
|
*/
|
|
556
558
|
executors?: Partial<AgentRequestExecutors>;
|
|
557
|
-
/** Passed through to `
|
|
558
|
-
actors?:
|
|
559
|
+
/** Passed through to `createAgentRuntime`: actor implementations merged onto the machine. */
|
|
560
|
+
actors?: (AgentRuntimeOptions<TMachine> & AgentRunInit<TMachine>)["actors"];
|
|
559
561
|
}
|
|
560
562
|
/** What {@link runSeam} returns: the seam's own answer, plus the run it caused. */
|
|
561
563
|
interface RunSeamResult<TMachine extends AnyStateMachine> {
|
|
562
564
|
/**
|
|
563
|
-
* The final `
|
|
565
|
+
* The final `runToQuiescence` result. `usage` accounts for the last leg.
|
|
564
566
|
*/
|
|
565
|
-
result:
|
|
567
|
+
result: AgentRunResult<TMachine>;
|
|
566
568
|
/** What the seam call returned, or `undefined` when the run never reached it. */
|
|
567
569
|
seamOutput: unknown;
|
|
568
570
|
/**
|