@statelyai/agent 2.0.0-alpha.18 → 2.0.0-alpha.19
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 -2
- package/dist/ai-sdk.d.cts +1 -1
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +3 -2
- package/dist/{decision-C11xuud2.mjs → decision-DsIkEuHz.mjs} +48 -28
- package/dist/{decision-DnQCQPew.cjs → decision-t26zsnSR.cjs} +64 -43
- package/dist/{event-log-store-CQJq8_v4.d.cts → event-log-store-Bz7HDBkE.d.cts} +11 -12
- package/dist/{event-log-store-B-1fcfkT.mjs → event-log-store-DmIDosD6.mjs} +22 -14
- package/dist/{event-log-store-yquOV1TX.cjs → event-log-store-a_TKy1gk.cjs} +22 -14
- package/dist/{event-log-store-BrC9Q1xW.d.mts → event-log-store-hrA1vqtN.d.mts} +11 -12
- package/dist/index.cjs +265 -109
- package/dist/index.d.cts +136 -80
- package/dist/index.d.mts +136 -80
- package/dist/index.mjs +262 -107
- package/dist/machines.cjs +13 -17
- package/dist/machines.d.cts +14 -17
- package/dist/machines.d.mts +14 -17
- package/dist/machines.mjs +13 -17
- package/dist/otel.cjs +1 -0
- package/dist/otel.d.cts +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/{run-agent-r9OD4z8F.d.cts → run-agent--4bbms-D.d.cts} +121 -48
- package/dist/{run-agent-DQIDikfd.d.mts → run-agent-CwmzAZwj.d.mts} +121 -48
- package/dist/{setup-agent-C3ETi_HZ.cjs → setup-agent-BFA4VKpN.cjs} +31 -33
- package/dist/{setup-agent-BrE2zFDy.mjs → setup-agent-CPFPN06s.mjs} +31 -28
- package/dist/sqlite.cjs +3 -9
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.mts +1 -1
- package/dist/sqlite.mjs +3 -9
- package/dist/{text-logic-VcWqO-Cl.d.cts → text-logic-Cavva1W6.d.cts} +24 -8
- package/dist/{text-logic-RvnlD-An.d.mts → text-logic-Er5KkTX6.d.mts} +24 -8
- package/dist/validate.cjs +436 -0
- package/dist/validate.d.cts +31 -0
- package/dist/validate.d.mts +31 -0
- package/dist/validate.mjs +411 -0
- package/package.json +15 -1
- package/schemas/agent-workflow.json +2 -2
- package/skills/generate-machine/SKILL.md +12 -14
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_errors = require("./errors-DUBBzRLP.cjs");
|
|
3
|
-
const require_setup_agent = require("./setup-agent-
|
|
4
|
-
const require_decision = require("./decision-
|
|
5
|
-
const require_event_log_store = require("./event-log-store-
|
|
3
|
+
const require_setup_agent = require("./setup-agent-BFA4VKpN.cjs");
|
|
4
|
+
const require_decision = require("./decision-t26zsnSR.cjs");
|
|
5
|
+
const require_event_log_store = require("./event-log-store-a_TKy1gk.cjs");
|
|
6
|
+
require("./validate.cjs");
|
|
6
7
|
let xstate = require("xstate");
|
|
7
8
|
//#region src/internal/state-request-pass.ts
|
|
8
9
|
async function runTextPhase(stateRequest, baseMessages, deps) {
|
|
@@ -103,7 +104,7 @@ async function runAdvancePhase(plan, deps) {
|
|
|
103
104
|
messages: [...deps.messages],
|
|
104
105
|
events,
|
|
105
106
|
attempts: []
|
|
106
|
-
}, deps.decide, {
|
|
107
|
+
}, { decide: deps.decide }, {
|
|
107
108
|
signal: deps.signal,
|
|
108
109
|
canTake: (event) => deps.getSnapshot().can(event)
|
|
109
110
|
});
|
|
@@ -139,8 +140,7 @@ async function runStateRequestPass(requests, deps) {
|
|
|
139
140
|
* {@link getAcceptedEvents}). A programmer/integration error, in the same
|
|
140
141
|
* class as runAgent's bind-time throws — it throws rather than settling an
|
|
141
142
|
* `error` result. A type-legal event a guard rejects is NOT this error (the
|
|
142
|
-
* machine simply takes no transition).
|
|
143
|
-
* {@link RunAgentOptions.onIllegalResumeEvent} `'ignore'`.
|
|
143
|
+
* machine simply takes no transition). Always enforced; there is no opt-out.
|
|
144
144
|
*/
|
|
145
145
|
var AgentIllegalResumeEventError = class extends require_errors.AgentError {
|
|
146
146
|
eventType;
|
|
@@ -258,7 +258,7 @@ function toJsonValue(value, ancestors) {
|
|
|
258
258
|
* in a JSONL file). Live values are sanitized rather than trusted:
|
|
259
259
|
*
|
|
260
260
|
* - Snapshots (`run.start`, `machine.transition`, `run.end`) go through the
|
|
261
|
-
* same JSON round-trip as
|
|
261
|
+
* same JSON round-trip as `machine.getPersistedSnapshot(...)`, so what lands on disk is
|
|
262
262
|
* what a resume would see.
|
|
263
263
|
* - `request.end`'s `raw` (a provider SDK object, frequently cyclic) is DROPPED
|
|
264
264
|
* unless `includeRaw` is set, in which case it is sanitized like everything
|
|
@@ -285,11 +285,92 @@ function serializeTraceEvent(event, options = {}) {
|
|
|
285
285
|
}
|
|
286
286
|
return out;
|
|
287
287
|
}
|
|
288
|
+
function snapshotNodes(snapshot) {
|
|
289
|
+
return (snapshot._nodes ?? []).map((raw) => {
|
|
290
|
+
const node = raw;
|
|
291
|
+
return {
|
|
292
|
+
id: node.id ?? "",
|
|
293
|
+
key: node.key ?? "",
|
|
294
|
+
...node.description !== void 0 ? { description: node.description } : {},
|
|
295
|
+
tags: [...node.tags ?? []],
|
|
296
|
+
...node.meta !== void 0 ? { meta: node.meta } : {},
|
|
297
|
+
ownEvents: [...node.ownEvents ?? []],
|
|
298
|
+
leaf: Object.keys(node.states ?? {}).length === 0
|
|
299
|
+
};
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* The active state nodes of a snapshot, as plain {@link AgentSnapshotNode}
|
|
304
|
+
* descriptors. The escape hatch under {@link getSnapshotRequests}: use it when
|
|
305
|
+
* your `getRequests` hook needs to build requests some other way, so host code
|
|
306
|
+
* never touches xstate's private `snapshot._nodes`.
|
|
307
|
+
*/
|
|
308
|
+
function getSnapshotNodes(snapshot) {
|
|
309
|
+
return snapshotNodes(snapshot);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Builds the {@link AgentStateRequest}s for a snapshot straight from its active
|
|
313
|
+
* state nodes — the prompts-in-descriptions recipe as a function, so a
|
|
314
|
+
* {@link RunAgentOptions.getRequests} hook is one line and never reaches into
|
|
315
|
+
* xstate's private `snapshot._nodes`:
|
|
316
|
+
*
|
|
317
|
+
* ```ts
|
|
318
|
+
* runAgent(machine, {
|
|
319
|
+
* executors,
|
|
320
|
+
* getRequests: (snapshot) => getSnapshotRequests(snapshot, { model: 'writer' }),
|
|
321
|
+
* });
|
|
322
|
+
* ```
|
|
323
|
+
*
|
|
324
|
+
* Each described active node becomes one request: `prompt` from the node's
|
|
325
|
+
* `description`, `kind: 'decision'` when it is tagged `'decision'`, `system`
|
|
326
|
+
* from `meta.role` when present, `allowedEvents` scoped to the node's own
|
|
327
|
+
* events, and an explicit `onDone` when the node has exactly one own event
|
|
328
|
+
* (single-outcome states advance deterministically; anything else falls
|
|
329
|
+
* through to a `decide` call). Nodes tagged `'waiting'` produce nothing, so the
|
|
330
|
+
* run settles idle for a human. Override any of it with `filter`/`map`.
|
|
331
|
+
*/
|
|
332
|
+
function getSnapshotRequests(snapshot, options) {
|
|
333
|
+
const filter = options.filter ?? ((node) => !!node.description && !node.tags.includes("waiting"));
|
|
334
|
+
const requests = [];
|
|
335
|
+
for (const node of snapshotNodes(snapshot)) {
|
|
336
|
+
if (!filter(node)) continue;
|
|
337
|
+
const system = node.meta?.role;
|
|
338
|
+
const request = {
|
|
339
|
+
model: options.model,
|
|
340
|
+
prompt: node.description ?? "",
|
|
341
|
+
kind: node.tags.includes("decision") ? "decision" : "text",
|
|
342
|
+
...system !== void 0 ? { system } : {},
|
|
343
|
+
...node.ownEvents.length > 0 ? { allowedEvents: node.ownEvents } : {},
|
|
344
|
+
...node.ownEvents.length === 1 ? { onDone: { type: node.ownEvents[0] } } : {}
|
|
345
|
+
};
|
|
346
|
+
const mapped = options.map ? options.map(request, node) : request;
|
|
347
|
+
if (mapped) requests.push(mapped);
|
|
348
|
+
}
|
|
349
|
+
return requests;
|
|
350
|
+
}
|
|
288
351
|
let nextRunAgentTraceId = 1;
|
|
352
|
+
/**
|
|
353
|
+
* Thrown into the invoke that would have made the call once
|
|
354
|
+
* {@link RunAgentOptions.maxModelCalls} is spent. It reaches the machine
|
|
355
|
+
* through the normal error channel, so an invoke's `onError` can branch on it
|
|
356
|
+
* (`error.code === 'max-model-calls'`, the same string the settled result's
|
|
357
|
+
* `cause` uses) and route to a degraded/finish state instead of failing the
|
|
358
|
+
* run. Unhandled, it settles `{ status: 'error', cause: 'max-model-calls' }`.
|
|
359
|
+
*
|
|
360
|
+
* ```ts
|
|
361
|
+
* onError: [
|
|
362
|
+
* { guard: ({ event }) => event.error?.code === 'max-model-calls', target: 'budgetSpent' },
|
|
363
|
+
* { target: 'failed' },
|
|
364
|
+
* ]
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
289
367
|
var AgentMaxModelCallsExceededError = class extends require_errors.AgentError {
|
|
290
|
-
|
|
291
|
-
|
|
368
|
+
/** The budget that was exceeded (`options.maxModelCalls`). */
|
|
369
|
+
maxModelCalls;
|
|
370
|
+
constructor(maxModelCalls) {
|
|
371
|
+
super("max-model-calls", `runAgent exceeded maxModelCalls (${maxModelCalls}). Raise the budget, or handle it in the invoke's onError (error.code === 'max-model-calls').`);
|
|
292
372
|
this.name = "AgentMaxModelCallsExceededError";
|
|
373
|
+
this.maxModelCalls = maxModelCalls;
|
|
293
374
|
}
|
|
294
375
|
};
|
|
295
376
|
function wrapsDecisionExhausted(error) {
|
|
@@ -329,7 +410,7 @@ function collectConfiguredInvokeSrcs(stateConfig, stateName, out) {
|
|
|
329
410
|
* walk into invoked child machines (their internal agent requests are opaque
|
|
330
411
|
* to the parent-level source walk otherwise).
|
|
331
412
|
*/
|
|
332
|
-
function
|
|
413
|
+
function isStateMachineLogic(logic) {
|
|
333
414
|
return !!logic && typeof logic === "object" && "config" in logic && "root" in logic && typeof logic.provide === "function" && typeof logic.sources === "object" && !!logic.sources?.actors;
|
|
334
415
|
}
|
|
335
416
|
/**
|
|
@@ -361,7 +442,7 @@ function assertMachineBindable(machine, effectiveSources, executors, ctx) {
|
|
|
361
442
|
const where = ctx.isChild ? `child machine '${ctx.childPath}' state` : "state";
|
|
362
443
|
for (const { stateName, src } of invokes) {
|
|
363
444
|
if (typeof src !== "string") {
|
|
364
|
-
if (
|
|
445
|
+
if (isStateMachineLogic(src)) {
|
|
365
446
|
assertChildMachineBindable(src, src, stateName, executors, ctx);
|
|
366
447
|
continue;
|
|
367
448
|
}
|
|
@@ -370,7 +451,7 @@ function assertMachineBindable(machine, effectiveSources, executors, ctx) {
|
|
|
370
451
|
}
|
|
371
452
|
const logic = effectiveSources[src];
|
|
372
453
|
if (logic === void 0) throw new Error(`runAgent: ${where} '${stateName}' invokes unregistered actor source '${src}'. Provide it via machine.provide({ actors: { '${src}': ... } }) or runAgent(machine, { actors: { '${src}': ... } }).`);
|
|
373
|
-
if (
|
|
454
|
+
if (isStateMachineLogic(logic)) {
|
|
374
455
|
assertChildMachineBindable(logic, src, stateName, executors, ctx);
|
|
375
456
|
continue;
|
|
376
457
|
}
|
|
@@ -416,18 +497,16 @@ function unrebindableChildRequestError(childPath, stateName, requestSrc, kind) {
|
|
|
416
497
|
return /* @__PURE__ */ new Error(`runAgent: child machine '${childPath}' (state '${stateName}') invokes ${kind} source '${requestSrc}', which has no host execution and is reached through a direct-object invoke src that runAgent cannot rebind. Requests reached through string-keyed actor sources inherit runAgent's generateText/streamText/decide executors automatically; a direct-object child machine does not. Either bind the request with its own executor (requestLogic.withExecutor(...)), or register the child as a string-keyed actor source (machine.provide({ actors: { <child>: childMachine } })) and invoke it by name.`);
|
|
417
498
|
}
|
|
418
499
|
/**
|
|
419
|
-
* True when the snapshot's active states declare a transition
|
|
420
|
-
* `'@agent.usage'` type
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
* a wildcard machine's context and event log byte-identical to a run without
|
|
427
|
-
* the feature. @internal
|
|
500
|
+
* True when the snapshot's active states declare a transition that would
|
|
501
|
+
* receive the reserved `'@agent.usage'` type — an explicit `on: { '@agent.usage'
|
|
502
|
+
* }` OR a catch-all `on: { '*': … }`. Plain XState semantics apply unmodified:
|
|
503
|
+
* a wildcard matches every event delivered to the machine, reserved ones
|
|
504
|
+
* included. (The MODEL-facing side stays closed: `getAcceptedEvents` drops
|
|
505
|
+
* `@agent.*` before any `allowedEvents` matching, so a wildcard never offers
|
|
506
|
+
* the reserved event as a decision candidate.) @internal
|
|
428
507
|
*/
|
|
429
508
|
function declaresUsageTransition(snapshot) {
|
|
430
|
-
return (0, xstate.getNextTransitions)(snapshot).some((transition) => transition.eventType ===
|
|
509
|
+
return (0, xstate.getNextTransitions)(snapshot).some((transition) => transition.eventType === "@agent.usage" || transition.eventType === "*");
|
|
431
510
|
}
|
|
432
511
|
/**
|
|
433
512
|
* Builds the ONE place a trace payload is emitted: it hands the payload to the
|
|
@@ -494,7 +573,7 @@ function bindTextLogic(logic, runCtx) {
|
|
|
494
573
|
const self = selfArg;
|
|
495
574
|
const { id, src } = selfIdAndSrc(self);
|
|
496
575
|
const executor = logic.mode === "stream" ? runCtx.streamText : runCtx.generateText;
|
|
497
|
-
if (!executor) throw new Error(`
|
|
576
|
+
if (!executor) throw new Error(`No '${logic.mode === "stream" ? "streamText" : "generateText"}' executor provided.`);
|
|
498
577
|
const requestWithTools = {
|
|
499
578
|
...request,
|
|
500
579
|
tools: request.tools ?? {}
|
|
@@ -566,17 +645,22 @@ function bindTextLogic(logic, runCtx) {
|
|
|
566
645
|
});
|
|
567
646
|
}
|
|
568
647
|
function createCountingDecide(runCtx, self) {
|
|
569
|
-
return async (attemptRequest) => {
|
|
648
|
+
return async (attemptRequest, info) => {
|
|
570
649
|
runCtx.consumeModelCall();
|
|
571
650
|
runCtx.emitTrace?.({
|
|
572
651
|
type: "request.start",
|
|
573
652
|
request: attemptRequest
|
|
574
653
|
}, self);
|
|
575
654
|
try {
|
|
655
|
+
const { id } = selfIdAndSrc(self);
|
|
576
656
|
const result = await runCtx.decide(runCtx.runId !== void 0 ? {
|
|
577
657
|
...attemptRequest,
|
|
578
658
|
runId: runCtx.runId
|
|
579
|
-
} : attemptRequest
|
|
659
|
+
} : attemptRequest, {
|
|
660
|
+
...info,
|
|
661
|
+
...runCtx.runId !== void 0 ? { runId: runCtx.runId } : {},
|
|
662
|
+
...info?.requestId === void 0 && id !== "" ? { requestId: id } : {}
|
|
663
|
+
});
|
|
580
664
|
const usage = require_decision.getCallUsage(result);
|
|
581
665
|
if (usage) {
|
|
582
666
|
const { src } = selfIdAndSrc(self);
|
|
@@ -622,7 +706,7 @@ function createCountingDecide(runCtx, self) {
|
|
|
622
706
|
*/
|
|
623
707
|
function bindDecisionLogic(logic, runCtx) {
|
|
624
708
|
const decisionLogic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal, self: selfArg }) => {
|
|
625
|
-
if (!runCtx.decide) throw new Error("
|
|
709
|
+
if (!runCtx.decide) throw new Error("No 'decide' executor provided.");
|
|
626
710
|
const self = selfArg;
|
|
627
711
|
const { id } = selfIdAndSrc(self);
|
|
628
712
|
const declaredEventTypes = logic.allowedEventTypes?.(input);
|
|
@@ -636,7 +720,7 @@ function bindDecisionLogic(logic, runCtx) {
|
|
|
636
720
|
...logic.request(input),
|
|
637
721
|
id,
|
|
638
722
|
events
|
|
639
|
-
}, createCountingDecide(runCtx, self), {
|
|
723
|
+
}, { decide: createCountingDecide(runCtx, self) }, {
|
|
640
724
|
maxRetries: logic.maxRetries,
|
|
641
725
|
signal,
|
|
642
726
|
canTake: (event) => actorRef ? actorRef.getSnapshot().can(event) : true
|
|
@@ -681,7 +765,7 @@ function rootTraceState(root) {
|
|
|
681
765
|
if (!state) {
|
|
682
766
|
const logic = root.logic;
|
|
683
767
|
const machineId = (logic?.config)?.id ?? logic?.id ?? "(machine)";
|
|
684
|
-
const machineVersion = logic ? require_decision.
|
|
768
|
+
const machineVersion = logic ? require_decision.resolveMachineVersion(logic) : "";
|
|
685
769
|
state = {
|
|
686
770
|
runId: `run_${nextProvideRunId++}`,
|
|
687
771
|
seq: 0,
|
|
@@ -753,9 +837,9 @@ function provideBindContext(machine, executors, options) {
|
|
|
753
837
|
* under a live `createActor` tree) on the `provideExecutors` path.
|
|
754
838
|
*
|
|
755
839
|
* Gating is identical on both: the target snapshot must be active, must declare
|
|
756
|
-
* an `'@agent.usage'` transition
|
|
757
|
-
*
|
|
758
|
-
* event. `onDropped` is the run path's straggler gate: it returns `true` for a
|
|
840
|
+
* an `'@agent.usage'` transition — explicitly, or through a catch-all
|
|
841
|
+
* `on: { '*' }` (see {@link declaresUsageTransition}) — and must be able to
|
|
842
|
+
* take the event. `onDropped` is the run path's straggler gate: it returns `true` for a
|
|
759
843
|
* call that settled after the cycle resolved, which drops the event (traced as
|
|
760
844
|
* `usage.dropped`) rather than delivering it. Uncontrolled mode has no cycle to
|
|
761
845
|
* settle, so it passes no gate and has no dropped stragglers.
|
|
@@ -797,6 +881,19 @@ function bindDecisionForProvide(machine, logic, executors, options) {
|
|
|
797
881
|
return bindDecisionLogic(logic, provideBindContext(machine, executors, options));
|
|
798
882
|
}
|
|
799
883
|
/**
|
|
884
|
+
* Recursively binds an invoked child state machine for {@link provideExecutors},
|
|
885
|
+
* with the same semantics `runAgent` applies ({@link rebindChildMachine}):
|
|
886
|
+
* string-keyed text/decision sources at any depth inherit the host executors,
|
|
887
|
+
* a source that carries its own executor is left alone, and a cycle is
|
|
888
|
+
* returned as-is. Each machine in the tree is bound with its own registered
|
|
889
|
+
* `setupAgent` schemas. Returns the original machine when nothing needed
|
|
890
|
+
* wrapping. @internal
|
|
891
|
+
*/
|
|
892
|
+
function bindChildMachineForProvide(childMachine, executors, options, visited) {
|
|
893
|
+
const ctxFor = (target) => provideBindContext(target, executors, options);
|
|
894
|
+
return rebindChildMachine(childMachine, ctxFor(childMachine), visited, ctxFor);
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
800
897
|
* Validates `input` against the machine's registered input schema, returning
|
|
801
898
|
* the schema's output — so defaults are filled and transforms applied before
|
|
802
899
|
* the value reaches `createActor` or the replayable event log.
|
|
@@ -830,9 +927,10 @@ function resolveMachineInput(machine, input) {
|
|
|
830
927
|
* (`executorBoundLogics`) is left as-is: explicit binding shadows inheritance.
|
|
831
928
|
* Cycle-safe via `visited` (a machine that invokes itself is returned as-is).
|
|
832
929
|
*/
|
|
833
|
-
function rebindChildMachine(childMachine, runCtx, visited) {
|
|
930
|
+
function rebindChildMachine(childMachine, runCtx, visited, ctxFor) {
|
|
834
931
|
if (visited.has(childMachine)) return childMachine;
|
|
835
932
|
const childVisited = new Set([...visited, childMachine]);
|
|
933
|
+
runCtx = ctxFor ? ctxFor(childMachine) : runCtx;
|
|
836
934
|
const sources = childMachine.sources.actors;
|
|
837
935
|
const wrapped = {};
|
|
838
936
|
for (const [key, logic] of Object.entries(sources)) {
|
|
@@ -844,8 +942,8 @@ function rebindChildMachine(childMachine, runCtx, visited) {
|
|
|
844
942
|
if (!require_decision.executorBoundLogics.has(logic)) wrapped[key] = bindTextLogic(logic, runCtx);
|
|
845
943
|
continue;
|
|
846
944
|
}
|
|
847
|
-
if (
|
|
848
|
-
const rebound = rebindChildMachine(logic, runCtx, childVisited);
|
|
945
|
+
if (isStateMachineLogic(logic)) {
|
|
946
|
+
const rebound = rebindChildMachine(logic, runCtx, childVisited, ctxFor);
|
|
849
947
|
if (rebound !== logic) wrapped[key] = rebound;
|
|
850
948
|
continue;
|
|
851
949
|
}
|
|
@@ -928,7 +1026,7 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
928
1026
|
let traceSeq = 0;
|
|
929
1027
|
const resolvedInput = resolveMachineInput(machine, options.input);
|
|
930
1028
|
const machineId = machine.config.id ?? machine.id ?? "(machine)";
|
|
931
|
-
const machineVersion =
|
|
1029
|
+
const machineVersion = require_decision.resolveMachineVersion(machine);
|
|
932
1030
|
const agentMeta = {
|
|
933
1031
|
machineId,
|
|
934
1032
|
version: machineVersion
|
|
@@ -950,10 +1048,10 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
950
1048
|
onTransition: options.onTransition
|
|
951
1049
|
});
|
|
952
1050
|
const consumeModelCall = () => {
|
|
953
|
-
if (budgetExceeded) throw new AgentMaxModelCallsExceededError();
|
|
1051
|
+
if (budgetExceeded) throw new AgentMaxModelCallsExceededError(maxModelCalls);
|
|
954
1052
|
if (modelCallCount + 1 > maxModelCalls) {
|
|
955
1053
|
budgetExceeded = true;
|
|
956
|
-
throw new AgentMaxModelCallsExceededError();
|
|
1054
|
+
throw new AgentMaxModelCallsExceededError(maxModelCalls);
|
|
957
1055
|
}
|
|
958
1056
|
modelCallCount += 1;
|
|
959
1057
|
};
|
|
@@ -1027,19 +1125,20 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
1027
1125
|
if (!require_decision.executorBoundLogics.has(logic)) wrappedSources[key] = bindTextLogic(logic, runCtx);
|
|
1028
1126
|
continue;
|
|
1029
1127
|
}
|
|
1030
|
-
if (
|
|
1128
|
+
if (isStateMachineLogic(logic)) {
|
|
1031
1129
|
const rebound = rebindChildMachine(logic, runCtx, new Set([machine]));
|
|
1032
1130
|
if (rebound !== logic) wrappedSources[key] = rebound;
|
|
1033
1131
|
continue;
|
|
1034
1132
|
}
|
|
1035
1133
|
}
|
|
1036
1134
|
const boundMachine = provided.provide({ actors: wrappedSources });
|
|
1037
|
-
const
|
|
1038
|
-
const
|
|
1135
|
+
const declaredIdlePredicate = options.isIdle ?? require_decision.getMachineIdlePredicate(machine);
|
|
1136
|
+
const isIdle = declaredIdlePredicate ?? (() => false);
|
|
1137
|
+
const machineDeclaresMigrate = typeof machine.config.migrate === "function";
|
|
1039
1138
|
let effectiveSnapshot = options.snapshot;
|
|
1040
1139
|
if (effectiveSnapshot !== void 0) {
|
|
1041
1140
|
const from = effectiveSnapshot.agentMeta?.version ?? effectiveSnapshot.version;
|
|
1042
|
-
if (from !== void 0 && from !== machineVersion) {
|
|
1141
|
+
if (from !== void 0 && from !== machineVersion && !(machineDeclaresMigrate && !options.migrateSnapshot)) {
|
|
1043
1142
|
const info = {
|
|
1044
1143
|
from,
|
|
1045
1144
|
to: machineVersion
|
|
@@ -1058,7 +1157,7 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
1058
1157
|
effectiveSnapshot = machine.getPersistedSnapshot(replayedSnapshot);
|
|
1059
1158
|
}
|
|
1060
1159
|
const machineOwnVersion = machine.version;
|
|
1061
|
-
if (effectiveSnapshot !== void 0 && effectiveSnapshot.version !== machineOwnVersion) {
|
|
1160
|
+
if (!machineDeclaresMigrate && effectiveSnapshot !== void 0 && effectiveSnapshot.version !== machineOwnVersion) {
|
|
1062
1161
|
const aligned = Object.assign(Object.create(Object.getPrototypeOf(effectiveSnapshot)), effectiveSnapshot);
|
|
1063
1162
|
if (machineOwnVersion === void 0) delete aligned.version;
|
|
1064
1163
|
else aligned.version = machineOwnVersion;
|
|
@@ -1072,7 +1171,7 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
1072
1171
|
if (!options.getRequests && !options.messages && messages.length === 0) return;
|
|
1073
1172
|
snapshot.messages = [...messages];
|
|
1074
1173
|
};
|
|
1075
|
-
if (effectiveSnapshot !== void 0 && options.event !== void 0
|
|
1174
|
+
if (effectiveSnapshot !== void 0 && options.event !== void 0) {
|
|
1076
1175
|
const acceptedTypes = require_decision.getAcceptedEvents((0, xstate.createActor)(boundMachine, { snapshot: effectiveSnapshot }).getSnapshot(), { schemas: runCtx.schemas }).map((descriptor) => descriptor.type);
|
|
1077
1176
|
const eventType = options.event.type;
|
|
1078
1177
|
if (!acceptedTypes.includes(eventType)) throw new AgentIllegalResumeEventError(eventType, acceptedTypes);
|
|
@@ -1218,9 +1317,9 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
1218
1317
|
const current = actor.getSnapshot();
|
|
1219
1318
|
if (isIdleSnapshot(current, { ignoreUserInputChildren: userInputIsPlaceholder })) {
|
|
1220
1319
|
if (!maybeInterpret(current)) {
|
|
1221
|
-
if (!
|
|
1320
|
+
if (!declaredIdlePredicate && current.status === "active" && !warnedHeuristicIdle && process.env.NODE_ENV !== "production") {
|
|
1222
1321
|
warnedHeuristicIdle = true;
|
|
1223
|
-
console.warn("[@statelyai/agent] runAgent settled idle via the timing heuristic (no
|
|
1322
|
+
console.warn("[@statelyai/agent] runAgent settled idle via the timing heuristic (no idle predicate declared). This is best-effort; for deterministic idle detection, declare setupAgent({ isIdle }) or pass runAgent(machine, { isIdle }), e.g. (s) => s.hasTag('waiting').");
|
|
1224
1323
|
}
|
|
1225
1324
|
settleIdle(current);
|
|
1226
1325
|
}
|
|
@@ -1275,11 +1374,11 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
1275
1374
|
});
|
|
1276
1375
|
return;
|
|
1277
1376
|
}
|
|
1278
|
-
if (!deliveringResumeEvent &&
|
|
1377
|
+
if (!deliveringResumeEvent && isIdle(snapshot) && isIdleSnapshot(snapshot, { ignoreUserInputChildren: userInputIsPlaceholder })) {
|
|
1279
1378
|
queueMicrotask(() => {
|
|
1280
1379
|
if (settled) return;
|
|
1281
1380
|
const current = actor.getSnapshot();
|
|
1282
|
-
if (
|
|
1381
|
+
if (isIdle(current) && isIdleSnapshot(current, { ignoreUserInputChildren: userInputIsPlaceholder })) {
|
|
1283
1382
|
if (!maybeInterpret(current)) settleIdle(current);
|
|
1284
1383
|
} else scheduleIdleCheck();
|
|
1285
1384
|
});
|
|
@@ -1576,9 +1675,12 @@ function createAgentRun(machine, options) {
|
|
|
1576
1675
|
* Throws at bind time if a source needs an executor kind that `executors` does
|
|
1577
1676
|
* not provide.
|
|
1578
1677
|
*
|
|
1579
|
-
*
|
|
1580
|
-
* machine
|
|
1581
|
-
*
|
|
1678
|
+
* Executor inheritance is RECURSIVE, exactly as in `runAgent`: a string-keyed
|
|
1679
|
+
* invoked child machine is rebound too, so its own text/decision requests — at
|
|
1680
|
+
* any depth — reach the same host executors. A direct-object invoke `src`
|
|
1681
|
+
* cannot be swapped via `.provide`, so nothing under one inherits; bind those
|
|
1682
|
+
* with `.withExecutor(...)` or register the child as a string-keyed source. A
|
|
1683
|
+
* source that already carries its own executor is never overwritten.
|
|
1582
1684
|
*/
|
|
1583
1685
|
function provideExecutors(machine, executors, options = {}) {
|
|
1584
1686
|
const bindOptions = {
|
|
@@ -1592,21 +1694,18 @@ function provideExecutors(machine, executors, options = {}) {
|
|
|
1592
1694
|
const invokedSrcs = getConfiguredInvokeSrcs(provided);
|
|
1593
1695
|
for (const [key, logic] of Object.entries(effectiveSources)) {
|
|
1594
1696
|
if (key === "agent.userInput") continue;
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
};
|
|
1601
|
-
else if (require_decision.isTextLogic(logic)) {
|
|
1602
|
-
const streaming = logic.mode === "stream";
|
|
1603
|
-
binding = {
|
|
1604
|
-
executorKey: streaming ? "streamText" : "generateText",
|
|
1605
|
-
kind: streaming ? "streaming text" : "text",
|
|
1606
|
-
bind: () => bindTextForProvide(provided, logic, executors, bindOptions)
|
|
1607
|
-
};
|
|
1697
|
+
if (isStateMachineLogic(logic)) {
|
|
1698
|
+
if (invokedSrcs.has(key)) assertChildBindable(logic, executors, key, new Set([provided]));
|
|
1699
|
+
const rebound = bindChildMachineForProvide(logic, executors, bindOptions, new Set([provided]));
|
|
1700
|
+
if (rebound !== logic) wrappedSources[key] = rebound;
|
|
1701
|
+
continue;
|
|
1608
1702
|
}
|
|
1609
|
-
|
|
1703
|
+
const requirement = executorRequirementOf(logic);
|
|
1704
|
+
if (!requirement) continue;
|
|
1705
|
+
const binding = {
|
|
1706
|
+
...requirement,
|
|
1707
|
+
bind: () => require_decision.isDecisionLogic(logic) ? bindDecisionForProvide(provided, logic, executors, bindOptions) : bindTextForProvide(provided, logic, executors, bindOptions)
|
|
1708
|
+
};
|
|
1610
1709
|
if (require_decision.executorBoundLogics.has(logic)) continue;
|
|
1611
1710
|
if (!executors[binding.executorKey]) {
|
|
1612
1711
|
if (invokedSrcs.has(key)) throw missingExecutorError(key, binding.kind, binding.executorKey);
|
|
@@ -1616,12 +1715,50 @@ function provideExecutors(machine, executors, options = {}) {
|
|
|
1616
1715
|
}
|
|
1617
1716
|
return withActors(provided, wrappedSources);
|
|
1618
1717
|
}
|
|
1718
|
+
/** The executor slot + label an agent logic needs, or `undefined` for a non-agent actor. */
|
|
1719
|
+
function executorRequirementOf(logic) {
|
|
1720
|
+
if (require_decision.isDecisionLogic(logic)) return {
|
|
1721
|
+
executorKey: "decide",
|
|
1722
|
+
kind: "decision"
|
|
1723
|
+
};
|
|
1724
|
+
if (require_decision.isTextLogic(logic)) return logic.mode === "stream" ? {
|
|
1725
|
+
executorKey: "streamText",
|
|
1726
|
+
kind: "streaming text"
|
|
1727
|
+
} : {
|
|
1728
|
+
executorKey: "generateText",
|
|
1729
|
+
kind: "text"
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Walks an invoked child machine's own invoked sources (recursively, at any
|
|
1734
|
+
* depth) and throws the same missing-executor error `provideExecutors` throws
|
|
1735
|
+
* for the top-level machine — before any actor starts. Mirrors runAgent's
|
|
1736
|
+
* `assertBindable` for the uncontrolled path. Cycle-safe via `visited`.
|
|
1737
|
+
*/
|
|
1738
|
+
function assertChildBindable(childMachine, executors, path, visited) {
|
|
1739
|
+
if (visited.has(childMachine)) return;
|
|
1740
|
+
const nextVisited = new Set([...visited, childMachine]);
|
|
1741
|
+
const sources = childMachine.sources.actors;
|
|
1742
|
+
for (const src of getConfiguredInvokeSrcs(childMachine)) {
|
|
1743
|
+
if (src === "agent.userInput") continue;
|
|
1744
|
+
const logic = sources[src];
|
|
1745
|
+
if (!logic) continue;
|
|
1746
|
+
if (isStateMachineLogic(logic)) {
|
|
1747
|
+
assertChildBindable(logic, executors, `${path} > ${src}`, nextVisited);
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
const requirement = executorRequirementOf(logic);
|
|
1751
|
+
if (!requirement || require_decision.executorBoundLogics.has(logic)) continue;
|
|
1752
|
+
if (!executors[requirement.executorKey]) throw missingExecutorError(`${path} > ${src}`, requirement.kind, requirement.executorKey);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1619
1755
|
function missingExecutorError(src, kind, executor) {
|
|
1620
1756
|
return /* @__PURE__ */ new Error(`provideExecutors: actor source '${src}' is a ${kind} source but no '${executor}' executor was provided. Add it to the executors object, or bind the source with its own executor (logic.withExecutor(...)) before calling provideExecutors.`);
|
|
1621
1757
|
}
|
|
1622
1758
|
//#endregion
|
|
1623
1759
|
//#region src/verify.ts
|
|
1624
1760
|
const DECIDE_SRC = "agent.decide";
|
|
1761
|
+
const USER_INPUT_SRC = "agent.userInput";
|
|
1625
1762
|
function normalizeInvokes(invoke) {
|
|
1626
1763
|
if (invoke === void 0 || invoke === null) return [];
|
|
1627
1764
|
return Array.isArray(invoke) ? invoke : [invoke];
|
|
@@ -1908,11 +2045,23 @@ const LINT_CHECKS = [
|
|
|
1908
2045
|
* call this to catch dead states, undeliverable decisions, un-rebindable
|
|
1909
2046
|
* invoke srcs, and output-contract gaps before ever running it.
|
|
1910
2047
|
*
|
|
2048
|
+
* Pass `{ throw: true }` for the one-liner form used in tests and generation
|
|
2049
|
+
* loops: it returns silently when the machine is clean and throws
|
|
2050
|
+
* {@link AgentLintError} (findings on `.diagnostics`) on error-severity
|
|
2051
|
+
* findings, or on warnings too with `{ throw: true, warnings: true }`.
|
|
2052
|
+
*
|
|
1911
2053
|
* @example
|
|
1912
2054
|
* ```ts
|
|
1913
2055
|
* const errors = lintAgentMachine(machine).filter((d) => d.severity === 'error');
|
|
1914
2056
|
* if (errors.length) throw new Error(errors.map((e) => `${e.path}: ${e.message}`).join('\n'));
|
|
1915
2057
|
* ```
|
|
2058
|
+
*
|
|
2059
|
+
* @example Throwing form
|
|
2060
|
+
* ```ts
|
|
2061
|
+
* test('agent machine is structurally sound', () => {
|
|
2062
|
+
* lintAgentMachine(machine, { throw: true });
|
|
2063
|
+
* });
|
|
2064
|
+
* ```
|
|
1916
2065
|
*/
|
|
1917
2066
|
function lintAgentMachine(machine, options = {}) {
|
|
1918
2067
|
const config = machine.config ?? {};
|
|
@@ -1927,10 +2076,16 @@ function lintAgentMachine(machine, options = {}) {
|
|
|
1927
2076
|
actors: registered.actors ?? machine.sources?.actors ?? {}
|
|
1928
2077
|
};
|
|
1929
2078
|
const disabled = new Set(options.disable ?? []);
|
|
1930
|
-
|
|
2079
|
+
const diagnostics = LINT_CHECKS.flatMap((check) => check(ctx)).filter((d) => !disabled.has(d.code));
|
|
2080
|
+
if (options.throw) {
|
|
2081
|
+
const failing = options.warnings ? diagnostics : diagnostics.filter((d) => d.severity === "error");
|
|
2082
|
+
if (failing.length > 0) throw new AgentLintError(machine.id ?? "(machine)", failing);
|
|
2083
|
+
}
|
|
2084
|
+
return diagnostics;
|
|
1931
2085
|
}
|
|
1932
2086
|
/**
|
|
1933
|
-
* Thrown by {
|
|
2087
|
+
* Thrown by `lintAgentMachine(machine, { throw: true })` when lint finds
|
|
2088
|
+
* failing diagnostics.
|
|
1934
2089
|
* `diagnostics` holds the findings; the message lists them one per finding,
|
|
1935
2090
|
* so a test runner's failure output reads like the CLI's lint report.
|
|
1936
2091
|
*/
|
|
@@ -1943,24 +2098,6 @@ var AgentLintError = class extends require_errors.AgentError {
|
|
|
1943
2098
|
this.diagnostics = diagnostics;
|
|
1944
2099
|
}
|
|
1945
2100
|
};
|
|
1946
|
-
/**
|
|
1947
|
-
* Asserts a machine passes {@link lintAgentMachine}: returns silently when
|
|
1948
|
-
* clean, throws {@link AgentLintError} (with the findings on `.diagnostics`)
|
|
1949
|
-
* otherwise. Fails on error-severity findings; set `warnings: true` to fail on
|
|
1950
|
-
* warnings too. The one-liner for tests and generation loops:
|
|
1951
|
-
*
|
|
1952
|
-
* @example
|
|
1953
|
-
* ```ts
|
|
1954
|
-
* test('agent machine is structurally sound', () => {
|
|
1955
|
-
* assertAgentMachine(machine);
|
|
1956
|
-
* });
|
|
1957
|
-
* ```
|
|
1958
|
-
*/
|
|
1959
|
-
function assertAgentMachine(machine, options = {}) {
|
|
1960
|
-
const diagnostics = lintAgentMachine(machine, options);
|
|
1961
|
-
const failing = options.warnings ? diagnostics : diagnostics.filter((d) => d.severity === "error");
|
|
1962
|
-
if (failing.length > 0) throw new AgentLintError(machine.id ?? "(machine)", failing);
|
|
1963
|
-
}
|
|
1964
2101
|
function pendingInvokes(step) {
|
|
1965
2102
|
const out = [];
|
|
1966
2103
|
for (const action of step.actions) {
|
|
@@ -2006,6 +2143,7 @@ async function simulateAgent(machine, options) {
|
|
|
2006
2143
|
decisions: mapValues(options.script.decisions ?? {}, (arr) => [...arr]),
|
|
2007
2144
|
invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr])
|
|
2008
2145
|
};
|
|
2146
|
+
if (options.script.userInput?.length) script.invokes[USER_INPUT_SRC] = [...options.script.userInput, ...script.invokes[USER_INPUT_SRC] ?? []];
|
|
2009
2147
|
let step = require_setup_agent.initialAgentStep(machine, options.input);
|
|
2010
2148
|
const trail = [];
|
|
2011
2149
|
for (let i = 0; i < maxSteps; i++) {
|
|
@@ -2072,13 +2210,16 @@ function mapValues(obj, fn) {
|
|
|
2072
2210
|
}
|
|
2073
2211
|
function scriptDryError(kind, src, id, request) {
|
|
2074
2212
|
const events = request?.kind === "decision" ? ` Candidate events: ${request.events.map((e) => e.type).join(", ") || "(none)"}.` : "";
|
|
2075
|
-
|
|
2213
|
+
const key = kind === "text" ? `text['${src}']` : kind === "decision" ? `decisions['${src}']` : src === USER_INPUT_SRC ? "userInput" : `invokes['${src}']`;
|
|
2214
|
+
return /* @__PURE__ */ new Error(`simulateAgent: script ran dry on a pending ${kind} request for src '${src}' (id '${id}'). Add an entry to the script's \`${key}\` queue.${events}`);
|
|
2076
2215
|
}
|
|
2077
2216
|
const MAX_ADVANCE_STEPS = 1e3;
|
|
2078
2217
|
async function explore(machine, options, stopWhen) {
|
|
2079
2218
|
const maxDepth = options.maxDepth ?? 8;
|
|
2080
2219
|
const maxPaths = options.maxPaths ?? 200;
|
|
2081
|
-
const
|
|
2220
|
+
const textScript = options.text ?? {};
|
|
2221
|
+
const invokeOutputs = { ...options.invokes ?? {} };
|
|
2222
|
+
if ("userInput" in options) invokeOutputs[USER_INPUT_SRC] = options.userInput;
|
|
2082
2223
|
const reachedStates = /* @__PURE__ */ new Set();
|
|
2083
2224
|
const reachedValues = [];
|
|
2084
2225
|
const terminals = [];
|
|
@@ -2103,22 +2244,22 @@ async function explore(machine, options, stopWhen) {
|
|
|
2103
2244
|
if (current.done) return { step: current };
|
|
2104
2245
|
const request = current.requests[0];
|
|
2105
2246
|
if (request && request.kind === "text") {
|
|
2106
|
-
if (!(request.src in
|
|
2247
|
+
if (!(request.src in textScript)) return {
|
|
2107
2248
|
step: current,
|
|
2108
2249
|
blockedSrc: request.src
|
|
2109
2250
|
};
|
|
2110
|
-
current = require_setup_agent.resolveAgentStep(machine, current, request,
|
|
2251
|
+
current = require_setup_agent.resolveAgentStep(machine, current, request, textScript[request.src]);
|
|
2111
2252
|
recordState(current.snapshot);
|
|
2112
2253
|
continue;
|
|
2113
2254
|
}
|
|
2114
2255
|
if (request && request.kind === "decision") return { step: current };
|
|
2115
2256
|
const [invoke] = pendingInvokes(current);
|
|
2116
2257
|
if (invoke) {
|
|
2117
|
-
if (!(invoke.src in
|
|
2258
|
+
if (!(invoke.src in invokeOutputs)) return {
|
|
2118
2259
|
step: current,
|
|
2119
2260
|
blockedSrc: invoke.src
|
|
2120
2261
|
};
|
|
2121
|
-
current = require_setup_agent.resolveAgentStep(machine, current, invoke.id,
|
|
2262
|
+
current = require_setup_agent.resolveAgentStep(machine, current, invoke.id, invokeOutputs[invoke.src]);
|
|
2122
2263
|
recordState(current.snapshot);
|
|
2123
2264
|
continue;
|
|
2124
2265
|
}
|
|
@@ -2210,9 +2351,10 @@ async function explore(machine, options, stopWhen) {
|
|
|
2210
2351
|
* depth, model-free, and reports which states are reached and how each path
|
|
2211
2352
|
* terminates. At each decision request it forks one branch per candidate event
|
|
2212
2353
|
* (guard-rejected candidates are counted in `prunedByGuard`, not explored); at
|
|
2213
|
-
* an idle wait it forks per externally-accepted event. Text
|
|
2214
|
-
*
|
|
2215
|
-
*
|
|
2354
|
+
* an idle wait it forks per externally-accepted event. Text requests resolve
|
|
2355
|
+
* from `text`, other invokes from `invokes` (or `userInput` for
|
|
2356
|
+
* `agent.userInput`) — all by-src canned-output maps, and a missing src halts
|
|
2357
|
+
* that branch with a `needs-output` terminal rather than throwing.
|
|
2216
2358
|
*
|
|
2217
2359
|
* Combinatorics are bounded by `maxDepth` (default 8) and `maxPaths` (default
|
|
2218
2360
|
* 200, reported via `hitPathCap`).
|
|
@@ -2228,13 +2370,14 @@ async function explorePaths(machine, options = {}) {
|
|
|
2228
2370
|
}
|
|
2229
2371
|
/**
|
|
2230
2372
|
* Answers "can the machine reach `statePath`?" by exploring its branches (a
|
|
2231
|
-
* thin wrapper over {@link explorePaths}). Returns
|
|
2232
|
-
* with the event sequence that reaches it, or
|
|
2373
|
+
* thin wrapper over {@link explorePaths}). Returns
|
|
2374
|
+
* `{ reachable: true, witness }` with the event sequence that reaches it, or
|
|
2375
|
+
* `{ reachable: false }`.
|
|
2233
2376
|
*
|
|
2234
2377
|
* @example
|
|
2235
2378
|
* ```ts
|
|
2236
|
-
* const {
|
|
2237
|
-
* //
|
|
2379
|
+
* const { reachable, witness } = await canReach(refundMachine, 'denied', { input: { request: 'x', amount: 5000 } });
|
|
2380
|
+
* // reachable → true; witness → [{ type: 'NEEDS_REVIEW' }, { type: 'DENY' }]
|
|
2238
2381
|
* ```
|
|
2239
2382
|
*/
|
|
2240
2383
|
async function canReach(machine, statePath, options = {}) {
|
|
@@ -2246,9 +2389,9 @@ async function canReach(machine, statePath, options = {}) {
|
|
|
2246
2389
|
}
|
|
2247
2390
|
});
|
|
2248
2391
|
return witness !== void 0 ? {
|
|
2249
|
-
|
|
2392
|
+
reachable: true,
|
|
2250
2393
|
witness
|
|
2251
|
-
} : {
|
|
2394
|
+
} : { reachable: false };
|
|
2252
2395
|
}
|
|
2253
2396
|
//#endregion
|
|
2254
2397
|
//#region src/internal/is-record.ts
|
|
@@ -2409,7 +2552,8 @@ function matchesTrajectory(actual, expected, options = {}) {
|
|
|
2409
2552
|
* Scripted executors — a keyless, deterministic stand-in for a model host.
|
|
2410
2553
|
*
|
|
2411
2554
|
* `createScriptedExecutors` builds a full `{ generateText, streamText, decide }`
|
|
2412
|
-
* set that plays back canned answers from FIFO
|
|
2555
|
+
* set (plus a `userInput` handler) that plays back canned answers from FIFO
|
|
2556
|
+
* queues, so `runAgent` (or
|
|
2413
2557
|
* `provideExecutors`, or a bare `TextLogic.execute`) runs with no API key and no
|
|
2414
2558
|
* network. It is the fastest way to see a machine run, and the least ceremonial
|
|
2415
2559
|
* way to test one: same machine, same executor contract, scripted answers.
|
|
@@ -2485,15 +2629,27 @@ function emitScriptedChunk(result, info) {
|
|
|
2485
2629
|
* decisions: [(request) => ({ type: request.events[0]!.type })],
|
|
2486
2630
|
* });
|
|
2487
2631
|
* ```
|
|
2632
|
+
*
|
|
2633
|
+
* @example Scripted human input
|
|
2634
|
+
* ```ts
|
|
2635
|
+
* const scripted = createScriptedExecutors({ userInput: ['ship it'] });
|
|
2636
|
+
* await runAgent(machine, { executors: scripted, userInput: scripted.userInput });
|
|
2637
|
+
* ```
|
|
2488
2638
|
*/
|
|
2489
2639
|
function createScriptedExecutors(script = {}) {
|
|
2490
2640
|
const decisions = [...script.decisions ?? []];
|
|
2491
2641
|
const text = [...script.text ?? []];
|
|
2642
|
+
const userInput = [...script.userInput ?? []];
|
|
2492
2643
|
const nextText = async (request, info) => {
|
|
2493
2644
|
if (text.length === 0) throw new require_errors.AgentError("scripted-executors-exhausted", `createScriptedExecutors: script ran dry on a pending text request ${describeText(request)}. Add another entry to the script's \`text\` queue.`);
|
|
2494
2645
|
return resolveScriptedTextEntry(text.shift(), request, info);
|
|
2495
2646
|
};
|
|
2496
2647
|
return {
|
|
2648
|
+
userInput: async (input) => {
|
|
2649
|
+
if (userInput.length === 0) throw new require_errors.AgentError("scripted-executors-exhausted", `createScriptedExecutors: script ran dry on a pending userInput request (prompt: ${input.prompt ? `'${input.prompt}'` : "(none)"}). Add another entry to the script's \`userInput\` queue.`);
|
|
2650
|
+
const entry = userInput.shift();
|
|
2651
|
+
return typeof entry === "function" ? await entry(input) : entry;
|
|
2652
|
+
},
|
|
2497
2653
|
generateText: nextText,
|
|
2498
2654
|
streamText: async (request, info) => {
|
|
2499
2655
|
const result = await nextText(request, info);
|
|
@@ -2567,22 +2723,22 @@ async function runSeam(machine, options) {
|
|
|
2567
2723
|
const queueKeyOf = (request) => request.name !== void 0 && queues.has(request.name) ? request.name : request.model;
|
|
2568
2724
|
/**
|
|
2569
2725
|
* Consumes this request's slot in the call plan, or resolves `undefined` when
|
|
2570
|
-
* its queue is dry.
|
|
2571
|
-
*
|
|
2726
|
+
* its queue is dry. With `repeatLast`, the last entry is replayed instead of
|
|
2727
|
+
* running dry.
|
|
2572
2728
|
*/
|
|
2573
2729
|
const takeScriptedSlot = async (request, info) => {
|
|
2574
2730
|
const queue = queues.get(queueKeyOf(request));
|
|
2575
2731
|
if (!queue?.length) return;
|
|
2576
|
-
return resolveScriptedTextEntry(queue.length === 1 ? queue[0] : queue.shift(), request, info);
|
|
2732
|
+
return resolveScriptedTextEntry(options.repeatLast && queue.length === 1 ? queue[0] : queue.shift(), request, info);
|
|
2577
2733
|
};
|
|
2578
2734
|
const scriptedAnswer = async (request, info) => {
|
|
2579
2735
|
const scripted = await takeScriptedSlot(request, info);
|
|
2580
|
-
if (!scripted) throw new require_errors.AgentError("seam-script-exhausted", `runSeam: no scripted answer left for request ${describeText(request)}. Add an entry to \`scripts.${queueKeyOf(request)}
|
|
2736
|
+
if (!scripted) throw new require_errors.AgentError("seam-script-exhausted", `runSeam: no scripted answer left for request ${describeText(request)}. Add an entry to \`scripts.${queueKeyOf(request)}\`, or pass \`repeatLast: true\` to replay its last entry down a longer branch.`);
|
|
2581
2737
|
return scripted;
|
|
2582
2738
|
};
|
|
2583
2739
|
const route = async (request, info) => {
|
|
2584
2740
|
const callIndex = calls++;
|
|
2585
|
-
const isSeam =
|
|
2741
|
+
const isSeam = request.name === seam.request && seamMatches++ === (seam.occurrence ?? 0);
|
|
2586
2742
|
if (isSeam && candidate) {
|
|
2587
2743
|
await takeScriptedSlot(request, info);
|
|
2588
2744
|
seamReached = true;
|
|
@@ -2620,7 +2776,7 @@ async function runSeam(machine, options) {
|
|
|
2620
2776
|
result = await runAgent(machine, {
|
|
2621
2777
|
...snapshot ? { snapshot } : { input: options.input },
|
|
2622
2778
|
...event ? { event } : {},
|
|
2623
|
-
...options.
|
|
2779
|
+
...options.isIdle ? { isIdle: options.isIdle } : {},
|
|
2624
2780
|
...options.actors ? { actors: options.actors } : {},
|
|
2625
2781
|
events,
|
|
2626
2782
|
executors,
|
|
@@ -2683,13 +2839,13 @@ exports.AgentEventLogConflictError = require_event_log_store.AgentEventLogConfli
|
|
|
2683
2839
|
exports.AgentIdleError = AgentIdleError;
|
|
2684
2840
|
exports.AgentIllegalResumeEventError = AgentIllegalResumeEventError;
|
|
2685
2841
|
exports.AgentLintError = AgentLintError;
|
|
2842
|
+
exports.AgentMaxModelCallsExceededError = AgentMaxModelCallsExceededError;
|
|
2686
2843
|
exports.AgentReplayDivergenceError = require_setup_agent.AgentReplayDivergenceError;
|
|
2687
2844
|
exports.AgentReplayMachineMismatchError = require_setup_agent.AgentReplayMachineMismatchError;
|
|
2688
2845
|
exports.AgentSnapshotVersionMismatchError = AgentSnapshotVersionMismatchError;
|
|
2689
2846
|
exports.NonSerializableAgentEventError = require_event_log_store.NonSerializableAgentEventError;
|
|
2690
2847
|
exports.appendMessages = require_setup_agent.appendMessages;
|
|
2691
2848
|
exports.assertAgentLogEntry = require_event_log_store.assertAgentLogEntry;
|
|
2692
|
-
exports.assertAgentMachine = assertAgentMachine;
|
|
2693
2849
|
exports.assertEventLogStoreConformance = require_event_log_store.assertEventLogStoreConformance;
|
|
2694
2850
|
exports.assertJsonSerializable = require_event_log_store.assertJsonSerializable;
|
|
2695
2851
|
exports.assistantMessage = require_decision.assistantMessage;
|
|
@@ -2716,6 +2872,8 @@ exports.getCallUsage = require_decision.getCallUsage;
|
|
|
2716
2872
|
exports.getJsonSchema = require_decision.getJsonSchema;
|
|
2717
2873
|
exports.getJsonSchemaSync = require_decision.getJsonSchemaSync;
|
|
2718
2874
|
exports.getMachineStructuralHash = require_decision.getMachineStructuralHash;
|
|
2875
|
+
exports.getSnapshotNodes = getSnapshotNodes;
|
|
2876
|
+
exports.getSnapshotRequests = getSnapshotRequests;
|
|
2719
2877
|
exports.getStateMeta = require_decision.getStateMeta;
|
|
2720
2878
|
exports.initEntry = require_setup_agent.initEntry;
|
|
2721
2879
|
exports.inspectTransitions = inspectTransitions;
|
|
@@ -2727,7 +2885,6 @@ exports.parseAgentEvent = require_decision.parseAgentEvent;
|
|
|
2727
2885
|
exports.parseModelRef = require_decision.parseModelRef;
|
|
2728
2886
|
exports.parseOutput = require_decision.parseOutput;
|
|
2729
2887
|
exports.parseStructuredEnvelope = require_decision.parseStructuredEnvelope;
|
|
2730
|
-
exports.persistSnapshot = require_decision.persistSnapshot;
|
|
2731
2888
|
exports.provideExecutors = provideExecutors;
|
|
2732
2889
|
exports.renderDecisionAttempts = require_decision.renderDecisionAttempts;
|
|
2733
2890
|
exports.replay = require_setup_agent.replay;
|
|
@@ -2746,4 +2903,3 @@ exports.systemMessage = require_decision.systemMessage;
|
|
|
2746
2903
|
exports.toolMessage = require_decision.toolMessage;
|
|
2747
2904
|
exports.traceTransitions = traceTransitions;
|
|
2748
2905
|
exports.userMessage = require_decision.userMessage;
|
|
2749
|
-
exports.verifyReplay = require_setup_agent.verifyReplay;
|