@statelyai/agent 2.0.0-alpha.15 → 2.0.0-alpha.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/ai-sdk.cjs +1 -1
  2. package/dist/ai-sdk.d.cts +2 -2
  3. package/dist/ai-sdk.d.mts +2 -2
  4. package/dist/ai-sdk.mjs +1 -1
  5. package/dist/{decision-D9Zi7Xi5.mjs → decision-C11xuud2.mjs} +35 -26
  6. package/dist/{decision-C0cUKvNt.cjs → decision-DnQCQPew.cjs} +46 -31
  7. package/dist/{event-log-store-D7pWtIhb.mjs → event-log-store-B-1fcfkT.mjs} +149 -141
  8. package/dist/{event-log-store-BkUNtyOF.d.mts → event-log-store-BrC9Q1xW.d.mts} +14 -12
  9. package/dist/{event-log-store-CVd2eyRy.d.cts → event-log-store-CQJq8_v4.d.cts} +14 -12
  10. package/dist/{event-log-store-CNT_7F0V.cjs → event-log-store-yquOV1TX.cjs} +148 -140
  11. package/dist/index.cjs +498 -520
  12. package/dist/index.d.cts +130 -104
  13. package/dist/index.d.mts +130 -104
  14. package/dist/index.mjs +497 -519
  15. package/dist/machines.cjs +1 -1
  16. package/dist/machines.d.cts +1 -1
  17. package/dist/machines.d.mts +1 -1
  18. package/dist/machines.mjs +1 -1
  19. package/dist/otel.d.cts +1 -1
  20. package/dist/otel.d.mts +1 -1
  21. package/dist/{run-agent-B_n4Qxye.d.cts → run-agent-BxjGaVpL.d.cts} +49 -109
  22. package/dist/{run-agent-BWzo4FLv.d.mts → run-agent-COHoCgQd.d.mts} +49 -109
  23. package/dist/{setup-agent-DeHRW-qX.mjs → setup-agent-CTg57Pa4.mjs} +154 -167
  24. package/dist/{setup-agent-CpK0ZRWV.cjs → setup-agent-D_EyJ0Ik.cjs} +159 -172
  25. package/dist/sqlite.cjs +1 -1
  26. package/dist/sqlite.d.cts +2 -2
  27. package/dist/sqlite.d.mts +2 -2
  28. package/dist/sqlite.mjs +1 -1
  29. package/dist/{text-logic-DZW7XWy9.d.cts → text-logic-BFX5q7fM.d.cts} +23 -2
  30. package/dist/{text-logic-C7anC7qX.d.mts → text-logic-DQW8_DWW.d.mts} +23 -2
  31. package/dist/{types-DFD28AWe.d.cts → types-DYpK3QF4.d.mts} +7 -6
  32. package/dist/{types-CTBhMnFu.d.mts → types-pJ5Hn8fv.d.cts} +7 -6
  33. package/package.json +28 -28
  34. package/readme.md +6 -19
  35. package/schemas/agent-workflow.json +9 -4
package/dist/ai-sdk.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_decision = require("./decision-C0cUKvNt.cjs");
2
+ const require_decision = require("./decision-DnQCQPew.cjs");
3
3
  let ai = require("ai");
4
4
  //#region src/ai-sdk/mappers.ts
5
5
  /**
package/dist/ai-sdk.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as ChosenEvent } from "./types-DFD28AWe.cjs";
2
- import { k as AgentDecisionExecutor, l as AgentRequestExecutors, o as AgentRequestExecutor } from "./text-logic-DZW7XWy9.cjs";
1
+ import { d as ChosenEvent } from "./types-pJ5Hn8fv.cjs";
2
+ import { A as AgentDecisionExecutor, l as AgentRequestExecutors, o as AgentRequestExecutor } from "./text-logic-BFX5q7fM.cjs";
3
3
  import { FinishReason, LanguageModel, LanguageModelUsage, ToolSet, TypedToolCall, TypedToolResult } from "ai";
4
4
 
5
5
  //#region src/ai-sdk/index.d.ts
package/dist/ai-sdk.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { d as ChosenEvent } from "./types-CTBhMnFu.mjs";
2
- import { k as AgentDecisionExecutor, l as AgentRequestExecutors, o as AgentRequestExecutor } from "./text-logic-C7anC7qX.mjs";
1
+ import { d as ChosenEvent } from "./types-DYpK3QF4.mjs";
2
+ import { A as AgentDecisionExecutor, l as AgentRequestExecutors, o as AgentRequestExecutor } from "./text-logic-DQW8_DWW.mjs";
3
3
  import { FinishReason, LanguageModel, LanguageModelUsage, ToolSet, TypedToolCall, TypedToolResult } from "ai";
4
4
 
5
5
  //#region src/ai-sdk/index.d.ts
package/dist/ai-sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { V as isStandardSchema, i as renderDecisionAttempts, p as buildEnvelopeSchema, v as getAgentOutputMode } from "./decision-D9Zi7Xi5.mjs";
1
+ import { H as isStandardSchema, _ as getAgentOutputMode, i as renderDecisionAttempts, p as buildEnvelopeSchema } from "./decision-C11xuud2.mjs";
2
2
  import { NoObjectGeneratedError, Output, generateText, stepCountIs, streamText, tool } from "ai";
3
3
  //#region src/ai-sdk/mappers.ts
4
4
  /**
@@ -98,6 +98,12 @@ function stableStructuralString(value, seen = /* @__PURE__ */ new WeakSet()) {
98
98
  seen.delete(obj);
99
99
  return out;
100
100
  }
