@statelyai/agent 2.0.0-alpha.11 → 2.0.0-alpha.12
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 -5
- package/dist/ai-sdk.d.cts +7 -4
- package/dist/ai-sdk.d.mts +7 -4
- package/dist/ai-sdk.mjs +1 -2
- package/dist/{events-JiVPYrct.mjs → decision-BezSD_YC.mjs} +327 -20
- package/dist/{events-CRQj3VtP.cjs → decision-dWGhBh0P.cjs} +401 -28
- package/dist/errors-BQRk9eiZ.d.cts +19 -0
- package/dist/errors-C9rxnWbX.d.mts +19 -0
- package/dist/errors-CeSXQx0v.mjs +23 -0
- package/dist/errors-DUBBzRLP.cjs +28 -0
- package/dist/event-log-store-CNT_7F0V.cjs +452 -0
- package/dist/event-log-store-CriMgX1D.d.mts +144 -0
- package/dist/event-log-store-D7pWtIhb.mjs +411 -0
- package/dist/event-log-store-Ruq18mGp.d.cts +144 -0
- package/dist/index.cjs +1050 -705
- package/dist/index.d.cts +538 -565
- package/dist/index.d.mts +538 -565
- package/dist/index.mjs +950 -644
- package/dist/machines.cjs +752 -0
- package/dist/machines.d.cts +372 -0
- package/dist/machines.d.mts +372 -0
- package/dist/machines.mjs +741 -0
- package/dist/otel.cjs +268 -0
- package/dist/otel.d.cts +67 -0
- package/dist/otel.d.mts +67 -0
- package/dist/otel.mjs +267 -0
- package/dist/run-agent-B1nBa7aV.d.mts +1111 -0
- package/dist/run-agent-qkg0evvW.d.cts +1111 -0
- package/dist/setup-agent-DAZZSjDS.mjs +1711 -0
- package/dist/setup-agent-DP95MFrI.cjs +1836 -0
- package/dist/sqlite.cjs +135 -0
- package/dist/sqlite.d.cts +57 -0
- package/dist/sqlite.d.mts +57 -0
- package/dist/sqlite.mjs +133 -0
- package/dist/{text-logic-CaKqgX4Y.d.mts → text-logic-CorGzucG.d.cts} +103 -59
- package/dist/{text-logic-Ckhr2kKC.d.cts → text-logic-DjR5PUVz.d.mts} +103 -59
- package/dist/{types-qm00QF91.d.mts → types-QbEfCVny.d.cts} +1 -1
- package/dist/{types-C9QiMjre.d.cts → types-_FXoFBGO.d.mts} +1 -1
- package/package.json +44 -39
- package/readme.md +28 -7
- package/schemas/agent-workflow.json +40 -21
- package/skills/generate-machine/SKILL.md +267 -0
- package/dist/adapter.cjs +0 -15
- package/dist/adapter.d.cts +0 -4
- package/dist/adapter.d.mts +0 -4
- package/dist/adapter.mjs +0 -2
- package/dist/decision-C3k4ve51.mjs +0 -227
- package/dist/decision-D8wJrM8W.cjs +0 -286
- package/dist/openai-compat.cjs +0 -309
- package/dist/openai-compat.d.cts +0 -59
- package/dist/openai-compat.d.mts +0 -59
- package/dist/openai-compat.mjs +0 -308
- package/dist/steps-BALp1eZo.d.mts +0 -198
- package/dist/steps-CVe54GPP.cjs +0 -420
- package/dist/steps-CkyyyuHd.mjs +0 -379
- package/dist/steps-MjnQI4aB.d.cts +0 -198
- package/dist/steps.cjs +0 -12
- package/dist/steps.d.cts +0 -3
- package/dist/steps.d.mts +0 -3
- package/dist/steps.mjs +0 -3
- package/dist/utils-BYqT_Dyv.d.cts +0 -108
- package/dist/utils-Do5wIJrh.d.mts +0 -108
- package/dist/zod.cjs +0 -31
- package/dist/zod.d.cts +0 -30
- package/dist/zod.d.mts +0 -30
- package/dist/zod.mjs +0 -30
package/dist/ai-sdk.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const require_decision = require("./decision-D8wJrM8W.cjs");
|
|
2
|
+
const require_decision = require("./decision-dWGhBh0P.cjs");
|
|
4
3
|
let ai = require("ai");
|
|
5
4
|
//#region src/ai-sdk/mappers.ts
|
|
6
5
|
/**
|
|
@@ -24,7 +23,7 @@ function toAiSdkTools(tools) {
|
|
|
24
23
|
})]);
|
|
25
24
|
continue;
|
|
26
25
|
}
|
|
27
|
-
if (
|
|
26
|
+
if (require_decision.isStandardSchema(descriptor.inputSchema)) {
|
|
28
27
|
entries.push([name, descriptor]);
|
|
29
28
|
continue;
|
|
30
29
|
}
|
|
@@ -80,7 +79,7 @@ function toAiSdkToolChoice(toolChoice) {
|
|
|
80
79
|
}
|
|
81
80
|
/** `true` when the request should use AI SDK structured `Output.object`. */
|
|
82
81
|
function isStructuredOutputRequest(request) {
|
|
83
|
-
return
|
|
82
|
+
return require_decision.getAgentOutputMode(request.outputSchema) === "structured";
|
|
84
83
|
}
|
|
85
84
|
/**
|
|
86
85
|
* Extracts the first complete top-level JSON value from `text`, or returns
|
|
@@ -214,7 +213,7 @@ function createAiSdkExecutors(options) {
|
|
|
214
213
|
...maxStepsSetting(request)
|
|
215
214
|
};
|
|
216
215
|
if (isStructuredOutputRequest(request)) {
|
|
217
|
-
const envelope =
|
|
216
|
+
const envelope = require_decision.buildEnvelopeSchema(request.outputSchema, { reasoning: request.reasoning });
|
|
218
217
|
const structuredOutput = withJsonRepair(ai.Output.object({ schema: envelope }));
|
|
219
218
|
const canRetry = !request.tools || Object.keys(request.tools).length === 0;
|
|
220
219
|
let result;
|
package/dist/ai-sdk.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { d as ChosenEvent } from "./types-QbEfCVny.cjs";
|
|
2
|
+
import { O as AgentDecisionExecutor, a as AgentRequestExecutor, c as AgentRequestExecutors } from "./text-logic-CorGzucG.cjs";
|
|
3
3
|
import { FinishReason, LanguageModel, LanguageModelUsage, ToolSet, TypedToolCall, TypedToolResult } from "ai";
|
|
4
4
|
|
|
5
5
|
//#region src/ai-sdk/index.d.ts
|
|
@@ -52,6 +52,9 @@ type AiSdkGenerateResult = {
|
|
|
52
52
|
* `reasoning: true` and the model produced it (see the structured-output
|
|
53
53
|
* envelope in {@link buildEnvelopeSchema}). Never enters machine context/output. */
|
|
54
54
|
reasoning?: string;
|
|
55
|
+
/** The call's token usage. Its flat `inputTokens`/`outputTokens`/`totalTokens`/
|
|
56
|
+
* `reasoningTokens`/`cachedInputTokens` fields are what `runAgent` folds into
|
|
57
|
+
* the run result's aggregated `AgentUsage`. */
|
|
55
58
|
usage: LanguageModelUsage;
|
|
56
59
|
finishReason: FinishReason;
|
|
57
60
|
toolCalls: TypedToolCall<ToolSet>[];
|
|
@@ -59,13 +62,13 @@ type AiSdkGenerateResult = {
|
|
|
59
62
|
};
|
|
60
63
|
/** Raw result shape from {@link AiSdkExecutors.streamText} — the `{ output }` envelope carrying the fully-accumulated text once the stream finishes (chunks are delivered separately via `onChunk`), plus the stream's final usage/finish metadata for `onResult`. */
|
|
61
64
|
type AiSdkStreamResult = {
|
|
62
|
-
output: string;
|
|
65
|
+
output: string; /** The stream's final (awaited) usage; aggregated into the run result's `AgentUsage`. */
|
|
63
66
|
usage: LanguageModelUsage;
|
|
64
67
|
finishReason: FinishReason;
|
|
65
68
|
};
|
|
66
69
|
/** Raw result shape from {@link AiSdkExecutors.decide} — the chosen event plus the AI SDK call metadata, delivered per decision attempt to `onResult`. */
|
|
67
70
|
type AiSdkDecideResult = {
|
|
68
|
-
event: ChosenEvent;
|
|
71
|
+
event: ChosenEvent; /** This attempt's usage; aggregated into the run result's `AgentUsage`. */
|
|
69
72
|
usage: LanguageModelUsage;
|
|
70
73
|
finishReason: FinishReason;
|
|
71
74
|
};
|
package/dist/ai-sdk.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { d as ChosenEvent } from "./types-_FXoFBGO.mjs";
|
|
2
|
+
import { O as AgentDecisionExecutor, a as AgentRequestExecutor, c as AgentRequestExecutors } from "./text-logic-DjR5PUVz.mjs";
|
|
3
3
|
import { FinishReason, LanguageModel, LanguageModelUsage, ToolSet, TypedToolCall, TypedToolResult } from "ai";
|
|
4
4
|
|
|
5
5
|
//#region src/ai-sdk/index.d.ts
|
|
@@ -52,6 +52,9 @@ type AiSdkGenerateResult = {
|
|
|
52
52
|
* `reasoning: true` and the model produced it (see the structured-output
|
|
53
53
|
* envelope in {@link buildEnvelopeSchema}). Never enters machine context/output. */
|
|
54
54
|
reasoning?: string;
|
|
55
|
+
/** The call's token usage. Its flat `inputTokens`/`outputTokens`/`totalTokens`/
|
|
56
|
+
* `reasoningTokens`/`cachedInputTokens` fields are what `runAgent` folds into
|
|
57
|
+
* the run result's aggregated `AgentUsage`. */
|
|
55
58
|
usage: LanguageModelUsage;
|
|
56
59
|
finishReason: FinishReason;
|
|
57
60
|
toolCalls: TypedToolCall<ToolSet>[];
|
|
@@ -59,13 +62,13 @@ type AiSdkGenerateResult = {
|
|
|
59
62
|
};
|
|
60
63
|
/** Raw result shape from {@link AiSdkExecutors.streamText} — the `{ output }` envelope carrying the fully-accumulated text once the stream finishes (chunks are delivered separately via `onChunk`), plus the stream's final usage/finish metadata for `onResult`. */
|
|
61
64
|
type AiSdkStreamResult = {
|
|
62
|
-
output: string;
|
|
65
|
+
output: string; /** The stream's final (awaited) usage; aggregated into the run result's `AgentUsage`. */
|
|
63
66
|
usage: LanguageModelUsage;
|
|
64
67
|
finishReason: FinishReason;
|
|
65
68
|
};
|
|
66
69
|
/** Raw result shape from {@link AiSdkExecutors.decide} — the chosen event plus the AI SDK call metadata, delivered per decision attempt to `onResult`. */
|
|
67
70
|
type AiSdkDecideResult = {
|
|
68
|
-
event: ChosenEvent;
|
|
71
|
+
event: ChosenEvent; /** This attempt's usage; aggregated into the run result's `AgentUsage`. */
|
|
69
72
|
usage: LanguageModelUsage;
|
|
70
73
|
finishReason: FinishReason;
|
|
71
74
|
};
|
package/dist/ai-sdk.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { l as renderDecisionAttempts } from "./decision-C3k4ve51.mjs";
|
|
1
|
+
import { J as isStandardSchema, T as getAgentOutputMode, b as buildEnvelopeSchema, l as renderDecisionAttempts } from "./decision-BezSD_YC.mjs";
|
|
3
2
|
import { NoObjectGeneratedError, Output, generateText, stepCountIs, streamText, tool } from "ai";
|
|
4
3
|
//#region src/ai-sdk/mappers.ts
|
|
5
4
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as AgentError } from "./errors-CeSXQx0v.mjs";
|
|
2
|
+
import { createAsyncLogic, createLogic, getNextTransitions } from "xstate";
|
|
2
3
|
//#region src/utils.ts
|
|
3
4
|
/**
|
|
4
5
|
* Deep-clones a snapshot to a plain-JSON value via a `JSON` round-trip, the
|
|
@@ -174,8 +175,7 @@ function getAgentMessages(snapshot) {
|
|
|
174
175
|
* Structural guard for a {@link StandardSchemaV1}: `true` when `value` carries
|
|
175
176
|
* the `~standard` marker. Used to tell an already-schema'd tool `inputSchema`
|
|
176
177
|
* (a Zod/Valibot/… schema) apart from an SDK-specific schema wrapper that core
|
|
177
|
-
* can't read directly — see the `ai-sdk` tool pass-through
|
|
178
|
-
* tool serialization.
|
|
178
|
+
* can't read directly — see the `ai-sdk` tool pass-through.
|
|
179
179
|
*/
|
|
180
180
|
function isStandardSchema(value) {
|
|
181
181
|
return typeof value === "object" && value !== null && "~standard" in value;
|
|
@@ -236,6 +236,24 @@ function getMachineSuspensionPredicate(machine) {
|
|
|
236
236
|
const config = machine.config;
|
|
237
237
|
return config ? machineSuspensionPredicates.get(config) : void 0;
|
|
238
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Lint-reachability sidecar for `fromConfig` machines.
|
|
241
|
+
*
|
|
242
|
+
* Static transition targets declared by the source config of a
|
|
243
|
+
* `setupAgent.fromConfig` machine, as `dotted state path → declared target
|
|
244
|
+
* strings` (every `on`/`always`/`after`/`onDone`/`choice` target plus each
|
|
245
|
+
* invoke's `onDone`/`onError`). The JSON layer folds a transition that carries a
|
|
246
|
+
* context patch into an opaque resolver function, erasing its target from
|
|
247
|
+
* `machine.config` — so `lintAgentMachine`'s reachability walk reads the
|
|
248
|
+
* targets from here instead. Keyed on the machine's root `config` object (like
|
|
249
|
+
* {@link machineSuspensionPredicates}) so it survives `machine.provide(...)`.
|
|
250
|
+
*/
|
|
251
|
+
const machineStaticTransitionTargets = /* @__PURE__ */ new WeakMap();
|
|
252
|
+
/** Reads the {@link machineStaticTransitionTargets} map carried by `machine` (via its root `config`), if any. */
|
|
253
|
+
function getMachineStaticTransitionTargets(machine) {
|
|
254
|
+
const config = machine.config;
|
|
255
|
+
return config ? machineStaticTransitionTargets.get(config) : void 0;
|
|
256
|
+
}
|
|
239
257
|
const unboundPlaceholderLogics = /* @__PURE__ */ new WeakSet();
|
|
240
258
|
/** Text/decision logics created WITH their own executor (withExecutor or the
|
|
241
259
|
* factory's second arg) — these are runnable as-is, so runAgent's bind check
|
|
@@ -243,7 +261,7 @@ const unboundPlaceholderLogics = /* @__PURE__ */ new WeakSet();
|
|
|
243
261
|
const executorBoundLogics = /* @__PURE__ */ new WeakSet();
|
|
244
262
|
function missingActor(src) {
|
|
245
263
|
const logic = createAsyncLogic({ run: async () => {
|
|
246
|
-
throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({
|
|
264
|
+
throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }).`);
|
|
247
265
|
} });
|
|
248
266
|
unboundPlaceholderLogics.add(logic);
|
|
249
267
|
return logic;
|
|
@@ -287,6 +305,33 @@ function parseModelRef(modelRef) {
|
|
|
287
305
|
modelId: modelRef.slice(slash + 1)
|
|
288
306
|
};
|
|
289
307
|
}
|
|
308
|
+
/** The token fields {@link AgentUsage} aggregates. @internal */
|
|
309
|
+
const AGENT_USAGE_TOKEN_FIELDS = [
|
|
310
|
+
"inputTokens",
|
|
311
|
+
"outputTokens",
|
|
312
|
+
"totalTokens",
|
|
313
|
+
"reasoningTokens",
|
|
314
|
+
"cachedInputTokens"
|
|
315
|
+
];
|
|
316
|
+
/**
|
|
317
|
+
* Reads a per-call {@link AgentCallUsage} off a raw executor result's `usage`
|
|
318
|
+
* field, keeping only finite numbers. Returns `undefined` when the result
|
|
319
|
+
* reports no usage at all. Works for our `{ output, usage }` envelope, for a
|
|
320
|
+
* raw Vercel AI SDK result (its `LanguageModelUsage` carries the same flat
|
|
321
|
+
* field names), and for any custom executor that follows the shape.
|
|
322
|
+
*
|
|
323
|
+
* @internal
|
|
324
|
+
*/
|
|
325
|
+
function extractCallUsage(raw) {
|
|
326
|
+
const usage = raw?.usage;
|
|
327
|
+
if (!usage || typeof usage !== "object") return;
|
|
328
|
+
let out;
|
|
329
|
+
for (const field of AGENT_USAGE_TOKEN_FIELDS) {
|
|
330
|
+
const value = usage[field];
|
|
331
|
+
if (typeof value === "number" && Number.isFinite(value)) (out ??= {})[field] = value;
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
290
335
|
const agentTextInputSchema = { "~standard": {
|
|
291
336
|
version: 1,
|
|
292
337
|
vendor: "statelyai-agent",
|
|
@@ -314,9 +359,16 @@ const stringOutputSchema = { "~standard": {
|
|
|
314
359
|
return typeof value === "string" ? { value } : { issues: [{ message: "Expected string output" }] };
|
|
315
360
|
}
|
|
316
361
|
} };
|
|
362
|
+
const noInputSchema = { "~standard": {
|
|
363
|
+
version: 1,
|
|
364
|
+
vendor: "statelyai-agent",
|
|
365
|
+
validate(value) {
|
|
366
|
+
return { value };
|
|
367
|
+
}
|
|
368
|
+
} };
|
|
317
369
|
function createBuiltinTextActor(src, mode, outputSchema) {
|
|
318
370
|
const logic = createAsyncLogic({ run: async () => {
|
|
319
|
-
throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({
|
|
371
|
+
throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }) or execute the returned agent request with executeAgentRequest(...).`);
|
|
320
372
|
} });
|
|
321
373
|
return Object.assign(logic, {
|
|
322
374
|
kind: "statelyai.textLogic",
|
|
@@ -365,7 +417,7 @@ const builtinTextActors = {
|
|
|
365
417
|
};
|
|
366
418
|
/** The unbound `agent.userInput` builtin registered by setupAgent (an unbound-placeholder logic — see internal/registry.ts). Output is `string` — what the human typed. @internal */
|
|
367
419
|
const userInputActor = createAsyncLogic({ run: async () => {
|
|
368
|
-
throw new Error(`'${USER_INPUT_ACTOR}' has no host execution. Provide an implementation with machine.provide({
|
|
420
|
+
throw new Error(`'${USER_INPUT_ACTOR}' has no host execution. Provide an implementation with machine.provide({ actors: { '${USER_INPUT_ACTOR}': ... } }).`);
|
|
369
421
|
} });
|
|
370
422
|
unboundPlaceholderLogics.add(userInputActor);
|
|
371
423
|
/**
|
|
@@ -384,7 +436,7 @@ function resolveTextLogicValue(value, args) {
|
|
|
384
436
|
/**
|
|
385
437
|
* Creates reusable, standalone {@link TextLogic}: an actor that, when run,
|
|
386
438
|
* resolves typed input to typed output via a model call. Register the
|
|
387
|
-
* result under `
|
|
439
|
+
* result under `actors:` and invoke it by name (equivalent to what
|
|
388
440
|
* `setupAgent({ requests })` builds internally for each request entry). Pass
|
|
389
441
|
* `execute` here, or bind it later with `.withExecutor(...)`, a runtime
|
|
390
442
|
* adapter's `machine.provide(...)`, or `runAgent`'s `generateText`/
|
|
@@ -402,8 +454,12 @@ function resolveTextLogicValue(value, args) {
|
|
|
402
454
|
* ```
|
|
403
455
|
*/
|
|
404
456
|
function createTextLogic(config, execute) {
|
|
457
|
+
const schemas = {
|
|
458
|
+
input: config.schemas?.input ?? noInputSchema,
|
|
459
|
+
output: config.schemas?.output ?? stringOutputSchema
|
|
460
|
+
};
|
|
405
461
|
const request = (input) => {
|
|
406
|
-
const args = { input: validateSchemaSync(
|
|
462
|
+
const args = { input: validateSchemaSync(schemas.input, input) };
|
|
407
463
|
return {
|
|
408
464
|
name: resolveTextLogicValue(config.name, args),
|
|
409
465
|
model: resolveTextLogicValue(config.model, args),
|
|
@@ -412,7 +468,7 @@ function createTextLogic(config, execute) {
|
|
|
412
468
|
messages: resolveTextLogicValue(config.messages, args),
|
|
413
469
|
tools: resolveTextLogicValue(config.tools, args),
|
|
414
470
|
toolChoice: resolveTextLogicValue(config.toolChoice, args),
|
|
415
|
-
outputSchema:
|
|
471
|
+
outputSchema: schemas.output,
|
|
416
472
|
reasoning: resolveTextLogicValue(config.reasoning, args),
|
|
417
473
|
temperature: resolveTextLogicValue(config.temperature, args),
|
|
418
474
|
maxOutputTokens: resolveTextLogicValue(config.maxOutputTokens, args),
|
|
@@ -425,7 +481,7 @@ function createTextLogic(config, execute) {
|
|
|
425
481
|
};
|
|
426
482
|
const logic = createAsyncLogic({ run: async ({ input, signal, system, self }, enq) => {
|
|
427
483
|
const resolvedRequest = request(input);
|
|
428
|
-
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 extract it with
|
|
484
|
+
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 extract it with getAgentEffects(..., { actors }).");
|
|
429
485
|
const result = await execute({
|
|
430
486
|
input,
|
|
431
487
|
request: resolvedRequest,
|
|
@@ -436,16 +492,16 @@ function createTextLogic(config, execute) {
|
|
|
436
492
|
});
|
|
437
493
|
const selfId = self?.id;
|
|
438
494
|
const output = await normalizeGeneratorResult(result, typeof selfId === "string" ? selfId : "text logic", { request: resolvedRequest });
|
|
439
|
-
return validateSchemaSync(
|
|
495
|
+
return validateSchemaSync(schemas.output, output);
|
|
440
496
|
} });
|
|
441
497
|
const textLogic = Object.assign(logic, {
|
|
442
498
|
kind: "statelyai.textLogic",
|
|
443
499
|
mode: config.mode ?? "generate",
|
|
444
|
-
schemas
|
|
500
|
+
schemas,
|
|
445
501
|
request,
|
|
446
502
|
async execute(input, executors) {
|
|
447
503
|
const { output } = await executeAgentTextRequest(config.mode ?? "generate", "textLogic", request(input), executors);
|
|
448
|
-
return validateSchemaSync(
|
|
504
|
+
return validateSchemaSync(schemas.output, output);
|
|
449
505
|
},
|
|
450
506
|
withExecutor(nextExecute) {
|
|
451
507
|
return createTextLogic(config, nextExecute);
|
|
@@ -465,7 +521,7 @@ function createTextLogic(config, execute) {
|
|
|
465
521
|
* @example
|
|
466
522
|
* ```ts
|
|
467
523
|
* childMachine.provide({
|
|
468
|
-
*
|
|
524
|
+
* actors: {
|
|
469
525
|
* researchTopic: bindRequestExecutor(setup.requests.researchTopic, generateText),
|
|
470
526
|
* },
|
|
471
527
|
* });
|
|
@@ -503,10 +559,6 @@ function getAgentOutputMode(schema) {
|
|
|
503
559
|
if (jsonSchema.type === void 0 && ("anyOf" in jsonSchema || "oneOf" in jsonSchema || "allOf" in jsonSchema)) return "structured";
|
|
504
560
|
return "text";
|
|
505
561
|
}
|
|
506
|
-
/** True when {@link getAgentOutputMode} classifies `schema` as `'structured'`. */
|
|
507
|
-
function isStructuredOutputSchema(schema) {
|
|
508
|
-
return getAgentOutputMode(schema) === "structured";
|
|
509
|
-
}
|
|
510
562
|
/**
|
|
511
563
|
* Builds the uniform structured-output envelope schema every structured request
|
|
512
564
|
* is sent to the provider as: a root object `{ result: <inner> }`, plus — when
|
|
@@ -692,6 +744,18 @@ function matchesEventPattern(eventType, pattern) {
|
|
|
692
744
|
if (pattern.endsWith(".*")) return eventType.startsWith(`${pattern.slice(0, -1)}`);
|
|
693
745
|
return eventType === pattern;
|
|
694
746
|
}
|
|
747
|
+
/**
|
|
748
|
+
* The namespace reserved for events the library itself delivers to a machine
|
|
749
|
+
* (`@agent.init`, `@agent.usage`). A machine may declare transitions on them,
|
|
750
|
+
* but they are never model-facing: {@link getAcceptedEvents} drops them before
|
|
751
|
+
* any `allowedEvents` matching, so they cannot be offered as a decision
|
|
752
|
+
* candidate (not even under a `'*'` wildcard) and {@link parseAgentEvent}
|
|
753
|
+
* rejects them — a model or a wire message must not be able to forge one.
|
|
754
|
+
* Matched as a prefix rather than a list so this module stays free of an import
|
|
755
|
+
* cycle back to `effects.ts`, where the constants live.
|
|
756
|
+
* @internal
|
|
757
|
+
*/
|
|
758
|
+
const RESERVED_AGENT_EVENT_PREFIX = "@agent.";
|
|
695
759
|
/** True when an `allowedEvents` entry is a wildcard pattern rather than a concrete event type. @internal */
|
|
696
760
|
function isEventPattern(entry) {
|
|
697
761
|
return entry === "*" || entry.endsWith(".*");
|
|
@@ -734,13 +798,29 @@ function parseAgentEvent(snapshot, event, options = {}) {
|
|
|
734
798
|
}
|
|
735
799
|
return event;
|
|
736
800
|
}
|
|
801
|
+
/**
|
|
802
|
+
* Lists the events a snapshot can currently accept, as {@link AgentEventDescriptor}s
|
|
803
|
+
* a model can be offered (via `resolveDecision`/an adapter's tool-per-event
|
|
804
|
+
* mapping). **Filters by event TYPE only** — it does not evaluate guards, so
|
|
805
|
+
* a type-legal-but-guard-rejected event can still appear here. Guard
|
|
806
|
+
* legality is checked separately, at decision-resolution time, via
|
|
807
|
+
* `snapshot.can(event)` (the `canTake` option of {@link resolveDecision} /
|
|
808
|
+
* {@link ResolveDecisionOptions}). Pass `eventTypes` to further narrow to a
|
|
809
|
+
* declared `allowedEvents` set — entries may be exact types or wildcard
|
|
810
|
+
* patterns (`'*'`, `'todo.*'`; see {@link matchesEventPattern}).
|
|
811
|
+
*
|
|
812
|
+
* XState-internal (`xstate.*`) and library-reserved
|
|
813
|
+
* ({@link RESERVED_AGENT_EVENT_PREFIX}) event types are always excluded, before
|
|
814
|
+
* any `allowedEvents` matching — a machine that handles `'@agent.usage'` still
|
|
815
|
+
* never offers it to a model.
|
|
816
|
+
*/
|
|
737
817
|
function getAcceptedEvents(snapshot, options = {}) {
|
|
738
818
|
const eventTypes = options.eventTypes;
|
|
739
819
|
const seen = /* @__PURE__ */ new Set();
|
|
740
820
|
const usedToolNames = /* @__PURE__ */ new Set();
|
|
741
821
|
return getNextTransitions(snapshot).flatMap((transitionDefinition) => {
|
|
742
822
|
const eventType = transitionDefinition.eventType;
|
|
743
|
-
if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
|
|
823
|
+
if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || eventType.startsWith(RESERVED_AGENT_EVENT_PREFIX) || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
|
|
744
824
|
seen.add(eventType);
|
|
745
825
|
const defaultToolName = sanitizeEventToolName(eventType);
|
|
746
826
|
const toolName = options.eventToolName ? options.eventToolName({
|
|
@@ -756,4 +836,231 @@ function getAcceptedEvents(snapshot, options = {}) {
|
|
|
756
836
|
});
|
|
757
837
|
}
|
|
758
838
|
//#endregion
|
|
759
|
-
|
|
839
|
+
//#region src/decision.ts
|
|
840
|
+
function decideRequestFromInput(input) {
|
|
841
|
+
const allowedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input) ?? [];
|
|
842
|
+
return {
|
|
843
|
+
kind: "decision",
|
|
844
|
+
id: "",
|
|
845
|
+
model: input.model,
|
|
846
|
+
system: input.system,
|
|
847
|
+
prompt: input.prompt,
|
|
848
|
+
messages: input.messages,
|
|
849
|
+
events: allowedEventTypes.filter((type) => !isEventPattern(type)).map((type) => ({
|
|
850
|
+
type,
|
|
851
|
+
toolName: sanitizeEventToolName(type)
|
|
852
|
+
})),
|
|
853
|
+
attempts: [],
|
|
854
|
+
temperature: input.temperature,
|
|
855
|
+
maxOutputTokens: input.maxOutputTokens,
|
|
856
|
+
topP: input.topP,
|
|
857
|
+
topK: input.topK,
|
|
858
|
+
seed: input.seed,
|
|
859
|
+
stopSequences: input.stopSequences,
|
|
860
|
+
metadata: input.metadata
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function decideActorWithExecutor(execute) {
|
|
864
|
+
const logic = createAsyncLogic({ run: async ({ input, signal }) => {
|
|
865
|
+
if (!execute) throw new Error(`'${DECIDE_ACTOR}' has no host execution. Provide an implementation with machine.provide({ actors: { '${DECIDE_ACTOR}': ... } }) or resolve the returned agent request with resolveDecision(...).`);
|
|
866
|
+
const resolvedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input);
|
|
867
|
+
if (resolvedEventTypes === void 0) throw new Error(`'${DECIDE_ACTOR}' input has omitted \`allowedEvents\`, which means "all currently-legal events" — but that requires a snapshot-aware host (runAgent or the step path) to resolve. Under a bare createActor(...), declare \`allowedEvents\` explicitly to use this actor here.`);
|
|
868
|
+
if (resolvedEventTypes.some(isEventPattern)) throw new Error(`'${DECIDE_ACTOR}' input uses wildcard \`allowedEvents\` patterns, which expand against the live snapshot — that requires a snapshot-aware host (runAgent or the step path). Under a bare createActor(...), list event types explicitly.`);
|
|
869
|
+
return resolveDecision(decideRequestFromInput(input), execute, {
|
|
870
|
+
maxRetries: input.maxRetries ?? 2,
|
|
871
|
+
signal
|
|
872
|
+
});
|
|
873
|
+
} });
|
|
874
|
+
return Object.assign(logic, {
|
|
875
|
+
kind: "statelyai.decisionLogic",
|
|
876
|
+
maxRetries: 2,
|
|
877
|
+
request: decideRequestFromInput,
|
|
878
|
+
allowedEventTypes: (input) => resolveAllowedEventTypes(input.allowedEvents, input),
|
|
879
|
+
withExecutor: (nextExecute) => decideActorWithExecutor(nextExecute)
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
function createDecideActor() {
|
|
883
|
+
return decideActorWithExecutor();
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Reserved event type the `agent.plan` builtin adds to every step's
|
|
887
|
+
* candidates as the explicit "no further action needed" move. Choosing it
|
|
888
|
+
* ends the plan (`stopped: 'done'`); it is never sent to the machine, so
|
|
889
|
+
* machines need no no-op sentinel event of their own.
|
|
890
|
+
*/
|
|
891
|
+
const PLAN_DONE_EVENT_TYPE = "agent.plan.done";
|
|
892
|
+
function createPlanActor() {
|
|
893
|
+
const logic = createLogic({
|
|
894
|
+
context: ({ input }) => ({
|
|
895
|
+
applied: [],
|
|
896
|
+
stepsRemaining: input.maxSteps ?? 8,
|
|
897
|
+
stopped: null
|
|
898
|
+
}),
|
|
899
|
+
run: ({ context, event }) => event.type === "plan.applied" ? { context: {
|
|
900
|
+
...context,
|
|
901
|
+
applied: [...context.applied, event.event],
|
|
902
|
+
stepsRemaining: context.stepsRemaining - 1
|
|
903
|
+
} } : event.type === "plan.ended" ? {
|
|
904
|
+
context: {
|
|
905
|
+
...context,
|
|
906
|
+
stopped: event.stopped
|
|
907
|
+
},
|
|
908
|
+
status: "done",
|
|
909
|
+
output: {
|
|
910
|
+
steps: context.applied,
|
|
911
|
+
stopped: event.stopped
|
|
912
|
+
}
|
|
913
|
+
} : void 0
|
|
914
|
+
});
|
|
915
|
+
return Object.assign(logic, {
|
|
916
|
+
kind: "statelyai.planLogic",
|
|
917
|
+
maxRetries: 2,
|
|
918
|
+
request: decideRequestFromInput,
|
|
919
|
+
allowedEventTypes: (input) => resolveAllowedEventTypes(input.allowedEvents, input)
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
const PLAN_LEDGER_SCOPE = {
|
|
923
|
+
emit: () => {},
|
|
924
|
+
self: { id: "agent.plan.ledger" }
|
|
925
|
+
};
|
|
926
|
+
/**
|
|
927
|
+
* Builds a fresh plan ledger snapshot from resolved plan input — the shared
|
|
928
|
+
* starting point for BOTH hosts (the step path reads the invoke child's own
|
|
929
|
+
* initial snapshot; runAgent seeds a local ledger with this). @internal
|
|
930
|
+
*/
|
|
931
|
+
function initialPlanLedger(logic, input) {
|
|
932
|
+
return logic.getInitialSnapshot(PLAN_LEDGER_SCOPE, input);
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Advances a plan ledger by one {@link PlanLedgerEvent}, returning the next
|
|
936
|
+
* snapshot (unwrapping `createLogic`'s `[snapshot, effects]` tuple). Pure — no
|
|
937
|
+
* mutation of the input snapshot. @internal
|
|
938
|
+
*/
|
|
939
|
+
function advancePlanLedger(logic, snapshot, event) {
|
|
940
|
+
const result = logic.transition(snapshot, event, PLAN_LEDGER_SCOPE);
|
|
941
|
+
return Array.isArray(result) ? result[0] : result;
|
|
942
|
+
}
|
|
943
|
+
/** Type guard: true for the `agent.plan` builtin logic (checks the `kind` marker). @internal */
|
|
944
|
+
function isPlanLogic(logic) {
|
|
945
|
+
return !!logic && logic.kind === "statelyai.planLogic";
|
|
946
|
+
}
|
|
947
|
+
function resolveAllowedEventTypes(allowedEvents, input) {
|
|
948
|
+
if (allowedEvents === void 0) return;
|
|
949
|
+
const resolved = typeof allowedEvents === "function" ? allowedEvents({ input }) : allowedEvents;
|
|
950
|
+
return typeof resolved === "string" ? [resolved] : resolved;
|
|
951
|
+
}
|
|
952
|
+
/** Type guard: true for any actor logic built by createDecisionLogic/createDecideActor (checks the `kind` marker). @internal */
|
|
953
|
+
function isDecisionLogic(value) {
|
|
954
|
+
return !!value && typeof value === "object" && value.kind === "statelyai.decisionLogic" && typeof value.request === "function";
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Thrown by {@link resolveDecision} when every attempt (up to
|
|
958
|
+
* `maxRetries + 1` of them) fails one of the three checks recorded in
|
|
959
|
+
* {@link DecisionAttempt.failure}. Carries the full `attempts` list for
|
|
960
|
+
* diagnostics; a machine typically routes this via the decision invoke's
|
|
961
|
+
* `onError`.
|
|
962
|
+
*/
|
|
963
|
+
var AgentDecisionExhaustedError = class extends AgentError {
|
|
964
|
+
attempts;
|
|
965
|
+
constructor(attempts) {
|
|
966
|
+
super("decision-exhausted", `Decision exhausted after ${attempts.length} attempt${attempts.length === 1 ? "" : "s"}: ` + attempts.map((attempt) => attempt.reason).join("; "));
|
|
967
|
+
this.name = "AgentDecisionExhaustedError";
|
|
968
|
+
this.attempts = attempts;
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
/**
|
|
972
|
+
* Renders a decision request's prior failed `attempts` into feedback messages
|
|
973
|
+
* a host appends to the model call so retries converge — the transport-agnostic
|
|
974
|
+
* "your last choice failed because X, choose again from Y" logic every adapter
|
|
975
|
+
* and raw-SDK host repeats. Returns one `user`-role {@link AgentMessage} per
|
|
976
|
+
* attempt (empty when there are none); adapters map each onto their wire
|
|
977
|
+
* message shape (`attempt.content` is always a string). Core never rewrites the
|
|
978
|
+
* request itself — this only turns the recorded attempts into messages.
|
|
979
|
+
*
|
|
980
|
+
* @example
|
|
981
|
+
* ```ts
|
|
982
|
+
* const messages = [...baseMessages, ...renderDecisionAttempts(request)];
|
|
983
|
+
* ```
|
|
984
|
+
*/
|
|
985
|
+
function renderDecisionAttempts(request) {
|
|
986
|
+
const types = request.events.map((event) => event.type).join(", ") || "(none)";
|
|
987
|
+
return request.attempts.map((attempt) => userMessage(`Your previous choice failed: ${attempt.reason}. Choose again from: ${types}`));
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Validation + retry core for decisions. No provider mechanics — the
|
|
991
|
+
* `executor` is responsible for making the model choose an event; this
|
|
992
|
+
* function only validates the choice and retries on failure, up to
|
|
993
|
+
* `options.maxRetries` (default 2, i.e. up to 3 attempts total).
|
|
994
|
+
*
|
|
995
|
+
* Each attempt is checked in order and can fail one of three ways (recorded
|
|
996
|
+
* as a {@link DecisionAttempt}): `'unknown-event'` (the chosen `type` is not
|
|
997
|
+
* among `request.events`), `'invalid-payload'` (the payload fails that
|
|
998
|
+
* event's schema), or `'rejected-by-guard'` (passes both checks but
|
|
999
|
+
* `options.canTake` returns `false` — a type/payload-legal event the
|
|
1000
|
+
* machine's guard rejects right now; omit `canTake` to skip this check).
|
|
1001
|
+
* Every prior failed attempt for this call is fed back to the executor on
|
|
1002
|
+
* the next attempt via `request.attempts`, so an adapter can render "your
|
|
1003
|
+
* last choice failed because X — try again" into the next model call; core
|
|
1004
|
+
* never rewrites the request itself. Exhausting all attempts throws
|
|
1005
|
+
* {@link AgentDecisionExhaustedError} with the full attempts list.
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```ts
|
|
1009
|
+
* const event = await resolveDecision(request, decide, {
|
|
1010
|
+
* canTake: (e) => snapshot.can(e),
|
|
1011
|
+
* });
|
|
1012
|
+
* ```
|
|
1013
|
+
*/
|
|
1014
|
+
async function resolveDecision(request, executor, options = {}) {
|
|
1015
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
1016
|
+
const attempts = [];
|
|
1017
|
+
const eventsByType = new Map(request.events.map((event) => [event.type, event]));
|
|
1018
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1019
|
+
options.signal?.throwIfAborted();
|
|
1020
|
+
const result = await executor({
|
|
1021
|
+
...request,
|
|
1022
|
+
attempts: [...attempts],
|
|
1023
|
+
signal: options.signal
|
|
1024
|
+
});
|
|
1025
|
+
if (!result || typeof result !== "object" || typeof result.event !== "object" || result.event === null || typeof result.event.type !== "string") throw new Error(`decide executor must return { event: { type: string, ... } }; got ${JSON.stringify(result)}. Wrap the chosen event: return { event: { type: 'SAFE' } }.`);
|
|
1026
|
+
const { event } = result;
|
|
1027
|
+
const descriptor = eventsByType.get(event.type);
|
|
1028
|
+
if (!descriptor) {
|
|
1029
|
+
attempts.push({
|
|
1030
|
+
event,
|
|
1031
|
+
failure: "unknown-event",
|
|
1032
|
+
reason: `'${event.type}' is not among the currently allowed events: ${request.events.map((candidate) => candidate.type).join(", ") || "(none)"}.`
|
|
1033
|
+
});
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
let validatedEvent = event;
|
|
1037
|
+
if (descriptor.inputSchema) {
|
|
1038
|
+
const { type, ...payload } = event;
|
|
1039
|
+
try {
|
|
1040
|
+
validatedEvent = {
|
|
1041
|
+
...validateSchemaSync(descriptor.inputSchema, payload),
|
|
1042
|
+
type
|
|
1043
|
+
};
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
attempts.push({
|
|
1046
|
+
event,
|
|
1047
|
+
failure: "invalid-payload",
|
|
1048
|
+
reason: `'${event.type}' payload failed validation: ${error instanceof Error ? error.message : String(error)}`
|
|
1049
|
+
});
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
if (options.canTake?.(validatedEvent) === false) {
|
|
1054
|
+
attempts.push({
|
|
1055
|
+
event: validatedEvent,
|
|
1056
|
+
failure: "rejected-by-guard",
|
|
1057
|
+
reason: `'${validatedEvent.type}' is not currently takeable (guard rejected it).`
|
|
1058
|
+
});
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
return validatedEvent;
|
|
1062
|
+
}
|
|
1063
|
+
throw new AgentDecisionExhaustedError(attempts);
|
|
1064
|
+
}
|
|
1065
|
+
//#endregion
|
|
1066
|
+
export { validateSchemaSync as $, parseStructuredEnvelope as A, missingActor as B, executeAgentTextRequest as C, normalizeGeneratorResult as D, isTextLogic as E, getMachineSuspensionPredicate as F, getJsonSchemaSync as G, findNonSerializableContextPaths as H, getRegisteredAgentExecutionOptions as I, isStandardSchema as J, getMachineStructuralHash as K, isUnboundPlaceholder as L, agentExecutionOptions as M, executorBoundLogics as N, parseModelRef as O, getMachineStaticTransitionTargets as P, userMessage as Q, machineStaticTransitionTargets as R, createTextLogic as S, getAgentOutputMode as T, getAgentMessages as U, assistantMessage as V, getJsonSchema as W, systemMessage as X, persistSnapshot as Y, toolMessage as Z, PLAN_ACTOR as _, createPlanActor as a, buildEnvelopeSchema as b, isPlanLogic as c, getAcceptedEvents as d, parseAgentEvent as f, INTERPRET_SOURCE as g, DECIDE_ACTOR as h, createDecideActor as i, userInputActor as j, parseOutput as k, renderDecisionAttempts as l, AGENT_USAGE_TOKEN_FIELDS as m, PLAN_DONE_EVENT_TYPE as n, initialPlanLedger as o, sanitizeEventToolName as p, getStateMeta as q, advancePlanLedger as r, isDecisionLogic as s, AgentDecisionExhaustedError as t, resolveDecision as u, USER_INPUT_ACTOR as v, extractCallUsage as w, builtinTextActors as x, bindRequestExecutor as y, machineSuspensionPredicates as z };
|