@statelyai/agent 2.0.0-alpha.30 → 2.0.0-alpha.32
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/CHANGELOG.md +1743 -0
- package/dist/ai-sdk.cjs +8 -8
- package/dist/ai-sdk.d.cts +1 -1
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +3 -3
- package/dist/{decision-CIb7ddIv.cjs → decision-BPdEaxPf.cjs} +30 -17
- package/dist/{decision-BHkvuImD.mjs → decision-CV7WiV5C.mjs} +22 -9
- package/dist/{event-log-BZRfBRd7.cjs → event-log-CKWeC7H-.cjs} +125 -27
- package/dist/{event-log-DJCMHek_.mjs → event-log-DAqsalT1.mjs} +108 -28
- package/dist/{event-log-store-DsP6GCRh.mjs → event-log-store-BGehvVTb.mjs} +2 -2
- package/dist/{event-log-store-CIIJohjj.d.mts → event-log-store-C2NjVlWU.d.mts} +13 -5
- package/dist/{event-log-store-DZR4IIWe.cjs → event-log-store-DKyE7JKr.cjs} +2 -2
- package/dist/{event-log-store-CLAC9q2Y.d.cts → event-log-store-s73r1Hxf.d.cts} +13 -5
- package/dist/index.cjs +129 -21
- package/dist/index.d.cts +59 -11
- package/dist/index.d.mts +59 -11
- package/dist/index.mjs +126 -19
- package/dist/log.cjs +40 -5
- package/dist/log.d.cts +1 -1
- package/dist/log.d.mts +1 -1
- package/dist/log.mjs +40 -5
- package/dist/machines.cjs +1 -1
- package/dist/machines.mjs +1 -1
- package/dist/{model-source-BCKg4qJ1.cjs → model-source-BQ97_l1-.cjs} +4 -5
- package/dist/{model-source-BaHwJ1wK.mjs → model-source-BilDEyqk.mjs} +1 -2
- package/dist/openai.cjs +2 -2
- package/dist/openai.d.cts +1 -1
- package/dist/openai.d.mts +1 -1
- package/dist/openai.mjs +2 -2
- package/dist/otel.d.cts +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/{run-agent-BpHqkaZw.mjs → run-agent-2UnSUhDb.mjs} +93 -37
- package/dist/{run-agent-CAVt5R-r.d.mts → run-agent-Atu-wEk7.d.mts} +17 -6
- package/dist/{run-agent-CTlMc3Jb.d.cts → run-agent-B5QhabdY.d.cts} +17 -6
- package/dist/{run-agent-ctLCvNgL.cjs → run-agent-Cg0xd3K-.cjs} +93 -37
- package/dist/{setup-agent-CLPCkXsu.d.cts → setup-agent-BiF1N3td.d.cts} +11 -3
- package/dist/{setup-agent-7rV_cujV.mjs → setup-agent-CmhB-U5P.mjs} +38 -3
- package/dist/{setup-agent-DoUk7EdC.cjs → setup-agent-CwUJyqKB.cjs} +38 -3
- package/dist/{setup-agent-CGELn1qZ.d.mts → setup-agent-J9f_Wu5T.d.mts} +11 -3
- package/dist/tanstack-ai.cjs +3 -3
- package/dist/tanstack-ai.d.cts +1 -1
- package/dist/tanstack-ai.d.mts +1 -1
- package/dist/tanstack-ai.mjs +3 -3
- package/dist/testing.cjs +73 -29
- package/dist/testing.d.cts +47 -10
- package/dist/testing.d.mts +47 -10
- package/dist/testing.mjs +65 -22
- package/dist/{text-logic-DmciuVUQ.d.cts → text-logic-CYP502VT.d.cts} +9 -3
- package/dist/{text-logic-BRYOPWvY.d.mts → text-logic-DuK9HIFM.d.mts} +9 -3
- package/dist/{usage-BkM3z02Y.d.mts → usage-D7JKAxLc.d.mts} +1 -1
- package/dist/{usage-Cm_ovz9V.d.cts → usage-DVw-646x.d.cts} +1 -1
- package/dist/validate.cjs +3 -3
- package/dist/validate.d.cts +2 -2
- package/dist/validate.d.mts +2 -2
- package/dist/validate.mjs +3 -3
- package/dist/{verify-Cn86rT8l.d.mts → verify-BVz4cURF.d.mts} +14 -6
- package/dist/{verify-COFWyprJ.mjs → verify-CoCvR1eM.mjs} +55 -5
- package/dist/{verify-CVlsTZ-V.cjs → verify-D_llRCTr.cjs} +57 -7
- package/dist/{verify-Dzwd4Mvk.d.cts → verify-Dk3Vwnha.d.cts} +14 -6
- package/package.json +34 -4
- package/readme.md +70 -40
- package/skills/add-evals/SKILL.md +23 -8
- package/skills/add-traces/SKILL.md +23 -11
- package/skills/generate-machine/SKILL.md +2 -2
- package/skills/graph-engineering/SKILL.md +1 -1
- package/skills/improve-machine/SKILL.md +7 -6
- package/skills/loop-to-machine/SKILL.md +34 -7
- package/skills/loop-to-machine/policy.md +14 -18
package/dist/ai-sdk.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_event_log = require("./event-log-
|
|
3
|
-
const require_decision = require("./decision-
|
|
4
|
-
const require_model_source = require("./model-source-
|
|
2
|
+
const require_event_log = require("./event-log-CKWeC7H-.cjs");
|
|
3
|
+
const require_decision = require("./decision-BPdEaxPf.cjs");
|
|
4
|
+
const require_model_source = require("./model-source-BQ97_l1-.cjs");
|
|
5
5
|
let ai = require("ai");
|
|
6
6
|
//#region src/ai-sdk/mappers.ts
|
|
7
7
|
/**
|
|
@@ -209,7 +209,7 @@ function toAiSdkOptionTool(options) {
|
|
|
209
209
|
*/
|
|
210
210
|
function fromAiSdkOptionCall(toolCall, options, ids) {
|
|
211
211
|
if (toolCall.toolName !== OPTION_TOOL_NAME) return unofferedOption(`tool '${toolCall.toolName}'`, ids);
|
|
212
|
-
const id =
|
|
212
|
+
const id = require_event_log.isRecord(toolCall.input) ? toolCall.input.option : void 0;
|
|
213
213
|
const option = typeof id === "string" ? options[ids.indexOf(id)] : void 0;
|
|
214
214
|
return option ? option.event : unofferedOption(`option ${JSON.stringify(id)}`, ids);
|
|
215
215
|
}
|
|
@@ -246,7 +246,7 @@ function maxStepsSetting(request) {
|
|
|
246
246
|
return typeof maxSteps === "number" ? { stopWhen: (0, ai.stepCountIs)(maxSteps) } : {};
|
|
247
247
|
}
|
|
248
248
|
function isDecisionModel(model) {
|
|
249
|
-
return
|
|
249
|
+
return require_event_log.isRecord(model) && (typeof model.doDecide === "function" || typeof model.doEvaluate === "function");
|
|
250
250
|
}
|
|
251
251
|
/**
|
|
252
252
|
* Splits a portable `"provider/model-id"` model ref (the convention JSON
|
|
@@ -498,16 +498,16 @@ function decisionResult(event, usage = {}, confidence) {
|
|
|
498
498
|
};
|
|
499
499
|
}
|
|
500
500
|
function unofferedChoice(error, ids) {
|
|
501
|
-
if (!ai.InvalidResponseDataError.isInstance(error) || !
|
|
501
|
+
if (!ai.InvalidResponseDataError.isInstance(error) || !require_event_log.isRecord(error.data)) return void 0;
|
|
502
502
|
const answer = error.data[DECISION_QUESTION_ID];
|
|
503
|
-
const choice =
|
|
503
|
+
const choice = require_event_log.isRecord(answer) ? answer.choice : void 0;
|
|
504
504
|
return typeof choice === "string" && !ids.includes(choice) ? choice : void 0;
|
|
505
505
|
}
|
|
506
506
|
function toDecisionState(value) {
|
|
507
507
|
if (typeof value === "string") return value;
|
|
508
508
|
try {
|
|
509
509
|
const json = JSON.parse(JSON.stringify(value ?? {}));
|
|
510
|
-
return
|
|
510
|
+
return require_event_log.isRecord(json) ? json : { value: json };
|
|
511
511
|
} catch {
|
|
512
512
|
return String(value);
|
|
513
513
|
}
|
package/dist/ai-sdk.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { s as AgentTools, u as ChosenEvent } from "./types-Dp5_Ru4R.cjs";
|
|
2
|
-
import { N as AgentDecisionRequest, c as AgentRequestExecutorInfo, f as AgentTextRequest, r as AgentFinishReason, t as AgentCallUsage } from "./text-logic-
|
|
2
|
+
import { N as AgentDecisionRequest, c as AgentRequestExecutorInfo, f as AgentTextRequest, r as AgentFinishReason, t as AgentCallUsage } from "./text-logic-CYP502VT.cjs";
|
|
3
3
|
import { Experimental_DecisionModel, FinishReason, LanguageModel, LanguageModelUsage, ModelMessage, ToolSet, TypedToolCall, TypedToolResult, generateText } from "ai";
|
|
4
4
|
import { AnyStateMachine } from "xstate";
|
|
5
5
|
//#region src/ai-sdk/mappers.d.ts
|
package/dist/ai-sdk.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { s as AgentTools, u as ChosenEvent } from "./types-Dp5_Ru4R.mjs";
|
|
2
|
-
import { N as AgentDecisionRequest, c as AgentRequestExecutorInfo, f as AgentTextRequest, r as AgentFinishReason, t as AgentCallUsage } from "./text-logic-
|
|
2
|
+
import { N as AgentDecisionRequest, c as AgentRequestExecutorInfo, f as AgentTextRequest, r as AgentFinishReason, t as AgentCallUsage } from "./text-logic-DuK9HIFM.mjs";
|
|
3
3
|
import { AnyStateMachine } from "xstate";
|
|
4
4
|
import { Experimental_DecisionModel, FinishReason, LanguageModel, LanguageModelUsage, ModelMessage, ToolSet, TypedToolCall, TypedToolResult, generateText } from "ai";
|
|
5
5
|
//#region src/ai-sdk/mappers.d.ts
|
package/dist/ai-sdk.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { R as providerOutputSchema,
|
|
2
|
-
import { c as renderDecisionAttempts,
|
|
3
|
-
import { t as resolveModelSource } from "./model-source-
|
|
1
|
+
import { R as providerOutputSchema, dt as AgentTruncatedError, j as getAgentOutputMode, lt as isRecord, tt as isStandardSchema } from "./event-log-DAqsalT1.mjs";
|
|
2
|
+
import { c as renderDecisionAttempts, o as decisionOptionIds } from "./decision-CV7WiV5C.mjs";
|
|
3
|
+
import { t as resolveModelSource } from "./model-source-BilDEyqk.mjs";
|
|
4
4
|
import { InvalidResponseDataError, NoObjectGeneratedError, Output, experimental_decide, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
|
|
5
5
|
//#region src/ai-sdk/mappers.ts
|
|
6
6
|
/**
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
const require_event_log = require("./event-log-
|
|
1
|
+
const require_event_log = require("./event-log-CKWeC7H-.cjs");
|
|
2
2
|
let xstate = require("xstate");
|
|
3
|
-
//#region src/internal/is-record.ts
|
|
4
|
-
/** True for a plain object-like value (arrays excluded). @internal */
|
|
5
|
-
function isRecord(value) {
|
|
6
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
3
|
//#region src/events.ts
|
|
10
4
|
/**
|
|
11
5
|
* Thrown by {@link parseAgentEvent} (and {@link eventFromInteraction}) when a
|
|
@@ -103,7 +97,7 @@ function isEventPattern(entry) {
|
|
|
103
97
|
* ```
|
|
104
98
|
*/
|
|
105
99
|
function parseAgentEvent(source, payload, options = {}) {
|
|
106
|
-
if (!isRecord(payload) || typeof payload.type !== "string") throw new AgentInvalidEventPayloadError("(unknown)", "expected an object with a string `type`.");
|
|
100
|
+
if (!require_event_log.isRecord(payload) || typeof payload.type !== "string") throw new AgentInvalidEventPayloadError("(unknown)", "expected an object with a string `type`.");
|
|
107
101
|
const event = payload;
|
|
108
102
|
if (isReservedAgentEvent(event.type)) throw new AgentInvalidEventPayloadError(event.type, "this event type is reserved for the library and cannot be sent from outside.");
|
|
109
103
|
const machine = typeof source.provide === "function" ? source : source.machine;
|
|
@@ -135,14 +129,18 @@ function parseAgentEvent(source, payload, options = {}) {
|
|
|
135
129
|
* ({@link RESERVED_AGENT_EVENT_PREFIX}) event types are always excluded, before
|
|
136
130
|
* any `allowedEvents` matching — a machine that handles `'@agent.usage'` still
|
|
137
131
|
* never offers it to a model.
|
|
132
|
+
*
|
|
133
|
+
* Pass `actor: "agent"` for the list a model may choose from: it also drops an
|
|
134
|
+
* event the state handles through a transition tagged `meta: { actor: "human" }`.
|
|
138
135
|
*/
|
|
139
136
|
function getAcceptedEvents(snapshot, options = {}) {
|
|
140
137
|
const eventTypes = options.eventTypes;
|
|
138
|
+
const humanEventTypes = options.actor === "agent" ? getHumanEventTypes(snapshot) : /* @__PURE__ */ new Set();
|
|
141
139
|
const seen = /* @__PURE__ */ new Set();
|
|
142
140
|
const usedToolNames = /* @__PURE__ */ new Set();
|
|
143
141
|
return (0, xstate.getNextTransitions)(snapshot).flatMap((transitionDefinition) => {
|
|
144
142
|
const eventType = transitionDefinition.eventType;
|
|
145
|
-
if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || isReservedAgentEvent(eventType) || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
|
|
143
|
+
if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || isReservedAgentEvent(eventType) || humanEventTypes.has(eventType) || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
|
|
146
144
|
seen.add(eventType);
|
|
147
145
|
const toolName = disambiguateEventToolName(sanitizeEventToolName(eventType), eventType, usedToolNames);
|
|
148
146
|
const inputSchema = (options.events ?? options.schemas?.events)?.[eventType];
|
|
@@ -153,6 +151,21 @@ function getAcceptedEvents(snapshot, options = {}) {
|
|
|
153
151
|
}];
|
|
154
152
|
});
|
|
155
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* The event types a snapshot handles through a transition tagged
|
|
156
|
+
* `meta: { actor: "human" }`: a person sends them, so no model is offered them.
|
|
157
|
+
* @internal
|
|
158
|
+
*/
|
|
159
|
+
function getHumanEventTypes(snapshot) {
|
|
160
|
+
let types = humanEventTypesBySnapshot.get(snapshot);
|
|
161
|
+
if (!types) {
|
|
162
|
+
types = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const transition of (0, xstate.getNextTransitions)(snapshot)) if (transition.meta?.actor === "human") types.add(transition.eventType);
|
|
164
|
+
humanEventTypesBySnapshot.set(snapshot, types);
|
|
165
|
+
}
|
|
166
|
+
return types;
|
|
167
|
+
}
|
|
168
|
+
const humanEventTypesBySnapshot = /* @__PURE__ */ new WeakMap();
|
|
156
169
|
//#endregion
|
|
157
170
|
//#region src/decision.ts
|
|
158
171
|
function optionsFromPayloads(eventTypes, payloads) {
|
|
@@ -172,8 +185,8 @@ function optionsFromPayloads(eventTypes, payloads) {
|
|
|
172
185
|
*/
|
|
173
186
|
function acceptedDecisionOptions(request, can) {
|
|
174
187
|
if (!request.options) return void 0;
|
|
175
|
-
const payloads = isRecord(request.input) ? request.input.payloads : void 0;
|
|
176
|
-
return (isRecord(payloads) ? optionsFromPayloads(request.events.map((event) => event.type), payloads) : request.options).filter((option) => {
|
|
188
|
+
const payloads = require_event_log.isRecord(request.input) ? request.input.payloads : void 0;
|
|
189
|
+
return (require_event_log.isRecord(payloads) ? optionsFromPayloads(request.events.map((event) => event.type), payloads) : request.options).filter((option) => {
|
|
177
190
|
try {
|
|
178
191
|
return can(option.event);
|
|
179
192
|
} catch {
|
|
@@ -565,22 +578,22 @@ Object.defineProperty(exports, "getAcceptedEvents", {
|
|
|
565
578
|
return getAcceptedEvents;
|
|
566
579
|
}
|
|
567
580
|
});
|
|
568
|
-
Object.defineProperty(exports, "
|
|
581
|
+
Object.defineProperty(exports, "getHumanEventTypes", {
|
|
569
582
|
enumerable: true,
|
|
570
583
|
get: function() {
|
|
571
|
-
return
|
|
584
|
+
return getHumanEventTypes;
|
|
572
585
|
}
|
|
573
586
|
});
|
|
574
|
-
Object.defineProperty(exports, "
|
|
587
|
+
Object.defineProperty(exports, "isDecisionLogic", {
|
|
575
588
|
enumerable: true,
|
|
576
589
|
get: function() {
|
|
577
|
-
return
|
|
590
|
+
return isDecisionLogic;
|
|
578
591
|
}
|
|
579
592
|
});
|
|
580
|
-
Object.defineProperty(exports, "
|
|
593
|
+
Object.defineProperty(exports, "isEventPattern", {
|
|
581
594
|
enumerable: true,
|
|
582
595
|
get: function() {
|
|
583
|
-
return
|
|
596
|
+
return isEventPattern;
|
|
584
597
|
}
|
|
585
598
|
});
|
|
586
599
|
Object.defineProperty(exports, "parseAgentEvent", {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import { H as getAgentExecutionOptions, T as DECIDE_ACTOR, V as executorBoundLogics, at as userMessage,
|
|
1
|
+
import { H as getAgentExecutionOptions, T as DECIDE_ACTOR, V as executorBoundLogics, at as userMessage, lt as isRecord, ot as validateSchemaSync, u as canonicalEventJson, ut as AgentError, z as resolveTextLogicValue } from "./event-log-DAqsalT1.mjs";
|
|
2
2
|
import { createAsyncLogic, getNextTransitions } from "xstate";
|
|
3
|
-
//#region src/internal/is-record.ts
|
|
4
|
-
/** True for a plain object-like value (arrays excluded). @internal */
|
|
5
|
-
function isRecord(value) {
|
|
6
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
3
|
//#region src/events.ts
|
|
10
4
|
/**
|
|
11
5
|
* Thrown by {@link parseAgentEvent} (and {@link eventFromInteraction}) when a
|
|
@@ -135,14 +129,18 @@ function parseAgentEvent(source, payload, options = {}) {
|
|
|
135
129
|
* ({@link RESERVED_AGENT_EVENT_PREFIX}) event types are always excluded, before
|
|
136
130
|
* any `allowedEvents` matching — a machine that handles `'@agent.usage'` still
|
|
137
131
|
* never offers it to a model.
|
|
132
|
+
*
|
|
133
|
+
* Pass `actor: "agent"` for the list a model may choose from: it also drops an
|
|
134
|
+
* event the state handles through a transition tagged `meta: { actor: "human" }`.
|
|
138
135
|
*/
|
|
139
136
|
function getAcceptedEvents(snapshot, options = {}) {
|
|
140
137
|
const eventTypes = options.eventTypes;
|
|
138
|
+
const humanEventTypes = options.actor === "agent" ? getHumanEventTypes(snapshot) : /* @__PURE__ */ new Set();
|
|
141
139
|
const seen = /* @__PURE__ */ new Set();
|
|
142
140
|
const usedToolNames = /* @__PURE__ */ new Set();
|
|
143
141
|
return getNextTransitions(snapshot).flatMap((transitionDefinition) => {
|
|
144
142
|
const eventType = transitionDefinition.eventType;
|
|
145
|
-
if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || isReservedAgentEvent(eventType) || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
|
|
143
|
+
if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || isReservedAgentEvent(eventType) || humanEventTypes.has(eventType) || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
|
|
146
144
|
seen.add(eventType);
|
|
147
145
|
const toolName = disambiguateEventToolName(sanitizeEventToolName(eventType), eventType, usedToolNames);
|
|
148
146
|
const inputSchema = (options.events ?? options.schemas?.events)?.[eventType];
|
|
@@ -153,6 +151,21 @@ function getAcceptedEvents(snapshot, options = {}) {
|
|
|
153
151
|
}];
|
|
154
152
|
});
|
|
155
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* The event types a snapshot handles through a transition tagged
|
|
156
|
+
* `meta: { actor: "human" }`: a person sends them, so no model is offered them.
|
|
157
|
+
* @internal
|
|
158
|
+
*/
|
|
159
|
+
function getHumanEventTypes(snapshot) {
|
|
160
|
+
let types = humanEventTypesBySnapshot.get(snapshot);
|
|
161
|
+
if (!types) {
|
|
162
|
+
types = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const transition of getNextTransitions(snapshot)) if (transition.meta?.actor === "human") types.add(transition.eventType);
|
|
164
|
+
humanEventTypesBySnapshot.set(snapshot, types);
|
|
165
|
+
}
|
|
166
|
+
return types;
|
|
167
|
+
}
|
|
168
|
+
const humanEventTypesBySnapshot = /* @__PURE__ */ new WeakMap();
|
|
156
169
|
//#endregion
|
|
157
170
|
//#region src/decision.ts
|
|
158
171
|
function optionsFromPayloads(eventTypes, payloads) {
|
|
@@ -517,4 +530,4 @@ async function resolveDecision(request, executors, options = {}) {
|
|
|
517
530
|
throw new AgentDecisionExhaustedError(attempts);
|
|
518
531
|
}
|
|
519
532
|
//#endregion
|
|
520
|
-
export { createDecisionRequest as a, renderDecisionAttempts as c, getAcceptedEvents as d,
|
|
533
|
+
export { createDecisionRequest as a, renderDecisionAttempts as c, getAcceptedEvents as d, getHumanEventTypes as f, createDecisionLogic as i, resolveDecision as l, parseAgentEvent as m, acceptedDecisionOptions as n, decisionOptionIds as o, isEventPattern as p, createDecideActor as r, isDecisionLogic as s, AgentDecisionExhaustedError as t, AgentInvalidEventPayloadError as u };
|
|
@@ -54,6 +54,31 @@ var AgentTruncatedError = class extends AgentError {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
//#endregion
|
|
57
|
+
//#region src/internal/is-record.ts
|
|
58
|
+
/** True for a plain object-like value (arrays excluded). @internal */
|
|
59
|
+
function isRecord(value) {
|
|
60
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* True for an object whose prototype is `null` or some realm's
|
|
64
|
+
* `Object.prototype`. Comparing against this realm's `Object.prototype` alone
|
|
65
|
+
* rejects plain objects built in another realm: a jest test file's vm
|
|
66
|
+
* context, or `structuredClone`'s output inside one. @internal
|
|
67
|
+
*/
|
|
68
|
+
function isPlainObject(value) {
|
|
69
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
70
|
+
const prototype = Object.getPrototypeOf(value);
|
|
71
|
+
return prototype === null || Object.getPrototypeOf(prototype) === null;
|
|
72
|
+
}
|
|
73
|
+
/** True for an `Error` from any realm, where `instanceof Error` sees only this one. @internal */
|
|
74
|
+
function isError(value) {
|
|
75
|
+
return value instanceof Error || Object.prototype.toString.call(value) === "[object Error]";
|
|
76
|
+
}
|
|
77
|
+
/** JSON text with object keys sorted and `undefined` fields dropped, for comparing JSON-like values. @internal */
|
|
78
|
+
function canonicalJson(value) {
|
|
79
|
+
return JSON.stringify(value, (_key, item) => isPlainObject(item) ? Object.fromEntries(Object.keys(item).sort().map((key) => [key, item[key]])) : item) ?? "undefined";
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
57
82
|
//#region src/utils.ts
|
|
58
83
|
/** Returns the human-readable text carried by a message, ignoring non-text parts. */
|
|
59
84
|
function getMessageText(message) {
|
|
@@ -103,8 +128,7 @@ function findNonSerializableContextPaths(context, limit = 5) {
|
|
|
103
128
|
seen.delete(obj);
|
|
104
129
|
return;
|
|
105
130
|
}
|
|
106
|
-
|
|
107
|
-
if (proto === Object.prototype || proto === null) {
|
|
131
|
+
if (isPlainObject(value)) {
|
|
108
132
|
seen.add(obj);
|
|
109
133
|
for (const [key, item] of Object.entries(value)) walk(item, `${path}.${key}`);
|
|
110
134
|
seen.delete(obj);
|
|
@@ -456,7 +480,7 @@ function responseMessagesOf(raw) {
|
|
|
456
480
|
}
|
|
457
481
|
function dropUndefined(value) {
|
|
458
482
|
if (Array.isArray(value)) return value.map(dropUndefined);
|
|
459
|
-
if (
|
|
483
|
+
if (isPlainObject(value)) {
|
|
460
484
|
const out = {};
|
|
461
485
|
for (const [key, entry] of Object.entries(value)) if (entry !== void 0) out[key] = dropUndefined(entry);
|
|
462
486
|
return out;
|
|
@@ -932,8 +956,7 @@ function assertJsonSerializable(value, path = "entry", ancestors = /* @__PURE__
|
|
|
932
956
|
assertNoHiddenKeys(value, path, (key) => key === "length" || arrayIndexPattern.test(key));
|
|
933
957
|
return;
|
|
934
958
|
}
|
|
935
|
-
|
|
936
|
-
if (prototype !== Object.prototype && prototype !== null) throw new NonSerializableAgentEventError(path, value.constructor?.name ?? "object");
|
|
959
|
+
if (!isPlainObject(value)) throw new NonSerializableAgentEventError(path, value.constructor?.name ?? "object");
|
|
937
960
|
assertNoHiddenKeys(value, path);
|
|
938
961
|
for (const [key, child] of Object.entries(value)) assertJsonSerializable(child, `${path}.${key}`, ancestors);
|
|
939
962
|
} finally {
|
|
@@ -990,22 +1013,32 @@ function exhaustedAttemptsOf(error) {
|
|
|
990
1013
|
return Array.isArray(attempts) && isJsonSerializable(attempts) ? attempts : void 0;
|
|
991
1014
|
}
|
|
992
1015
|
/**
|
|
993
|
-
*
|
|
994
|
-
*
|
|
995
|
-
*
|
|
1016
|
+
* Marks an object {@link normalizeEventErrors} made from an `Error`, so replay
|
|
1017
|
+
* restores that object, and only that one, as an `Error`: a thrown plain
|
|
1018
|
+
* `{ name, message }` stays a plain object.
|
|
1019
|
+
*/
|
|
1020
|
+
const NORMALIZED_ERROR_KEY = "@error";
|
|
1021
|
+
/**
|
|
1022
|
+
* Replaces `Error` instances with plain `{ name, message, code?, cause?,
|
|
1023
|
+
* "@error": true }` objects so an `xstate.error.actor` payload survives the
|
|
1024
|
+
* log's JSON round-trip. `code` is kept when it is a string or a number; a
|
|
1025
|
+
* decision-exhausted error also keeps its `attempts`. Replay turns the
|
|
1026
|
+
* object back into an `Error` (see `restoreJournaledEvent`). Cycles
|
|
996
1027
|
* are preserved by identity; non-plain objects are left alone (and are then
|
|
997
1028
|
* rejected by {@link assertJsonSerializable}).
|
|
998
1029
|
*/
|
|
999
1030
|
function normalizeEventErrors(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
1000
|
-
if (value
|
|
1031
|
+
if (isError(value)) {
|
|
1001
1032
|
const normalized = {
|
|
1002
1033
|
name: value.name,
|
|
1003
|
-
message: value.message
|
|
1034
|
+
message: value.message,
|
|
1035
|
+
[NORMALIZED_ERROR_KEY]: true
|
|
1004
1036
|
};
|
|
1005
1037
|
seen.set(value, normalized);
|
|
1006
1038
|
if (value.cause !== void 0) normalized.cause = normalizeEventErrors(value.cause, seen);
|
|
1007
|
-
|
|
1008
|
-
|
|
1039
|
+
const code = value.code;
|
|
1040
|
+
if (typeof code === "string" || typeof code === "number" && Number.isFinite(code)) normalized.code = code;
|
|
1041
|
+
if (code === DECISION_EXHAUSTED_CODE) {
|
|
1009
1042
|
const attempts = exhaustedAttemptsOf(value);
|
|
1010
1043
|
if (attempts !== void 0) normalized.attempts = structuredClone(attempts);
|
|
1011
1044
|
}
|
|
@@ -1020,8 +1053,7 @@ function normalizeEventErrors(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
|
1020
1053
|
for (const item of value) result.push(normalizeEventErrors(item, seen));
|
|
1021
1054
|
return result;
|
|
1022
1055
|
}
|
|
1023
|
-
|
|
1024
|
-
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
1056
|
+
if (!isPlainObject(value)) return value;
|
|
1025
1057
|
const result = {};
|
|
1026
1058
|
seen.set(value, result);
|
|
1027
1059
|
for (const [key, item] of Object.entries(value)) result[key] = normalizeEventErrors(item, seen);
|
|
@@ -1046,7 +1078,7 @@ function canonicalizeForHash(value, seen = /* @__PURE__ */ new WeakSet(), positi
|
|
|
1046
1078
|
if (typeof value === "symbol") return `[symbol:${String(value)}]`;
|
|
1047
1079
|
if (value === null || typeof value !== "object") return value;
|
|
1048
1080
|
if (value instanceof Date) return `[Date:${value.toISOString()}]`;
|
|
1049
|
-
if (value
|
|
1081
|
+
if (isError(value)) {
|
|
1050
1082
|
if (seen.has(value)) return "[circular]";
|
|
1051
1083
|
seen.add(value);
|
|
1052
1084
|
try {
|
|
@@ -1141,7 +1173,7 @@ function createReplayEntry(machine, entries, event, options = {}) {
|
|
|
1141
1173
|
recordedAt: options.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1142
1174
|
machineId: machineIdOf(machine),
|
|
1143
1175
|
machineVersion,
|
|
1144
|
-
event: normalizeDoneActorEvent(normalizeEventErrors(event)),
|
|
1176
|
+
event: normalizeDoneActorEvent(pruneUndefined(normalizeEventErrors(event))),
|
|
1145
1177
|
...options.causationId !== void 0 ? { causationId: options.causationId } : {},
|
|
1146
1178
|
...options.metadata !== void 0 ? { metadata: options.metadata } : {},
|
|
1147
1179
|
...options.attempts !== void 0 && options.attempts.length > 0 ? { attempts: options.attempts } : {}
|
|
@@ -1154,15 +1186,23 @@ function createReplayEntry(machine, entries, event, options = {}) {
|
|
|
1154
1186
|
}).persistedSnapshot) };
|
|
1155
1187
|
return entry;
|
|
1156
1188
|
}
|
|
1157
|
-
function pruneUndefined(value) {
|
|
1158
|
-
if (Array.isArray(value)) return value.map(pruneUndefined);
|
|
1189
|
+
function pruneUndefined(value, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
1159
1190
|
if (value === null || typeof value !== "object") return value;
|
|
1160
|
-
if (
|
|
1191
|
+
if (ancestors.has(value)) return value;
|
|
1192
|
+
if (Array.isArray(value)) {
|
|
1193
|
+
ancestors.add(value);
|
|
1194
|
+
const result = value.map((item) => pruneUndefined(item, ancestors));
|
|
1195
|
+
ancestors.delete(value);
|
|
1196
|
+
return result;
|
|
1197
|
+
}
|
|
1198
|
+
if (!isPlainObject(value)) return value;
|
|
1199
|
+
ancestors.add(value);
|
|
1161
1200
|
const result = {};
|
|
1162
1201
|
for (const [key, child] of Object.entries(value)) {
|
|
1163
1202
|
if (child === void 0) continue;
|
|
1164
|
-
result[key] = pruneUndefined(child);
|
|
1203
|
+
result[key] = pruneUndefined(child, ancestors);
|
|
1165
1204
|
}
|
|
1205
|
+
ancestors.delete(value);
|
|
1166
1206
|
return result;
|
|
1167
1207
|
}
|
|
1168
1208
|
/**
|
|
@@ -1199,8 +1239,8 @@ function isSnapshotInitEntry(entry) {
|
|
|
1199
1239
|
* Minted once per run and inherited verbatim by every resume of that log, so
|
|
1200
1240
|
* it names one lineage rather than one process. Entry ids cannot serve this
|
|
1201
1241
|
* role — the built-in ids are index-derived, so every log's init entry is
|
|
1202
|
-
* `evt_00000000`. A fork
|
|
1203
|
-
*
|
|
1242
|
+
* `evt_00000000`. A fork ({@link forkEventLog}, `store.fork`) mints its own
|
|
1243
|
+
* and records the parent's in `metadata.forkedFrom`.
|
|
1204
1244
|
*
|
|
1205
1245
|
* Returns `undefined` for a log with no init entry and for one written without
|
|
1206
1246
|
* the field — callers must treat the id as optional.
|
|
@@ -1248,12 +1288,31 @@ function validateReplayEntries(entries, options = {}) {
|
|
|
1248
1288
|
/**
|
|
1249
1289
|
* The prefix `[0, upToIndex)` of a log, as a new log. `upToIndex` is exclusive
|
|
1250
1290
|
* and must leave the init entry in place, so the fork is still self-contained.
|
|
1291
|
+
*
|
|
1292
|
+
* The fork is its own lineage: its init entry gets a fresh
|
|
1293
|
+
* `metadata.executionId` and `metadata.forkedFrom: { executionId?, logIndex }`:
|
|
1294
|
+
* the cutoff, and the parent's id when it had one. Call keys derive
|
|
1295
|
+
* from the execution id, so a fork's calls never collide with its parent's.
|
|
1296
|
+
* Every other entry is copied as is.
|
|
1251
1297
|
*/
|
|
1252
1298
|
function forkEventLog(entries, upToIndex) {
|
|
1253
1299
|
if (!Number.isInteger(upToIndex) || upToIndex < 1 || upToIndex > entries.length) throw new AgentEventLogError(`forkEventLog: upToIndex must be an integer in [1, ${entries.length}]; got ${String(upToIndex)}.`);
|
|
1254
1300
|
const forked = entries.slice(0, upToIndex);
|
|
1255
1301
|
if (!isInitEntry(forked[0])) throw new AgentEventLogError(`forkEventLog: the forked prefix must start with the reserved '${AGENT_INIT_EVENT_TYPE}' entry.`);
|
|
1256
|
-
|
|
1302
|
+
const [init, ...rest] = forked;
|
|
1303
|
+
const parentExecutionId = getLogExecutionId(forked);
|
|
1304
|
+
const forkedFrom = {
|
|
1305
|
+
...parentExecutionId !== void 0 ? { executionId: parentExecutionId } : {},
|
|
1306
|
+
logIndex: upToIndex
|
|
1307
|
+
};
|
|
1308
|
+
return [{
|
|
1309
|
+
...init,
|
|
1310
|
+
metadata: {
|
|
1311
|
+
...init.metadata,
|
|
1312
|
+
executionId: crypto.randomUUID(),
|
|
1313
|
+
forkedFrom
|
|
1314
|
+
}
|
|
1315
|
+
}, ...rest];
|
|
1257
1316
|
}
|
|
1258
1317
|
const DONE_ACTOR_EVENT_TYPE = "xstate.done.actor";
|
|
1259
1318
|
const ERROR_ACTOR_EVENT_TYPE = "xstate.error.actor";
|
|
@@ -1359,12 +1418,33 @@ function rebindActorSession(event, snapshot, sessions) {
|
|
|
1359
1418
|
sessionId
|
|
1360
1419
|
};
|
|
1361
1420
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1421
|
+
/**
|
|
1422
|
+
* The event as the machine received it live: a done event's dropped
|
|
1423
|
+
* `undefined` output restored, and an error event's normalized error turned
|
|
1424
|
+
* back into an `Error`, so `error instanceof Error` holds on replay too.
|
|
1425
|
+
*/
|
|
1426
|
+
function restoreJournaledEvent(event) {
|
|
1427
|
+
if (event.type === DONE_ACTOR_EVENT_TYPE && !Object.hasOwn(event, "output")) return {
|
|
1365
1428
|
...event,
|
|
1366
1429
|
output: void 0
|
|
1367
1430
|
};
|
|
1431
|
+
if (event.type.startsWith(ERROR_ACTOR_EVENT_TYPE)) {
|
|
1432
|
+
const error = event.error;
|
|
1433
|
+
if (isNormalizedError(error)) return {
|
|
1434
|
+
...event,
|
|
1435
|
+
error: toError(error)
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
return event;
|
|
1439
|
+
}
|
|
1440
|
+
function isNormalizedError(value) {
|
|
1441
|
+
return isPlainObject(value) && value[NORMALIZED_ERROR_KEY] === true && typeof value.name === "string" && typeof value.message === "string";
|
|
1442
|
+
}
|
|
1443
|
+
function toError(normalized) {
|
|
1444
|
+
const { name, message, cause, [NORMALIZED_ERROR_KEY]: _marker, ...rest } = normalized;
|
|
1445
|
+
const error = new Error(message, cause === void 0 ? void 0 : { cause: isNormalizedError(cause) ? toError(cause) : cause });
|
|
1446
|
+
error.name = name;
|
|
1447
|
+
return Object.assign(error, rest);
|
|
1368
1448
|
}
|
|
1369
1449
|
function verifyEntry(entry, persisted, mode) {
|
|
1370
1450
|
if (mode === false) return;
|
|
@@ -1412,7 +1492,7 @@ function foldLog(machine, entries, options = {}) {
|
|
|
1412
1492
|
verifyEntry(entries[0], persisted, options.verify);
|
|
1413
1493
|
const sessions = /* @__PURE__ */ new Map();
|
|
1414
1494
|
const apply = (entry) => {
|
|
1415
|
-
const event =
|
|
1495
|
+
const event = restoreJournaledEvent(rebindActorSession(entry.event, snapshot, sessions));
|
|
1416
1496
|
[snapshot] = (0, xstate.transition)(machine, snapshot, event);
|
|
1417
1497
|
persisted = machine.getPersistedSnapshot(snapshot);
|
|
1418
1498
|
return persisted;
|
|
@@ -1610,6 +1690,12 @@ Object.defineProperty(exports, "canonicalEventJson", {
|
|
|
1610
1690
|
return canonicalEventJson;
|
|
1611
1691
|
}
|
|
1612
1692
|
});
|
|
1693
|
+
Object.defineProperty(exports, "canonicalJson", {
|
|
1694
|
+
enumerable: true,
|
|
1695
|
+
get: function() {
|
|
1696
|
+
return canonicalJson;
|
|
1697
|
+
}
|
|
1698
|
+
});
|
|
1613
1699
|
Object.defineProperty(exports, "createReplayEntry", {
|
|
1614
1700
|
enumerable: true,
|
|
1615
1701
|
get: function() {
|
|
@@ -1742,6 +1828,18 @@ Object.defineProperty(exports, "initEntry", {
|
|
|
1742
1828
|
return initEntry;
|
|
1743
1829
|
}
|
|
1744
1830
|
});
|
|
1831
|
+
Object.defineProperty(exports, "isPlainObject", {
|
|
1832
|
+
enumerable: true,
|
|
1833
|
+
get: function() {
|
|
1834
|
+
return isPlainObject;
|
|
1835
|
+
}
|
|
1836
|
+
});
|
|
1837
|
+
Object.defineProperty(exports, "isRecord", {
|
|
1838
|
+
enumerable: true,
|
|
1839
|
+
get: function() {
|
|
1840
|
+
return isRecord;
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1745
1843
|
Object.defineProperty(exports, "isStandardSchema", {
|
|
1746
1844
|
enumerable: true,
|
|
1747
1845
|
get: function() {
|