@statelyai/agent 1.1.6 → 2.0.0-next.1

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 (62) hide show
  1. package/.changeset/cyan-carpets-perform.md +5 -0
  2. package/.changeset/fast-donkeys-argue.md +5 -0
  3. package/.changeset/light-hats-drive.md +9 -0
  4. package/.changeset/old-jobs-check.md +5 -0
  5. package/.changeset/pre.json +13 -0
  6. package/.vscode/launch.json +6 -0
  7. package/CHANGELOG.md +22 -0
  8. package/dist/index.d.mts +262 -171
  9. package/dist/index.d.ts +262 -171
  10. package/dist/index.js +383 -274
  11. package/dist/index.mjs +386 -272
  12. package/examples/chatbot-alt.ts +57 -0
  13. package/examples/chatbot.ts +12 -17
  14. package/examples/cot.ts +26 -23
  15. package/examples/customer-service-sim.ts +107 -0
  16. package/examples/email.ts +37 -41
  17. package/examples/example.ts +6 -6
  18. package/examples/executor.ts +66 -0
  19. package/examples/goal.ts +12 -12
  20. package/examples/helpers/helpers.ts +26 -14
  21. package/examples/joke.ts +79 -76
  22. package/examples/jugs.ts +125 -0
  23. package/examples/multi.ts +5 -5
  24. package/examples/newspaper.ts +98 -104
  25. package/examples/number.ts +6 -5
  26. package/examples/raffle.ts +11 -12
  27. package/examples/river-crossing.ts +140 -0
  28. package/examples/sandbox.ts +1 -1
  29. package/examples/simple.ts +5 -3
  30. package/examples/summary.ts +121 -0
  31. package/examples/support.ts +6 -6
  32. package/examples/ticTacToe.ts +86 -45
  33. package/examples/todo.ts +7 -7
  34. package/examples/tutor.ts +15 -15
  35. package/examples/verify.ts +3 -3
  36. package/examples/weather.ts +6 -9
  37. package/examples/wiki.ts +27 -8
  38. package/examples/word.ts +16 -11
  39. package/package.json +16 -11
  40. package/readme.md +1 -1
  41. package/src/agent-experimental.ts +1 -1
  42. package/src/agent.test.ts +243 -214
  43. package/src/agent.ts +286 -95
  44. package/src/decide.test.ts +276 -0
  45. package/src/decide.ts +163 -0
  46. package/src/index.ts +1 -1
  47. package/src/middleware.ts +91 -0
  48. package/src/mockModel.ts +47 -0
  49. package/src/planners/shortestPath.test.ts +94 -0
  50. package/src/planners/shortestPath.ts +177 -0
  51. package/src/planners/simple.ts +105 -0
  52. package/src/strategies/chain-of-note.ts +6 -55
  53. package/src/text.ts +51 -144
  54. package/src/types.ts +187 -212
  55. package/src/utils.ts +48 -4
  56. package/vitest.config.ts +9 -3
  57. package/src/adapters/vercel.ts +0 -7
  58. package/src/decision.test.ts +0 -179
  59. package/src/decision.ts +0 -84
  60. package/src/memory.ts +0 -25
  61. package/src/planners/shortestPathPlanner.ts +0 -22
  62. package/src/planners/simplePlanner.ts +0 -139
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
- // src/planners/simplePlanner.ts
8
- import { tool } from "ai";
7
+ // src/planners/simple.ts
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,26 @@ 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
+ function isMachineActor(actor) {
74
+ return "src" in actor && typeof actor.src === "object" && actor.src !== null && "definition" in actor.src;
75
+ }
76
+
77
+ // src/planners/simple.ts
78
+ import { getNextSnapshot } from "xstate";
59
79
 
60
80
  // src/templates/defaultText.ts
61
81
  var defaultTextTemplate = (data) => {
@@ -70,6 +90,10 @@ ${data.goal}
70
90
  };
71
91
 
72
92
  // src/text.ts
93
+ import {
94
+ generateText,
95
+ streamText
96
+ } from "ai";
73
97
  import {
74
98
  fromObservable,
75
99
  fromPromise,
@@ -88,120 +112,26 @@ async function getMessages(agent, prompt, options) {
88
112
  });
89
113
  return messages;
