@statelyai/agent 1.0.0-beta.1 → 1.0.0

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/index.mjs CHANGED
@@ -9,13 +9,36 @@ import {
9
9
  import { tool } from "ai";
10
10
 
11
11
  // src/utils.ts
12
+ import hash from "object-hash";
12
13
  function getAllTransitions(state) {
13
14
  const nodes = state._nodes;
14
- const transitions = nodes.map((node) => [...node.transitions.values()]).flat(2).map((transition) => ({
15
- ...transition,
16
- guard: typeof transition.guard === "string" ? { type: transition.guard } : transition.guard
17
- // TODO: fix
18
- }));
15
+ const transitions = nodes.map((node) => [...node.transitions.values()]).map((nodeTransitions) => {
16
+ return nodeTransitions.map((nodeEventTransitions) => {
17
+ return nodeEventTransitions.map((transition) => {
18
+ return {
19
+ ...transition,
20
+ guard: typeof transition.guard === "string" ? { type: transition.guard } : transition.guard
21
+ // TODO: fix
22
+ };
23
+ });
24
+ });
25
+ }).flat(2);
26
+ return transitions;
27
+ }
28
+ function getAllMachineTransitions(stateNode) {
29
+ const transitions = [...stateNode.transitions.values()].map((nodeTransitions) => {
30
+ return nodeTransitions.map((transition) => {
31
+ return {
32
+ ...transition,
33
+ guard: typeof transition.guard === "string" ? { type: transition.guard } : transition.guard
34
+ // TODO: fix
35
+ };
36
+ });
37
+ }).flat(2);
38
+ for (const s of Object.values(stateNode.states)) {
39
+ const stateTransitions = getAllMachineTransitions(s);
40
+ transitions.push(...stateTransitions);
41
+ }
19
42
  return transitions;
20
43
  }
21
44
  function wrapInXml(tagName, content) {
@@ -26,6 +49,14 @@ function randomId() {
26
49
  const random = Math.random().toString(36).substring(2, 9);
27
50
  return timestamp + random;
28
51
  }
52
+ var machineHashes = /* @__PURE__ */ new WeakMap();
53
+ function getMachineHash(machine) {
54
+ if (machineHashes.has(machine)) return machineHashes.get(machine);
55
+ const transitions = getAllMachineTransitions(machine.root);
56
+ const machineHash = hash(transitions);
57
+ machineHashes.set(machine, machineHash);
58
+ return machineHash;
59
+ }
29
60
 
30
61
  // src/templates/defaultText.ts
31
62
  var defaultTextTemplate = (data) => {
@@ -39,87 +70,6 @@ ${data.goal}
39
70
  `.trim();
40
71
  };
41
72
 
42
- // src/planners/simplePlanner.ts
43
- function getTransitions(state, machine) {
44
- if (!machine) {
45
- return [];
46
- }
47
- const resolvedState = machine.resolveState(state);
48
- return getAllTransitions(resolvedState);
49
- }
50
- var simplePlannerPromptTemplate = (data) => {
51
- return `
52
- ${defaultTextTemplate(data)}
53
-
54
- Only make a single tool call to achieve the above goal.
55
- `.trim();
56
- };
57
- async function simplePlanner(agent, input) {
58
- const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(input.events).map(([eventType, { description }]) => ({
59
- eventType,
60
- description
61
- }));
62
- const filter = (eventType) => Object.keys(input.events).includes(eventType);
63
- const functionNameMapping = {};
64
- const toolTransitions = transitions.filter((t) => {
65
- return filter(t.eventType);
66
- }).map((t) => {
67
- const name = t.eventType.replace(/\./g, "_");
68
- functionNameMapping[name] = t.eventType;
69
- return {
70
- type: "function",
71
- eventType: t.eventType,
72
- description: t.description,
73
- name
74
- };
75
- });
76
- const toolMap = {};
77
- for (const toolTransitionData of toolTransitions) {
78
- const toolZodType = input.events?.[toolTransitionData.eventType];
79
- if (!toolZodType) {
80
- continue;
81
- }
82
- toolMap[toolTransitionData.name] = tool({
83
- description: toolZodType?.description ?? toolTransitionData.description,
84
- parameters: toolZodType,
85
- execute: async (params) => {
86
- const event = {
87
- type: toolTransitionData.eventType,
88
- ...params
89
- };
90
- return event;
91
- }
92
- });
93
- }
94
- const prompt = simplePlannerPromptTemplate({
95
- context: input.state.context,
96
- goal: input.goal
97
- });
98
- const result = await agent.generateText({
99
- ...input,
100
- prompt,
101
- tools: toolMap,
102
- toolChoice: "required"
103
- });
104
- const singleResult = result.toolResults[0];
105
- if (!singleResult) {
106
- console.warn("No tool call results returned");
107
- return void 0;
108
- }
109
- return {
110
- goal: input.goal,
111
- state: input.state,
112
- steps: [
113
- {
114
- event: singleResult.result
115
- }
116
- ],
117
- nextEvent: singleResult.result,
118
- sessionId: agent.sessionId,
119
- timestamp: Date.now()
120
- };
121
- }
122
-
123
73
  // src/text.ts
124
74
  import {
125
75
  fromObservable,
@@ -128,9 +78,7 @@ import {
128
78
  } from "xstate";
129
79
  async function getMessages(agent, prompt, options) {
130
80
  let messages = [];
131
- if (options.messages === true) {
132
- messages = agent.select((s) => s.messages);
133
- } else if (typeof options.messages === "function") {
81
+ if (typeof options.messages === "function") {
134
82
  messages = await options.messages(agent);
135
83
  } else if (options.messages) {
136
84
  messages = options.messages;
@@ -222,14 +170,13 @@ async function agentStreamText(agent, options) {
222
170
  return result;
223
171
  }
224
172
  function fromTextStream(agent, defaultOptions) {
225
- return fromObservable(({ input, self }) => {
226
- const context = input.context === true ? (self._parent?.getSnapshot()).context : input.context;
173
+ return fromObservable(({ input }) => {
227
174
  const observers = /* @__PURE__ */ new Set();
228
175
  (async () => {
229
176
  const result = await agentStreamText(agent, {
230
177
  ...defaultOptions,
231
178
  ...input,
232
- context
179
+ context: input.context
233
180
  });
234
181
  for await (const part of result.fullStream) {
235
182
  if (part.type === "text-delta") {
@@ -253,16 +200,103 @@ function fromTextStream(agent, defaultOptions) {
253
200
  });
254
201
  }
255
202
  function fromText(agent, defaultOptions) {
256
- return fromPromise(async ({ input, self }) => {
257
- const context = input.context === true ? (self._parent?.getSnapshot()).context : input.context;
203
+ return fromPromise(async ({ input }) => {
258
204
  return await agentGenerateText(agent, {
259
205
  ...input,
260
206
  ...defaultOptions,
261
- context
207
+ context: input.context
262
208
  });
263
209
  });
264
210
  }
265
211
 
212
+ // src/planners/simplePlanner.ts
213
+ function getTransitions(state, machine) {
214
+ if (!machine) {
215
+ return [];
216
+ }
217
+ const resolvedState = machine.resolveState(state);
218
+ return getAllTransitions(resolvedState);
219
+ }
220
+ var simplePlannerPromptTemplate = (data) => {
221
+ return `
222
+ ${defaultTextTemplate(data)}
223
+
224
+ Make at most one tool call to achieve the above goal. If the goal cannot be achieved with any tool calls, do not make any tool call.
225
+ `.trim();
226
+ };
227
+ async function simplePlanner(agent, input) {
228
+ const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(input.events).map(([eventType, { description }]) => ({
229
+ eventType,
230
+ description
231
+ }));
232
+ const filter = (eventType) => Object.keys(input.events).includes(eventType);
233
+ const functionNameMapping = {};
234
+ const toolTransitions = transitions.filter((t) => {
235
+ return filter(t.eventType);
236
+ }).map((t) => {
237
+ const name = t.eventType.replace(/\./g, "_");
238
+ functionNameMapping[name] = t.eventType;
239
+ return {
240
+ type: "function",
241
+ eventType: t.eventType,
242
+ description: t.description,
243
+ name
244
+ };
245
+ });
246
+ const toolMap = {};
247
+ for (const toolTransitionData of toolTransitions) {
248
+ const toolZodType = input.events?.[toolTransitionData.eventType];
249
+ if (!toolZodType) {
250
+ continue;
251
+ }
252
+ toolMap[toolTransitionData.name] = tool({
253
+ description: toolZodType?.description ?? toolTransitionData.description,
254
+ parameters: toolZodType,
255
+ execute: async (params) => {
256
+ const event = {
257
+ type: toolTransitionData.eventType,
258
+ ...params
259
+ };
260
+ return event;
261
+ }
262
+ });
263
+ }
264
+ if (!Object.keys(toolMap).length) {
265
+ return void 0;
266
+ }
267
+ const prompt = simplePlannerPromptTemplate({
268
+ context: input.state.context,
269
+ goal: input.goal
270
+ });
271
+ const messages = await getMessages(agent, prompt, input);
272
+ const result = await agent.generateText({
273
+ toolChoice: "required",
274
+ ...input,
275
+ prompt,
276
+ messages,
277
+ tools: toolMap
278
+ });
279
+ const singleResult = result.toolResults[0];
280
+ if (!singleResult) {
281
+ console.log(toolMap);
282
+ console.warn("No tool call results returned");
283
+ return void 0;
284
+ }
285
+ return {
286
+ goal: input.goal,
287
+ state: input.state,
288
+ execute: async (state) => {
289
+ if (JSON.stringify(state) === JSON.stringify(input.state)) {
290
+ return singleResult.result;
291
+ }
292
+ return void 0;
293
+ },
294
+ nextEvent: singleResult.result,
295
+ sessionId: agent.sessionId,
296
+ timestamp: Date.now()
297
+ };
298
+ }
299
+
266
300
  // src/decision.ts
267
301
  import { fromPromise as fromPromise2 } from "xstate";
268
302
  async function agentDecide(agent, options) {
@@ -389,6 +423,7 @@ function createAgent({
389
423
  description,
390
424
  model,
391
425
  events,
426
+ context,
392
427
  planner = simplePlanner,
393
428
  stringify = JSON.stringify,
394
429
  getMemory,
@@ -427,6 +462,7 @@ function createAgent({
427
462
  });
428
463
  return message;
429
464
  };
465
+ agent.getMessages = () => agent.getSnapshot().context.messages;
430
466
  agent.generateText = (opts) => agentGenerateText(agent, opts);
431
467
  agent.streamText = (opts) => agentStreamText(agent, opts);
432
468
  agent.addFeedback = (feedbackInput) => {
@@ -441,12 +477,17 @@ function createAgent({
441
477
  });
442
478
  return feedback;
443
479
  };
480
+ agent.getFeedback = () => agent.getSnapshot().context.feedback;
444
481
  agent.addObservation = (observationInput) => {
482
+ const { prevState, event, state } = observationInput;
445
483
  const observation = {
446
- ...observationInput,
484
+ prevState,
485
+ event,
486
+ state,
447
487
  id: observationInput.id ?? randomId(),
448
488
  sessionId: agent.sessionId,
449
- timestamp: observationInput.timestamp ?? Date.now()
489
+ timestamp: observationInput.timestamp ?? Date.now(),
490
+ machineHash: observationInput.machine ? getMachineHash(observationInput.machine) : void 0
450
491
  };
451
492
  agent.send({
452
493
  type: "agent.observe",
@@ -454,12 +495,14 @@ function createAgent({
454
495
  });
455
496
  return observation;
456
497
  };
498
+ agent.getObservations = () => agent.getSnapshot().context.observations;
457
499
  agent.addPlan = (plan) => {
458
500
  agent.send({
459
501
  type: "agent.plan",
460
502
  plan
461
503
  });
462
504
  };
505
+ agent.getPlans = () => agent.getSnapshot().context.plans;
463
506
  agent.interact = (actorRef, getInput) => {
464
507
  let prevState = void 0;
465
508
  let subscribed = true;
@@ -486,7 +529,8 @@ function createAgent({
486
529
  const observationInput = {
487
530
  event: inspEvent.event,
488
531
  prevState,
489
- state: inspEvent.snapshot
532
+ state: inspEvent.snapshot,
533
+ machine: actorRef.src
490
534
  };
491
535
  await handleObservation(observationInput);
492
536
  }
@@ -496,7 +540,8 @@ function createAgent({
496
540
  prevState: void 0,
497
541
  event: { type: "" },
498
542
  // TODO: unknown events?
499
- state: actorRef.getSnapshot()
543
+ state: actorRef.getSnapshot(),
544
+ machine: actorRef.src
500
545
  });
501
546
  }
502
547
  return {
@@ -506,12 +551,11 @@ function createAgent({
506
551
  // TODO: make this actually unsubscribe
507
552
  };
508
553
  };
554
+ agent.types = {};
509
555
  agent.start();
510
556
  return agent;
511
557
  }
512
558
  export {
513
- agentDecide,
514
- agentGenerateText,
515
559
  createAgent,
516
560
  fromDecision,
517
561
  fromText,
@@ -13,20 +13,18 @@ const agent = createAgent({
13
13
  }),
14
14
  'agent.endConversation': z.object({}).describe('Stop the conversation'),
15
15
  },
16
+ context: {
17
+ userMessage: z.string(),
18
+ },
16
19
  });
17
20
 
18
21
  const machine = setup({
19
- types: {
20
- context: {} as {
21
- conversation: string[];
22
- },
23
- events: agent.eventTypes,
24
- },
22
+ types: agent.types,
25
23
  actors: { agent: fromDecision(agent), getFromTerminal },
26
24
  }).createMachine({
27
25
  initial: 'listening',
28
26
  context: {
29
- conversation: [],
27
+ userMessage: '',
30
28
  },
31
29
  states: {
32
30
  listening: {
@@ -35,8 +33,7 @@ const machine = setup({
35
33
  input: 'User:',
36
34
  onDone: {
37
35
  actions: assign({
38
- conversation: (x) =>
39
- x.context.conversation.concat('User: ' + x.event.output),
36
+ userMessage: (x) => x.event.output,
40
37
  }),
41
38
  target: 'responding',
42
39
  },
@@ -47,20 +44,15 @@ const machine = setup({
47
44
  src: 'agent',
48
45
  input: (x) => ({
49
46
  context: {
50
- conversation: x.context.conversation,
47
+ userMessage: 'User says: ' + x.context.userMessage,
51
48
  },
49
+ messages: agent.getMessages(),
52
50
  goal: 'Respond to the user, unless they want to end the conversation.',
53
51
  }),
54
52
  },
55
53
  on: {
56
54
  'agent.respond': {
57
- actions: [
58
- assign({
59
- conversation: (x) =>
60
- x.context.conversation.concat('Assistant: ' + x.event.response),
61
- }),
62
- log((x) => `Agent: ${x.event.response}`),
63
- ],
55
+ actions: [log((x) => `Agent: ${x.event.response}`)],
64
56
  target: 'listening',
65
57
  },
66
58
  'agent.endConversation': 'finished',
package/examples/cot.ts CHANGED
@@ -17,16 +17,14 @@ const agent = createAgent({
17
17
  answer: z.string().describe('The answer to the question'),
18
18
  }),
19
19
  },
20
+ context: {
21
+ question: z.string().nullable(),
22
+ thought: z.string().nullable(),
23
+ },
20
24
  });
21
25
 
22
26
  const machine = setup({
23
- types: {
24
- context: {} as {
25
- question: string | null;
26
- thought: string | null;
27
- },
28
- events: agent.eventTypes,
29
- },
27
+ types: agent.types,
30
28
  actors: { agent: fromDecision(agent), getFromTerminal },
31
29
  }).createMachine({
32
30
  initial: 'asking',
package/examples/email.ts CHANGED
@@ -19,7 +19,7 @@ const agent = createAgent({
19
19
 
20
20
  const machine = setup({
21
21
  types: {
22
- events: agent.eventTypes,
22
+ events: agent.types.events,
23
23
  input: {} as {
24
24
  email: string;
25
25
  instructions: string;
@@ -50,7 +50,7 @@ const machine = setup({
50
50
  instructions: x.context.instructions,
51
51
  clarifications: x.context.clarifications,
52
52
  },
53
- messages: agent.select((ctx) => ctx.messages),
53
+ messages: agent.getMessages(),
54
54
  goal: 'Respond to the email given the instructions and the provided clarifications. If not enough information is provided, ask for clarification. Otherwise, if you are absolutely sure that there is no ambiguous or missing information, create and submit a response email.',
55
55
  }),
56
56
  },
@@ -18,7 +18,7 @@ const agent = createAgent({
18
18
 
19
19
  const machine = setup({
20
20
  types: {
21
- events: agent.eventTypes,
21
+ events: agent.types.events,
22
22
  },
23
23
  actors: { agent: fromDecision(agent), summarizer: fromText(agent) },
24
24
  }).createMachine({
@@ -32,11 +32,11 @@ const machine = setup({
32
32
  invoke: [
33
33
  {
34
34
  src: 'summarizer',
35
- input: {
36
- context: true,
35
+ input: (x) => ({
36
+ context: x.context,
37
37
  prompt:
38
38
  'Summarize the patient visit in a single sentence. The summary should be in English.',
39
- },
39
+ }),
40
40
  onDone: {
41
41
  actions: assign({
42
42
  englishSummary: ({ event }) => event.output.text,
@@ -45,11 +45,11 @@ const machine = setup({
45
45
  },
46
46
  {
47
47
  src: 'summarizer',
48
- input: {
49
- context: true,
48
+ input: (x) => ({
49
+ context: x.context,
50
50
  prompt:
51
51
  'Summarize the patient visit in a single sentence. The summary should be in Spanish.',
52
- },
52
+ }),
53
53
  onDone: {
54
54
  actions: assign({
55
55
  spanishSummary: ({ event }) => event.output.text,
package/examples/goal.ts CHANGED
@@ -25,7 +25,7 @@ const machine = setup({
25
25
  question: string | null;
26
26
  goal: string | null;
27
27
  },
28
- events: agent.eventTypes,
28
+ events: agent.types.events,
29
29
  },
30
30
  actors: { decider, getFromTerminal },
31
31
  }).createMachine({
package/examples/joke.ts CHANGED
@@ -67,19 +67,17 @@ const agent = createAgent({
67
67
  .describe('Explains why the joke was irrelevant'),
68
68
  'agent.markAsRelevant': z.object({}).describe('The joke was relevant'),
69
69
  },
70
+ context: {
71
+ topic: z.string().describe('The topic for the joke'),
72
+ jokes: z.array(z.string()).describe('The jokes told so far'),
73
+ desire: z.string().nullable().describe('The user desire'),
74
+ lastRating: z.number().nullable().describe('The last joke rating'),
75
+ loader: z.string().nullable().describe('The loader text'),
76
+ },
70
77
  });
71
78
 
72
79
  const jokeMachine = setup({
73
- types: {
74
- context: {} as {
75
- topic: string;
76
- jokes: string[];
77
- desire: string | null;
78
- lastRating: number | null;
79
- loader: string | null;
80
- },
81
- events: agent.eventTypes,
82
- },
80
+ types: agent.types,
83
81
  actors: {
84
82
  agent: fromDecision(agent),
85
83
  loader,
@@ -20,7 +20,7 @@ const machine = setup({
20
20
  previousGuesses: number[];
21
21
  answer: number | null;
22
22
  },
23
- events: agent.eventTypes,
23
+ events: agent.types.events,
24
24
  },
25
25
  actors: {
26
26
  agent: fromDecision(agent),
@@ -27,7 +27,7 @@ const machine = setup({
27
27
  lastInput: string | null;
28
28
  entries: string[];
29
29
  },
30
- events: {} as typeof agent.eventTypes | { type: 'draw' },
30
+ events: {} as typeof agent.types.events | { type: 'draw' },
31
31
  },
32
32
  actors: { agent: fromDecision(agent), getFromTerminal },
33
33
  }).createMachine({
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+ import { createAgent } from '../src';
3
+ import { openai } from '@ai-sdk/openai';
4
+ import { createMachine } from 'xstate';
5
+
6
+ const agent = createAgent({
7
+ model: openai('gpt-4o'),
8
+ events: {
9
+ doSomething: z.object({}).describe('Do something'),
10
+ },
11
+ });
12
+
13
+ async function main() {
14
+ const machine = createMachine({
15
+ on: {
16
+ doSomething: {},
17
+ },
18
+ });
19
+ const result = await agent.decide({
20
+ goal: 'Do not do anything',
21
+ state: { value: {}, context: {} },
22
+ machine,
23
+ });
24
+
25
+ console.log(result);
26
+ }
27
+
28
+ main();
@@ -35,7 +35,7 @@ const agent = createAgent({
35
35
 
36
36
  const machine = setup({
37
37
  types: {
38
- events: agent.eventTypes,
38
+ events: agent.types.events,
39
39
  input: {} as string,
40
40
  context: {} as {
41
41
  customerIssue: string;
@@ -23,25 +23,30 @@ const agent = createAgent({
23
23
  }),
24
24
  reset: z.object({}).describe('Reset the game to the initial state'),
25
25
  },
26
+ context: {
27
+ board: z
28
+ .array(z.union([z.literal(null), z.literal('x'), z.literal('o')]))
29
+ .describe('The 3x3 board represented as a 9-element array.'),
30
+ moves: z
31
+ .number()
32
+ .min(0)
33
+ .max(9)
34
+ .describe('The number of moves made in the game.'),
35
+ player: z
36
+ .union([z.literal('x'), z.literal('o')])
37
+ .describe('The current player (x or o)'),
38
+ gameReport: z.string(),
39
+ },
26
40
  });
27
41
 
28
42
  type Player = 'x' | 'o';
29
43
 
30
- interface GameContext {
31
- board: (Player | null)[];
32
- moves: number;
33
- player: Player;
34
- gameReport: string;
35
- events: string[];
36
- }
37
-
38
44
  const initialContext = {
39
45
  board: Array(9).fill(null) as Array<Player | null>,
40
46
  moves: 0,
41
47
  player: 'x' as Player,
42
48
  gameReport: '',
43
- events: [],
44
- } satisfies GameContext;
49
+ } satisfies typeof agent.types.context;
45
50
 
46
51
  function getWinner(board: typeof initialContext.board): Player | null {
47
52
  const lines = [
@@ -64,8 +69,8 @@ function getWinner(board: typeof initialContext.board): Player | null {
64
69
 
65
70
  export const ticTacToeMachine = setup({
66
71
  types: {
67
- context: {} as GameContext,
68
- events: agent.eventTypes,
72
+ context: agent.types.context,
73
+ events: agent.types.events,
69
74
  },
70
75
  actors: {
71
76
  agent: fromDecision(agent),
@@ -81,16 +86,8 @@ export const ticTacToeMachine = setup({
81
86
  },
82
87
  moves: ({ context }) => context.moves + 1,
83
88
  player: ({ context }) => (context.player === 'x' ? 'o' : 'x'),
84
- events: ({ context, event }) => {
85
- return [...context.events, JSON.stringify(event)];
86
- },
87
89
  }),
88
90
  resetGame: assign(initialContext),
89
- recordEvent: assign({
90
- events: ({ context, event }) => {
91
- return [...context.events, JSON.stringify(event)];
92
- },
93
- }),
94
91
  printBoard: ({ context }) => {
95
92
  // Print the context.board in a 3 x 3 grid format
96
93
  let boardString = '';
@@ -172,7 +169,7 @@ export const ticTacToeMachine = setup({
172
169
  src: 'gameReporter',
173
170
  input: ({ context }) => ({
174
171
  context: {
175
- events: context.events,
172
+ events: agent.getObservations().map((o) => o.event),
176
173
  board: context.board,
177
174
  },
178
175
  prompt: 'Provide a short game report analyzing the game.',
package/examples/todo.ts CHANGED
@@ -36,7 +36,9 @@ const machine = setup({
36
36
  todos: Todo[];
37
37
  command: string | null;
38
38
  },
39
- events: {} as typeof agent.eventTypes | { type: 'assist'; command: string },
39
+ events: {} as
40
+ | typeof agent.types.events
41
+ | { type: 'assist'; command: string },
40
42
  },
41
43
  actors: { agent: fromDecision(agent), getFromTerminal },
42
44
  }).createMachine({
package/examples/tutor.ts CHANGED
@@ -28,7 +28,7 @@ const machine = setup({
28
28
  context: {} as {
29
29
  conversation: string[];
30
30
  },
31
- events: agent.eventTypes,
31
+ events: agent.types.events,
32
32
  },
33
33
  actors: { agent: fromDecision(agent), getFromTerminal },
34
34
  }).createMachine({