101
+ /**
102
+ * djb2 → unsigned 32-bit → 8-char hex. No dependencies. A change detector, not
103
+ * a cryptographic digest.
104
+ *
105
+ * @internal
106
+ */
101
107
  function djb2Hex(input) {
102
108
  let hash = 5381;
103
109
  for (let i = 0; i < input.length; i++) hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
@@ -226,9 +232,10 @@ const agentExecutionOptions = /* @__PURE__ */ new WeakMap();
226
232
  * object. `config` is shared by reference across `machine.provide(...)` (unlike
227
233
  * the machine object itself), so a predicate registered here travels with the
228
234
  * machine through `.provide` — which is why it is keyed on `config`, not the
229
- * machine. Set by `setupAgent({ isSuspended })` in `createMachine`, read by
230
- * `runAgent` (below the host `options.isSuspended` override, above the timing
231
- * heuristic).
235
+ * machine. Set by `setupAgent({ isSuspended })` in `createMachine` and by
236
+ * `setupAgent.fromConfig` (its `isSuspended` option or the config's
237
+ * `suspendedTags`), read by `runAgent` (below the host `options.isSuspended`
238
+ * override, above the timing heuristic).
232
239
  */
233
240
  const machineSuspensionPredicates = /* @__PURE__ */ new WeakMap();
234
241
  /** Reads the {@link machineSuspensionPredicates} predicate carried by `machine` (via its root `config`), if any. */
@@ -313,15 +320,26 @@ const AGENT_USAGE_TOKEN_FIELDS = [
313
320
  "cachedInputTokens"
314
321
  ];
315
322
  /**
316
- * Reads a per-call {@link AgentCallUsage} off a raw executor result's `usage`
317
- * field, keeping only finite numbers. Returns `undefined` when the result
318
- * reports no usage at all. Works for our `{ output, usage }` envelope, for a
319
- * raw Vercel AI SDK result (its `LanguageModelUsage` carries the same flat
320
- * field names), and for any custom executor that follows the shape.
323
+ * Reads a settled call's per-call {@link AgentCallUsage} off a RAW executor
324
+ * result's `usage` field, keeping only finite numbers the same normalization
325
+ * `runAgent` applies before it delivers `'@agent.usage'`. Returns `undefined`
326
+ * when the result reports no usage at all. Works for our `{ output, usage }`
327
+ * envelope, for a raw Vercel AI SDK result (its `LanguageModelUsage` carries
328
+ * the same flat field names), and for any custom executor that follows the
329
+ * shape.
321
330
  *
322
- * @internal
331
+ * The seam for the step-loop path, where the host holds the raw result itself:
332
+ *
333
+ * ```ts
334
+ * const { output, raw } = await executeAgentRequest(effect, executors);
335
+ * const usage = getCallUsage(raw);
336
+ * if (usage) append({ type: AGENT_USAGE_EVENT_TYPE, usage }); // journal + transition, like any event
337
+ * append(effect.toDoneEvent(output));
338
+ * ```
339
+ *
340
+ * See "Token usage on this path" in docs/steps.md for the full loop.
323
341
  */
324
- function extractCallUsage(raw) {
342
+ function getCallUsage(raw) {
325
343
  const usage = raw?.usage;
326
344
  if (!usage || typeof usage !== "object") return;
327
345
  let out;
@@ -344,13 +362,14 @@ const agentTextInputSchema = { "~standard": {
344
362
  return { value: request };
345
363
  }
346
364
  } };
347
- const unknownOutputSchema = { "~standard": {
365
+ const passthroughSchema = { "~standard": {
348
366
  version: 1,
349
367
  vendor: "statelyai-agent",
350
368
  validate(value) {
351
369
  return { value };
352
370
  }
353
371
  } };
372
+ const unknownOutputSchema = passthroughSchema;
354
373
  const stringOutputSchema = { "~standard": {
355
374
  version: 1,
356
375
  vendor: "statelyai-agent",
@@ -358,13 +377,7 @@ const stringOutputSchema = { "~standard": {
358
377
  return typeof value === "string" ? { value } : { issues: [{ message: "Expected string output" }] };
359
378
  }
360
379
  } };
361
- const noInputSchema = { "~standard": {
362
- version: 1,
363
- vendor: "statelyai-agent",
364
- validate(value) {
365
- return { value };
366
- }
367
- } };
380
+ const noInputSchema = passthroughSchema;
368
381
  function createBuiltinTextActor(src, mode, outputSchema) {
369
382
  const logic = createAsyncLogic({ run: async () => {
370
383
  throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }) or execute the returned agent request with executeAgentRequest(...).`);
@@ -384,7 +397,7 @@ function createBuiltinTextActor(src, mode, outputSchema) {
384
397
  return validateSchemaSync(outputSchema, output);
385
398
  },
386
399
  withExecutor(execute) {
387
- return Object.assign(createTextLogic({
400
+ return createTextLogic({
388
401
  mode,
389
402
  schemas: {
390
403
  input: agentTextInputSchema,
@@ -405,7 +418,7 @@ function createBuiltinTextActor(src, mode, outputSchema) {
405
418
  seed: ({ input }) => input.seed,
406
419
  stopSequences: ({ input }) => input.stopSequences,
407
420
  metadata: ({ input }) => input.metadata
408
- }, execute));
421
+ }, execute);
409
422
  }
410
423
  });
411
424
  }
@@ -552,7 +565,7 @@ function isTextLogic(value) {
552
565
  * as `'text'`.
553
566
  */
554
567
  function getAgentOutputMode(schema) {
555
- const jsonSchema = getStandardSchemaJson(schema);
568
+ const jsonSchema = getJsonSchemaSync(schema);
556
569
  if (!jsonSchema) return "text";
557
570
  if (jsonSchema.type === "object" || jsonSchema.type === "array") return "structured";
558
571
  if (jsonSchema.type === void 0 && ("anyOf" in jsonSchema || "oneOf" in jsonSchema || "allOf" in jsonSchema)) return "structured";
@@ -616,10 +629,6 @@ function parseStructuredEnvelope(request, value) {
616
629
  if (!request.outputSchema) throw new Error("parseStructuredEnvelope: the request declares no outputSchema.");
617
630
  return validateSchemaSync(buildEnvelopeSchema(request.outputSchema, { reasoning: request.reasoning }), value);
618
631
  }
619
- function getStandardSchemaJson(schema) {
620
- const jsonSchema = (schema?.["~standard"])?.jsonSchema?.input?.();
621
- return jsonSchema && !(jsonSchema instanceof Promise) ? jsonSchema : void 0;
622
- }
623
632
  /**
624
633
  * Merges request-declared and call-site `tools`, dispatches to the
625
634
  * `mode`-appropriate executor (`generateText`/`streamText`), and normalizes
@@ -1000,4 +1009,4 @@ async function resolveDecision(request, executor, options = {}) {
1000
1009
  throw new AgentDecisionExhaustedError(attempts);
1001
1010
  }
1002
1011
  //#endregion
1003
- export { isUnboundPlaceholder as A, getStateMeta as B, parseStructuredEnvelope as C, getMachineStaticTransitionTargets as D, executorBoundLogics as E, findNonSerializableContextPaths as F, userMessage as G, persistSnapshot as H, getAgentMessages as I, validateSchemaSync as K, getJsonSchema as L, machineSuspensionPredicates as M, missingActor as N, getMachineSuspensionPredicate as O, assistantMessage as P, getJsonSchemaSync as R, parseOutput as S, agentExecutionOptions as T, systemMessage as U, isStandardSchema as V, toolMessage as W, extractCallUsage as _, resolveDecision as a, normalizeGeneratorResult as b, AGENT_USAGE_TOKEN_FIELDS as c, USER_INPUT_ACTOR as d, bindRequestExecutor as f, executeAgentTextRequest as g, createTextLogic as h, renderDecisionAttempts as i, machineStaticTransitionTargets as j, getRegisteredAgentExecutionOptions as k, DECIDE_ACTOR as l, builtinTextActors as m, createDecideActor as n, getAcceptedEvents as o, buildEnvelopeSchema as p, isDecisionLogic as r, parseAgentEvent as s, AgentDecisionExhaustedError as t, INTERPRET_SOURCE as u, getAgentOutputMode as v, userInputActor as w, parseModelRef as x, isTextLogic as y, getMachineStructuralHash as z };
1012
+ export { isUnboundPlaceholder as A, getMachineStructuralHash as B, parseStructuredEnvelope as C, getMachineStaticTransitionTargets as D, executorBoundLogics as E, djb2Hex as F, toolMessage as G, isStandardSchema as H, findNonSerializableContextPaths as I, userMessage as K, getAgentMessages as L, machineSuspensionPredicates as M, missingActor as N, getMachineSuspensionPredicate as O, assistantMessage as P, getJsonSchema as R, parseOutput as S, agentExecutionOptions as T, persistSnapshot as U, getStateMeta as V, systemMessage as W, getAgentOutputMode as _, resolveDecision as a, normalizeGeneratorResult as b, AGENT_USAGE_TOKEN_FIELDS as c, USER_INPUT_ACTOR as d, bindRequestExecutor as f, executeAgentTextRequest as g, createTextLogic as h, renderDecisionAttempts as i, machineStaticTransitionTargets as j, getRegisteredAgentExecutionOptions as k, DECIDE_ACTOR as l, builtinTextActors as m, createDecideActor as n, getAcceptedEvents as o, buildEnvelopeSchema as p, validateSchemaSync as q, isDecisionLogic as r, parseAgentEvent as s, AgentDecisionExhaustedError as t, INTERPRET_SOURCE as u, getCallUsage as v, userInputActor as w, parseModelRef as x, isTextLogic as y, getJsonSchemaSync as z };
@@ -98,6 +98,12 @@ function stableStructuralString(value, seen = /* @__PURE__ */ new WeakSet()) {
98
98
  seen.delete(obj);
99
99
  return out;
100
100
  }
101
+ /**
102
+ * djb2 → unsigned 32-bit → 8-char hex. No dependencies. A change detector, not
103
+ * a cryptographic digest.
104
+ *
105
+ * @internal
106
+ */
101
107
  function djb2Hex(input) {
102
108
  let hash = 5381;
103
109
  for (let i = 0; i < input.length; i++) hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
@@ -226,9 +232,10 @@ const agentExecutionOptions = /* @__PURE__ */ new WeakMap();
226
232
  * object. `config` is shared by reference across `machine.provide(...)` (unlike
227
233
  * the machine object itself), so a predicate registered here travels with the
228
234
  * machine through `.provide` — which is why it is keyed on `config`, not the
229
- * machine. Set by `setupAgent({ isSuspended })` in `createMachine`, read by
230
- * `runAgent` (below the host `options.isSuspended` override, above the timing
231
- * heuristic).
235
+ * machine. Set by `setupAgent({ isSuspended })` in `createMachine` and by
236
+ * `setupAgent.fromConfig` (its `isSuspended` option or the config's
237
+ * `suspendedTags`), read by `runAgent` (below the host `options.isSuspended`
238
+ * override, above the timing heuristic).
232
239
  */
233
240
  const machineSuspensionPredicates = /* @__PURE__ */ new WeakMap();
234
241
  /** Reads the {@link machineSuspensionPredicates} predicate carried by `machine` (via its root `config`), if any. */
@@ -313,15 +320,26 @@ const AGENT_USAGE_TOKEN_FIELDS = [
313
320
  "cachedInputTokens"
314
321
  ];
315
322
  /**
316
- * Reads a per-call {@link AgentCallUsage} off a raw executor result's `usage`
317
- * field, keeping only finite numbers. Returns `undefined` when the result
318
- * reports no usage at all. Works for our `{ output, usage }` envelope, for a
319
- * raw Vercel AI SDK result (its `LanguageModelUsage` carries the same flat
320
- * field names), and for any custom executor that follows the shape.
323
+ * Reads a settled call's per-call {@link AgentCallUsage} off a RAW executor
324
+ * result's `usage` field, keeping only finite numbers the same normalization
325
+ * `runAgent` applies before it delivers `'@agent.usage'`. Returns `undefined`
326
+ * when the result reports no usage at all. Works for our `{ output, usage }`
327
+ * envelope, for a raw Vercel AI SDK result (its `LanguageModelUsage` carries
328
+ * the same flat field names), and for any custom executor that follows the
329
+ * shape.
321
330
  *
322
- * @internal
331
+ * The seam for the step-loop path, where the host holds the raw result itself:
332
+ *
333
+ * ```ts
334
+ * const { output, raw } = await executeAgentRequest(effect, executors);
335
+ * const usage = getCallUsage(raw);
336
+ * if (usage) append({ type: AGENT_USAGE_EVENT_TYPE, usage }); // journal + transition, like any event
337
+ * append(effect.toDoneEvent(output));
338
+ * ```
339
+ *
340
+ * See "Token usage on this path" in docs/steps.md for the full loop.
323
341
  */
324
- function extractCallUsage(raw) {
342
+ function getCallUsage(raw) {
325
343
  const usage = raw?.usage;
326
344
  if (!usage || typeof usage !== "object") return;
327
345
  let out;
@@ -344,13 +362,14 @@ const agentTextInputSchema = { "~standard": {
344
362
  return { value: request };
345
363
  }
346
364
  } };
347
- const unknownOutputSchema = { "~standard": {
365
+ const passthroughSchema = { "~standard": {
348
366
  version: 1,
349
367
  vendor: "statelyai-agent",
350
368
  validate(value) {
351
369
  return { value };
352
370
  }
353
371
  } };
372
+ const unknownOutputSchema = passthroughSchema;
354
373
  const stringOutputSchema = { "~standard": {
355
374
  version: 1,
356
375
  vendor: "statelyai-agent",
@@ -358,13 +377,7 @@ const stringOutputSchema = { "~standard": {
358
377
  return typeof value === "string" ? { value } : { issues: [{ message: "Expected string output" }] };
359
378
  }
360
379
  } };
361
- const noInputSchema = { "~standard": {
362
- version: 1,
363
- vendor: "statelyai-agent",
364
- validate(value) {
365
- return { value };
366
- }
367
- } };
380
+ const noInputSchema = passthroughSchema;
368
381
  function createBuiltinTextActor(src, mode, outputSchema) {
369
382
  const logic = (0, xstate.createAsyncLogic)({ run: async () => {
370
383
  throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }) or execute the returned agent request with executeAgentRequest(...).`);
@@ -384,7 +397,7 @@ function createBuiltinTextActor(src, mode, outputSchema) {
384
397
  return validateSchemaSync(outputSchema, output);
385
398
  },
386
399
  withExecutor(execute) {
387
- return Object.assign(createTextLogic({
400
+ return createTextLogic({
388
401
  mode,
389
402
  schemas: {
390
403
  input: agentTextInputSchema,
@@ -405,7 +418,7 @@ function createBuiltinTextActor(src, mode, outputSchema) {
405
418
  seed: ({ input }) => input.seed,
406
419
  stopSequences: ({ input }) => input.stopSequences,
407
420
  metadata: ({ input }) => input.metadata
408
- }, execute));
421
+ }, execute);
409
422
  }
410
423
  });
411
424
  }
@@ -552,7 +565,7 @@ function isTextLogic(value) {
552
565
  * as `'text'`.
553
566
  */
554
567
  function getAgentOutputMode(schema) {
555
- const jsonSchema = getStandardSchemaJson(schema);
568
+ const jsonSchema = getJsonSchemaSync(schema);
556
569
  if (!jsonSchema) return "text";
557
570
  if (jsonSchema.type === "object" || jsonSchema.type === "array") return "structured";
558
571
  if (jsonSchema.type === void 0 && ("anyOf" in jsonSchema || "oneOf" in jsonSchema || "allOf" in jsonSchema)) return "structured";
@@ -616,10 +629,6 @@ function parseStructuredEnvelope(request, value) {
616
629
  if (!request.outputSchema) throw new Error("parseStructuredEnvelope: the request declares no outputSchema.");
617
630
  return validateSchemaSync(buildEnvelopeSchema(request.outputSchema, { reasoning: request.reasoning }), value);
618
631
  }
619
- function getStandardSchemaJson(schema) {
620
- const jsonSchema = (schema?.["~standard"])?.jsonSchema?.input?.();
621
- return jsonSchema && !(jsonSchema instanceof Promise) ? jsonSchema : void 0;
622
- }
623
632
  /**
624
633
  * Merges request-declared and call-site `tools`, dispatches to the
625
634
  * `mode`-appropriate executor (`generateText`/`streamText`), and normalizes
@@ -1072,22 +1081,22 @@ Object.defineProperty(exports, "createTextLogic", {
1072
1081
  return createTextLogic;
1073
1082
  }
1074
1083
  });
1075
- Object.defineProperty(exports, "executeAgentTextRequest", {
1084
+ Object.defineProperty(exports, "djb2Hex", {
1076
1085
  enumerable: true,
1077
1086
  get: function() {
1078
- return executeAgentTextRequest;
1087
+ return djb2Hex;
1079
1088
  }
1080
1089
  });
1081
- Object.defineProperty(exports, "executorBoundLogics", {
1090
+ Object.defineProperty(exports, "executeAgentTextRequest", {
1082
1091
  enumerable: true,
1083
1092
  get: function() {
1084
- return executorBoundLogics;
1093
+ return executeAgentTextRequest;
1085
1094
  }
1086
1095
  });
1087
- Object.defineProperty(exports, "extractCallUsage", {
1096
+ Object.defineProperty(exports, "executorBoundLogics", {
1088
1097
  enumerable: true,
1089
1098
  get: function() {
1090
- return extractCallUsage;
1099
+ return executorBoundLogics;
1091
1100
  }
1092
1101
  });
1093
1102
  Object.defineProperty(exports, "findNonSerializableContextPaths", {
@@ -1114,6 +1123,12 @@ Object.defineProperty(exports, "getAgentOutputMode", {
1114
1123
  return getAgentOutputMode;
1115
1124
  }
1116
1125
  });
1126
+ Object.defineProperty(exports, "getCallUsage", {
1127
+ enumerable: true,
1128
+ get: function() {
1129
+ return getCallUsage;
1130
+ }
1131
+ });
1117
1132
  Object.defineProperty(exports, "getJsonSchema", {
1118
1133
  enumerable: true,
1119
1134
  get: function() {
@@ -1,146 +1,12 @@
1
1
  import { t as AgentError } from "./errors-CeSXQx0v.mjs";
2
- //#region src/event-log-store.ts
3
- /** The durable replay-entry envelope version. */
4
- const AGENT_EVENT_SCHEMA_VERSION = 1;
5
- /** A precise failure when an entry would not survive a JSON round-trip. */
6
- var NonSerializableAgentEventError = class extends AgentError {
7
- path;
8
- valueType;
9
- constructor(path, valueType) {
10
- super("non-serializable-event", `Agent event field '${path}' is not JSON-serializable (${valueType}).`);
11
- this.name = "NonSerializableAgentEventError";
12
- this.path = path;
13
- this.valueType = valueType;
14
- }
15
- };
16
- /**
17
- * Rejects values JSON would drop or coerce. Unlike `JSON.stringify`, this does
18
- * not silently erase `undefined`/functions or turn non-finite numbers into
19
- * `null`; durable entries contain only plain JSON values.
20
- */
21
- function assertJsonSerializable(value, path = "entry", ancestors = /* @__PURE__ */ new WeakSet()) {
22
- if (value === null || typeof value === "string" || typeof value === "boolean") return;
23
- if (typeof value === "number") {
24
- if (!Number.isFinite(value) || Object.is(value, -0)) throw new NonSerializableAgentEventError(path, Object.is(value, -0) ? "-0" : String(value));
25
- return;
26
- }
27
- if (typeof value !== "object") throw new NonSerializableAgentEventError(path, typeof value);
28
- if (ancestors.has(value)) throw new NonSerializableAgentEventError(path, "circular reference");
29
- ancestors.add(value);
30
- if (Array.isArray(value)) {
31
- for (let index = 0; index < value.length; index++) {
32
- if (!Object.hasOwn(value, index)) throw new NonSerializableAgentEventError(`${path}[${index}]`, "array hole");
33
- assertJsonSerializable(value[index], `${path}[${index}]`, ancestors);
34
- }
35
- const extraKey = Object.keys(value).find((key) => !/^(?:0|[1-9]\d*)$/.test(key) || Number(key) >= value.length);
36
- if (extraKey !== void 0) throw new NonSerializableAgentEventError(`${path}.${extraKey}`, "array property");
37
- const symbols = Object.getOwnPropertySymbols(value);
38
- if (symbols.length > 0) throw new NonSerializableAgentEventError(`${path}.[${String(symbols[0])}]`, "symbol key");
39
- const hiddenKey = Object.getOwnPropertyNames(value).find((key) => key !== "length" && !/^(?:0|[1-9]\d*)$/.test(key) && !Object.getOwnPropertyDescriptor(value, key)?.enumerable);
40
- if (hiddenKey !== void 0) throw new NonSerializableAgentEventError(`${path}.${hiddenKey}`, "non-enumerable property");
41
- ancestors.delete(value);
42
- return;
43
- }
44
- const prototype = Object.getPrototypeOf(value);
45
- if (prototype !== Object.prototype && prototype !== null) {
46
- const type = value.constructor?.name ?? "object";
47
- ancestors.delete(value);
48
- throw new NonSerializableAgentEventError(path, type);
49
- }
50
- const symbols = Object.getOwnPropertySymbols(value);
51
- if (symbols.length > 0) {
52
- ancestors.delete(value);
53
- throw new NonSerializableAgentEventError(`${path}.[${String(symbols[0])}]`, "symbol key");
54
- }
55
- const hiddenKey = Object.getOwnPropertyNames(value).find((key) => !Object.getOwnPropertyDescriptor(value, key)?.enumerable);
56
- if (hiddenKey !== void 0) {
57
- ancestors.delete(value);
58
- throw new NonSerializableAgentEventError(`${path}.${hiddenKey}`, "non-enumerable property");
59
- }
60
- for (const [key, child] of Object.entries(value)) assertJsonSerializable(child, `${path}.${key}`, ancestors);
61
- ancestors.delete(value);
62
- }
63
- /** Validates the complete durable envelope before append/export/replay. */
64
- function assertAgentLogEntry(entry) {
65
- if (entry === null || typeof entry !== "object" || Array.isArray(entry)) throw new Error("Agent event entry must be an object.");
66
- assertJsonSerializable(entry);
67
- const candidate = entry;
68
- if (candidate.schemaVersion !== 1) throw new Error(`Unsupported agent event schema version '${String(candidate.schemaVersion)}'; expected '1'.`);
69
- if (!Number.isInteger(candidate.index) || candidate.index < 0) throw new Error(`Agent event entry.index must be a non-negative integer; got ${String(candidate.index)}.`);
70
- if (typeof candidate.id !== "string" || !candidate.id || typeof candidate.machineId !== "string" || !candidate.machineId || typeof candidate.machineVersion !== "string" || !candidate.machineVersion) throw new Error("Agent event entry requires non-empty id, machineId, and machineVersion.");
71
- if (typeof candidate.recordedAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(candidate.recordedAt) || Number.isNaN(Date.parse(candidate.recordedAt))) throw new Error(`Agent event entry.recordedAt is not RFC 3339: '${String(candidate.recordedAt)}'.`);
72
- if (candidate.event === null || typeof candidate.event !== "object" || Array.isArray(candidate.event) || typeof candidate.event.type !== "string" || !candidate.event.type) throw new Error("Agent event entry.event requires a non-empty string type.");
73
- if (candidate.causationId !== void 0 && (typeof candidate.causationId !== "string" || !candidate.causationId)) throw new Error("Agent event entry.causationId must be a non-empty string when present.");
74
- if (candidate.correlationId !== void 0 && (typeof candidate.correlationId !== "string" || !candidate.correlationId)) throw new Error("Agent event entry.correlationId must be a non-empty string when present.");
75
- if (candidate.metadata !== void 0 && (candidate.metadata === null || typeof candidate.metadata !== "object" || Array.isArray(candidate.metadata))) throw new Error("Agent event entry.metadata must be an object when present.");
76
- if (candidate.verification !== void 0 && (candidate.verification === null || typeof candidate.verification !== "object" || typeof candidate.verification.stateHash !== "string" || !candidate.verification.stateHash || typeof candidate.verification.effectsHash !== "string" || !candidate.verification.effectsHash)) throw new Error("Agent event entry.verification requires non-empty stateHash and effectsHash strings.");
77
- }
78
- /**
79
- * Rejection from {@link AgentEventLogStore.append} when the thread's stored
80
- * length is not the `expectedIndex` the writer held — a concurrent writer
81
- * appended first. `actualLength` is the length core found.
82
- */
83
- var AgentEventLogConflictError = class extends AgentError {
84
- threadId;
85
- expectedIndex;
86
- actualLength;
87
- constructor(threadId, expectedIndex, actualLength) {
88
- super("event-log-conflict", `AgentEventLogStore.append: length conflict on thread "${threadId}": expected length ${expectedIndex} but found ${actualLength} — a concurrent writer won.`);
89
- this.name = "AgentEventLogConflictError";
90
- this.threadId = threadId;
91
- this.expectedIndex = expectedIndex;
92
- this.actualLength = actualLength;
93
- }
94
- };
2
+ //#region src/event-log-store-conformance.ts
95
3
  /**
96
- * In-memory reference store, and the baseline the conformance suite runs
97
- * against. Per-thread entries are held in a contiguous, index-ordered list;
98
- * append's check-and-push runs synchronously (no `await` between reading the
99
- * length and writing), so two appends racing on the same `expectedIndex`
100
- * resolve to exactly one winner. Every stored and returned entry is
101
- * `structuredClone`d, so a caller can neither mutate stored state through a
102
- * value it appended nor through a value it read.
4
+ * The runner-agnostic conformance suite for {@link AgentEventLogStore}
5
+ * implementations. Split out of `event-log-store.ts` so the store contract and
6
+ * the reference implementation stay readable; re-exported from there (and from
7
+ * the package root) so the public surface is unchanged.
8
+ * @module
103
9
  */
104
- function createInMemoryEventLogStore() {
105
- const threads = /* @__PURE__ */ new Map();
106
- const clone = (value) => structuredClone(value);
107
- return {
108
- async append({ threadId, expectedIndex, entries }) {
109
- for (const entry of entries) assertAgentLogEntry(entry);
110
- for (let i = 0; i < entries.length; i++) if (entries[i].index !== expectedIndex + i) throw new Error(`AgentEventLogStore.append: entry.index (${entries[i].index}) must be contiguous from expectedIndex (${expectedIndex}); expected ${expectedIndex + i} at position ${i} on thread "${threadId}".`);
111
- const list = threads.get(threadId);
112
- const length = list ? list.length : 0;
113
- if (length !== expectedIndex) throw new AgentEventLogConflictError(threadId, expectedIndex, length);
114
- const ids = new Set((list ?? []).map((entry) => entry.id));
115
- for (const entry of entries) {
116
- if (ids.has(entry.id)) throw new Error(`AgentEventLogStore.append: duplicate event id "${entry.id}" in thread "${threadId}".`);
117
- ids.add(entry.id);
118
- }
119
- const cloned = entries.map((entry) => clone(entry));
120
- if (list) for (const entry of cloned) list.push(entry);
121
- else threads.set(threadId, cloned);
122
- },
123
- async read(threadId, options) {
124
- const list = threads.get(threadId) ?? [];
125
- const from = options?.from ?? 0;
126
- return list.slice(from).map((entry) => clone(entry));
127
- },
128
- async length(threadId) {
129
- return threads.get(threadId)?.length ?? 0;
130
- },
131
- async fork({ threadId, newThreadId, upToIndex, atEventId }) {
132
- if ((threads.get(newThreadId)?.length ?? 0) > 0) throw new Error(`AgentEventLogStore.fork: newThreadId "${newThreadId}" already has entries.`);
133
- const source = threads.get(threadId);
134
- if (!source) throw new Error(`AgentEventLogStore.fork: unknown source thread "${threadId}".`);
135
- if (upToIndex !== void 0 && atEventId !== void 0) throw new Error("AgentEventLogStore.fork: pass either upToIndex or atEventId, not both.");
136
- const eventIndex = atEventId === void 0 ? void 0 : source.findIndex((entry) => entry.id === atEventId);
137
- if (atEventId !== void 0 && eventIndex === -1) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" has no event id "${atEventId}".`);
138
- const upTo = eventIndex === void 0 ? upToIndex ?? source.length : eventIndex + 1;
139
- if (upTo < 0 || upTo > source.length) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" (length ${source.length}) has no index ${upTo} to fork up to.`);
140
- threads.set(newThreadId, source.slice(0, upTo).map((entry) => clone(entry)));
141
- }
142
- };
143
- }
144
10
  function fail(message) {
145
11
  throw new Error(`event-log-store conformance: ${message}`);
146
12
  }
@@ -408,4 +274,146 @@ async function assertEventLogStoreConformance(create) {
408
274
  }
409
275
  }
410
276
  //#endregion
411
- export { assertEventLogStoreConformance as a, assertAgentLogEntry as i, AgentEventLogConflictError as n, assertJsonSerializable as o, NonSerializableAgentEventError as r, createInMemoryEventLogStore as s, AGENT_EVENT_SCHEMA_VERSION as t };
277
+ //#region src/event-log-store.ts
278
+ /** The durable replay-entry envelope version. */
279
+ const AGENT_EVENT_SCHEMA_VERSION = 1;
280
+ /** A precise failure when an entry would not survive a JSON round-trip. */
281
+ var NonSerializableAgentEventError = class extends AgentError {
282
+ path;
283
+ valueType;
284
+ constructor(path, valueType) {
285
+ super("non-serializable-event", `Agent event field '${path}' is not JSON-serializable (${valueType}).`);
286
+ this.name = "NonSerializableAgentEventError";
287
+ this.path = path;
288
+ this.valueType = valueType;
289
+ }
290
+ };
291
+ function assertNoHiddenKeys(value, path, isOwnKey) {
292
+ const symbols = Object.getOwnPropertySymbols(value);
293
+ if (symbols.length > 0) throw new NonSerializableAgentEventError(`${path}.[${String(symbols[0])}]`, "symbol key");
294
+ const hiddenKey = Object.getOwnPropertyNames(value).find((key) => !isOwnKey?.(key) && !Object.getOwnPropertyDescriptor(value, key)?.enumerable);
295
+ if (hiddenKey !== void 0) throw new NonSerializableAgentEventError(`${path}.${hiddenKey}`, "non-enumerable property");
296
+ }
297
+ const arrayIndexPattern = /^(?:0|[1-9]\d*)$/;
298
+ /**
299
+ * Rejects values JSON would drop or coerce. Unlike `JSON.stringify`, this does
300
+ * not silently erase `undefined`/functions or turn non-finite numbers into
301
+ * `null`; durable entries contain only plain JSON values.
302
+ */
303
+ function assertJsonSerializable(value, path = "entry", ancestors = /* @__PURE__ */ new WeakSet()) {
304
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
305
+ if (typeof value === "number") {
306
+ if (!Number.isFinite(value) || Object.is(value, -0)) throw new NonSerializableAgentEventError(path, Object.is(value, -0) ? "-0" : String(value));
307
+ return;
308
+ }
309
+ if (typeof value !== "object") throw new NonSerializableAgentEventError(path, typeof value);
310
+ if (ancestors.has(value)) throw new NonSerializableAgentEventError(path, "circular reference");
311
+ ancestors.add(value);
312
+ try {
313
+ if (Array.isArray(value)) {
314
+ for (let index = 0; index < value.length; index++) {
315
+ if (!Object.hasOwn(value, index)) throw new NonSerializableAgentEventError(`${path}[${index}]`, "array hole");
316
+ assertJsonSerializable(value[index], `${path}[${index}]`, ancestors);
317
+ }
318
+ const extraKey = Object.keys(value).find((key) => !arrayIndexPattern.test(key) || Number(key) >= value.length);
319
+ if (extraKey !== void 0) throw new NonSerializableAgentEventError(`${path}.${extraKey}`, "array property");
320
+ assertNoHiddenKeys(value, path, (key) => key === "length" || arrayIndexPattern.test(key));
321
+ return;
322
+ }
323
+ const prototype = Object.getPrototypeOf(value);
324
+ if (prototype !== Object.prototype && prototype !== null) throw new NonSerializableAgentEventError(path, value.constructor?.name ?? "object");
325
+ assertNoHiddenKeys(value, path);
326
+ for (const [key, child] of Object.entries(value)) assertJsonSerializable(child, `${path}.${key}`, ancestors);
327
+ } finally {
328
+ ancestors.delete(value);
329
+ }
330
+ }
331
+ function requireNonEmptyString(value, label, qualifier = "") {
332
+ if (typeof value !== "string" || !value) throw new Error(`Agent event ${label} must be a non-empty string${qualifier}.`);
333
+ }
334
+ /** Validates the complete durable envelope before append/export/replay. */
335
+ function assertAgentLogEntry(entry) {
336
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) throw new Error("Agent event entry must be an object.");
337
+ assertJsonSerializable(entry);
338
+ const candidate = entry;
339
+ if (candidate.schemaVersion !== 1) throw new Error(`Unsupported agent event schema version '${String(candidate.schemaVersion)}'; expected '1'.`);
340
+ if (!Number.isInteger(candidate.index) || candidate.index < 0) throw new Error(`Agent event entry.index must be a non-negative integer; got ${String(candidate.index)}.`);
341
+ for (const field of [
342
+ "id",
343
+ "machineId",
344
+ "machineVersion"
345
+ ]) requireNonEmptyString(candidate[field], `entry.${field}`);
346
+ if (typeof candidate.recordedAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(candidate.recordedAt) || Number.isNaN(Date.parse(candidate.recordedAt))) throw new Error(`Agent event entry.recordedAt is not RFC 3339: '${String(candidate.recordedAt)}'.`);
347
+ if (candidate.event === null || typeof candidate.event !== "object" || Array.isArray(candidate.event) || typeof candidate.event.type !== "string" || !candidate.event.type) throw new Error("Agent event entry.event requires a non-empty string type.");
348
+ for (const field of ["causationId", "correlationId"]) if (candidate[field] !== void 0) requireNonEmptyString(candidate[field], `entry.${field}`, " when present");
349
+ if (candidate.metadata !== void 0 && (candidate.metadata === null || typeof candidate.metadata !== "object" || Array.isArray(candidate.metadata))) throw new Error("Agent event entry.metadata must be an object when present.");
350
+ if (candidate.verification !== void 0 && (candidate.verification === null || typeof candidate.verification !== "object" || typeof candidate.verification.stateHash !== "string" || !candidate.verification.stateHash || typeof candidate.verification.effectsHash !== "string" || !candidate.verification.effectsHash)) throw new Error("Agent event entry.verification requires non-empty stateHash and effectsHash strings.");
351
+ }
352
+ /**
353
+ * Rejection from {@link AgentEventLogStore.append} when the thread's stored
354
+ * length is not the `expectedIndex` the writer held — a concurrent writer
355
+ * appended first. `actualLength` is the length core found.
356
+ */
357
+ var AgentEventLogConflictError = class extends AgentError {
358
+ threadId;
359
+ expectedIndex;
360
+ actualLength;
361
+ constructor(threadId, expectedIndex, actualLength) {
362
+ super("event-log-conflict", `AgentEventLogStore.append: length conflict on thread "${threadId}": expected length ${expectedIndex} but found ${actualLength} — a concurrent writer won.`);
363
+ this.name = "AgentEventLogConflictError";
364
+ this.threadId = threadId;
365
+ this.expectedIndex = expectedIndex;
366
+ this.actualLength = actualLength;
367
+ }
368
+ };
369
+ /**
370
+ * In-memory reference store, and the baseline the conformance suite runs
371
+ * against. Per-thread entries are held in a contiguous, index-ordered list;
372
+ * append's check-and-push runs synchronously (no `await` between reading the
373
+ * length and writing), so two appends racing on the same `expectedIndex`
374
+ * resolve to exactly one winner. Every stored and returned entry is
375
+ * `structuredClone`d, so a caller can neither mutate stored state through a
376
+ * value it appended nor through a value it read.
377
+ */
378
+ function createInMemoryEventLogStore() {
379
+ const threads = /* @__PURE__ */ new Map();
380
+ const clone = (value) => structuredClone(value);
381
+ return {
382
+ async append({ threadId, expectedIndex, entries }) {
383
+ for (const entry of entries) assertAgentLogEntry(entry);
384
+ for (let i = 0; i < entries.length; i++) if (entries[i].index !== expectedIndex + i) throw new Error(`AgentEventLogStore.append: entry.index (${entries[i].index}) must be contiguous from expectedIndex (${expectedIndex}); expected ${expectedIndex + i} at position ${i} on thread "${threadId}".`);
385
+ const list = threads.get(threadId);
386
+ const length = list ? list.length : 0;
387
+ if (length !== expectedIndex) throw new AgentEventLogConflictError(threadId, expectedIndex, length);
388
+ const ids = new Set((list ?? []).map((entry) => entry.id));
389
+ for (const entry of entries) {
390
+ if (ids.has(entry.id)) throw new Error(`AgentEventLogStore.append: duplicate event id "${entry.id}" in thread "${threadId}".`);
391
+ ids.add(entry.id);
392
+ }
393
+ const cloned = entries.map((entry) => clone(entry));
394
+ if (list) for (const entry of cloned) list.push(entry);
395
+ else threads.set(threadId, cloned);
396
+ },
397
+ async read(threadId, options) {
398
+ const list = threads.get(threadId) ?? [];
399
+ const from = options?.from ?? 0;
400
+ return list.slice(from).map((entry) => clone(entry));
401
+ },
402
+ async length(threadId) {
403
+ return threads.get(threadId)?.length ?? 0;
404
+ },
405
+ async fork({ threadId, newThreadId, upToIndex, atEventId }) {
406
+ if ((threads.get(newThreadId)?.length ?? 0) > 0) throw new Error(`AgentEventLogStore.fork: newThreadId "${newThreadId}" already has entries.`);
407
+ const source = threads.get(threadId);
408
+ if (!source) throw new Error(`AgentEventLogStore.fork: unknown source thread "${threadId}".`);
409
+ if (upToIndex !== void 0 && atEventId !== void 0) throw new Error("AgentEventLogStore.fork: pass either upToIndex or atEventId, not both.");
410
+ const eventIndex = atEventId === void 0 ? void 0 : source.findIndex((entry) => entry.id === atEventId);
411
+ if (atEventId !== void 0 && eventIndex === -1) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" has no event id "${atEventId}".`);
412
+ const upTo = eventIndex === void 0 ? upToIndex ?? source.length : eventIndex + 1;
413
+ if (upTo < 0 || upTo > source.length) throw new Error(`AgentEventLogStore.fork: thread "${threadId}" (length ${source.length}) has no index ${upTo} to fork up to.`);
414
+ threads.set(newThreadId, source.slice(0, upTo).map((entry) => clone(entry)));
415
+ }
416
+ };
417
+ }
418
+ //#endregion
419
+ export { assertJsonSerializable as a, assertAgentLogEntry as i, AgentEventLogConflictError as n, createInMemoryEventLogStore as o, NonSerializableAgentEventError as r, assertEventLogStoreConformance as s, AGENT_EVENT_SCHEMA_VERSION as t };