@statelyai/agent 2.0.0-alpha.29 → 2.0.0-alpha.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +1733 -0
  2. package/dist/ai-sdk.cjs +9 -9
  3. package/dist/ai-sdk.d.cts +5 -5
  4. package/dist/ai-sdk.d.mts +5 -5
  5. package/dist/ai-sdk.mjs +4 -4
  6. package/dist/{decision-Bl_rKQnU.mjs → decision-DqO018Tp.mjs} +23 -10
  7. package/dist/{decision-oevZnkBe.cjs → decision-Iys2hzg3.cjs} +31 -18
  8. package/dist/{event-log-Dtb16p1p.cjs → event-log-Bdww2l1D.cjs} +104 -25
  9. package/dist/{event-log-GPZO9p7_.mjs → event-log-DE430ZCa.mjs} +87 -26
  10. package/dist/{event-log-store-bCdskPNS.cjs → event-log-store-BWMLi6oh.cjs} +1 -1
  11. package/dist/{event-log-store-yalq8C77.mjs → event-log-store-CCWdTPEP.mjs} +1 -1
  12. package/dist/{event-log-store-CN9ygggF.d.cts → event-log-store-DTf_OzB-.d.cts} +1 -1
  13. package/dist/{event-log-store-DS836wch.d.mts → event-log-store-RziIobdW.d.mts} +1 -1
  14. package/dist/index.cjs +136 -23
  15. package/dist/index.d.cts +58 -11
  16. package/dist/index.d.mts +58 -11
  17. package/dist/index.mjs +133 -21
  18. package/dist/log.cjs +2 -2
  19. package/dist/log.d.cts +1 -1
  20. package/dist/log.d.mts +1 -1
  21. package/dist/log.mjs +2 -2
  22. package/dist/machines.cjs +1 -1
  23. package/dist/machines.mjs +1 -1
  24. package/dist/{model-source-C7FFP2Sj.mjs → model-source-CXsSZPq0.mjs} +1 -2
  25. package/dist/{model-source-DjJPjGSP.cjs → model-source-DQ7KgJaT.cjs} +4 -5
  26. package/dist/openai.cjs +3 -3
  27. package/dist/openai.d.cts +2 -2
  28. package/dist/openai.d.mts +2 -2
  29. package/dist/openai.mjs +3 -3
  30. package/dist/otel.d.cts +1 -1
  31. package/dist/otel.d.mts +1 -1
  32. package/dist/{run-agent-C9ubX8Tx.d.cts → run-agent-B5opVUxx.d.cts} +25 -6
  33. package/dist/{run-agent-CxSyZmIE.d.mts → run-agent-D4MwS5GY.d.mts} +25 -6
  34. package/dist/{run-agent-CuQT1xSS.mjs → run-agent-DWZ1uyBJ.mjs} +100 -37
  35. package/dist/{run-agent-cToZ3K42.cjs → run-agent-tmk58M0x.cjs} +100 -37
  36. package/dist/{setup-agent-DniLvDu7.mjs → setup-agent-BaffhCcM.mjs} +38 -3
  37. package/dist/{setup-agent-tsc3vm5I.d.mts → setup-agent-CPyg2ic7.d.mts} +11 -3
  38. package/dist/{setup-agent-DTF3NQg3.cjs → setup-agent-D0L8Anl_.cjs} +38 -3
  39. package/dist/{setup-agent-DlbgwsIJ.d.cts → setup-agent-yFFVXf5s.d.cts} +11 -3
  40. package/dist/tanstack-ai.cjs +3 -3
  41. package/dist/tanstack-ai.d.cts +2 -2
  42. package/dist/tanstack-ai.d.mts +2 -2
  43. package/dist/tanstack-ai.mjs +3 -3
  44. package/dist/testing.cjs +176 -26
  45. package/dist/testing.d.cts +128 -10
  46. package/dist/testing.d.mts +128 -10
  47. package/dist/testing.mjs +168 -20
  48. package/dist/{text-logic-BDbGjYMh.d.mts → text-logic-BwdrDpO9.d.mts} +6 -2
  49. package/dist/{text-logic-9fv1i9aK.d.cts → text-logic-j9hShB7d.d.cts} +6 -2
  50. package/dist/{usage-D8UphgEA.d.mts → usage-BYqv7VBw.d.cts} +1 -1
  51. package/dist/{usage-R1NTmLxY.d.cts → usage-Bl4nK8D-.d.mts} +1 -1
  52. package/dist/validate.cjs +3 -3
  53. package/dist/validate.d.cts +2 -2
  54. package/dist/validate.d.mts +2 -2
  55. package/dist/validate.mjs +3 -3
  56. package/dist/{verify-B5qGC5WN.mjs → verify-BH5e0XLK.mjs} +55 -5
  57. package/dist/{verify-DYH_2VzU.d.cts → verify-CPKDPMwY.d.cts} +14 -6
  58. package/dist/{verify-Cqghu4gz.cjs → verify-CS_xxvVU.cjs} +57 -7
  59. package/dist/{verify-CBuYIpsa.d.mts → verify-tkymWz6X.d.mts} +14 -6
  60. package/package.json +34 -4
  61. package/readme.md +72 -42
  62. package/skills/add-evals/SKILL.md +144 -0
  63. package/skills/add-traces/SKILL.md +147 -0
  64. package/skills/generate-machine/SKILL.md +6 -6
  65. package/skills/graph-engineering/SKILL.md +33 -0
  66. package/skills/improve-machine/SKILL.md +73 -21
  67. package/skills/improve-machine/reference.md +12 -7
  68. package/skills/loop-to-machine/SKILL.md +210 -0
  69. package/skills/loop-to-machine/policy.md +154 -0
