@statelyai/agent 1.1.6 → 2.0.0-next.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.
Files changed (54) hide show
  1. package/.changeset/light-hats-drive.md +9 -0
  2. package/.changeset/pre.json +10 -0
  3. package/.vscode/launch.json +6 -0
  4. package/CHANGELOG.md +10 -0
  5. package/dist/index.d.mts +262 -165
  6. package/dist/index.d.ts +262 -165
  7. package/dist/index.js +368 -263
  8. package/dist/index.mjs +371 -261
  9. package/examples/chatbot-alt.ts +57 -0
  10. package/examples/chatbot.ts +11 -16
  11. package/examples/cot.ts +25 -22
  12. package/examples/customer-service-sim.ts +107 -0
  13. package/examples/email.ts +14 -14
  14. package/examples/example.ts +5 -5
  15. package/examples/executor.ts +66 -0
  16. package/examples/goal.ts +11 -11
  17. package/examples/helpers/helpers.ts +26 -14
  18. package/examples/joke.ts +78 -75
  19. package/examples/jugs.ts +125 -0
  20. package/examples/multi.ts +4 -4
  21. package/examples/newspaper.ts +98 -104
  22. package/examples/number.ts +5 -4
  23. package/examples/raffle.ts +10 -11
  24. package/examples/river-crossing.ts +140 -0
  25. package/examples/sandbox.ts +1 -1
  26. package/examples/simple.ts +4 -2
  27. package/examples/summary.ts +121 -0
  28. package/examples/support.ts +5 -5
  29. package/examples/ticTacToe.ts +86 -45
  30. package/examples/todo.ts +6 -6
  31. package/examples/tutor.ts +13 -13
  32. package/examples/verify.ts +2 -2
  33. package/examples/weather.ts +5 -8
  34. package/examples/wiki.ts +26 -7
  35. package/examples/word.ts +15 -10
  36. package/package.json +13 -10
  37. package/readme.md +1 -1
  38. package/src/agent-experimental.ts +1 -1
  39. package/src/agent.test.ts +117 -228
  40. package/src/agent.ts +469 -81
  41. package/src/{decision.test.ts → decide.test.ts} +26 -50
  42. package/src/decide.ts +153 -0
  43. package/src/index.ts +1 -1
  44. package/src/middleware.ts +103 -0
  45. package/src/mockModel.ts +47 -0
  46. package/src/planners/shortestPathPlanner.ts +151 -13
  47. package/src/planners/simplePlanner.ts +57 -85
  48. package/src/strategies/chain-of-note.ts +6 -55
  49. package/src/text.ts +51 -144
  50. package/src/types.ts +172 -204
  51. package/src/utils.ts +37 -4
  52. package/src/adapters/vercel.ts +0 -7
  53. package/src/decision.ts +0 -84
  54. package/src/memory.ts +0 -25
package/src/agent.ts CHANGED
@@ -1,34 +1,42 @@
1
1
  import {
2
+ Actor,
3
+ ActorRefLike,
2
4
  AnyEventObject,
3
5
  AnyStateMachine,
4
- createActor,
5
6
  EventObject,
6
7
  fromTransition,
7
- Observer,
8
- toObserver,
8
+ Subscription,
9
9
  } from 'xstate';
10
10
  import { ZodContextMapping, ZodEventMapping } from './schemas';
11
11
  import {
12
- Agent,
13
12
  AgentLogic,
14
13
  AgentMessage,
15
14
  AgentPlanner,
16
15
  EventsFromZodEventMapping,
17
16
  GenerateTextOptions,
18
17
  AgentLongTermMemory,
19
- AIAdapter,
20
18
  ObservedState,
21
19
  AgentObservationInput,
22
20
  AgentMemoryContext,
23
21
  AgentObservation,
24
22
  ContextFromZodContextMapping,
25
23
  AgentFeedback,
24
+ AgentMessageInput,
25
+ AgentFeedbackInput,
26
+ AgentPlan,
27
+ Compute,
28
+ AgentDecisionInput,
29
+ AgentDecideOptions,
26
30
  } from './types';