90
114
  }
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) {
115
+ function fromTextStream(agent, options) {
116
+ const template = options?.template ?? defaultTextTemplate;
197
117
  return fromObservable(({ input }) => {
198
118
  const observers = /* @__PURE__ */ new Set();
199
119
  (async () => {
200
- const result = await agentStreamText(agent, {
201
- ...defaultOptions,
202
- ...input,
120
+ const model = input.model ? agent.wrap(input.model) : agent.model;
121
+ const goal = typeof input.prompt === "string" ? input.prompt : await input.prompt(agent);
122
+ const promptWithContext = template({
123
+ goal,
203
124
  context: input.context
204
125
  });
126
+ const messages = await getMessages(agent, promptWithContext, input);
127
+ const result = await streamText({
128
+ ...options,
129
+ ...input,
130
+ prompt: void 0,
131
+ // overwritten by messages
132
+ model,
133
+ messages
134
+ });
205
135
  for await (const part of result.fullStream) {
206
136
  if (part.type === "text-delta") {
207
137
  observers.forEach((observer) => {
@@ -223,32 +153,97 @@ function fromTextStream(agent, defaultOptions) {
223
153
  };
224
154
  });
225
155
  }
226
- function fromText(agent, defaultOptions) {
156
+ function fromText(agent, options) {
157
+ const resolvedOptions = {
158
+ ...agent.defaultOptions,
159
+ ...options
160
+ };
161
+ const template = resolvedOptions.template ?? defaultTextTemplate;
227
162
  return fromPromise(async ({ input }) => {
228
- return await agentGenerateText(agent, {
229
- ...input,
230
- ...defaultOptions,
163
+ const goal = typeof input.prompt === "string" ? input.prompt : await input.prompt(agent);
164
+ const promptWithContext = template({
165
+ goal,
231
166
  context: input.context
232
167
  });
168
+ const messages = await getMessages(agent, promptWithContext, input);
169
+ const model = input.model ? agent.wrap(input.model) : agent.model;
170
+ return await generateText({
171
+ ...input,
172
+ ...options,
173
+ prompt: void 0,
174
+ messages,
175
+ model
176
+ });
233
177
  });
234
178
  }
235
179
 
236
- // src/planners/simplePlanner.ts
237
- function getTransitions(state, machine) {
238
- if (!machine) {
239
- return [];
180
+ // src/decide.ts
181
+ import { fromPromise as fromPromise2 } from "xstate";
182
+ import { tool } from "ai";
183
+ async function agentDecide(agent, options) {
184
+ const resolvedOptions = {
185
+ ...agent.defaultOptions,
186
+ ...options
187
+ };
188
+ const {
189
+ planner = simplePlanner,
190
+ goal,
191
+ events = agent.events,
192
+ state,
193
+ machine,
194
+ model = agent.model,
195
+ messages,
196
+ ...otherPlanInput
197
+ } = resolvedOptions;
198
+ let attempts = 0;
199
+ const maxAttempts = resolvedOptions.maxAttempts ?? 2;
200
+ let plan;
201
+ while (attempts++ < maxAttempts) {
202
+ plan = await planner(agent, {
203
+ model,
204
+ goal,
205
+ events,
206
+ state,
207
+ machine,
208
+ messages,
209
+ // TODO: fix UIMessage thing
210
+ ...otherPlanInput
211
+ });
212
+ if (plan?.nextEvent) {
213
+ agent.addPlan(plan);
214
+ await resolvedOptions.execute?.(plan.nextEvent);
215
+ }
240
216
  }
241
- const resolvedState = machine.resolveState(state);
242
- return getAllTransitions(resolvedState);
217
+ return plan;
243
218
  }
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) {
219
+ function fromDecision(agent, defaultInput) {
220
+ return fromPromise2(async ({ input, self }) => {
221
+ const parentRef = self._parent;
222
+ if (!parentRef) {
223
+ return;
224
+ }
225
+ const snapshot = parentRef.getSnapshot();
226
+ const inputObject = typeof input === "string" ? { goal: input } : input;
227
+ const resolvedInput = {
228
+ ...defaultInput,
229
+ ...inputObject
230
+ };
231
+ const state = {
232
+ value: snapshot.value,
233
+ context: resolvedInput.context
234
+ };
235
+ const plan = await agentDecide(agent, {
236
+ machine: parentRef.logic,
237
+ state,
238
+ execute: async (event) => {
239
+ parentRef.send(event);
240
+ },
241
+ ...resolvedInput
242
+ });
243
+ return plan;
244
+ });
245
+ }
246
+ function getToolMap(_agent, input) {
252
247
  const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(input.events).map(([eventType, { description }]) => ({
253
248
  eventType,
254
249
  description
@@ -288,17 +283,56 @@ async function simplePlanner(agent, input) {
288
283
  if (!Object.keys(toolMap).length) {
289
284
  return void 0;
290
285
  }
286
+ return toolMap;
287
+ }
288
+
289
+ // src/planners/simple.ts
290
+ var simplePlannerPromptTemplate = (data) => {
291
+ return `
292
+ ${defaultTextTemplate(data)}
293
+
294
+ 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.
295
+ `.trim();
296
+ };
297
+ async function simplePlanner(agent, input) {
298
+ const toolMap = getToolMap(agent, input);
299
+ if (!toolMap) {
300
+ return void 0;
301
+ }
291
302
  const prompt = simplePlannerPromptTemplate({
292
303
  context: input.state.context,
293
304
  goal: input.goal
294
305
  });
295
306
  const messages = await getMessages(agent, prompt, input);
296
- const result = await agent.generateText({
297
- toolChoice: "required",
298
- ...input,
299
- prompt,
307
+ const model = input.model ? agent.wrap(input.model) : agent.model;
308
+ const {
309
+ state,
310
+ machine,
311
+ previousPlan,
312
+ events,
313
+ goal,
314
+ model: _,
315
+ ...rest
316
+ } = input;
317
+ const machineState = input.machine ? input.machine.resolveState({
318
+ ...input.state,
319
+ context: input.state.context
320
+ }) : void 0;
321
+ const result = await generateText2({
322
+ ...rest,
323
+ system: input.system ?? agent.description,
324
+ model,
300
325
  messages,
301
- tools: toolMap
326
+ tools: toolMap,
327
+ toolChoice: input.toolChoice ?? "required"
328
+ });
329
+ result.response.messages.forEach((m) => {
330
+ const message = m;
331
+ agent.addMessage({
332
+ ...message,
333
+ id: randomId(),
334
+ timestamp: Date.now()
335
+ });
302
336
  });
303
337
  const singleResult = result.toolResults[0];
304
338
  if (!singleResult) {
@@ -306,89 +340,94 @@ async function simplePlanner(agent, input) {
306
340
  return void 0;
307
341
  }
308
342
  return {
343
+ planner: "simple",
309
344
  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
- },
345
+ goalState: input.state,
317
346
  nextEvent: singleResult.result,
318
- sessionId: agent.sessionId,
319
- timestamp: Date.now()
347
+ episodeId: agent.episodeId,
348
+ timestamp: Date.now(),
349
+ paths: [
350
+ {
351
+ state: void 0,
352
+ steps: [
353
+ {
354
+ event: singleResult.result,
355
+ state: machine && machineState ? getNextSnapshot(machine, machineState, singleResult.result) : void 0
356
+ }
357
+ ]
358
+ }
359
+ ]
320
360
  };
321
361
  }
322
362
 
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;
363
+ // src/agent.ts
364
+ import {
365
+ experimental_wrapLanguageModel
366
+ } from "ai";
367
+
368
+ // src/middleware.ts
369
+ function createAgentMiddleware(agent) {
370
+ const middleware = {
371
+ transformParams: async ({ params }) => {
372
+ return params;
373
+ },
374
+ wrapGenerate: async ({ doGenerate, params }) => {
375
+ const id = randomId();
376
+ params.prompt.forEach((message) => {
377
+ agent.addMessage({
378
+ id,
379
+ ...message,
380
+ timestamp: Date.now()
381
+ });
382
+ });
383
+ const result = await doGenerate();
384
+ return result;
385
+ },
386
+ wrapStream: async ({ doStream, params }) => {
387
+ const id = randomId();
388
+ params.prompt.forEach((message) => {
389
+ message.content;
390
+ agent.addMessage({
391
+ id,
392
+ ...message,
393
+ timestamp: Date.now()
394
+ });
395
+ });
396
+ const { stream, ...rest } = await doStream();
397
+ let generatedText = "";
398
+ const transformStream = new TransformStream({
399
+ transform(chunk, controller) {
400
+ if (chunk.type === "text-delta") {
401
+ generatedText += chunk.textDelta;
402
+ }
403
+ controller.enqueue(chunk);
404
+ },
405
+ flush() {
406
+ const content = [];
407
+ if (generatedText) {
408
+ content.push({
409
+ type: "text",
410
+ text: generatedText
411
+ });
412
+ }
413
+ agent.addMessage({
414
+ id: randomId(),
415
+ timestamp: Date.now(),
416
+ role: "assistant",
417
+ content,
418
+ responseId: id
419
+ });
420
+ }
421
+ });
422
+ return {
423
+ stream: stream.pipeThrough(transformStream),
424
+ ...rest
425
+ };
358
426
  }
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
- });
427
+ };
428
+ return middleware;
383
429
  }
384
430
 
385
- // src/adapters/vercel.ts
386
- import { generateText, streamText } from "ai";
387
- var vercelAdapter = {
388
- generateText,
389
- streamText
390
- };
391
-
392
431
  // src/agent.ts
393
432
  var agentLogic = fromTransition(
394
433
  (state, event, { emit }) => {
@@ -429,8 +468,10 @@ var agentLogic = fromTransition(
429
468
  });
430
469
  break;
431
470
  }
432
- default:
471
+ default: {
472
+ console.warn("Unrecognized event", event);
433
473
  break;
474
+ }
434
475
  }
435
476
  return state;
436
477
  },
@@ -442,111 +483,149 @@ var agentLogic = fromTransition(
442
483
  })
443
484
  );
444
485
  function createAgent({
445
- name,
486
+ id,
446
487
  description,
447
488
  model,
448
489
  events,
449
490
  context,
450
491
  planner = simplePlanner,
451
- stringify = JSON.stringify,
452
- getMemory,
453
- logic = agentLogic,
454
- adapter = vercelAdapter,
455
- ...generateTextOptions
492
+ logic = agentLogic
456
493
  }) {
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) => {
494
+ return new Agent({
495
+ id,
496
+ context,
497
+ events,
498
+ description,
499
+ planner,
500
+ model,
501
+ logic
502
+ });
503
+ }
504
+ var Agent = class extends Actor {
505
+ // todo
506
+ constructor({
507
+ logic = agentLogic,
508
+ id,
509
+ name,
510
+ description,
511
+ model,
512
+ events,
513
+ context,
514
+ planner = simplePlanner
515
+ }) {
516
+ super(logic);
517
+ this.model = model;
518
+ this.episodeId = id ?? randomId();
519
+ this.name = name;
520
+ this.description = description;
521
+ this.events = events;
522
+ this.context = context;
523
+ this.planner = planner;
524
+ this.types = {};
525
+ this.start();
526
+ }
527
+ /**
528
+ * Called whenever the agent (LLM assistant) receives or sends a message.
529
+ */
530
+ onMessage(fn) {
531
+ return this.on("message", (ev) => fn(ev.message));
532
+ }
533
+ /**
534
+ * Retrieves messages from the agent's short-term (local) memory.
535
+ */
536
+ addMessage(messageInput) {
475
537
  const message = {
476
538
  ...messageInput,
477
539
  id: messageInput.id ?? randomId(),
478
540
  timestamp: messageInput.timestamp ?? Date.now(),
479
- sessionId: agent.sessionId,
480
- correlationId: messageInput.correlationId ?? randomId()
541
+ episodeId: this.episodeId
481
542
  };
482
- agent.send({
543
+ this.send({
483
544
  type: "agent.message",
484
545
  message
485
546
  });
486
547
  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) => {
548
+ }
549
+ getMessages() {
550
+ return this.getSnapshot().context.messages;
551
+ }
552
+ addFeedback(feedbackInput) {
492
553
  const feedback = {
493
554
  ...feedbackInput,
494
555
  attributes: { ...feedbackInput.attributes },
495
556
  reward: feedbackInput.reward ?? 0,
496
557
  timestamp: feedbackInput.timestamp ?? Date.now(),
497
- sessionId: agent.sessionId
558
+ episodeId: this.episodeId
498
559
  };
499
- agent.send({
560
+ this.send({
500
561
  type: "agent.feedback",
501
562
  feedback
502
563
  });
503
564
  return feedback;
504
- };
505
- agent.getFeedback = () => agent.getSnapshot().context.feedback;
506
- agent.addObservation = (observationInput) => {
565
+ }
566
+ /**
567
+ * Retrieves feedback from the agent's short-term (local) memory.
568
+ */
569
+ getFeedback() {
570
+ return this.getSnapshot().context.feedback;
571
+ }
572
+ addObservation(observationInput) {
507
573
  const { prevState, event, state } = observationInput;
508
574
  const observation = {
509
575
  prevState,
510
576
  event,
511
577
  state,
512
578
  id: observationInput.id ?? randomId(),
513
- sessionId: agent.sessionId,
579
+ episodeId: this.episodeId,
514
580
  timestamp: observationInput.timestamp ?? Date.now(),
515
581
  machineHash: observationInput.machine ? getMachineHash(observationInput.machine) : void 0
516
582
  };
517
- agent.send({
583
+ this.send({
518
584
  type: "agent.observe",
519
585
  observation
520
586
  });
521
587
  return observation;
522
- };
523
- agent.getObservations = () => agent.getSnapshot().context.observations;
524
- agent.addPlan = (plan) => {
525
- agent.send({
588
+ }
589
+ /**
590
+ * Retrieves observations from the agent's short-term (local) memory.
591
+ */
592
+ getObservations() {
593
+ return this.getSnapshot().context.observations;
594
+ }
595
+ addPlan(plan) {
596
+ this.send({
526
597
  type: "agent.plan",
527
598
  plan
528
599
  });
529
- };
530
- agent.getPlans = () => agent.getSnapshot().context.plans;
531
- agent.interact = (actorRef, getInput) => {
600
+ }
601
+ /**
602
+ * Retrieves strategies from the agent's short-term (local) memory.
603
+ */
604
+ getPlans() {
605
+ return this.getSnapshot().context.plans;
606
+ }
607
+ interact(actorRef, getInput) {
608
+ const actorRefCheck = isActorRef(actorRef) && actorRef.src;
609
+ const machine = isMachineActor(actorRef) ? actorRef.src : void 0;
532
610
  let prevState = void 0;
533
611
  let subscribed = true;
612
+ const agent = this;
534
613
  async function handleObservation(observationInput) {
535
614
  const observation = agent.addObservation(observationInput);
536
615
  const input = getInput?.(observation);
537
616
  if (input) {
538
- await agentDecide(agent, {
539
- machine: actorRef.src,
617
+ const res = await agentDecide(agent, {
618
+ machine,
540
619
  state: observation.state,
541
- execute: async (event) => {
542
- actorRef.send(event);
543
- },
544
620
  ...input
545
621
  });
622
+ if (res?.nextEvent) {
623
+ actorRef.send(res.nextEvent);
624
+ }
546
625
  }
547
626
  prevState = observationInput.state;
548
627
  }
549
- actorRef.system.inspect({
628
+ const sub = actorRefCheck ? actorRef.system.inspect({
550
629
  next: async (inspEvent) => {
551
630
  if (!subscribed || inspEvent.actorRef !== actorRef || inspEvent.type !== "@xstate.snapshot") {
552
631
  return;
@@ -559,7 +638,7 @@ function createAgent({
559
638
  };
560
639
  await handleObservation(observationInput);
561
640
  }
562
- });
641
+ }) : void 0;
563
642
  if (actorRef._processingStatus === 1) {
564
643
  handleObservation({
565
644
  prevState: void 0,
@@ -571,15 +650,50 @@ function createAgent({
571
650
  }
572
651
  return {
573
652
  unsubscribe: () => {
653
+ sub?.unsubscribe();
574
654
  subscribed = false;
575
655
  }
576
- // TODO: make this actually unsubscribe
577
656
  };
578
- };
579
- agent.types = {};
580
- agent.start();
581
- return agent;
582
- }
657
+ }
658
+ observe(actorRef) {
659
+ let prevState = actorRef.getSnapshot();
660
+ const actorRefCheck = isActorRef(actorRef);
661
+ const sub = actorRefCheck ? actorRef.system.inspect({
662
+ next: async (inspEvent) => {
663
+ if (inspEvent.actorRef !== actorRef || inspEvent.type !== "@xstate.snapshot") {
664
+ return;
665
+ }
666
+ const observationInput = {
667
+ event: inspEvent.event,
668
+ prevState,
669
+ state: inspEvent.snapshot,
670
+ machine: actorRef.src
671
+ };
672
+ prevState = observationInput.state;
673
+ this.addObservation(observationInput);
674
+ }
675
+ }) : void 0;
676
+ return sub ?? { unsubscribe: () => {
677
+ } };
678
+ }
679
+ wrap(modelToWrap) {
680
+ return experimental_wrapLanguageModel({
681
+ model: modelToWrap,
682
+ middleware: createAgentMiddleware(this)
683
+ });
684
+ }
685
+ /**
686
+ * Resolves with an `AgentPlan` based on the information provided in the `options`, including:
687
+ *
688
+ * - The `goal` for the agent to achieve
689
+ * - The observed current `state`
690
+ * - The `machine` (e.g. a state machine) that specifies what can happen next
691
+ * - Additional `context`
692
+ */
693
+ decide(opts) {
694
+ return agentDecide(this, opts);
695
+ }
696
+ };
583
697
  export {
584
698
  createAgent,
585
699
  fromDecision,