@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/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  // src/agent.ts
2
2
  import {
3
- createActor,
3
+ Actor,
4
4
  fromTransition
5
5
  } from "xstate";
6
6
 
7
7
  // src/planners/simplePlanner.ts
8
- import { tool } from "ai";
8
+ import { generateText as generateText2 } from "ai";
9
9
 
10
10
  // src/utils.ts
11
11
  import hash from "object-hash";
@@ -43,10 +43,10 @@ function getAllMachineTransitions(stateNode) {
43
43
  function wrapInXml(tagName, content) {
44
44
  return `<${tagName}>${content}</${tagName}>`;
45
45
  }
46
- function randomId() {
46
+ function randomId(prefix) {
47
47
  const timestamp = Date.now().toString(36);
48
48
  const random = Math.random().toString(36).substring(2, 9);
49
- return timestamp + random;
49
+ return `${prefix || ""}${timestamp}${random}`;
50
50
  }
51
51
  var machineHashes = /* @__PURE__ */ new WeakMap();
52
52
  function getMachineHash(machine) {
@@ -56,6 +56,23 @@ function getMachineHash(machine) {
56
56
  machineHashes.set(machine, machineHash);
57
57
  return machineHash;
58
58
  }
59
+ function isActorRef(actorRefLike) {
60
+ return "src" in actorRefLike && "system" in actorRefLike && "sessionId" in actorRefLike;
61
+ }
62
+ function getTransitions(state, machine) {
63
+ if (!machine) {
64
+ return [];
65
+ }
66
+ const resolvedState = machine.resolveState({
67
+ ...state,
68
+ // Need this property defined to make TS happy
69
+ context: state.context
70
+ });
71
+ return getAllTransitions(resolvedState);
72
+ }
73
+
74
+ // src/planners/simplePlanner.ts
75
+ import { getNextSnapshot } from "xstate";
59
76
 
60
77
  // src/templates/defaultText.ts
61
78
  var defaultTextTemplate = (data) => {
@@ -70,6 +87,10 @@ ${data.goal}
70
87
  };
71
88
 
72
89
  // src/text.ts
90
+ import {
91
+ generateText,
92
+ streamText
93
+ } from "ai";
73
94
  import {
74
95
  fromObservable,
75
96
  fromPromise,
@@ -88,120 +109,26 @@ async function getMessages(agent, prompt, options) {
88
109
  });
89
110
  return messages;
90
111
  }
91
- async function agentGenerateText(agent, options) {
92
- const resolvedOptions = {
93
- ...agent.defaultOptions,
94
- ...options,
95
- correlationId: options.correlationId ?? randomId()
96
- };
97
- const template = resolvedOptions.template ?? defaultTextTemplate;
98
- const id = randomId();
99
- const goal = typeof resolvedOptions.prompt === "string" ? resolvedOptions.prompt : await resolvedOptions.prompt(agent);
100
- const promptWithContext = template({
101
- goal,
102
- context: resolvedOptions.context
103
- });
104
- const messages = await getMessages(agent, promptWithContext, resolvedOptions);
105
- agent.addMessage({
106
- id,
107
- role: "user",
108
- content: promptWithContext,
109
- timestamp: Date.now(),
110
- correlationId: resolvedOptions.correlationId,
111
- parentCorrelationId: resolvedOptions.parentCorrelationId
112
- });
113
- const result = await agent.adapter.generateText({
114
- ...resolvedOptions,
115
- prompt: void 0,
116
- messages
117
- });
118
- agent.addMessage({
119
- content: result.text,
120
- id,
121
- role: "assistant",
122
- timestamp: Date.now(),
123
- responseId: id,
124
- result,
125
- correlationId: resolvedOptions.correlationId,
126
- parentCorrelationId: resolvedOptions.parentCorrelationId
127
- });
128
- return {
129
- ...result,
130
- parentCorrelationId: resolvedOptions.parentCorrelationId,
131
- correlationId: resolvedOptions.correlationId
132
- };
133
- }
134
- async function agentStreamText(agent, options) {
135
- const resolvedOptions = {
136
- ...agent.defaultOptions,
137
- ...options,
138
- correlationId: options.correlationId ?? randomId()
139
- };
140
- const template = resolvedOptions.template ?? defaultTextTemplate;
141
- const id = randomId();
142
- const goal = typeof resolvedOptions.prompt === "string" ? resolvedOptions.prompt : await resolvedOptions.prompt(agent);
143
- const promptWithContext = template({
144
- goal,
145
- context: resolvedOptions.context
146
- });
147
- const messages = await getMessages(agent, promptWithContext, resolvedOptions);
148
- agent.addMessage({
149
- role: "user",
150
- content: promptWithContext,
151
- id,
152
- timestamp: Date.now(),
153
- correlationId: resolvedOptions.correlationId,
154
- parentCorrelationId: resolvedOptions.parentCorrelationId
155
- });
156
- const result = await agent.adapter.streamText({
157
- ...resolvedOptions,
158
- prompt: void 0,
159
- messages,
160
- onFinish: async (res) => {
161
- agent.addMessage({
162
- role: "assistant",
163
- result: {
164
- text: res.text,
165
- finishReason: res.finishReason,
166
- logprobs: void 0,
167
- responseMessages: [],
168
- toolCalls: [],
169
- toolResults: [],
170
- usage: res.usage,
171
- warnings: res.warnings,
172
- rawResponse: res.rawResponse,
173
- roundtrips: [],
174
- // TODO: how do we get this information?,
175
- steps: res.steps,
176
- response: res.response,
177
- experimental_providerMetadata: res.experimental_providerMetadata
178
- },
179
- content: res.text,
180
- id: randomId(),
181
- timestamp: Date.now(),
182
- responseId: id,
183
- correlationId: resolvedOptions.correlationId,
184
- parentCorrelationId: resolvedOptions.parentCorrelationId
185
- });
186
- }
187
- });
188
- return {
189
- ...result,
190
- textStream: result.textStream,
191
- fullStream: result.fullStream,
192
- parentCorrelationId: resolvedOptions.parentCorrelationId,
193
- correlationId: resolvedOptions.correlationId
194
- };
195
- }
196
- function fromTextStream(agent, defaultOptions) {
112
+ function fromTextStream(agent, options) {
113
+ const template = options?.template ?? defaultTextTemplate;
197
114
  return fromObservable(({ input }) => {
198
115
  const observers = /* @__PURE__ */ new Set();
199
116
  (async () => {
200
- const result = await agentStreamText(agent, {
201
- ...defaultOptions,
202
- ...input,
117
+ const model = input.model ? agent.wrap(input.model) : agent.model;
118
+ const goal = typeof input.prompt === "string" ? input.prompt : await input.prompt(agent);
119
+ const promptWithContext = template({
120
+ goal,
203
121
  context: input.context
204
122
  });
123
+ const messages = await getMessages(agent, promptWithContext, input);
124
+ const result = await streamText({
125
+ ...options,
126
+ ...input,
127
+ prompt: void 0,
128
+ // overwritten by messages
129
+ model,
130
+ messages
131
+ });
205
132
  for await (const part of result.fullStream) {
206
133
  if (part.type === "text-delta") {
207
134
  observers.forEach((observer) => {
@@ -223,32 +150,89 @@ function fromTextStream(agent, defaultOptions) {
223
150
  };
224
151
  });
225
152
  }
226
- function fromText(agent, defaultOptions) {
153
+ function fromText(agent, options) {
154
+ const resolvedOptions = {
155
+ ...agent.defaultOptions,
156
+ ...options
157
+ };
158
+ const template = resolvedOptions.template ?? defaultTextTemplate;
227
159
  return fromPromise(async ({ input }) => {
228
- return await agentGenerateText(agent, {
229
- ...input,
230
- ...defaultOptions,
160
+ const goal = typeof input.prompt === "string" ? input.prompt : await input.prompt(agent);
161
+ const promptWithContext = template({
162
+ goal,
231
163
  context: input.context
232
164
  });
165
+ const messages = await getMessages(agent, promptWithContext, input);
166
+ const model = input.model ? agent.wrap(input.model) : agent.model;
167
+ return await generateText({
168
+ ...input,
169
+ ...options,
170
+ prompt: void 0,
171
+ messages,
172
+ model
173
+ });
233
174
  });
234
175
  }
235
176
 
236
- // src/planners/simplePlanner.ts
237
- function getTransitions(state, machine) {
238
- if (!machine) {
239
- return [];
177
+ // src/decide.ts
178
+ import { fromPromise as fromPromise2 } from "xstate";
179
+ import { tool } from "ai";
180
+ async function agentDecide(agent, options) {
181
+ const resolvedOptions = {
182
+ ...agent.defaultOptions,
183
+ ...options
184
+ };
185
+ const {
186
+ planner = simplePlanner,
187
+ goal,
188
+ events = agent.events,
189
+ state,
190
+ machine,
191
+ model = agent.model,
192
+ ...otherPlanInput
193
+ } = resolvedOptions;
194
+ const plan = await planner(agent, {
195
+ model,
196
+ goal,
197
+ events,
198
+ state,
199
+ machine,
200
+ ...otherPlanInput
201
+ });
202
+ if (plan?.nextEvent) {
203
+ agent.addPlan(plan);
204
+ await resolvedOptions.execute?.(plan.nextEvent);
240
205
  }
241
- const resolvedState = machine.resolveState(state);
242
- return getAllTransitions(resolvedState);
206
+ return plan;
243
207
  }
244
- var simplePlannerPromptTemplate = (data) => {
245
- return `
246
- ${defaultTextTemplate(data)}
247
-
248
- 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.
249
- `.trim();
250
- };
251
- async function simplePlanner(agent, input) {
208
+ function fromDecision(agent, defaultInput) {
209
+ return fromPromise2(async ({ input, self }) => {
210
+ const parentRef = self._parent;
211
+ if (!parentRef) {
212
+ return;
213
+ }
214
+ const snapshot = parentRef.getSnapshot();
215
+ const inputObject = typeof input === "string" ? { goal: input } : input;
216
+ const resolvedInput = {
217
+ ...defaultInput,
218
+ ...inputObject
219
+ };
220
+ const state = {
221
+ value: snapshot.value,
222
+ context: resolvedInput.context
223
+ };
224
+ const plan = await agentDecide(agent, {
225
+ machine: parentRef.logic,
226
+ state,
227
+ execute: async (event) => {
228
+ parentRef.send(event);
229
+ },
230
+ ...resolvedInput
231
+ });
232
+ return plan;
233
+ });
234
+ }
235
+ function getToolMap(_agent, input) {
252
236
  const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(input.events).map(([eventType, { description }]) => ({
253
237
  eventType,
254
238
  description
@@ -288,17 +272,55 @@ async function simplePlanner(agent, input) {
288
272
  if (!Object.keys(toolMap).length) {
289
273
  return void 0;
290
274
  }
275
+ return toolMap;
276
+ }
277
+
278
+ // src/planners/simplePlanner.ts
279
+ var simplePlannerPromptTemplate = (data) => {
280
+ return `
281
+ ${defaultTextTemplate(data)}
282
+
283
+ 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.
284
+ `.trim();
285
+ };
286
+ async function simplePlanner(agent, input) {
287
+ const toolMap = getToolMap(agent, input);
288
+ if (!toolMap) {
289
+ return void 0;
290
+ }
291
291
  const prompt = simplePlannerPromptTemplate({
292
292
  context: input.state.context,
293
293
  goal: input.goal
294
294
  });
295
295
  const messages = await getMessages(agent, prompt, input);
296
- const result = await agent.generateText({
297
- toolChoice: "required",
298
- ...input,
299
- prompt,
296
+ const model = input.model ? agent.wrap(input.model) : agent.model;
297
+ const {
298
+ state,
299
+ machine,
300
+ previousPlan,
301
+ events,
302
+ goal,
303
+ model: _,
304
+ ...rest
305
+ } = input;
306
+ const machineState = input.machine ? input.machine.resolveState({
307
+ ...input.state,
308
+ context: input.state.context
309
+ }) : void 0;
310
+ const result = await generateText2({
311
+ ...rest,
312
+ model,
300
313
  messages,
301
- tools: toolMap
314
+ tools: toolMap,
315
+ toolChoice: input.toolChoice ?? "required"
316
+ });
317
+ result.responseMessages.forEach((m) => {
318
+ const message = m;
319
+ agent.addMessage({
320
+ ...message,
321
+ id: randomId(),
322
+ timestamp: Date.now()
323
+ });
302
324
  });
303
325
  const singleResult = result.toolResults[0];
304
326
  if (!singleResult) {
@@ -306,89 +328,100 @@ async function simplePlanner(agent, input) {
306
328
  return void 0;
307
329
  }
308
330
  return {
331
+ planner: "simple",
309
332
  goal: input.goal,
310
- state: input.state,
311
- execute: async (state) => {
312
- if (JSON.stringify(state) === JSON.stringify(input.state)) {
313
- return singleResult.result;
314
- }
315
- return void 0;
316
- },
333
+ goalState: input.state,
317
334
  nextEvent: singleResult.result,
318
- sessionId: agent.sessionId,
319
- timestamp: Date.now()
335
+ episodeId: agent.episodeId,
336
+ timestamp: Date.now(),
337
+ paths: [
338
+ {
339
+ state: void 0,
340
+ steps: [
341
+ {
342
+ event: singleResult.result,
343
+ state: machine && machineState ? getNextSnapshot(machine, machineState, singleResult.result) : void 0
344
+ }
345
+ ]
346
+ }
347
+ ]
320
348
  };
321
349
  }
322
350
 
323
- // src/decision.ts
324
- import { fromPromise as fromPromise2 } from "xstate";
325
- async function agentDecide(agent, options) {
326
- const resolvedOptions = {
327
- ...agent.defaultOptions,
328
- ...options
329
- };
330
- const {
331
- planner = simplePlanner,
332
- goal,
333
- events = agent.events,
334
- state,
335
- machine,
336
- model = agent.model,
337
- ...otherPlanInput
338
- } = resolvedOptions;
339
- const plan = await planner(agent, {
340
- model,
341
- goal,
342
- events,
343
- state,
344
- machine,
345
- ...otherPlanInput
346
- });
347
- if (plan?.nextEvent) {
348
- agent.addPlan(plan);
349
- await resolvedOptions.execute?.(plan.nextEvent);
350
- }
351
- return plan;
352
- }
353
- function fromDecision(agent, defaultInput) {
354
- return fromPromise2(async ({ input, self }) => {
355
- const parentRef = self._parent;
356
- if (!parentRef) {
357
- return;
351
+ // src/agent.ts
352
+ import {
353
+ experimental_wrapLanguageModel
354
+ } from "ai";
355
+
356
+ // src/middleware.ts
357
+ function createAgentMiddleware(agent) {
358
+ const middleware = {
359
+ transformParams: async ({ params }) => {
360
+ return params;
361
+ },
362
+ wrapGenerate: async ({ doGenerate, params }) => {
363
+ const id = randomId();
364
+ params.prompt.forEach((p) => {
365
+ agent.addMessage({
366
+ id,
367
+ ...p,
368
+ timestamp: Date.now(),
369
+ correlationId: params.providerMetadata?.correlationId,
370
+ parentCorrelationId: params.providerMetadata?.parentCorrelationId
371
+ });
372
+ });
373
+ const result = await doGenerate();
374
+ return result;
375
+ },
376
+ wrapStream: async ({ doStream, params }) => {
377
+ const id = randomId();
378
+ params.prompt.forEach((message) => {
379
+ message.content;
380
+ agent.addMessage({
381
+ id,
382
+ ...message,
383
+ timestamp: Date.now(),
384
+ correlationId: params.providerMetadata?.correlationId,
385
+ parentCorrelationId: params.providerMetadata?.parentCorrelationId
386
+ });
387
+ });
388
+ const { stream, ...rest } = await doStream();
389
+ let generatedText = "";
390
+ const transformStream = new TransformStream({
391
+ transform(chunk, controller) {
392
+ if (chunk.type === "text-delta") {
393
+ generatedText += chunk.textDelta;
394
+ }
395
+ controller.enqueue(chunk);
396
+ },
397
+ flush() {
398
+ const content = [];
399
+ if (generatedText) {
400
+ content.push({
401
+ type: "text",
402
+ text: generatedText
403
+ });
404
+ }
405
+ agent.addMessage({
406
+ id: randomId(),
407
+ timestamp: Date.now(),
408
+ role: "assistant",
409
+ content,
410
+ responseId: id,
411
+ correlationId: params.providerMetadata?.correlationId,
412
+ parentCorrelationId: params.providerMetadata?.parentCorrelationId
413
+ });
414
+ }
415
+ });
416
+ return {
417
+ stream: stream.pipeThrough(transformStream),
418
+ ...rest
419
+ };
358
420
  }
359
- const snapshot = parentRef.getSnapshot();
360
- const inputObject = typeof input === "string" ? { goal: input } : input;
361
- const resolvedInput = {
362
- ...defaultInput,
363
- ...inputObject
364
- };
365
- const contextToInclude = resolvedInput.context === true ? (
366
- // include entire context
367
- parentRef.getSnapshot().context
368
- ) : resolvedInput.context;
369
- const state = {
370
- value: snapshot.value,
371
- context: contextToInclude
372
- };
373
- const plan = await agentDecide(agent, {
374
- machine: parentRef.logic,
375
- state,
376
- execute: async (event) => {
377
- parentRef.send(event);
378
- },
379
- ...resolvedInput
380
- });
381
- return plan;
382
- });
421
+ };
422
+ return middleware;
383
423
  }
384
424
 
385
- // src/adapters/vercel.ts
386
- import { generateText, streamText } from "ai";
387
- var vercelAdapter = {
388
- generateText,
389
- streamText
390
- };
391
-
392
425
  // src/agent.ts
393
426
  var agentLogic = fromTransition(
394
427
  (state, event, { emit }) => {
@@ -442,6 +475,7 @@ var agentLogic = fromTransition(
442
475
  })
443
476
  );
444
477
  function createAgent({
478
+ id,
445
479
  name,
446
480
  description,
447
481
  model,
@@ -451,92 +485,133 @@ function createAgent({
451
485
  stringify = JSON.stringify,
452
486
  getMemory,
453
487
  logic = agentLogic,
454
- adapter = vercelAdapter,
455
488
  ...generateTextOptions
456
489
  }) {
457
- const agent = createActor(logic);
458
- agent.events = events;
459
- agent.model = model;
460
- agent.name = name;
461
- agent.description = description;
462
- agent.adapter = adapter;
463
- agent.defaultOptions = { ...generateTextOptions, model };
464
- agent.select = (selector) => {
465
- return selector(agent.getSnapshot().context);
466
- };
467
- agent.memory = getMemory ? getMemory(agent) : void 0;
468
- agent.onMessage = (callback) => {
469
- agent.on("message", (ev) => callback(ev.message));
470
- };
471
- agent.decide = (opts) => {
472
- return agentDecide(agent, opts);
473
- };
474
- agent.addMessage = (messageInput) => {
490
+ return new Agent({
491
+ id,
492
+ context,
493
+ events,
494
+ name,
495
+ description,
496
+ planner,
497
+ model,
498
+ logic
499
+ });
500
+ }
501
+ var Agent = class extends Actor {
502
+ // todo
503
+ constructor({
504
+ logic = agentLogic,
505
+ id,
506
+ name,
507
+ description,
508
+ model,
509
+ events,
510
+ context,
511
+ planner = simplePlanner
512
+ }) {
513
+ super(logic);
514
+ this.model = model;
515
+ this.episodeId = id ?? randomId();
516
+ this.name = name;
517
+ this.description = description;
518
+ this.events = events;
519
+ this.context = context;
520
+ this.planner = planner;
521
+ this.types = {};
522
+ this.start();
523
+ }
524
+ /**
525
+ * Called whenever the agent (LLM assistant) receives or sends a message.
526
+ */
527
+ onMessage(fn) {
528
+ return this.on("message", (ev) => fn(ev.message));
529
+ }
530
+ /**
531
+ * Retrieves messages from the agent's short-term (local) memory.
532
+ */
533
+ addMessage(messageInput) {
475
534
  const message = {
476
535
  ...messageInput,
477
536
  id: messageInput.id ?? randomId(),
478
537
  timestamp: messageInput.timestamp ?? Date.now(),
479
- sessionId: agent.sessionId,
480
- correlationId: messageInput.correlationId ?? randomId()
538
+ episodeId: this.episodeId
481
539
  };
482
- agent.send({
540
+ this.send({
483
541
  type: "agent.message",
484
542
  message
485
543
  });
486
544
  return message;
487
- };
488
- agent.getMessages = () => agent.getSnapshot().context.messages;
489
- agent.generateText = (opts) => agentGenerateText(agent, opts);
490
- agent.streamText = (opts) => agentStreamText(agent, opts);
491
- agent.addFeedback = (feedbackInput) => {
545
+ }
546
+ getMessages() {
547
+ return this.getSnapshot().context.messages;
548
+ }
549
+ addFeedback(feedbackInput) {
492
550
  const feedback = {
493
551
  ...feedbackInput,
494
552
  attributes: { ...feedbackInput.attributes },
495
553
  reward: feedbackInput.reward ?? 0,
496
554
  timestamp: feedbackInput.timestamp ?? Date.now(),
497
- sessionId: agent.sessionId
555
+ episodeId: this.episodeId
498
556
  };
499
- agent.send({
557
+ this.send({
500
558
  type: "agent.feedback",
501
559
  feedback
502
560
  });
503
561
  return feedback;
504
- };
505
- agent.getFeedback = () => agent.getSnapshot().context.feedback;
506
- agent.addObservation = (observationInput) => {
562
+ }
563
+ /**
564
+ * Retrieves feedback from the agent's short-term (local) memory.
565
+ */
566
+ getFeedback() {
567
+ return this.getSnapshot().context.feedback;
568
+ }
569
+ addObservation(observationInput) {
507
570
  const { prevState, event, state } = observationInput;
508
571
  const observation = {
509
572
  prevState,
510
573
  event,
511
574
  state,
512
575
  id: observationInput.id ?? randomId(),
513
- sessionId: agent.sessionId,
576
+ episodeId: this.episodeId,
514
577
  timestamp: observationInput.timestamp ?? Date.now(),
515
578
  machineHash: observationInput.machine ? getMachineHash(observationInput.machine) : void 0
516
579
  };
517
- agent.send({
580
+ this.send({
518
581
  type: "agent.observe",
519
582
  observation
520
583
  });
521
584
  return observation;
522
- };
523
- agent.getObservations = () => agent.getSnapshot().context.observations;
524
- agent.addPlan = (plan) => {
525
- agent.send({
585
+ }
586
+ /**
587
+ * Retrieves observations from the agent's short-term (local) memory.
588
+ */
589
+ getObservations() {
590
+ return this.getSnapshot().context.observations;
591
+ }
592
+ addPlan(plan) {
593
+ this.send({
526
594
  type: "agent.plan",
527
595
  plan
528
596
  });
529
- };
530
- agent.getPlans = () => agent.getSnapshot().context.plans;
531
- agent.interact = (actorRef, getInput) => {
597
+ }
598
+ /**
599
+ * Retrieves strategies from the agent's short-term (local) memory.
600
+ */
601
+ getPlans() {
602
+ return this.getSnapshot().context.plans;
603
+ }
604
+ interact(actorRef, getInput) {
605
+ const actorRefCheck = isActorRef(actorRef);
532
606
  let prevState = void 0;
533
607
  let subscribed = true;
608
+ const agent = this;
534
609
  async function handleObservation(observationInput) {
535
610
  const observation = agent.addObservation(observationInput);
536
611
  const input = getInput?.(observation);
537
612
  if (input) {
538
613
  await agentDecide(agent, {
539
- machine: actorRef.src,
614
+ machine: actorRefCheck ? actorRef.src : void 0,
540
615
  state: observation.state,
541
616
  execute: async (event) => {
542
617
  actorRef.send(event);
@@ -546,7 +621,7 @@ function createAgent({
546
621
  }
547
622
  prevState = observationInput.state;
548
623
  }
549
- actorRef.system.inspect({
624
+ const sub = actorRefCheck ? actorRef.system.inspect({
550
625
  next: async (inspEvent) => {
551
626
  if (!subscribed || inspEvent.actorRef !== actorRef || inspEvent.type !== "@xstate.snapshot") {
552
627
  return;
@@ -559,7 +634,7 @@ function createAgent({
559
634
  };
560
635
  await handleObservation(observationInput);
561
636
  }
562
- });
637
+ }) : void 0;
563
638
  if (actorRef._processingStatus === 1) {
564
639
  handleObservation({
565
640
  prevState: void 0,
@@ -571,15 +646,50 @@ function createAgent({
571
646
  }
572
647
  return {
573
648
  unsubscribe: () => {
649
+ sub?.unsubscribe();
574
650
  subscribed = false;
575
651
  }
576
- // TODO: make this actually unsubscribe
577
652
  };
578
- };
579
- agent.types = {};
580
- agent.start();
581
- return agent;
582
- }
653
+ }
654
+ observe(actorRef) {
655
+ let prevState = actorRef.getSnapshot();
656
+ const actorRefCheck = isActorRef(actorRef);
657
+ const sub = actorRefCheck ? actorRef.system.inspect({
658
+ next: async (inspEvent) => {
659
+ if (inspEvent.actorRef !== actorRef || inspEvent.type !== "@xstate.snapshot") {
660
+ return;
661
+ }
662
+ const observationInput = {
663
+ event: inspEvent.event,
664
+ prevState,
665
+ state: inspEvent.snapshot,
666
+ machine: actorRef.src
667
+ };
668
+ prevState = observationInput.state;
669
+ this.addObservation(observationInput);
670
+ }
671
+ }) : void 0;
672
+ return sub ?? { unsubscribe: () => {
673
+ } };
674
+ }
675
+ wrap(modelToWrap) {
676
+ return experimental_wrapLanguageModel({
677
+ model: modelToWrap,
678
+ middleware: createAgentMiddleware(this)
679
+ });
680
+ }
681
+ /**
682
+ * Resolves with an `AgentPlan` based on the information provided in the `options`, including:
683
+ *
684
+ * - The `goal` for the agent to achieve
685
+ * - The observed current `state`
686
+ * - The `machine` (e.g. a state machine) that specifies what can happen next
687
+ * - Additional `context`
688
+ */
689
+ decide(opts) {
690
+ return agentDecide(this, opts);
691
+ }
692
+ };
583
693
  export {
584
694
  createAgent,
585
695
  fromDecision,