27
31
  import { simplePlanner } from './planners/simplePlanner';
28
- import { agentGenerateText, agentStreamText } from './text';
29
- import { agentDecide } from './decision';
30
- import { vercelAdapter } from './adapters/vercel';
31
- import { getMachineHash, randomId } from './utils';
32
+ import { agentDecide } from './decide';
33
+ import { getMachineHash, isActorRef, randomId } from './utils';
34
+ import {
35
+ experimental_wrapLanguageModel,
36
+ LanguageModel,
37
+ LanguageModelV1,
38
+ } from 'ai';
39
+ import { createAgentMiddleware } from './middleware';
32
40
 
33
41
  export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
34
42
  (state, event, { emit }) => {
@@ -89,16 +97,16 @@ export function createAgent<
89
97
  TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
90
98
  TContext = ContextFromZodContextMapping<TContextSchema>
91
99
  >({
100
+ id,
92
101
  name,
93
102
  description,
94
103
  model,
95
104
  events,
96
105
  context,
97
- planner = simplePlanner as AgentPlanner<Agent<TContext, TEvents>>,
106
+ planner = simplePlanner as AgentPlanner<Agent<TContextSchema, TEventSchemas>>,
98
107
  stringify = JSON.stringify,
99
108
  getMemory,
100
109
  logic = agentLogic as AgentLogic<TEvents>,
101
- adapter = vercelAdapter,
102
110
  ...generateTextOptions
103
111
  }: {
104
112
  /**
@@ -130,110 +138,441 @@ export function createAgent<
130
138
  */
131
139
  events: TEventSchemas;
132
140
  context?: TContextSchema;
133
- planner?: AgentPlanner<Agent<TContext, TEvents>>;
141
+ planner?: AgentPlanner<Agent<TContextSchema, TEventSchemas>>;
134
142
  stringify?: typeof JSON.stringify;
135
143
  /**
136
144
  * A function that retrieves the agent's long term memory
137
145
  */
138
- getMemory?: (agent: Agent<TContext, TEvents>) => AgentLongTermMemory;
146
+ getMemory?: (
147
+ agent: Agent<TContextSchema, TEventSchemas>
148
+ ) => AgentLongTermMemory;
139
149
  /**
140
150
  * Agent logic
141
151
  */
142
152
  logic?: AgentLogic<TEvents>;
143
- adapter?: AIAdapter;
144
- } & GenerateTextOptions): Agent<TContext, TEvents> {
145
- const agent = createActor(logic) as unknown as Agent<TContext, TEvents>;
146
- agent.events = events;
147
- agent.model = model;
148
- agent.name = name;
149
- agent.description = description;
150
- agent.adapter = adapter;
151
- agent.defaultOptions = { ...generateTextOptions, model };
152
- agent.select = (selector) => {
153
- return selector(agent.getSnapshot().context);
154
- };
155
- agent.memory = getMemory ? getMemory(agent) : undefined;
153
+ } & GenerateTextOptions): Agent<TContextSchema, TEventSchemas> {
154
+ return new Agent({
155
+ id,
156
+ context,
157
+ events,
158
+ name,
159
+ description,
160
+ planner,
161
+ model,
162
+ logic,
163
+ }) as any;
164
+ // const agent = createActor(logic) as unknown as Agent<TContext, TEvents>;
165
+ // agent.events = events;
166
+ // agent.model = model;
167
+ // agent.name = name;
168
+ // agent.description = description;
169
+ // agent.defaultOptions = { ...generateTextOptions, model };
170
+ // agent.memory = getMemory ? getMemory(agent) : undefined;
156
171
 
157
- agent.onMessage = (callback) => {
158
- agent.on('message', (ev) => callback(ev.message));
159
- };
172
+ // agent.onMessage = (callback) => {
173
+ // agent.on('message', (ev) => callback(ev.message));
174
+ // };
175
+
176
+ // agent.decide = (opts) => {
177
+ // return agentDecide(agent, opts);
178
+ // };
179
+
180
+ // agent.addMessage = (messageInput) => {
181
+ // const message = {
182
+ // ...messageInput,
183
+ // id: messageInput.id ?? randomId(),
184
+ // timestamp: messageInput.timestamp ?? Date.now(),
185
+ // sessionId: agent.sessionId,
186
+ // } satisfies AgentMessage;
187
+ // agent.send({
188
+ // type: 'agent.message',
189
+ // message,
190
+ // });
191
+
192
+ // return message;
193
+ // };
194
+ // agent.getMessages = () => agent.getSnapshot().context.messages;
195
+
196
+ // agent.addFeedback = (feedbackInput) => {
197
+ // const feedback = {
198
+ // ...feedbackInput,
199
+ // attributes: { ...feedbackInput.attributes },
200
+ // reward: feedbackInput.reward ?? 0,
201
+ // timestamp: feedbackInput.timestamp ?? Date.now(),
202
+ // sessionId: agent.sessionId,
203
+ // } satisfies AgentFeedback;
204
+ // agent.send({
205
+ // type: 'agent.feedback',
206
+ // feedback,
207
+ // });
208
+ // return feedback;
209
+ // };
210
+ // agent.getFeedback = () => agent.getSnapshot().context.feedback;
211
+
212
+ // agent.addObservation = (observationInput) => {
213
+ // const { prevState, event, state } = observationInput;
214
+ // const observedState = { context: state.context, value: state.value };
215
+ // const observedPrevState = prevState
216
+ // ? {
217
+ // context: prevState.context,
218
+ // value: prevState.value,
219
+ // }
220
+ // : undefined;
221
+ // const observation = {
222
+ // prevState: observedPrevState,
223
+ // event,
224
+ // state: observedState,
225
+ // id: observationInput.id ?? randomId(),
226
+ // sessionId: agent.sessionId,
227
+ // timestamp: observationInput.timestamp ?? Date.now(),
228
+ // machineHash: observationInput.machine
229
+ // ? getMachineHash(observationInput.machine)
230
+ // : undefined,
231
+ // } satisfies AgentObservation<any>;
232
+
233
+ // agent.send({
234
+ // type: 'agent.observe',
235
+ // observation,
236
+ // });
237
+
238
+ // return observation;
239
+ // };
240
+ // agent.getObservations = () => agent.getSnapshot().context.observations;
241
+
242
+ // agent.addPlan = (plan) => {
243
+ // agent.send({
244
+ // type: 'agent.plan',
245
+ // plan,
246
+ // });
247
+ // };
248
+ // agent.getPlans = () => agent.getSnapshot().context.plans;
249
+
250
+ // agent.interact = ((actorRef, getInput) => {
251
+ // let prevState: ObservedState | undefined = undefined;
252
+ // let subscribed = true;
253
+
254
+ // async function handleObservation(observationInput: AgentObservationInput) {
255
+ // const observation = agent.addObservation(observationInput);
256
+
257
+ // const input = getInput?.(observation);
258
+
259
+ // if (input) {
260
+ // await agentDecide(agent, {
261
+ // machine: actorRef.src as AnyStateMachine,
262
+ // state: observation.state,
263
+ // execute: async (event) => {
264
+ // actorRef.send(event);
265
+ // },
266
+ // ...input,
267
+ // });
268
+ // }
269
+
270
+ // prevState = observationInput.state;
271
+ // }
272
+
273
+ // // Inspect system, but only observe specified actor
274
+ // const sub = actorRef.system.inspect({
275
+ // next: async (inspEvent) => {
276
+ // if (
277
+ // !subscribed ||
278
+ // inspEvent.actorRef !== actorRef ||
279
+ // inspEvent.type !== '@xstate.snapshot'
280
+ // ) {
281
+ // return;
282
+ // }
283
+
284
+ // const observationInput = {
285
+ // event: inspEvent.event,
286
+ // prevState,
287
+ // state: inspEvent.snapshot as any,
288
+ // machine: (actorRef as any).src,
289
+ // } satisfies AgentObservationInput;
290
+
291
+ // await handleObservation(observationInput);
292
+ // },
293
+ // });
294
+
295
+ // // If actor already started, interact with current state
296
+ // if ((actorRef as any)._processingStatus === 1) {
297
+ // handleObservation({
298
+ // prevState: undefined,
299
+ // event: { type: '' }, // TODO: unknown events?
300
+ // state: actorRef.getSnapshot(),
301
+ // machine: (actorRef as any).src,
302
+ // });
303
+ // }
304
+
305
+ // return {
306
+ // unsubscribe: () => {
307
+ // sub.unsubscribe();
308
+ // subscribed = false;
309
+ // },
310
+ // };
311
+ // }) as typeof agent.interact;
312
+
313
+ // agent.observe = (actorRef) => {
314
+ // let prevState: ObservedState = actorRef.getSnapshot();
315
+
316
+ // const sub = actorRef.system.inspect({
317
+ // next: async (inspEvent) => {
318
+ // if (
319
+ // inspEvent.actorRef !== actorRef ||
320
+ // inspEvent.type !== '@xstate.snapshot'
321
+ // ) {
322
+ // return;
323
+ // }
324
+
325
+ // const observationInput = {
326
+ // event: inspEvent.event,
327
+ // prevState,
328
+ // state: inspEvent.snapshot as any,
329
+ // machine: (actorRef as any).src,
330
+ // } satisfies AgentObservationInput;
331
+
332
+ // prevState = observationInput.state;
160
333
 
161
- agent.decide = (opts) => {
162
- return agentDecide(agent, opts);
334
+ // agent.addObservation(observationInput);
335
+ // },
336
+ // });
337
+
338
+ // return sub;
339
+ // };
340
+
341
+ // agent.types = {} as any;
342
+
343
+ // agent.wrap = (modelToWrap) =>
344
+ // experimental_wrapLanguageModel({
345
+ // model: modelToWrap,
346
+ // middleware: createAgentMiddleware(agent),
347
+ // });
348
+
349
+ // agent.model = experimental_wrapLanguageModel({
350
+ // model,
351
+ // middleware: createAgentMiddleware(agent),
352
+ // });
353
+
354
+ // agent.start();
355
+
356
+ // return agent;
357
+ }
358
+
359
+ export class Agent<
360
+ const TContextSchema extends ZodContextMapping,
361
+ const TEventSchemas extends ZodEventMapping,
362
+ TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
363
+ TContext = ContextFromZodContextMapping<TContextSchema>
364
+ > extends Actor<AgentLogic<TEvents>> {
365
+ /**
366
+ * The name of the agent. All agents with the same name are related and
367
+ * able to share experiences (observations, feedback) with each other.
368
+ */
369
+ public name?: string;
370
+ /**
371
+ * The unique identifier for the agent.
372
+ */
373
+ public episodeId: string;
374
+ public description?: string;
375
+ public events: TEventSchemas;
376
+ public context?: TContextSchema;
377
+ public planner?: AgentPlanner<Agent<TContextSchema, TEventSchemas>>;
378
+ public types: {
379
+ events: TEvents;
380
+ context: Compute<TContext>;
163
381
  };
382
+ public model: LanguageModel;
383
+ public memory: AgentLongTermMemory | undefined;
384
+ public defaultOptions: any; // todo
385
+
386
+ constructor({
387
+ logic = agentLogic as AgentLogic<TEvents>,
388
+ id,
389
+ name,
390
+ description,
391
+ model,
392
+ events,
393
+ context,
394
+ planner = simplePlanner,
395
+ }: {
396
+ logic: AgentLogic<TEvents>;
397
+ id?: string;
398
+ name?: string;
399
+ description?: string;
400
+ model: GenerateTextOptions['model'];
401
+ events: TEventSchemas;
402
+ context?: TContextSchema;
403
+ planner?: AgentPlanner<Agent<TContextSchema, TEventSchemas>>;
404
+ }) {
405
+ super(logic);
406
+ this.model = model;
407
+ this.episodeId = id ?? randomId();
408
+ this.name = name;
409
+ this.description = description;
410
+ this.events = events;
411
+ this.context = context;
412
+ this.planner = planner;
413
+ this.types = {} as any;
164
414
 
165
- agent.addMessage = (messageInput) => {
415
+ this.start();
416
+ }
417
+
418
+ /**
419
+ * Called whenever the agent (LLM assistant) receives or sends a message.
420
+ */
421
+ public onMessage(fn: (message: AgentMessage) => void) {
422
+ return this.on('message', (ev) => fn(ev.message));
423
+ }
424
+
425
+ /**
426
+ * Retrieves messages from the agent's short-term (local) memory.
427
+ */
428
+ public addMessage(messageInput: AgentMessageInput) {
166
429
  const message = {
167
430
  ...messageInput,
168
431
  id: messageInput.id ?? randomId(),
169
432
  timestamp: messageInput.timestamp ?? Date.now(),
170
- sessionId: agent.sessionId,
171
- correlationId: messageInput.correlationId ?? randomId(),
433
+ episodeId: this.episodeId,
172
434
  } satisfies AgentMessage;
173
- agent.send({
435
+ this.send({
174
436
  type: 'agent.message',
175
437
  message,
176
438
  });
177
439
 
178
440
  return message;
179
- };
180
- agent.getMessages = () => agent.getSnapshot().context.messages;
181
-
182
- agent.generateText = (opts) => agentGenerateText(agent, opts);
441
+ }
183
442
 
184
- agent.streamText = (opts) => agentStreamText(agent, opts);
443
+ public getMessages() {
444
+ return this.getSnapshot().context.messages;
445
+ }
185
446
 
186
- agent.addFeedback = (feedbackInput) => {
447
+ public addFeedback(feedbackInput: AgentFeedbackInput) {
187
448
  const feedback = {
188
449
  ...feedbackInput,
189
450
  attributes: { ...feedbackInput.attributes },
190
451
  reward: feedbackInput.reward ?? 0,
191
452
  timestamp: feedbackInput.timestamp ?? Date.now(),
192
- sessionId: agent.sessionId,
453
+ episodeId: this.episodeId,
193
454
  } satisfies AgentFeedback;
194
- agent.send({
455
+ this.send({
195
456
  type: 'agent.feedback',
196
457
  feedback,
197
458
  });
198
459
  return feedback;
199
- };
200
- agent.getFeedback = () => agent.getSnapshot().context.feedback;
460
+ }
461
+
462
+ /**
463
+ * Retrieves feedback from the agent's short-term (local) memory.
464
+ */
465
+ public getFeedback() {
466
+ return this.getSnapshot().context.feedback;
467
+ }
201
468
 
202
- agent.addObservation = (observationInput) => {
469
+ public addObservation(
470
+ observationInput: AgentObservationInput
471
+ ): AgentObservation<any> {
203
472
  const { prevState, event, state } = observationInput;
204
473
  const observation = {
205
474
  prevState,
206
475
  event,
207
476
  state,
208
477
  id: observationInput.id ?? randomId(),
209
- sessionId: agent.sessionId,
478
+ episodeId: this.episodeId,
210
479
  timestamp: observationInput.timestamp ?? Date.now(),
211
480
  machineHash: observationInput.machine
212
481
  ? getMachineHash(observationInput.machine)
213
482
  : undefined,
214
483
  } satisfies AgentObservation<any>;
215
484
 
216
- agent.send({
485
+ this.send({
217
486
  type: 'agent.observe',
218
487
  observation,
219
488
  });
220
489
 
221
490
  return observation;
222
- };
223
- agent.getObservations = () => agent.getSnapshot().context.observations;
491
+ }
492
+
493
+ /**
494
+ * Retrieves observations from the agent's short-term (local) memory.
495
+ */
496
+ public getObservations() {
497
+ return this.getSnapshot().context.observations;
498
+ }
224
499
 
225
- agent.addPlan = (plan) => {
226
- agent.send({
500
+ public addPlan(plan: AgentPlan<TEvents>) {
501
+ this.send({
227
502
  type: 'agent.plan',
228
503
  plan,
229
504
  });
230
- };
231
- agent.getPlans = () => agent.getSnapshot().context.plans;
505
+ }
506
+ /**
507
+ * Retrieves strategies from the agent's short-term (local) memory.
508
+ */
509
+ public getPlans() {
510
+ return this.getSnapshot().context.plans;
511
+ }
512
+
513
+ /**
514
+ * Interacts with this state machine actor by inspecting state transitions and storing them as observations.
515
+ *
516
+ * Observations contain the `prevState`, `event`, and current `state` of this
517
+ * actor, as well as other properties that are useful when recalled.
518
+ * These observations are stored in the `agent`'s short-term (local) memory
519
+ * and can be retrieved via `agent.getObservations()`.
520
+ *
521
+ * @example
522
+ * ```ts
523
+ * // Only observes the actor's state transitions
524
+ * agent.interact(actor);
525
+ *
526
+ * actor.start();
527
+ * ```
528
+ */
529
+ public interact<TActor extends ActorRefLike>(actorRef: TActor): Subscription;
530
+ /**
531
+ * Interacts with this state machine actor by:
532
+ * 1. Inspecting state transitions and storing them as observations
533
+ * 2. Deciding what to do next (which event to send the actor) based on
534
+ * the agent input returned from `getInput(observation)`, if `getInput(…)` is provided as the 2nd argument.
535
+ *
536
+ * Observations contain the `prevState`, `event`, and current `state` of this
537
+ * actor, as well as other properties that are useful when recalled.
538
+ * These observations are stored in the `agent`'s short-term (local) memory
539
+ * and can be retrieved via `agent.getObservations()`.
540
+ *
541
+ * @example
542
+ * ```ts
543
+ * // Observes the actor's state transitions and
544
+ * // makes a decision if on the "summarize" state
545
+ * agent.interact(actor, observed => {
546
+ * if (observed.state.matches('summarize')) {
547
+ * return {
548
+ * context: observed.state.context,
549
+ * goal: 'Summarize the message'
550
+ * }
551
+ * }
552
+ * });
553
+ *
554
+ * actor.start();
555
+ * ```
556
+ */
557
+ public interact<TActor extends ActorRefLike>(
558
+ actorRef: TActor,
559
+ getInput: (
560
+ observation: AgentObservation<TActor>
561
+ ) => AgentDecisionInput | undefined
562
+ ): Subscription;
563
+ public interact<TActor extends ActorRefLike>(
564
+ actorRef: TActor,
565
+ getInput?: (
566
+ observation: AgentObservation<TActor>
567
+ ) => AgentDecisionInput | undefined
568
+ ): Subscription {
569
+ const actorRefCheck = isActorRef(actorRef);
232
570
 
233
- agent.interact = ((actorRef, getInput) => {
234
571
  let prevState: ObservedState | undefined = undefined;
235
572
  let subscribed = true;
236
573
 
574
+ const agent = this;
575
+
237
576
  async function handleObservation(observationInput: AgentObservationInput) {
238
577
  const observation = agent.addObservation(observationInput);
239
578
 
@@ -241,7 +580,9 @@ export function createAgent<
241
580
 
242
581
  if (input) {
243
582
  await agentDecide(agent, {
244
- machine: actorRef.src as AnyStateMachine,
583
+ machine: actorRefCheck
584
+ ? (actorRef.src as AnyStateMachine)
585
+ : undefined,
245
586
  state: observation.state,
246
587
  execute: async (event) => {
247
588
  actorRef.send(event);
@@ -254,26 +595,28 @@ export function createAgent<
254
595
  }
255
596
 
256
597
  // Inspect system, but only observe specified actor
257
- actorRef.system.inspect({
258
- next: async (inspEvent) => {
259
- if (
260
- !subscribed ||
261
- inspEvent.actorRef !== actorRef ||
262
- inspEvent.type !== '@xstate.snapshot'
263
- ) {
264
- return;
265
- }
266
-
267
- const observationInput = {
268
- event: inspEvent.event,
269
- prevState,
270
- state: inspEvent.snapshot as any,
271
- machine: (actorRef as any).src,
272
- } satisfies AgentObservationInput;
273
-
274
- await handleObservation(observationInput);
275
- },
276
- });
598
+ const sub = actorRefCheck
599
+ ? actorRef.system.inspect({
600
+ next: async (inspEvent) => {
601
+ if (
602
+ !subscribed ||
603
+ inspEvent.actorRef !== actorRef ||
604
+ inspEvent.type !== '@xstate.snapshot'
605
+ ) {
606
+ return;
607
+ }
608
+
609
+ const observationInput = {
610
+ event: inspEvent.event,
611
+ prevState,
612
+ state: inspEvent.snapshot as any,
613
+ machine: (actorRef as any).src,
614
+ } satisfies AgentObservationInput;
615
+
616
+ await handleObservation(observationInput);
617
+ },
618
+ })
619
+ : undefined;
277
620
 
278
621
  // If actor already started, interact with current state
279
622
  if ((actorRef as any)._processingStatus === 1) {
@@ -287,14 +630,59 @@ export function createAgent<
287
630
 
288
631
  return {
289
632
  unsubscribe: () => {
633
+ sub?.unsubscribe();
290
634
  subscribed = false;
291
- }, // TODO: make this actually unsubscribe
635
+ },
292
636
  };
293
- }) as typeof agent.interact;
637
+ }
638
+
639
+ public observe<TActor extends ActorRefLike>(actorRef: TActor): Subscription {
640
+ let prevState: ObservedState = actorRef.getSnapshot();
641
+ const actorRefCheck = isActorRef(actorRef);
642
+
643
+ const sub = actorRefCheck
644
+ ? actorRef.system.inspect({
645
+ next: async (inspEvent) => {
646
+ if (
647
+ inspEvent.actorRef !== actorRef ||
648
+ inspEvent.type !== '@xstate.snapshot'
649
+ ) {
650
+ return;
651
+ }
652
+
653
+ const observationInput = {
654
+ event: inspEvent.event,
655
+ prevState,
656
+ state: inspEvent.snapshot as any,
657
+ machine: (actorRef as any).src,
658
+ } satisfies AgentObservationInput;
659
+
660
+ prevState = observationInput.state;
294
661
 
295
- agent.types = {} as any;
662
+ this.addObservation(observationInput);
663
+ },
664
+ })
665
+ : undefined;
666
+
667
+ return sub ?? { unsubscribe: () => {} };
668
+ }
296
669
 
297
- agent.start();
670
+ public wrap(modelToWrap: LanguageModelV1) {
671
+ return experimental_wrapLanguageModel({
672
+ model: modelToWrap,
673
+ middleware: createAgentMiddleware(this),
674
+ });
675
+ }
298
676
 
299
- return agent;
677
+ /**
678
+ * Resolves with an `AgentPlan` based on the information provided in the `options`, including:
679
+ *
680
+ * - The `goal` for the agent to achieve
681
+ * - The observed current `state`
682
+ * - The `machine` (e.g. a state machine) that specifies what can happen next
683
+ * - Additional `context`
684
+ */
685
+ public decide(opts: AgentDecideOptions) {
686
+ return agentDecide(this, opts);
687
+ }
300
688
  }