@statelyai/agent 2.0.0-alpha.19 → 2.0.0-alpha.21
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 +89 -25
- package/dist/ai-sdk.d.cts +56 -12
- package/dist/ai-sdk.d.mts +56 -12
- package/dist/ai-sdk.mjs +89 -26
- package/dist/{decision-t26zsnSR.cjs → decision-BnTCsuJv.cjs} +16 -4
- package/dist/{decision-DsIkEuHz.mjs → decision-JWx6n3xR.mjs} +5 -5
- package/dist/index.cjs +378 -24
- package/dist/index.d.cts +211 -19
- package/dist/index.d.mts +211 -19
- package/dist/index.mjs +378 -26
- package/dist/machines.cjs +1 -1
- package/dist/machines.d.cts +1 -1
- package/dist/machines.d.mts +1 -1
- package/dist/machines.mjs +1 -1
- package/dist/otel.d.cts +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/{run-agent-CwmzAZwj.d.mts → run-agent-DBNI8ETM.d.mts} +3 -3
- package/dist/{run-agent--4bbms-D.d.cts → run-agent-Do094mGu.d.cts} +3 -3
- package/dist/{setup-agent-BFA4VKpN.cjs → setup-agent-91FSuZbB.cjs} +2 -2
- package/dist/{setup-agent-CPFPN06s.mjs → setup-agent-DqqdcdNh.mjs} +2 -2
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.mts +1 -1
- package/dist/{text-logic-Er5KkTX6.d.mts → text-logic-BR5twXCv.d.mts} +8 -5
- package/dist/{text-logic-Cavva1W6.d.cts → text-logic-Mmbgb2jy.d.cts} +8 -5
- package/dist/{types-DYpK3QF4.d.mts → types-CvWRGFxP.d.mts} +6 -1
- package/dist/{types-pJ5Hn8fv.d.cts → types-DSdj2tGs.d.cts} +6 -1
- package/dist/validate.cjs +2 -2
- package/dist/validate.mjs +1 -1
- package/package.json +28 -28
- package/readme.md +4 -4
- package/schemas/agent-workflow.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { t as AgentError } from "./errors-CeSXQx0v.mjs";
|
|
2
|
-
import { _ as resolveAgentStep, a as AGENT_USAGE_EVENT_TYPE, b as messagesSchema, c as createReplayEntry, d as initEntry, f as replay, g as initialAgentStep, h as getInvokeEffectMetadata, i as AGENT_INIT_EVENT_TYPE, l as diffEventLogs, m as executeAgentRequest, n as getAgentSchemas, o as AgentReplayDivergenceError, p as validateReplayEntries, r as setupAgent, s as AgentReplayMachineMismatchError, t as createAgentSchemas, u as getAgentEffects, v as transitionAgentStep, y as appendMessages } from "./setup-agent-
|
|
3
|
-
import { A as
|
|
2
|
+
import { _ as resolveAgentStep, a as AGENT_USAGE_EVENT_TYPE, b as messagesSchema, c as createReplayEntry, d as initEntry, f as replay, g as initialAgentStep, h as getInvokeEffectMetadata, i as AGENT_INIT_EVENT_TYPE, l as diffEventLogs, m as executeAgentRequest, n as getAgentSchemas, o as AgentReplayDivergenceError, p as validateReplayEntries, r as setupAgent, s as AgentReplayMachineMismatchError, t as createAgentSchemas, u as getAgentEffects, v as transitionAgentStep, y as appendMessages } from "./setup-agent-DqqdcdNh.mjs";
|
|
3
|
+
import { A as getMachineStaticTransitionTargets, B as getJsonSchema, C as parseModelRef, G as resolveMachineVersion, H as getMachineStructuralHash, I as assistantMessage, J as userMessage, K as systemMessage, M as isUnboundPlaceholder, O as executorBoundLogics, R as findNonSerializableContextPaths, S as normalizeGeneratorResult, T as parseStructuredEnvelope, U as getStateMeta, V as getJsonSchemaSync, W as isStandardSchema, Y as validateSchemaSync, _ as createTextLogic, a as resolveDecision, b as getCallUsage, c as AGENT_USAGE_TOKEN_FIELDS, d as INTERPRET_SOURCE, h as buildEnvelopeSchema, i as renderDecisionAttempts, j as getRegisteredAgentExecutionOptions, k as getMachineIdlePredicate, m as bindRequestExecutor, o as getAcceptedEvents, q as toolMessage, r as isDecisionLogic, s as parseAgentEvent, t as AgentDecisionExhaustedError, w as parseOutput, x as isTextLogic, y as getAgentOutputMode, z as getAgentMessages } from "./decision-JWx6n3xR.mjs";
|
|
4
4
|
import { a as assertJsonSerializable, i as assertAgentLogEntry, n as AgentEventLogConflictError, o as createInMemoryEventLogStore, r as NonSerializableAgentEventError, s as assertEventLogStoreConformance, t as AGENT_EVENT_SCHEMA_VERSION } from "./event-log-store-DmIDosD6.mjs";
|
|
5
|
-
import { createActor, createAsyncLogic, getNextTransitions, isMachineSnapshot } from "xstate";
|
|
5
|
+
import { createActor, createAsyncLogic, deliverEvent, getNextTransitions, isMachineSnapshot } from "xstate";
|
|
6
|
+
import { createDurable } from "xstate/durable";
|
|
6
7
|
//#region src/internal/state-request-pass.ts
|
|
7
8
|
async function runTextPhase(stateRequest, baseMessages, deps) {
|
|
8
9
|
const { model, system } = stateRequest;
|
|
@@ -284,7 +285,7 @@ function serializeTraceEvent(event, options = {}) {
|
|
|
284
285
|
return out;
|
|
285
286
|
}
|
|
286
287
|
function snapshotNodes(snapshot) {
|
|
287
|
-
return (snapshot.
|
|
288
|
+
return (snapshot.nodes ?? []).map((raw) => {
|
|
288
289
|
const node = raw;
|
|
289
290
|
return {
|
|
290
291
|
id: node.id ?? "",
|
|
@@ -567,9 +568,13 @@ function invokingActorOf(self, runCtx) {
|
|
|
567
568
|
* identical event shapes by construction. @internal
|
|
568
569
|
*/
|
|
569
570
|
function bindTextLogic(logic, runCtx) {
|
|
570
|
-
return logic.withExecutor(async ({ request, self: selfArg, signal }) => {
|
|
571
|
+
return logic.withExecutor(async ({ request: rawRequest, self: selfArg, signal }) => {
|
|
571
572
|
const self = selfArg;
|
|
572
573
|
const { id, src } = selfIdAndSrc(self);
|
|
574
|
+
const request = rawRequest.name === void 0 && src !== "" && src !== "agent.generateText" && src !== "agent.streamText" ? {
|
|
575
|
+
...rawRequest,
|
|
576
|
+
name: src
|
|
577
|
+
} : rawRequest;
|
|
573
578
|
const executor = logic.mode === "stream" ? runCtx.streamText : runCtx.generateText;
|
|
574
579
|
if (!executor) throw new Error(`No '${logic.mode === "stream" ? "streamText" : "generateText"}' executor provided.`);
|
|
575
580
|
const requestWithTools = {
|
|
@@ -1159,6 +1164,7 @@ function createAgentSession(machine, options, lifecycle) {
|
|
|
1159
1164
|
const aligned = Object.assign(Object.create(Object.getPrototypeOf(effectiveSnapshot)), effectiveSnapshot);
|
|
1160
1165
|
if (machineOwnVersion === void 0) delete aligned.version;
|
|
1161
1166
|
else aligned.version = machineOwnVersion;
|
|
1167
|
+
delete aligned.machine;
|
|
1162
1168
|
effectiveSnapshot = aligned;
|
|
1163
1169
|
}
|
|
1164
1170
|
const priorMessages = getAgentMessages(effectiveSnapshot);
|
|
@@ -2082,8 +2088,8 @@ function lintAgentMachine(machine, options = {}) {
|
|
|
2082
2088
|
return diagnostics;
|
|
2083
2089
|
}
|
|
2084
2090
|
/**
|
|
2085
|
-
* Thrown by `lintAgentMachine(machine, { throw: true })`
|
|
2086
|
-
* failing diagnostics.
|
|
2091
|
+
* Thrown by `lintAgentMachine(machine, { throw: true })` (and its
|
|
2092
|
+
* {@link assertAgentMachine} alias) when lint finds failing diagnostics.
|
|
2087
2093
|
* `diagnostics` holds the findings; the message lists them one per finding,
|
|
2088
2094
|
* so a test runner's failure output reads like the CLI's lint report.
|
|
2089
2095
|
*/
|
|
@@ -2096,6 +2102,26 @@ var AgentLintError = class extends AgentError {
|
|
|
2096
2102
|
this.diagnostics = diagnostics;
|
|
2097
2103
|
}
|
|
2098
2104
|
};
|
|
2105
|
+
/**
|
|
2106
|
+
* Asserts a machine passes {@link lintAgentMachine}: returns silently when
|
|
2107
|
+
* clean, throws {@link AgentLintError} (with the findings on `.diagnostics`)
|
|
2108
|
+
* otherwise — sugar for `lintAgentMachine(machine, { ...options, throw: true })`.
|
|
2109
|
+
* Fails on error-severity findings; set `warnings: true` to fail on warnings
|
|
2110
|
+
* too. The one-liner for tests and generation loops:
|
|
2111
|
+
*
|
|
2112
|
+
* @example
|
|
2113
|
+
* ```ts
|
|
2114
|
+
* test('agent machine is structurally sound', () => {
|
|
2115
|
+
* assertAgentMachine(machine);
|
|
2116
|
+
* });
|
|
2117
|
+
* ```
|
|
2118
|
+
*/
|
|
2119
|
+
function assertAgentMachine(machine, options = {}) {
|
|
2120
|
+
lintAgentMachine(machine, {
|
|
2121
|
+
...options,
|
|
2122
|
+
throw: true
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2099
2125
|
function pendingInvokes(step) {
|
|
2100
2126
|
const out = [];
|
|
2101
2127
|
for (const action of step.actions) {
|
|
@@ -2122,6 +2148,21 @@ function takeFromQueue(channel, src) {
|
|
|
2122
2148
|
* the real transition logic. Returns the terminal `status`, final `snapshot`,
|
|
2123
2149
|
* and a `trail` of every step taken.
|
|
2124
2150
|
*
|
|
2151
|
+
* Decisions run through the live run's own validation/retry core,
|
|
2152
|
+
* {@link resolveDecision}, with the script standing in for the model: each
|
|
2153
|
+
* attempt consumes the next queued {@link ChosenEvent} for that src, so an
|
|
2154
|
+
* unknown, payload-invalid, or guard-rejected event is NOT silently swallowed
|
|
2155
|
+
* — the next queued decision is tried, exactly as a live run re-asks the
|
|
2156
|
+
* model. The decision logic's `maxRetries` caps attempts as it would live;
|
|
2157
|
+
* when retries continue past the end of the queue, the last queued decision
|
|
2158
|
+
* repeats (a scripted model that insists). The repeat applies only within one
|
|
2159
|
+
* decision request's retries — each new decision request must have its own
|
|
2160
|
+
* queued entry, or the dry-script error throws as usual. Exhausting all
|
|
2161
|
+
* attempts delivers
|
|
2162
|
+
* the resulting {@link AgentDecisionExhaustedError} to the machine as the
|
|
2163
|
+
* decision invoke's error (so an `onError` transition observes it, as it
|
|
2164
|
+
* would live); with no `onError` to catch it, it is thrown.
|
|
2165
|
+
*
|
|
2125
2166
|
* Throws a descriptive error when the script runs dry mid-request, naming the
|
|
2126
2167
|
* pending request's kind, src, and id so the missing scripted response is
|
|
2127
2168
|
* obvious.
|
|
@@ -2130,7 +2171,10 @@ function takeFromQueue(channel, src) {
|
|
|
2130
2171
|
* ```ts
|
|
2131
2172
|
* const { status, snapshot } = simulateAgent(machine, {
|
|
2132
2173
|
* input: { topic: 'state machines' },
|
|
2133
|
-
* script: {
|
|
2174
|
+
* script: {
|
|
2175
|
+
* decisions: { 'agent.decide': [{ type: 'ESCALATE' }] },
|
|
2176
|
+
* events: [{ type: 'APPROVE' }], // crosses the human gate
|
|
2177
|
+
* },
|
|
2134
2178
|
* });
|
|
2135
2179
|
* ```
|
|
2136
2180
|
*/
|
|
@@ -2139,11 +2183,12 @@ async function simulateAgent(machine, options) {
|
|
|
2139
2183
|
const script = {
|
|
2140
2184
|
text: mapValues(options.script.text ?? {}, (arr) => [...arr]),
|
|
2141
2185
|
decisions: mapValues(options.script.decisions ?? {}, (arr) => [...arr]),
|
|
2142
|
-
invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr])
|
|
2186
|
+
invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr]),
|
|
2187
|
+
events: [...options.script.events ?? []]
|
|
2143
2188
|
};
|
|
2144
2189
|
if (options.script.userInput?.length) script.invokes[USER_INPUT_SRC] = [...options.script.userInput, ...script.invokes[USER_INPUT_SRC] ?? []];
|
|
2145
2190
|
let step = initialAgentStep(machine, options.input);
|
|
2146
|
-
const trail = [];
|
|
2191
|
+
const trail = [{ state: step.snapshot.value }];
|
|
2147
2192
|
for (let i = 0; i < maxSteps; i++) {
|
|
2148
2193
|
if (step.done) return {
|
|
2149
2194
|
status: "done",
|
|
@@ -2153,14 +2198,10 @@ async function simulateAgent(machine, options) {
|
|
|
2153
2198
|
const request = step.requests[0];
|
|
2154
2199
|
if (request) {
|
|
2155
2200
|
if (request.kind === "decision") {
|
|
2156
|
-
const
|
|
2157
|
-
const
|
|
2158
|
-
|
|
2159
|
-
step =
|
|
2160
|
-
trail.push({
|
|
2161
|
-
state: step.snapshot.value,
|
|
2162
|
-
appliedEvent: taken.value
|
|
2163
|
-
});
|
|
2201
|
+
const invokeMeta = findInvokeMetadata(step, request.id);
|
|
2202
|
+
const src = invokeMeta?.src ?? request.id;
|
|
2203
|
+
const decisionSrc = src in (script.decisions ?? {}) ? src : request.id in (script.decisions ?? {}) ? request.id : src;
|
|
2204
|
+
step = await applyScriptedDecision(machine, step, request, decisionSrc, invokeMeta?.logic, script, trail);
|
|
2164
2205
|
continue;
|
|
2165
2206
|
}
|
|
2166
2207
|
const taken = takeFromQueue(script.text, request.src);
|
|
@@ -2191,6 +2232,18 @@ async function simulateAgent(machine, options) {
|
|
|
2191
2232
|
});
|
|
2192
2233
|
continue;
|
|
2193
2234
|
}
|
|
2235
|
+
const external = script.events;
|
|
2236
|
+
if (external.length > 0) {
|
|
2237
|
+
const event = external.shift();
|
|
2238
|
+
if (!step.snapshot.can(event)) throw new Error(`simulateAgent: scripted external event '${event.type}' cannot be taken in state ${JSON.stringify(step.snapshot.value)} (no handler, or its guard rejected it). A live run's send would be silently dropped here; fix the script's \`events\` queue.`);
|
|
2239
|
+
step = transitionAgentStep(machine, step, event);
|
|
2240
|
+
trail.push({
|
|
2241
|
+
state: step.snapshot.value,
|
|
2242
|
+
appliedEvent: event,
|
|
2243
|
+
external: true
|
|
2244
|
+
});
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2194
2247
|
return {
|
|
2195
2248
|
status: "idle",
|
|
2196
2249
|
snapshot: step.snapshot,
|
|
@@ -2203,6 +2256,66 @@ async function simulateAgent(machine, options) {
|
|
|
2203
2256
|
trail
|
|
2204
2257
|
};
|
|
2205
2258
|
}
|
|
2259
|
+
async function applyScriptedDecision(machine, step, request, decisionSrc, invokeLogic, script, trail) {
|
|
2260
|
+
const dequeued = [];
|
|
2261
|
+
const decide = async () => {
|
|
2262
|
+
const taken = takeFromQueue(script.decisions, decisionSrc);
|
|
2263
|
+
if (taken.found) {
|
|
2264
|
+
dequeued.push(taken.value);
|
|
2265
|
+
return { event: taken.value };
|
|
2266
|
+
}
|
|
2267
|
+
if (dequeued.length === 0) throw scriptDryError("decision", decisionSrc, request.id, request);
|
|
2268
|
+
return { event: dequeued[dequeued.length - 1] };
|
|
2269
|
+
};
|
|
2270
|
+
const maxRetries = (isDecisionLogic(invokeLogic) ? invokeLogic : resolveRegisteredDecisionLogic(machine, decisionSrc))?.maxRetries;
|
|
2271
|
+
let chosen;
|
|
2272
|
+
try {
|
|
2273
|
+
chosen = await resolveDecision(request, { decide }, {
|
|
2274
|
+
maxRetries,
|
|
2275
|
+
canTake: (event) => step.snapshot.can(event)
|
|
2276
|
+
});
|
|
2277
|
+
} catch (error) {
|
|
2278
|
+
if (!(error instanceof AgentDecisionExhaustedError)) throw error;
|
|
2279
|
+
const next = transitionAgentStep(machine, step, {
|
|
2280
|
+
type: "xstate.error.actor",
|
|
2281
|
+
actorId: request.id,
|
|
2282
|
+
...sessionIdOf(step.snapshot, request.id),
|
|
2283
|
+
error
|
|
2284
|
+
});
|
|
2285
|
+
if (next.snapshot.status === "error") throw error;
|
|
2286
|
+
trail.push({
|
|
2287
|
+
state: next.snapshot.value,
|
|
2288
|
+
rejectedEvents: dequeued
|
|
2289
|
+
});
|
|
2290
|
+
return next;
|
|
2291
|
+
}
|
|
2292
|
+
const next = transitionAgentStep(machine, step, chosen);
|
|
2293
|
+
const rejected = dequeued.slice(0, -1);
|
|
2294
|
+
trail.push({
|
|
2295
|
+
state: next.snapshot.value,
|
|
2296
|
+
appliedEvent: chosen,
|
|
2297
|
+
...rejected.length > 0 ? { rejectedEvents: rejected } : {}
|
|
2298
|
+
});
|
|
2299
|
+
return next;
|
|
2300
|
+
}
|
|
2301
|
+
function findInvokeMetadata(step, id) {
|
|
2302
|
+
for (const action of step.actions) {
|
|
2303
|
+
const metadata = getInvokeEffectMetadata(action);
|
|
2304
|
+
if (metadata?.id !== id) continue;
|
|
2305
|
+
return {
|
|
2306
|
+
...typeof metadata.src === "string" ? { src: metadata.src } : {},
|
|
2307
|
+
logic: metadata.logic ?? (typeof metadata.src === "object" ? metadata.src : void 0)
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
function resolveRegisteredDecisionLogic(machine, src) {
|
|
2312
|
+
const candidate = getRegisteredAgentExecutionOptions(machine).actors?.[src] ?? machine.sources?.actors?.[src];
|
|
2313
|
+
return isDecisionLogic(candidate) ? candidate : void 0;
|
|
2314
|
+
}
|
|
2315
|
+
function sessionIdOf(snapshot, id) {
|
|
2316
|
+
const child = snapshot.children[id];
|
|
2317
|
+
return typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {};
|
|
2318
|
+
}
|
|
2206
2319
|
function mapValues(obj, fn) {
|
|
2207
2320
|
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, fn(value)]));
|
|
2208
2321
|
}
|
|
@@ -2239,6 +2352,10 @@ async function explore(machine, options, stopWhen) {
|
|
|
2239
2352
|
const advance = (step) => {
|
|
2240
2353
|
let current = step;
|
|
2241
2354
|
for (let i = 0; i < MAX_ADVANCE_STEPS; i++) {
|
|
2355
|
+
if (stopWhen?.(current.snapshot)) return {
|
|
2356
|
+
step: current,
|
|
2357
|
+
hit: true
|
|
2358
|
+
};
|
|
2242
2359
|
if (current.done) return { step: current };
|
|
2243
2360
|
const request = current.requests[0];
|
|
2244
2361
|
if (request && request.kind === "text") {
|
|
@@ -2271,8 +2388,8 @@ async function explore(machine, options, stopWhen) {
|
|
|
2271
2388
|
hitPathCap = true;
|
|
2272
2389
|
return;
|
|
2273
2390
|
}
|
|
2274
|
-
const { step: settled, blockedSrc } = advance(step);
|
|
2275
|
-
if (
|
|
2391
|
+
const { step: settled, blockedSrc, hit } = advance(step);
|
|
2392
|
+
if (hit) {
|
|
2276
2393
|
witness = path;
|
|
2277
2394
|
return;
|
|
2278
2395
|
}
|
|
@@ -2367,8 +2484,11 @@ async function explorePaths(machine, options = {}) {
|
|
|
2367
2484
|
return (await explore(machine, options)).report;
|
|
2368
2485
|
}
|
|
2369
2486
|
/**
|
|
2370
|
-
* Answers "can the machine reach
|
|
2371
|
-
*
|
|
2487
|
+
* Answers "can the machine reach this?" by exploring its branches (a thin
|
|
2488
|
+
* wrapper over {@link explorePaths}). The target is either a state path string
|
|
2489
|
+
* (`snapshot.matches(...)` semantics) or a snapshot predicate — the predicate
|
|
2490
|
+
* form checks any property (a context invariant, a tag, a state+context
|
|
2491
|
+
* combination) without reifying a sentinel state for it. Returns
|
|
2372
2492
|
* `{ reachable: true, witness }` with the event sequence that reaches it, or
|
|
2373
2493
|
* `{ reachable: false }`.
|
|
2374
2494
|
*
|
|
@@ -2377,11 +2497,21 @@ async function explorePaths(machine, options = {}) {
|
|
|
2377
2497
|
* const { reachable, witness } = await canReach(refundMachine, 'denied', { input: { request: 'x', amount: 5000 } });
|
|
2378
2498
|
* // reachable → true; witness → [{ type: 'NEEDS_REVIEW' }, { type: 'DENY' }]
|
|
2379
2499
|
* ```
|
|
2500
|
+
*
|
|
2501
|
+
* @example Predicate target — a violation property, no sentinel state needed
|
|
2502
|
+
* ```ts
|
|
2503
|
+
* const violation = await canReach(
|
|
2504
|
+
* refundMachine,
|
|
2505
|
+
* (snapshot) => snapshot.matches('issued') && !snapshot.context.approved,
|
|
2506
|
+
* { input: { amount: 5000 } },
|
|
2507
|
+
* );
|
|
2508
|
+
* // violation.reachable → false is the safety proof
|
|
2509
|
+
* ```
|
|
2380
2510
|
*/
|
|
2381
|
-
async function canReach(machine,
|
|
2382
|
-
const { witness } = await explore(machine, options, (snapshot) => {
|
|
2511
|
+
async function canReach(machine, target, options = {}) {
|
|
2512
|
+
const { witness } = await explore(machine, options, typeof target === "function" ? target : (snapshot) => {
|
|
2383
2513
|
try {
|
|
2384
|
-
return snapshot.matches(
|
|
2514
|
+
return snapshot.matches(target);
|
|
2385
2515
|
} catch {
|
|
2386
2516
|
return false;
|
|
2387
2517
|
}
|
|
@@ -2713,6 +2843,8 @@ async function runSeam(machine, options) {
|
|
|
2713
2843
|
let calls = 0;
|
|
2714
2844
|
let seamMatches = 0;
|
|
2715
2845
|
let seamOutput;
|
|
2846
|
+
let seamUsage;
|
|
2847
|
+
const callLog = [];
|
|
2716
2848
|
let seamReached = false;
|
|
2717
2849
|
let callsBeforeSeam = -1;
|
|
2718
2850
|
let seamStateAt = 0;
|
|
@@ -2737,6 +2869,15 @@ async function runSeam(machine, options) {
|
|
|
2737
2869
|
const route = async (request, info) => {
|
|
2738
2870
|
const callIndex = calls++;
|
|
2739
2871
|
const isSeam = request.name === seam.request && seamMatches++ === (seam.occurrence ?? 0);
|
|
2872
|
+
const ledger = (source) => {
|
|
2873
|
+
callLog.push({
|
|
2874
|
+
...request.name !== void 0 ? { name: request.name } : {},
|
|
2875
|
+
model: request.model,
|
|
2876
|
+
key: queueKeyOf(request),
|
|
2877
|
+
source,
|
|
2878
|
+
seam: isSeam
|
|
2879
|
+
});
|
|
2880
|
+
};
|
|
2740
2881
|
if (isSeam && candidate) {
|
|
2741
2882
|
await takeScriptedSlot(request, info);
|
|
2742
2883
|
seamReached = true;
|
|
@@ -2745,15 +2886,19 @@ async function runSeam(machine, options) {
|
|
|
2745
2886
|
seamEventAt = liveEvents;
|
|
2746
2887
|
const result = await candidate(request, info);
|
|
2747
2888
|
seamOutput = await seamOutputOf(result, request);
|
|
2889
|
+
seamUsage = getCallUsage(result);
|
|
2890
|
+
ledger("candidate");
|
|
2748
2891
|
return result;
|
|
2749
2892
|
}
|
|
2750
2893
|
const scripted = await scriptedAnswer(request, info);
|
|
2894
|
+
ledger("script");
|
|
2751
2895
|
if (!isSeam) return scripted;
|
|
2752
2896
|
seamReached = true;
|
|
2753
2897
|
callsBeforeSeam = callIndex;
|
|
2754
2898
|
seamStateAt = statePath.length;
|
|
2755
2899
|
seamEventAt = liveEvents;
|
|
2756
2900
|
seamOutput = scripted.output;
|
|
2901
|
+
seamUsage = scripted.usage;
|
|
2757
2902
|
return scripted;
|
|
2758
2903
|
};
|
|
2759
2904
|
const executors = {
|
|
@@ -2815,7 +2960,9 @@ async function runSeam(machine, options) {
|
|
|
2815
2960
|
return {
|
|
2816
2961
|
result,
|
|
2817
2962
|
seamOutput,
|
|
2963
|
+
...seamUsage !== void 0 ? { seamUsage } : {},
|
|
2818
2964
|
callsBeforeSeam,
|
|
2965
|
+
calls: callLog,
|
|
2819
2966
|
before: {
|
|
2820
2967
|
statePath: statePath.slice(0, splitStateAt),
|
|
2821
2968
|
events: events.slice(0, splitAt)
|
|
@@ -2827,4 +2974,209 @@ async function runSeam(machine, options) {
|
|
|
2827
2974
|
};
|
|
2828
2975
|
}
|
|
2829
2976
|
//#endregion
|
|
2830
|
-
|
|
2977
|
+
//#region src/durable.ts
|
|
2978
|
+
/**
|
|
2979
|
+
* The durable host runner: {@link runDurableAgent} drives an executor-bound
|
|
2980
|
+
* agent machine on xstate's `createDurable` execution (`xstate/durable`),
|
|
2981
|
+
* with the agent event log as the journal.
|
|
2982
|
+
*
|
|
2983
|
+
* Where {@link replay} + `getAgentEffects` hand a host an effect list to run
|
|
2984
|
+
* itself, `runDurableAgent` owns the whole loop on the durable runtime:
|
|
2985
|
+
* invoked actors execute live through xstate's own runtime, every EXTERNAL
|
|
2986
|
+
* event (invoke completions included) is appended to the log, and a resume
|
|
2987
|
+
* folds the log back through pure transitions — an invoke whose completion is
|
|
2988
|
+
* already journaled is never re-started, so recorded model calls are never
|
|
2989
|
+
* re-executed. Crash recovery re-runs only the work that was still in flight.
|
|
2990
|
+
*
|
|
2991
|
+
* @module
|
|
2992
|
+
*/
|
|
2993
|
+
const DONE_ACTOR_EVENT_TYPE = "xstate.done.actor";
|
|
2994
|
+
const ERROR_ACTOR_EVENT_TYPE = "xstate.error.actor";
|
|
2995
|
+
function completionActorId(event) {
|
|
2996
|
+
if (event.type !== DONE_ACTOR_EVENT_TYPE && event.type !== ERROR_ACTOR_EVENT_TYPE) return;
|
|
2997
|
+
const actorId = event.actorId;
|
|
2998
|
+
return typeof actorId === "string" ? actorId : void 0;
|
|
2999
|
+
}
|
|
3000
|
+
/** Thrown by the adapter's `waitForEvent` when nothing can produce an event. */
|
|
3001
|
+
const IDLE = Symbol("agent.durable.idle");
|
|
3002
|
+
function createMailbox() {
|
|
3003
|
+
const queue = [];
|
|
3004
|
+
const waiters = [];
|
|
3005
|
+
return {
|
|
3006
|
+
push(event) {
|
|
3007
|
+
const waiter = waiters.shift();
|
|
3008
|
+
if (waiter) waiter(event);
|
|
3009
|
+
else queue.push(event);
|
|
3010
|
+
},
|
|
3011
|
+
take() {
|
|
3012
|
+
const next = queue.shift();
|
|
3013
|
+
if (next !== void 0) return Promise.resolve(next);
|
|
3014
|
+
return new Promise((resolve) => waiters.push(resolve));
|
|
3015
|
+
},
|
|
3016
|
+
size: () => queue.length
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
/**
|
|
3020
|
+
* Runs an agent machine as a durable execution: journal in, journal out.
|
|
3021
|
+
*
|
|
3022
|
+
* A fresh call starts from `input` and appends a reserved init entry; a
|
|
3023
|
+
* resume call folds `entries` through pure transitions first — invokes whose
|
|
3024
|
+
* completions are journaled are suppressed (their recorded results replay
|
|
3025
|
+
* instead of re-executing), while work that was in flight at the crash
|
|
3026
|
+
* re-executes live. After the journal, an optional `options.event` is
|
|
3027
|
+
* delivered. The call settles:
|
|
3028
|
+
*
|
|
3029
|
+
* - `done` when the machine reaches a final state, with `output`;
|
|
3030
|
+
* - `idle` when the frontier needs an external event the host has not
|
|
3031
|
+
* supplied (no live work pending, or `isIdle` says the pending work is a
|
|
3032
|
+
* human wait). Persist `entries` and call again with them later.
|
|
3033
|
+
*
|
|
3034
|
+
* ```ts
|
|
3035
|
+
* const first = await runDurableAgent(machine, { input, executors });
|
|
3036
|
+
* // ... persist first.entries; later, in a new process:
|
|
3037
|
+
* const next = await runDurableAgent(machine, {
|
|
3038
|
+
* entries: first.entries,
|
|
3039
|
+
* event: { type: "APPROVE" },
|
|
3040
|
+
* executors,
|
|
3041
|
+
* });
|
|
3042
|
+
* ```
|
|
3043
|
+
*
|
|
3044
|
+
* @experimental Built on xstate's experimental `xstate/durable` entrypoint.
|
|
3045
|
+
*/
|
|
3046
|
+
async function runDurableAgent(machine, options = {}) {
|
|
3047
|
+
const bound = options.executors ? provideExecutors(machine, options.executors, {
|
|
3048
|
+
actors: options.actors,
|
|
3049
|
+
onChunk: options.onChunk,
|
|
3050
|
+
onTrace: options.onTrace
|
|
3051
|
+
}) : options.actors ? machine.provide({ actors: options.actors }) : machine;
|
|
3052
|
+
const machineId = machine.config.id ?? machine.id ?? "(machine)";
|
|
3053
|
+
const machineVersion = options.machineVersion ?? resolveMachineVersion(machine);
|
|
3054
|
+
const priorEntries = options.entries ?? [];
|
|
3055
|
+
if (priorEntries.length > 0) validateReplayEntries(priorEntries, {
|
|
3056
|
+
machineId,
|
|
3057
|
+
machineVersion
|
|
3058
|
+
}, "Durable journal entries");
|
|
3059
|
+
const hasInit = priorEntries[0]?.event.type === AGENT_INIT_EVENT_TYPE;
|
|
3060
|
+
const input = hasInit ? priorEntries[0].event.input : options.input;
|
|
3061
|
+
const journal = priorEntries.slice(hasInit ? 1 : 0).map((entry) => entry.event);
|
|
3062
|
+
const journaledCompletions = /* @__PURE__ */ new Map();
|
|
3063
|
+
for (const event of journal) {
|
|
3064
|
+
const actorId = completionActorId(event);
|
|
3065
|
+
if (actorId !== void 0) journaledCompletions.set(actorId, (journaledCompletions.get(actorId) ?? 0) + 1);
|
|
3066
|
+
}
|
|
3067
|
+
const storedExecutionId = hasInit ? priorEntries[0].metadata?.executionId : void 0;
|
|
3068
|
+
const executionId = typeof storedExecutionId === "string" ? storedExecutionId : hasInit ? priorEntries[0].id : crypto.randomUUID();
|
|
3069
|
+
const mailbox = createMailbox();
|
|
3070
|
+
const rootAddress = machineId;
|
|
3071
|
+
const suppressedChildren = /* @__PURE__ */ new WeakSet();
|
|
3072
|
+
const startsSeen = /* @__PURE__ */ new Map();
|
|
3073
|
+
const liveInFlight = /* @__PURE__ */ new Set();
|
|
3074
|
+
const findChildRef = (effect) => {
|
|
3075
|
+
const raw = effect;
|
|
3076
|
+
const candidates = [raw.actor, ...Array.isArray(raw.args) ? raw.args : []];
|
|
3077
|
+
for (const candidate of candidates) {
|
|
3078
|
+
const ref = candidate;
|
|
3079
|
+
if (ref && typeof ref.sessionId === "string" && typeof ref.id === "string") return ref;
|
|
3080
|
+
}
|
|
3081
|
+
};
|
|
3082
|
+
let replaying = journal.length > 0;
|
|
3083
|
+
let liveEventConsumed = false;
|
|
3084
|
+
let latestSnapshot;
|
|
3085
|
+
const execution = createDurable(bound, {
|
|
3086
|
+
executionId,
|
|
3087
|
+
sendEvent(source, target, event) {
|
|
3088
|
+
if (target.address === rootAddress) {
|
|
3089
|
+
mailbox.push(event);
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
3092
|
+
deliverEvent(source, target, event);
|
|
3093
|
+
},
|
|
3094
|
+
runtime(_metadata, effect) {
|
|
3095
|
+
const type = effect.type;
|
|
3096
|
+
if (type === "@xstate.spawn" || type === "@xstate.start") {
|
|
3097
|
+
const child = findChildRef(effect);
|
|
3098
|
+
if (!child) return {};
|
|
3099
|
+
if (type === "@xstate.spawn") {
|
|
3100
|
+
const seen = (startsSeen.get(child.id) ?? 0) + 1;
|
|
3101
|
+
startsSeen.set(child.id, seen);
|
|
3102
|
+
if (seen <= (journaledCompletions.get(child.id) ?? 0)) suppressedChildren.add(child);
|
|
3103
|
+
else liveInFlight.add(child.id);
|
|
3104
|
+
}
|
|
3105
|
+
if (suppressedChildren.has(child)) return {
|
|
3106
|
+
spawnActor() {},
|
|
3107
|
+
startActor() {}
|
|
3108
|
+
};
|
|
3109
|
+
}
|
|
3110
|
+
return {};
|
|
3111
|
+
},
|
|
3112
|
+
executeAction(action) {
|
|
3113
|
+
if (replaying) return;
|
|
3114
|
+
action.exec?.();
|
|
3115
|
+
},
|
|
3116
|
+
async waitForEvent() {
|
|
3117
|
+
if (mailbox.size() > 0) return await mailbox.take();
|
|
3118
|
+
if (liveInFlight.size > 0 && !(options.isIdle?.(latestSnapshot) ?? false)) return await mailbox.take();
|
|
3119
|
+
if (!liveEventConsumed && options.event !== void 0) {
|
|
3120
|
+
liveEventConsumed = true;
|
|
3121
|
+
return options.event;
|
|
3122
|
+
}
|
|
3123
|
+
throw IDLE;
|
|
3124
|
+
}
|
|
3125
|
+
});
|
|
3126
|
+
const entries = [...priorEntries];
|
|
3127
|
+
const entryOptions = {
|
|
3128
|
+
machineVersion,
|
|
3129
|
+
verification: options.verification ?? false
|
|
3130
|
+
};
|
|
3131
|
+
const appendEntry = (event) => {
|
|
3132
|
+
const entry = createReplayEntry(machine, entries, event, entryOptions);
|
|
3133
|
+
entries.push(entry);
|
|
3134
|
+
options.onEntry?.(entry);
|
|
3135
|
+
};
|
|
3136
|
+
if (!hasInit) {
|
|
3137
|
+
const entry = initEntry(machine, input, {
|
|
3138
|
+
...entryOptions,
|
|
3139
|
+
metadata: { executionId }
|
|
3140
|
+
});
|
|
3141
|
+
entries.push(entry);
|
|
3142
|
+
options.onEntry?.(entry);
|
|
3143
|
+
}
|
|
3144
|
+
let journalIndex = 0;
|
|
3145
|
+
let [snapshot, effects] = execution.initialTransition(input);
|
|
3146
|
+
latestSnapshot = snapshot;
|
|
3147
|
+
for (;;) {
|
|
3148
|
+
await execution.executeEffects(effects);
|
|
3149
|
+
const machineSnapshot = snapshot;
|
|
3150
|
+
if (machineSnapshot.status === "done") return {
|
|
3151
|
+
status: "done",
|
|
3152
|
+
output: machineSnapshot.output,
|
|
3153
|
+
snapshot,
|
|
3154
|
+
entries
|
|
3155
|
+
};
|
|
3156
|
+
if (machineSnapshot.status === "error") throw machineSnapshot.error;
|
|
3157
|
+
let event;
|
|
3158
|
+
let fromJournal = false;
|
|
3159
|
+
if (journalIndex < journal.length) {
|
|
3160
|
+
event = journal[journalIndex];
|
|
3161
|
+
journalIndex++;
|
|
3162
|
+
fromJournal = true;
|
|
3163
|
+
replaying = journalIndex < journal.length;
|
|
3164
|
+
} else try {
|
|
3165
|
+
event = await execution.waitForEvent();
|
|
3166
|
+
} catch (error) {
|
|
3167
|
+
if (error === IDLE) return {
|
|
3168
|
+
status: "idle",
|
|
3169
|
+
snapshot,
|
|
3170
|
+
entries
|
|
3171
|
+
};
|
|
3172
|
+
throw error;
|
|
3173
|
+
}
|
|
3174
|
+
const completedId = completionActorId(event);
|
|
3175
|
+
if (completedId !== void 0) liveInFlight.delete(completedId);
|
|
3176
|
+
if (!fromJournal) appendEntry(event);
|
|
3177
|
+
[snapshot, effects] = execution.transition(snapshot, event);
|
|
3178
|
+
latestSnapshot = snapshot;
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
//#endregion
|
|
3182
|
+
export { AGENT_EVENT_SCHEMA_VERSION, AGENT_INIT_EVENT_TYPE, AGENT_TRACE_SCHEMA_VERSION, AGENT_USAGE_EVENT_TYPE, AgentDecisionExhaustedError, AgentError, AgentEventLogConflictError, AgentIdleError, AgentIllegalResumeEventError, AgentLintError, AgentMaxModelCallsExceededError, AgentReplayDivergenceError, AgentReplayMachineMismatchError, AgentSnapshotVersionMismatchError, NonSerializableAgentEventError, appendMessages, assertAgentLogEntry, assertAgentMachine, assertEventLogStoreConformance, assertJsonSerializable, assistantMessage, bindRequestExecutor, buildEnvelopeSchema, canReach, createAgentActor, createAgentRun, createAgentSchemas, createInMemoryEventLogStore, createReplayEntry, createScriptedExecutors, createTextLogic, diffEventLogs, executeAgentRequest, explorePaths, generateResult, getAcceptedEvents, getAgentEffects, getAgentMessages, getAgentOutputMode, getAgentSchemas, getCallUsage, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, getSnapshotNodes, getSnapshotRequests, getStateMeta, initEntry, inspectTransitions, isStandardSchema, lintAgentMachine, matchesTrajectory, messagesSchema, parseAgentEvent, parseModelRef, parseOutput, parseStructuredEnvelope, provideExecutors, renderDecisionAttempts, replay, resolveDecision, runAgent, runDurableAgent, runSeam, serializeTraceEvent, setupAgent, simulateAgent, systemMessage, toolMessage, traceTransitions, userMessage };
|
package/dist/machines.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_setup_agent = require("./setup-agent-
|
|
2
|
+
const require_setup_agent = require("./setup-agent-91FSuZbB.cjs");
|
|
3
3
|
//#region src/machines/internal.ts
|
|
4
4
|
/** The builtin inline text request every preset lowers a request entry to. */
|
|
5
5
|
const GENERATE_TEXT_SRC = "agent.generateText";
|
package/dist/machines.d.cts
CHANGED
package/dist/machines.d.mts
CHANGED
package/dist/machines.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as setupAgent } from "./setup-agent-
|
|
1
|
+
import { r as setupAgent } from "./setup-agent-DqqdcdNh.mjs";
|
|
2
2
|
//#region src/machines/internal.ts
|
|
3
3
|
/** The builtin inline text request every preset lowers a request entry to. */
|
|
4
4
|
const GENERATE_TEXT_SRC = "agent.generateText";
|
package/dist/otel.d.cts
CHANGED
package/dist/otel.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { T as WithAgentInputSchema, c as AgentTools, d as ChosenEvent, h as InferInput, n as AgentMessage, v as StandardSchemaV1 } from "./types-
|
|
1
|
+
import { T as WithAgentInputSchema, c as AgentTools, d as ChosenEvent, h as InferInput, n as AgentMessage, v as StandardSchemaV1 } from "./types-CvWRGFxP.mjs";
|
|
2
2
|
import { t as AgentError } from "./errors-C9rxnWbX.mjs";
|
|
3
|
-
import { H as AgentRequestSource, N as AgentDecisionRequest, V as AgentRequestOptions, d as AgentTextRequest, f as AgentUsage, l as AgentRequestExecutors, p as AgentUserInput, t as AgentCallUsage, u as AgentRequestMode, z as AgentEventDescriptor } from "./text-logic-
|
|
3
|
+
import { H as AgentRequestSource, N as AgentDecisionRequest, V as AgentRequestOptions, d as AgentTextRequest, f as AgentUsage, l as AgentRequestExecutors, p as AgentUserInput, t as AgentCallUsage, u as AgentRequestMode, z as AgentEventDescriptor } from "./text-logic-BR5twXCv.mjs";
|
|
4
4
|
import { i as AgentLogEntry, o as JsonValue } from "./event-log-store-hrA1vqtN.mjs";
|
|
5
5
|
import { AnyActorLogic, AnyActorRef, AnyMachineSnapshot, AnyStateMachine, AsyncActorLogic, EmittedFrom, EventFromLogic, EventObject, ExecutableActionObject, InputFrom, InspectionEvent, OutputFrom, Snapshot, SnapshotFrom, createActor } from "xstate";
|
|
6
6
|
|
|
@@ -934,7 +934,7 @@ declare class AgentMaxModelCallsExceededError extends AgentError {
|
|
|
934
934
|
* declared input schema — and machines reached through `.provide(...)`, which
|
|
935
935
|
* drops the brand — fall back to xstate's `InputFrom`.
|
|
936
936
|
*/
|
|
937
|
-
type AgentInputFrom<TMachine extends AnyStateMachine> = InputFrom<TMachine
|
|
937
|
+
type AgentInputFrom<TMachine extends AnyStateMachine> = [NonNullable<InputFrom<TMachine>>] extends [WithAgentInputSchema<infer TInputSchema>] ? [TInputSchema] extends [StandardSchemaV1] ? InferInput<TInputSchema> : InputFrom<TMachine> : InputFrom<TMachine>;
|
|
938
938
|
/**
|
|
939
939
|
* Runs an agent machine to completion or idle: a `createActor` host that
|
|
940
940
|
* binds `options`' host executors onto the machine's `agent.*`/`TextLogic`/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { T as WithAgentInputSchema, c as AgentTools, d as ChosenEvent, h as InferInput, n as AgentMessage, v as StandardSchemaV1 } from "./types-
|
|
1
|
+
import { T as WithAgentInputSchema, c as AgentTools, d as ChosenEvent, h as InferInput, n as AgentMessage, v as StandardSchemaV1 } from "./types-DSdj2tGs.cjs";
|
|
2
2
|
import { t as AgentError } from "./errors-BQRk9eiZ.cjs";
|
|
3
|
-
import { H as AgentRequestSource, N as AgentDecisionRequest, V as AgentRequestOptions, d as AgentTextRequest, f as AgentUsage, l as AgentRequestExecutors, p as AgentUserInput, t as AgentCallUsage, u as AgentRequestMode, z as AgentEventDescriptor } from "./text-logic-
|
|
3
|
+
import { H as AgentRequestSource, N as AgentDecisionRequest, V as AgentRequestOptions, d as AgentTextRequest, f as AgentUsage, l as AgentRequestExecutors, p as AgentUserInput, t as AgentCallUsage, u as AgentRequestMode, z as AgentEventDescriptor } from "./text-logic-Mmbgb2jy.cjs";
|
|
4
4
|
import { i as AgentLogEntry, o as JsonValue } from "./event-log-store-Bz7HDBkE.cjs";
|
|
5
5
|
import { AnyActorLogic, AnyActorRef, AnyMachineSnapshot, AnyStateMachine, AsyncActorLogic, EmittedFrom, EventFromLogic, EventObject, ExecutableActionObject, InputFrom, InspectionEvent, OutputFrom, Snapshot, SnapshotFrom, createActor } from "xstate";
|
|
6
6
|
|
|
@@ -934,7 +934,7 @@ declare class AgentMaxModelCallsExceededError extends AgentError {
|
|
|
934
934
|
* declared input schema — and machines reached through `.provide(...)`, which
|
|
935
935
|
* drops the brand — fall back to xstate's `InputFrom`.
|
|
936
936
|
*/
|
|
937
|
-
type AgentInputFrom<TMachine extends AnyStateMachine> = InputFrom<TMachine
|
|
937
|
+
type AgentInputFrom<TMachine extends AnyStateMachine> = [NonNullable<InputFrom<TMachine>>] extends [WithAgentInputSchema<infer TInputSchema>] ? [TInputSchema] extends [StandardSchemaV1] ? InferInput<TInputSchema> : InputFrom<TMachine> : InputFrom<TMachine>;
|
|
938
938
|
/**
|
|
939
939
|
* Runs an agent machine to completion or idle: a `createActor` host that
|
|
940
940
|
* binds `options`' host executors onto the machine's `agent.*`/`TextLogic`/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_errors = require("./errors-DUBBzRLP.cjs");
|
|
2
|
-
const require_decision = require("./decision-
|
|
2
|
+
const require_decision = require("./decision-BnTCsuJv.cjs");
|
|
3
3
|
const require_event_log_store = require("./event-log-store-a_TKy1gk.cjs");
|
|
4
4
|
require("./validate.cjs");
|
|
5
5
|
let xstate = require("xstate");
|
|
@@ -919,7 +919,7 @@ function createRequestsFromWorkflowConfig(config, compileSchema) {
|
|
|
919
919
|
messages: resolver(request.messages),
|
|
920
920
|
tools: request.tools,
|
|
921
921
|
toolChoice: request.toolChoice,
|
|
922
|
-
|
|
922
|
+
includeReasoning: request.includeReasoning,
|
|
923
923
|
temperature: resolver(request.temperature),
|
|
924
924
|
maxOutputTokens: resolver(request.maxOutputTokens),
|
|
925
925
|
topP: resolver(request.topP),
|