@statelyai/agent 2.0.0-alpha.28 → 2.0.0-alpha.30
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 +150 -27
- package/dist/ai-sdk.d.cts +59 -36
- package/dist/ai-sdk.d.mts +59 -36
- package/dist/ai-sdk.mjs +148 -24
- package/dist/{decision-RQ0iv0oZ.mjs → decision-BHkvuImD.mjs} +74 -8
- package/dist/{decision-CmM8-HHz.cjs → decision-CIb7ddIv.cjs} +113 -30
- package/dist/{text-logic-f5F5sCdc.cjs → event-log-BZRfBRd7.cjs} +814 -7
- package/dist/{text-logic-CIqHAmBi.mjs → event-log-DJCMHek_.mjs} +680 -4
- package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CIIJohjj.d.mts} +90 -5
- package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-CLAC9q2Y.d.cts} +90 -5
- package/dist/event-log-store-DZR4IIWe.cjs +95 -0
- package/dist/event-log-store-DsP6GCRh.mjs +84 -0
- package/dist/index.cjs +576 -58
- package/dist/index.d.cts +104 -510
- package/dist/index.d.mts +104 -510
- package/dist/index.mjs +547 -32
- package/dist/log.cjs +77 -20
- package/dist/log.d.cts +5 -5
- package/dist/log.d.mts +5 -5
- package/dist/log.mjs +58 -3
- package/dist/machines.cjs +1 -1
- package/dist/machines.d.cts +15 -15
- package/dist/machines.d.mts +15 -15
- package/dist/machines.mjs +1 -1
- package/dist/model-source-BCKg4qJ1.cjs +44 -0
- package/dist/model-source-BaHwJ1wK.mjs +39 -0
- package/dist/openai.cjs +11 -11
- package/dist/openai.d.cts +28 -26
- package/dist/openai.d.mts +28 -26
- package/dist/openai.mjs +3 -3
- package/dist/otel.cjs +5 -4
- package/dist/otel.d.cts +5 -7
- package/dist/otel.d.mts +5 -7
- package/dist/otel.mjs +5 -3
- package/dist/{run-agent-xNTxpt82.mjs → run-agent-BpHqkaZw.mjs} +181 -44
- package/dist/{run-agent-COXXc0RW.d.mts → run-agent-CAVt5R-r.d.mts} +95 -30
- package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CTlMc3Jb.d.cts} +95 -30
- package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-ctLCvNgL.cjs} +230 -88
- package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-7rV_cujV.mjs} +15 -9
- package/dist/setup-agent-CGELn1qZ.d.mts +577 -0
- package/dist/setup-agent-CLPCkXsu.d.cts +577 -0
- package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DoUk7EdC.cjs} +31 -20
- package/dist/tanstack-ai.cjs +627 -0
- package/dist/tanstack-ai.d.cts +204 -0
- package/dist/tanstack-ai.d.mts +204 -0
- package/dist/tanstack-ai.mjs +618 -0
- package/dist/testing.cjs +204 -985
- package/dist/testing.d.cts +132 -347
- package/dist/testing.d.mts +132 -347
- package/dist/testing.mjs +191 -973
- package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-BRYOPWvY.d.mts} +52 -12
- package/dist/{text-logic-D-czVPYp.d.mts → text-logic-DmciuVUQ.d.cts} +52 -12
- package/dist/{types-BgNbNKnJ.d.mts → types-Dp5_Ru4R.d.cts} +2 -2
- package/dist/{types-hR5f5YXa.d.cts → types-Dp5_Ru4R.d.mts} +2 -2
- package/dist/usage-BkM3z02Y.d.mts +17 -0
- package/dist/usage-Cm_ovz9V.d.cts +17 -0
- package/dist/{utils-_q7F-AZu.d.mts → utils-BnXdWih3.d.mts} +1 -2
- package/dist/{utils-o4YRdHJX.d.cts → utils-ebVpCYoF.d.cts} +1 -2
- package/dist/validate.cjs +239 -8
- package/dist/validate.d.cts +110 -3
- package/dist/validate.d.mts +110 -3
- package/dist/validate.mjs +238 -7
- package/dist/verify-COFWyprJ.mjs +1181 -0
- package/dist/verify-CVlsTZ-V.cjs +1222 -0
- package/dist/verify-Cn86rT8l.d.mts +414 -0
- package/dist/verify-Dzwd4Mvk.d.cts +414 -0
- package/package.json +44 -31
- package/readme.md +40 -28
- package/skills/add-evals/SKILL.md +129 -0
- package/skills/add-traces/SKILL.md +135 -0
- package/skills/generate-machine/SKILL.md +143 -69
- package/skills/graph-engineering/SKILL.md +33 -0
- package/skills/improve-machine/SKILL.md +241 -0
- package/skills/improve-machine/reference.md +101 -0
- package/skills/loop-to-machine/SKILL.md +183 -0
- package/skills/loop-to-machine/policy.md +158 -0
- package/dist/event-log-store-2pAFiWuc.mjs +0 -576
- package/dist/event-log-store-C-COhIXP.cjs +0 -702
- package/dist/usage-BdEjFuIi.mjs +0 -5
- package/dist/usage-efOQPdf_.cjs +0 -10
package/dist/validate.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { D as STREAM_TEXT_ACTOR, E as GENERATE_TEXT_ACTOR, T as DECIDE_ACTOR } from "./event-log-DJCMHek_.mjs";
|
|
2
|
+
import { i as setupAgentFromConfig } from "./setup-agent-7rV_cujV.mjs";
|
|
3
|
+
import { a as explorePaths, o as lintAgentMachine } from "./verify-COFWyprJ.mjs";
|
|
1
4
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
2
5
|
//#region schemas/agent-workflow.json
|
|
3
6
|
var agent_workflow_default = {
|
|
@@ -359,13 +362,10 @@ var agent_workflow_default = {
|
|
|
359
362
|
}
|
|
360
363
|
};
|
|
361
364
|
//#endregion
|
|
362
|
-
//#region src/validate/
|
|
365
|
+
//#region src/validate/validate-config.ts
|
|
363
366
|
/**
|
|
364
|
-
* Schema validation for agent workflow configs.
|
|
365
|
-
*
|
|
366
|
-
* Lives behind the `@statelyai/agent/validate` subpath so the core package
|
|
367
|
-
* stays dependency-free. This module imports `ajv`, which is declared as an
|
|
368
|
-
* optional peer dependency.
|
|
367
|
+
* Schema validation for agent workflow configs. Imports `ajv`, an optional
|
|
368
|
+
* peer dependency of the package.
|
|
369
369
|
*/
|
|
370
370
|
let compiledWorkflowValidator;
|
|
371
371
|
function getWorkflowValidator() {
|
|
@@ -400,4 +400,235 @@ function validateAgentConfig(config) {
|
|
|
400
400
|
};
|
|
401
401
|
}
|
|
402
402
|
//#endregion
|
|
403
|
-
|
|
403
|
+
//#region src/validate/verify-config.ts
|
|
404
|
+
const BUILTIN_SRCS = [
|
|
405
|
+
DECIDE_ACTOR,
|
|
406
|
+
GENERATE_TEXT_ACTOR,
|
|
407
|
+
STREAM_TEXT_ACTOR
|
|
408
|
+
];
|
|
409
|
+
const MAX_DETAIL_LINES = 10;
|
|
410
|
+
/**
|
|
411
|
+
* Runs every pre-run gate on a model-authored workflow config and stops at
|
|
412
|
+
* the first failure. Never throws on a bad config: each failure becomes a gate
|
|
413
|
+
* record whose `detail` is a repair prompt.
|
|
414
|
+
*
|
|
415
|
+
* - `validate`: the config's shape, against the shipped JSON Schema.
|
|
416
|
+
* - `vocabulary`: names the config references, against `allow`.
|
|
417
|
+
* - `lower`: `setupAgent.fromConfig` (unresolved guards/actions, bad targets).
|
|
418
|
+
* - `lint`: `lintAgentMachine` (errors fail; warnings fail with `lint.warnings`).
|
|
419
|
+
* - `explore`: `explorePaths` from each `explore.inputs` entry; every terminal
|
|
420
|
+
* must be one of `explore.terminals`, no cap (`maxPaths`, `maxNodes`) may cut
|
|
421
|
+
* it short, every event's payloads must be tried (`unexpandedEvents`), and
|
|
422
|
+
* every reached text request or invoke needs an `explore.outputs` entry.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```ts
|
|
426
|
+
* const result = await verifyAgentConfig(candidate, { compileSchema, allow: { events: ["APPROVE", "DENY"] } });
|
|
427
|
+
* const failed = result.gates.find((gate) => !gate.ok);
|
|
428
|
+
* if (failed) feedback.push(`gate '${failed.name}' failed: ${failed.detail}`);
|
|
429
|
+
* ```
|
|
430
|
+
*/
|
|
431
|
+
async function verifyAgentConfig(config, options) {
|
|
432
|
+
const gates = [];
|
|
433
|
+
const record = (name, problems, notes = []) => {
|
|
434
|
+
const lines = problems.length ? problems : notes;
|
|
435
|
+
gates.push({
|
|
436
|
+
name,
|
|
437
|
+
ok: problems.length === 0,
|
|
438
|
+
...lines.length ? { detail: formatDetail(lines) } : {}
|
|
439
|
+
});
|
|
440
|
+
return problems.length === 0;
|
|
441
|
+
};
|
|
442
|
+
const fail = (extra = {}) => ({
|
|
443
|
+
ok: false,
|
|
444
|
+
gates,
|
|
445
|
+
...extra
|
|
446
|
+
});
|
|
447
|
+
if (!record("validate", validateAgentConfig(config).errors.map((error) => `${error.path} ${error.message}`))) return fail();
|
|
448
|
+
const typed = config;
|
|
449
|
+
if (options.allow && !record("vocabulary", checkVocabulary(typed, options.allow))) return fail({ config: typed });
|
|
450
|
+
let machine;
|
|
451
|
+
try {
|
|
452
|
+
machine = setupAgentFromConfig(typed, {
|
|
453
|
+
compileSchema: options.compileSchema,
|
|
454
|
+
...options.guards ? { guards: options.guards } : {},
|
|
455
|
+
...options.actions ? { actions: options.actions } : {}
|
|
456
|
+
}).machine;
|
|
457
|
+
if (options.actors) machine = machine.provide({ actors: options.actors });
|
|
458
|
+
} catch (error) {
|
|
459
|
+
record("lower", [errorMessage(error)]);
|
|
460
|
+
return fail({ config: typed });
|
|
461
|
+
}
|
|
462
|
+
record("lower", []);
|
|
463
|
+
const diagnostics = lintAgentMachine(machine).map((d) => ({
|
|
464
|
+
failing: d.severity === "error" || options.lint?.warnings === true,
|
|
465
|
+
line: `${d.severity} ${d.code} at '${d.path}': ${d.message}`
|
|
466
|
+
}));
|
|
467
|
+
if (!record("lint", diagnostics.filter((d) => d.failing).map((d) => d.line), diagnostics.map((d) => d.line))) return fail({
|
|
468
|
+
config: typed,
|
|
469
|
+
machine
|
|
470
|
+
});
|
|
471
|
+
if (options.explore && !record("explore", await checkExplore(machine, options.explore))) return fail({
|
|
472
|
+
config: typed,
|
|
473
|
+
machine
|
|
474
|
+
});
|
|
475
|
+
return {
|
|
476
|
+
ok: true,
|
|
477
|
+
gates,
|
|
478
|
+
config: typed,
|
|
479
|
+
machine
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
/** A config's `T | T[]` field as a list of objects. */
|
|
483
|
+
const nodes = (value) => (value === void 0 ? [] : Array.isArray(value) ? value : [value]).filter((node) => !!node && typeof node === "object");
|
|
484
|
+
const hasTemplate = (value) => typeof value === "string" && value.includes("{{");
|
|
485
|
+
const list = (names) => names.length ? names.join(", ") : "(none)";
|
|
486
|
+
/** Every state in the config, depth-first, with its dotted path. */
|
|
487
|
+
function walkStates(states, prefix = "") {
|
|
488
|
+
if (!states || typeof states !== "object") return [];
|
|
489
|
+
return Object.entries(states).flatMap(([key, state]) => {
|
|
490
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
491
|
+
return [{
|
|
492
|
+
path,
|
|
493
|
+
state
|
|
494
|
+
}, ...walkStates(state?.states, path)];
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
/** Every transition on a state: `on`, `after`, `always`, `choice`, `onDone`, and invoke done/error. */
|
|
498
|
+
function transitionsOf(state) {
|
|
499
|
+
return [
|
|
500
|
+
...Object.values(state.on ?? {}).flatMap(nodes),
|
|
501
|
+
...Object.values(state.after ?? {}).flatMap(nodes),
|
|
502
|
+
...nodes(state.always),
|
|
503
|
+
...nodes(state.choice),
|
|
504
|
+
...nodes(state.onDone),
|
|
505
|
+
...nodes(state.invoke).flatMap((invoke) => [...nodes(invoke.onDone), ...nodes(invoke.onError)])
|
|
506
|
+
];
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Paths of raw xstate expression slots (`{ "@expr": ... }`, `{ "@code": ... }`).
|
|
510
|
+
* xstate evaluates such an object whole, ignoring its literal siblings, so a
|
|
511
|
+
* slot would hide the model ref or `allowedEvents` the checks below read.
|
|
512
|
+
*/
|
|
513
|
+
function rawExpressionPaths(value, path) {
|
|
514
|
+
if (Array.isArray(value)) return value.flatMap((item, index) => rawExpressionPaths(item, `${path}[${index}]`));
|
|
515
|
+
if (!value || typeof value !== "object") return [];
|
|
516
|
+
const node = value;
|
|
517
|
+
if (typeof node["@expr"] === "string" || typeof node["@code"] === "string") return [path];
|
|
518
|
+
return Object.entries(node).flatMap(([key, item]) => rawExpressionPaths(item, path ? `${path}.${key}` : key));
|
|
519
|
+
}
|
|
520
|
+
function checkVocabulary(config, allow) {
|
|
521
|
+
const problems = rawExpressionPaths(config, "").map((path) => `${path || "(root)"} uses a raw '@expr' slot, which the vocabulary cannot check; write {{ }} templates instead`);
|
|
522
|
+
const checkModel = (where, model) => {
|
|
523
|
+
if (!allow.models) return;
|
|
524
|
+
if (typeof model !== "string" || hasTemplate(model)) problems.push(`${where} needs a literal string model ref; allowed: ${list(allow.models)}`);
|
|
525
|
+
else if (!allow.models.includes(model)) problems.push(`${where} uses model '${model}'; allowed: ${list(allow.models)}`);
|
|
526
|
+
};
|
|
527
|
+
for (const [kind, declared, allowed] of [[
|
|
528
|
+
"request",
|
|
529
|
+
config.requests,
|
|
530
|
+
allow.requests
|
|
531
|
+
], [
|
|
532
|
+
"actor",
|
|
533
|
+
config.actors,
|
|
534
|
+
allow.actors
|
|
535
|
+
]]) {
|
|
536
|
+
if (!allowed) continue;
|
|
537
|
+
for (const key of Object.keys(declared ?? {})) if (!allowed.includes(key)) problems.push(`${kind} '${key}' is not in the vocabulary; allowed: ${list(allowed)}`);
|
|
538
|
+
}
|
|
539
|
+
for (const [key, request] of Object.entries(config.requests ?? {})) checkModel(`request '${key}'`, request.model);
|
|
540
|
+
const srcs = allow.actors ? [
|
|
541
|
+
...BUILTIN_SRCS,
|
|
542
|
+
...allow.actors,
|
|
543
|
+
...allow.requests ?? Object.keys(config.requests ?? {})
|
|
544
|
+
] : void 0;
|
|
545
|
+
for (const { path, state } of walkStates(config.states)) {
|
|
546
|
+
for (const invoke of nodes(state.invoke)) {
|
|
547
|
+
const src = String(invoke.src);
|
|
548
|
+
if (srcs && !srcs.includes(src)) {
|
|
549
|
+
problems.push(`state '${path}' invokes unknown src '${src}'; allowed: ${list(srcs)}`);
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (!BUILTIN_SRCS.includes(src)) continue;
|
|
553
|
+
const input = invoke.input;
|
|
554
|
+
if ((allow.models || src === "agent.decide" && allow.events) && !isPlainObject(input)) {
|
|
555
|
+
problems.push(`state '${path}': '${src}' input must be a literal object`);
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
checkModel(`state '${path}': '${src}'`, input?.model);
|
|
559
|
+
if (src !== "agent.decide" || !allow.events) continue;
|
|
560
|
+
const allowedEvents = input.allowedEvents;
|
|
561
|
+
if (!Array.isArray(allowedEvents) || allowedEvents.length === 0) {
|
|
562
|
+
problems.push(`state '${path}': agent.decide needs a literal, non-empty allowedEvents list; allowed: ${list(allow.events)}`);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
for (const type of allowedEvents) if (typeof type !== "string" || !allow.events.includes(type)) problems.push(`state '${path}': agent.decide allowedEvents entry '${String(type)}' is not allowed; allowed: ${list(allow.events)}`);
|
|
566
|
+
}
|
|
567
|
+
const actionsAt = [["entry", state.entry], ["exit", state.exit]];
|
|
568
|
+
for (const transition of transitionsOf(state)) {
|
|
569
|
+
const guard = transition.guard;
|
|
570
|
+
if (allow.guards && typeof guard === "string" && !hasTemplate(guard) && !allow.guards.includes(guard)) problems.push(`state '${path}' uses unknown named guard '${guard}'; allowed: ${list(allow.guards)}`);
|
|
571
|
+
actionsAt.push(["a transition", transition.actions]);
|
|
572
|
+
}
|
|
573
|
+
if (!allow.actions) continue;
|
|
574
|
+
for (const [where, actions] of actionsAt) for (const action of nodes(actions)) if (typeof action.type === "string" && !allow.actions.includes(action.type)) problems.push(`state '${path}' ${where} uses unknown named action '${action.type}'; allowed: ${list(allow.actions)}`);
|
|
575
|
+
}
|
|
576
|
+
return problems;
|
|
577
|
+
}
|
|
578
|
+
const isPlainObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
579
|
+
async function checkExplore(machine, options) {
|
|
580
|
+
const { inputs, terminals = ["done"], ...rest } = options;
|
|
581
|
+
if (inputs.length === 0) return ["explore.inputs is empty, so no path was explored; give at least one input"];
|
|
582
|
+
const allowed = terminals;
|
|
583
|
+
const maxDepth = options.maxDepth ?? 8;
|
|
584
|
+
const problems = /* @__PURE__ */ new Set();
|
|
585
|
+
for (const [index, input] of inputs.entries()) {
|
|
586
|
+
const at = inputs.length > 1 ? `input ${index}: ` : "";
|
|
587
|
+
let report;
|
|
588
|
+
try {
|
|
589
|
+
report = await explorePaths(machine, {
|
|
590
|
+
...rest,
|
|
591
|
+
input
|
|
592
|
+
});
|
|
593
|
+
} catch (error) {
|
|
594
|
+
problems.add(`${at}exploration threw: ${errorMessage(error)}`);
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
for (const terminal of report.terminals) {
|
|
598
|
+
if (allowed.includes(terminal.status)) continue;
|
|
599
|
+
const trail = `path [${terminal.path.map((event) => event.type).join(", ")}]`;
|
|
600
|
+
const state = JSON.stringify(terminal.state);
|
|
601
|
+
switch (terminal.status) {
|
|
602
|
+
case "max-depth":
|
|
603
|
+
problems.add(`${at}${trail} hit maxDepth ${maxDepth} in state ${state} without ending; the workflow may loop`);
|
|
604
|
+
break;
|
|
605
|
+
case "needs-output":
|
|
606
|
+
problems.add(`${at}${trail} reached '${terminal.missingSrc}' in state ${state}, which has no output to explore with; add an explore.outputs entry for it`);
|
|
607
|
+
break;
|
|
608
|
+
case "error":
|
|
609
|
+
problems.add(`${at}${trail} errored in state ${state} with no onError: ${errorMessage(terminal.error)}`);
|
|
610
|
+
break;
|
|
611
|
+
default: problems.add(`${at}${trail} ends '${terminal.status}' in state ${state}; every path must end ${allowed.map((t) => `'${t}'`).join(" or ")}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (report.hitPathCap) problems.add(`${at}a cap (maxPaths ${options.maxPaths ?? 200} or maxNodes ${options.maxNodes ?? 1e5}) cut exploration short; the workflow branches too much to verify`);
|
|
615
|
+
if (report.unexpandedEvents.length > 0) problems.add(`${at}explore did not try every payload of: ${report.unexpandedEvents.join(", ")}; add explore.payloads entries for them`);
|
|
616
|
+
}
|
|
617
|
+
return [...problems];
|
|
618
|
+
}
|
|
619
|
+
function formatDetail(lines) {
|
|
620
|
+
const shown = lines.slice(0, MAX_DETAIL_LINES);
|
|
621
|
+
const hidden = lines.length - shown.length;
|
|
622
|
+
return [...shown, ...hidden > 0 ? [`(${hidden} more)`] : []].join("\n");
|
|
623
|
+
}
|
|
624
|
+
function errorMessage(error) {
|
|
625
|
+
if (error instanceof Error) return error.message;
|
|
626
|
+
if (typeof error === "string") return error;
|
|
627
|
+
try {
|
|
628
|
+
return JSON.stringify(error);
|
|
629
|
+
} catch {
|
|
630
|
+
return String(error);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
634
|
+
export { validateAgentConfig, verifyAgentConfig };
|