@@ -0,0 +1,154 @@
1
+ # A policy machine
2
+
3
+ A policy answers one decision of the environment. It is a `setupAgent` machine, so `describeMachine`, traces, and evals work on it, and `improve-machine` can change it one diff at a time.
4
+
5
+ - **Input:** the facts it reads, the options the environment offered, and the environment's prompt. **Output:** one event, from a final state.
6
+ - **It proposes; the environment disposes.** The environment's guards still judge whatever the policy returns, so the policy needs no copy of the rules to be safe. It may import the environment's schemas, types, constants, and exported guard functions to decide well, and nothing else from it.
7
+ - **Its first version reproduces the current behavior.** When it still asks the model, it passes the environment's `system`, `prompt`, and `metadata.facts` through unchanged, so the model sees what it saw before.
8
+ - **Declare `version`** and bump it on every change. Its `description` is the environment's goal with measurable success criteria; ask the user to confirm it.
9
+ - **Where it lives:** one file next to the environment (`policy.ts`, or `policy/` with its `CHANGELOG.md`), holding the machine, `policyDecider`, and `withPolicy`.
10
+
11
+ ## What the environment exports
12
+
13
+ The event schemas and the facts schema, so the policy reuses them instead of redeclaring them. The `agent.decide` input passes the facts as `metadata: { facts }`. The facts must carry everything any decider reads: the policy's rules, and the adapter that turns a request into a call to your model (the ticket, the customer, the order's owner). A field the adapter needs and the facts lack changes what the model sees, so the policy no longer reproduces the current behavior.
14
+
15
+ ```ts
16
+ // In the environment file (exported there):
17
+ // Everything a decider reads: the policy's rules, and the model adapter when the policy asks the model.
18
+ export const factsSchema = z.object({
19
+ ticket: z.string(),
20
+ customerId: z.string(),
21
+ order: z.object({ orderId: z.string(), customerId: z.string(), amount: z.number(), refunded: z.boolean() }).nullable(),
22
+ requestedAmount: z.number().nullable(),
23
+ });
24
+ export const deskEvents = {
25
+ REFUND: z.object({ amount: z.number() }),
26
+ ESCALATE: z.object({}),
27
+ CLOSE: z.object({}),
28
+ };
29
+ ```
30
+
31
+ ## The policy
32
+
33
+ ```ts
34
+ import { z } from "zod";
35
+ import { createPolicyDecider, setupAgent, type AgentDecisionExecutor, type AgentRequestExecutors } from "@statelyai/agent";
36
+ import { deskEvents, factsSchema } from "./desk.js";
37
+
38
+ /** One proposal: an environment event with its `type`. */
39
+ const proposalSchema = z.discriminatedUnion("type", [
40
+ deskEvents.REFUND.extend({ type: z.literal("REFUND") }),
41
+ deskEvents.ESCALATE.extend({ type: z.literal("ESCALATE") }),
42
+ deskEvents.CLOSE.extend({ type: z.literal("CLOSE") }),
43
+ ]);
44
+ type Proposal = z.infer<typeof proposalSchema>;
45
+
46
+ const policySetup = setupAgent({
47
+ input: z.object({
48
+ facts: factsSchema,
49
+ options: z.array(proposalSchema),
50
+ // The environment's own prompt, passed through so the model sees what it saw before.
51
+ system: z.string().optional(),
52
+ prompt: z.string().optional(),
53
+ }),
54
+ context: z.object({
55
+ facts: factsSchema,
56
+ options: z.array(proposalSchema),
57
+ system: z.string().optional(),
58
+ prompt: z.string().optional(),
59
+ chosen: proposalSchema.nullable(),
60
+ }),
61
+ output: proposalSchema,
62
+ // Only the events the policy can propose, not every environment event.
63
+ events: { REFUND: deskEvents.REFUND, ESCALATE: deskEvents.ESCALATE, CLOSE: deskEvents.CLOSE },
64
+ });
65
+
66
+ /** The offered options of one type, as `payloads` entries (`type` removed). */
67
+ const payloadsOf = (options: readonly Proposal[], type: Proposal["type"]) =>
68
+ options.filter((option) => option.type === type).map(({ type: _type, ...payload }) => payload);
69
+
70
+ const offers = (options: readonly Proposal[], type: Proposal["type"]) =>
71
+ options.some((option) => option.type === type);
72
+
73
+ export const policyMachine = policySetup.createMachine({
74
+ id: "resolution-policy",
75
+ version: "1",
76
+ description: "Pick one offered resolution per ticket. Success: the desk's outcome matches the expected outcome.",
77
+ context: ({ input }) => ({ ...input, chosen: null }),
78
+ initial: "checking",
79
+ states: {
80
+ // A rule ported from the old code: an already-refunded order closes, no model call.
81
+ checking: {
82
+ type: "choice",
83
+ choice: ({ context }) =>
84
+ context.facts.order?.refunded && offers(context.options, "CLOSE")
85
+ ? { target: "chosen", context: { chosen: { type: "CLOSE" as const } } }
86
+ : { target: "asking" },
87
+ },
88
+ asking: {
89
+ invoke: {
90
+ src: "agent.decide",
91
+ input: ({ context }) => ({
92
+ model: "decider",
93
+ name: "pickResolution",
94
+ system: context.system,
95
+ prompt: context.prompt ?? `Facts: ${JSON.stringify(context.facts)}`,
96
+ allowedEvents: ["REFUND", "ESCALATE", "CLOSE"],
97
+ // Only what the environment offered; `[]` withholds a type.
98
+ payloads: {
99
+ REFUND: payloadsOf(context.options, "REFUND"),
100
+ ESCALATE: payloadsOf(context.options, "ESCALATE"),
101
+ CLOSE: payloadsOf(context.options, "CLOSE"),
102
+ },
103
+ // Same shape as the environment's request, so the model adapter reads the facts unchanged.
104
+ metadata: { facts: context.facts },
105
+ }),
106
+ onError: { target: "gaveUp" },
107
+ },
108
+ on: {
109
+ REFUND: ({ event }) => ({
110
+ target: "chosen",
111
+ context: { chosen: { type: "REFUND" as const, amount: event.amount } },
112
+ }),
113
+ ESCALATE: { target: "chosen", context: { chosen: { type: "ESCALATE" as const } } },
114
+ CLOSE: { target: "chosen", context: { chosen: { type: "CLOSE" as const } } },
115
+ },
116
+ },
117
+ chosen: { type: "final", output: ({ context }) => context.chosen! },
118
+ gaveUp: { type: "final", output: { type: "ESCALATE" as const } },
119
+ },
120
+ });
121
+
122
+ /**
123
+ * Runs the policy as the environment's decider. createPolicyDecider passes the request's
124
+ * `metadata.facts`, offered events, `system`, and `prompt` as the policy's input (its input
125
+ * schema validates them), and returns the policy's output event with its token usage.
126
+ */
127
+ export function policyDecider(executors: AgentRequestExecutors): AgentDecisionExecutor {
128
+ return createPolicyDecider(policyMachine, { executors });
129
+ }
130
+ ```
131
+
132
+ - Every state that invokes `agent.decide` needs an `onError`, or `lintAgentMachine` warns. A policy that cannot choose falls back to the safest event the environment always accepts (here `ESCALATE`).
133
+ - A rule the policy applies without a model is a `choice` state over the facts, checked against what was offered (`offers`), since the environment may have withheld it.
134
+
135
+ ## Running it as the environment's decider
136
+
137
+ ```ts
138
+ /** The model's executors, with the policy answering the environment's `chooseResolution`. */
139
+ export function withPolicy(modelExecutors: AgentRequestExecutors): AgentRequestExecutors {
140
+ const decideWithPolicy = policyDecider(modelExecutors);
141
+ return {
142
+ ...modelExecutors,
143
+ decide: (request, info) => {
144
+ if (request.name === "chooseResolution") return decideWithPolicy(request, info);
145
+ if (!modelExecutors.decide) throw new Error("no decide executor");
146
+ return modelExecutors.decide(request, info);
147
+ },
148
+ };
149
+ }
150
+ ```
151
+
152
+ Pass `withPolicy(modelExecutors)` as the environment's `executors` in the runner. That one line is the only runner change a policy needs. Decisions with any other name still reach the model.
153
+
154
+ A runner that already takes a decide executor for the decision (`runMachine({ decide })`) needs no routing: pass `policyDecider(modelExecutors)` there. If your model is not an executor (a function that returns an option index, say), wrap it in a decide executor first, the same adapter the runner uses, and keep that adapter out of the environment's imports.