@statelyai/agent 2.0.0-next.3 → 2.0.0-next.4

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.js CHANGED
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(src_exports);
40
40
  // src/agent.ts
41
41
  var import_xstate4 = require("xstate");
42
42
 
43
- // src/strategies/simple.ts
43
+ // src/strategies/simpleStrategy.ts
44
44
  var import_ai3 = require("ai");
45
45
 
46
46
  // src/utils.ts
@@ -60,22 +60,6 @@ function getAllTransitions(state) {
60
60
  }).flat(2);
61
61
  return transitions;
62
62
  }
63
- function getAllMachineTransitions(stateNode) {
64
- const transitions = [...stateNode.transitions.values()].map((nodeTransitions) => {
65
- return nodeTransitions.map((transition) => {
66
- return {
67
- ...transition,
68
- guard: typeof transition.guard === "string" ? { type: transition.guard } : transition.guard
69
- // TODO: fix
70
- };
71
- });
72
- }).flat(2);
73
- for (const s of Object.values(stateNode.states)) {
74
- const stateTransitions = getAllMachineTransitions(s);
75
- transitions.push(...stateTransitions);
76
- }
77
- return transitions;
78
- }
79
63
  function wrapInXml(tagName, content) {
80
64
  return `<${tagName}>${content}</${tagName}>`;
81
65
  }
@@ -93,14 +77,6 @@ function randomId(prefix) {
93
77
  const random = Math.random().toString(36).substring(2, 9);
94
78
  return `${prefix || ""}${timestamp}${random}`;
95
79
  }
96
- var machineHashes = /* @__PURE__ */ new WeakMap();
97
- function getMachineHash(machine) {
98
- if (machineHashes.has(machine)) return machineHashes.get(machine);
99
- const transitions = getAllMachineTransitions(machine.root);
100
- const machineHash = (0, import_object_hash.default)(transitions);
101
- machineHashes.set(machine, machineHash);
102
- return machineHash;
103
- }
104
80
  function isActorRef(actorRefLike) {
105
81
  return "src" in actorRefLike && "system" in actorRefLike && "sessionId" in actorRefLike;
106
82
  }
@@ -119,7 +95,7 @@ function isMachineActor(actor) {
119
95
  return "src" in actor && typeof actor.src === "object" && actor.src !== null && "definition" in actor.src;
120
96
  }
121
97
 
122
- // src/strategies/simple.ts
98
+ // src/strategies/simpleStrategy.ts
123
99
  var import_xstate3 = require("xstate");
124
100
 
125
101
  // src/text.ts
@@ -196,7 +172,6 @@ function fromTextStream(agent, options) {
196
172
  }
197
173
  function fromText(agent, options) {
198
174
  const resolvedOptions = {
199
- ...agent.defaultOptions,
200
175
  ...options
201
176
  };
202
177
  const template = resolvedOptions.template ?? defaultTextTemplate;
@@ -222,10 +197,7 @@ function fromText(agent, options) {
222
197
  var import_xstate2 = require("xstate");
223
198
  var import_ai2 = require("ai");
224
199
  async function agentDecide(agent, options) {
225
- const resolvedOptions = {
226
- ...agent.defaultOptions,
227
- ...options
228
- };
200
+ const resolvedOptions = options;
229
201
  const {
230
202
  strategy = agent.strategy,
231
203
  goal,
@@ -235,6 +207,8 @@ async function agentDecide(agent, options) {
235
207
  machine,
236
208
  model = agent.model,
237
209
  messages,
210
+ episodeId = agent.episodeId,
211
+ maxAttempts = 2,
238
212
  ...otherDecideInput
239
213
  } = resolvedOptions;
240
214
  const filteredEventSchemas = allowedEvents ? Object.fromEntries(
@@ -243,7 +217,6 @@ async function agentDecide(agent, options) {
243
217
  })
244
218
  ) : events;
245
219
  let attempts = 0;
246
- const maxAttempts = resolvedOptions.maxAttempts ?? 2;
247
220
  let decision;
248
221
  const minimalState = {
249
222
  value: state.value,
@@ -251,6 +224,7 @@ async function agentDecide(agent, options) {
251
224
  };
252
225
  while (attempts++ < maxAttempts) {
253
226
  decision = await strategy(agent, {
227
+ episodeId,
254
228
  model,
255
229
  goal,
256
230
  events: filteredEventSchemas,
@@ -262,7 +236,6 @@ async function agentDecide(agent, options) {
262
236
  });
263
237
  if (decision?.nextEvent) {
264
238
  agent.addDecision(decision);
265
- await resolvedOptions.execute?.(decision.nextEvent);
266
239
  break;
267
240
  }
268
241
  }
@@ -283,22 +256,23 @@ function fromDecision(agent, defaultInput) {
283
256
  const decision = await agentDecide(agent, {
284
257
  machine: parentRef.logic,
285
258
  state: snapshot,
286
- execute: async (event) => {
287
- parentRef.send(event);
288
- },
289
259
  ...resolvedInput,
290
260
  // @ts-ignore
291
261
  messages: resolvedInput.messages
292
262
  });
263
+ if (decision?.nextEvent) {
264
+ parentRef.send(decision.nextEvent);
265
+ }
293
266
  return decision;
294
267
  });
295
268
  }
296
- function getToolMap(_agent, input) {
297
- const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(input.events).map(([eventType, { description }]) => ({
269
+ function getToolMap(agent, input) {
270
+ const events = input.events ?? agent.events;
271
+ const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(events).map(([eventType, { description }]) => ({
298
272
  eventType,
299
273
  description
300
274
  }));
301
- const filter = (eventType) => Object.keys(input.events).includes(eventType);
275
+ const filter = (eventType) => Object.keys(events).includes(eventType);
302
276
  const functionNameMapping = {};
303
277
  const toolTransitions = transitions.filter((t) => {
304
278
  return filter(t.eventType);
@@ -336,7 +310,7 @@ function getToolMap(_agent, input) {
336
310
  return toolMap;
337
311
  }
338
312
 
339
- // src/strategies/simple.ts
313
+ // src/strategies/simpleStrategy.ts
340
314
  var simpleStrategyPromptTemplate = (data) => {
341
315
  return `
342
316
  ${convertToXml(data)}
@@ -388,7 +362,7 @@ async function simpleStrategy(agent, input) {
388
362
  goal: input.goal,
389
363
  goalState: input.state,
390
364
  nextEvent: singleResult.result,
391
- episodeId: agent.episodeId,
365
+ episodeId: input.episodeId ?? agent.episodeId,
392
366
  timestamp: Date.now(),
393
367
  paths: [
394
368
  {
@@ -545,7 +519,6 @@ function createAgent({
545
519
  });
546
520
  }
547
521
  var Agent = class extends import_xstate4.Actor {
548
- // todo
549
522
  constructor({
550
523
  logic = agentLogic,
551
524
  id,
@@ -605,7 +578,7 @@ var Agent = class extends import_xstate4.Actor {
605
578
  comment: feedbackInput.comment ?? void 0,
606
579
  attributes: { ...feedbackInput.attributes },
607
580
  timestamp: feedbackInput.timestamp ?? Date.now(),
608
- episodeId: this.episodeId
581
+ episodeId: feedbackInput.episodeId ?? this.episodeId
609
582
  };
610
583
  this.send({
611
584
  type: "agent.feedback",
@@ -626,9 +599,12 @@ var Agent = class extends import_xstate4.Actor {
626
599
  event,
627
600
  state,
628
601
  id: observationInput.id ?? randomId(),
629
- episodeId: this.episodeId,
602
+ episodeId: observationInput.episodeId ?? this.episodeId,
630
603
  timestamp: observationInput.timestamp ?? Date.now(),
631
- machineHash: observationInput.machine ? getMachineHash(observationInput.machine) : void 0
604
+ decisionId: observationInput.decisionId
605
+ // machineHash: observationInput.machine
606
+ // ? getMachineHash(observationInput.machine)
607
+ // : undefined,
632
608
  };
633
609
  this.send({
634
610
  type: "agent.observe",
@@ -687,19 +663,18 @@ var Agent = class extends import_xstate4.Actor {
687
663
  event: inspEvent.event,
688
664
  prevState,
689
665
  state: inspEvent.snapshot,
690
- machine: actorRef.src,
691
- goal: decision?.goal
666
+ goal: decision?.goal,
667
+ decisionId
692
668
  };
693
669
  await handleObservation(observationInput);
694
670
  }
695
671
  }) : void 0;
696
672
  if (actorRef._processingStatus === 1) {
697
673
  handleObservation({
674
+ decisionId: void 0,
698
675
  prevState: void 0,
699
676
  event: void 0,
700
- state: actorRef.getSnapshot(),
701
- machine: actorRef.src,
702
- goal: void 0
677
+ state: actorRef.getSnapshot()
703
678
  });
704
679
  }
705
680
  return {
@@ -720,10 +695,10 @@ var Agent = class extends import_xstate4.Actor {
720
695
  const decisionId = inspEvent.event["_decision"];
721
696
  const decision = decisionId ? this.getDecisions().find((d) => d.id === decisionId) : void 0;
722
697
  const observationInput = {
698
+ decisionId,
723
699
  event: inspEvent.event,
724
700
  prevState,
725
701
  state: inspEvent.snapshot,
726
- machine: actorRef.src,
727
702
  goal: decision?.goal
728
703
  };
729
704
  prevState = observationInput.state;
@@ -747,8 +722,8 @@ var Agent = class extends import_xstate4.Actor {
747
722
  * - The `machine` (e.g. a state machine) that specifies what can happen next
748
723
  * - Additional `context`
749
724
  */
750
- async decide(opts) {
751
- return agentDecide(this, opts);
725
+ async decide(input) {
726
+ return agentDecide(this, input);
752
727
  }
753
728
  };
754
729
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  fromTransition
5
5
  } from "xstate";
6
6
 
7
- // src/strategies/simple.ts
7
+ // src/strategies/simpleStrategy.ts
8
8
  import { generateText as generateText2 } from "ai";
9
9
 
10
10
  // src/utils.ts
@@ -24,22 +24,6 @@ function getAllTransitions(state) {
24
24
  }).flat(2);
25
25
  return transitions;
26
26
  }
27
- function getAllMachineTransitions(stateNode) {
28
- const transitions = [...stateNode.transitions.values()].map((nodeTransitions) => {
29
- return nodeTransitions.map((transition) => {
30
- return {
31
- ...transition,
32
- guard: typeof transition.guard === "string" ? { type: transition.guard } : transition.guard
33
- // TODO: fix
34
- };
35
- });
36
- }).flat(2);
37
- for (const s of Object.values(stateNode.states)) {
38
- const stateTransitions = getAllMachineTransitions(s);
39
- transitions.push(...stateTransitions);
40
- }
41
- return transitions;
42
- }
43
27
  function wrapInXml(tagName, content) {
44
28
  return `<${tagName}>${content}</${tagName}>`;
45
29
  }
@@ -57,14 +41,6 @@ function randomId(prefix) {
57
41
  const random = Math.random().toString(36).substring(2, 9);
58
42
  return `${prefix || ""}${timestamp}${random}`;
59
43
  }
60
- var machineHashes = /* @__PURE__ */ new WeakMap();
61
- function getMachineHash(machine) {
62
- if (machineHashes.has(machine)) return machineHashes.get(machine);
63
- const transitions = getAllMachineTransitions(machine.root);
64
- const machineHash = hash(transitions);
65
- machineHashes.set(machine, machineHash);
66
- return machineHash;
67
- }
68
44
  function isActorRef(actorRefLike) {
69
45
  return "src" in actorRefLike && "system" in actorRefLike && "sessionId" in actorRefLike;
70
46
  }
@@ -83,7 +59,7 @@ function isMachineActor(actor) {
83
59
  return "src" in actor && typeof actor.src === "object" && actor.src !== null && "definition" in actor.src;
84
60
  }
85
61
 
86
- // src/strategies/simple.ts
62
+ // src/strategies/simpleStrategy.ts
87
63
  import { getNextSnapshot } from "xstate";
88
64
 
89
65
  // src/text.ts
@@ -167,7 +143,6 @@ function fromTextStream(agent, options) {
167
143
  }
168
144
  function fromText(agent, options) {
169
145
  const resolvedOptions = {
170
- ...agent.defaultOptions,
171
146
  ...options
172
147
  };
173
148
  const template = resolvedOptions.template ?? defaultTextTemplate;
@@ -193,10 +168,7 @@ function fromText(agent, options) {
193
168
  import { fromPromise as fromPromise2 } from "xstate";
194
169
  import { tool } from "ai";
195
170
  async function agentDecide(agent, options) {
196
- const resolvedOptions = {
197
- ...agent.defaultOptions,
198
- ...options
199
- };
171
+ const resolvedOptions = options;
200
172
  const {
201
173
  strategy = agent.strategy,
202
174
  goal,
@@ -206,6 +178,8 @@ async function agentDecide(agent, options) {
206
178
  machine,
207
179
  model = agent.model,
208
180
  messages,
181
+ episodeId = agent.episodeId,
182
+ maxAttempts = 2,
209
183
  ...otherDecideInput
210
184
  } = resolvedOptions;
211
185
  const filteredEventSchemas = allowedEvents ? Object.fromEntries(
@@ -214,7 +188,6 @@ async function agentDecide(agent, options) {
214
188
  })
215
189
  ) : events;
216
190
  let attempts = 0;
217
- const maxAttempts = resolvedOptions.maxAttempts ?? 2;
218
191
  let decision;
219
192
  const minimalState = {
220
193
  value: state.value,
@@ -222,6 +195,7 @@ async function agentDecide(agent, options) {
222
195
  };
223
196
  while (attempts++ < maxAttempts) {
224
197
  decision = await strategy(agent, {
198
+ episodeId,
225
199
  model,
226
200
  goal,
227
201
  events: filteredEventSchemas,
@@ -233,7 +207,6 @@ async function agentDecide(agent, options) {
233
207
  });
234
208
  if (decision?.nextEvent) {
235
209
  agent.addDecision(decision);
236
- await resolvedOptions.execute?.(decision.nextEvent);
237
210
  break;
238
211
  }
239
212
  }
@@ -254,22 +227,23 @@ function fromDecision(agent, defaultInput) {
254
227
  const decision = await agentDecide(agent, {
255
228
  machine: parentRef.logic,
256
229
  state: snapshot,
257
- execute: async (event) => {
258
- parentRef.send(event);
259
- },
260
230
  ...resolvedInput,
261
231
  // @ts-ignore
262
232
  messages: resolvedInput.messages
263
233
  });
234
+ if (decision?.nextEvent) {
235
+ parentRef.send(decision.nextEvent);
236
+ }
264
237
  return decision;
265
238
  });
266
239
  }
267
- function getToolMap(_agent, input) {
268
- const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(input.events).map(([eventType, { description }]) => ({
240
+ function getToolMap(agent, input) {
241
+ const events = input.events ?? agent.events;
242
+ const transitions = input.machine ? getTransitions(input.state, input.machine) : Object.entries(events).map(([eventType, { description }]) => ({
269
243
  eventType,
270
244
  description
271
245
  }));
272
- const filter = (eventType) => Object.keys(input.events).includes(eventType);
246
+ const filter = (eventType) => Object.keys(events).includes(eventType);
273
247
  const functionNameMapping = {};
274
248
  const toolTransitions = transitions.filter((t) => {
275
249
  return filter(t.eventType);
@@ -307,7 +281,7 @@ function getToolMap(_agent, input) {
307
281
  return toolMap;
308
282
  }
309
283
 
310
- // src/strategies/simple.ts
284
+ // src/strategies/simpleStrategy.ts
311
285
  var simpleStrategyPromptTemplate = (data) => {
312
286
  return `
313
287
  ${convertToXml(data)}
@@ -359,7 +333,7 @@ async function simpleStrategy(agent, input) {
359
333
  goal: input.goal,
360
334
  goalState: input.state,
361
335
  nextEvent: singleResult.result,
362
- episodeId: agent.episodeId,
336
+ episodeId: input.episodeId ?? agent.episodeId,
363
337
  timestamp: Date.now(),
364
338
  paths: [
365
339
  {
@@ -518,7 +492,6 @@ function createAgent({
518
492
  });
519
493
  }
520
494
  var Agent = class extends Actor {
521
- // todo
522
495
  constructor({
523
496
  logic = agentLogic,
524
497
  id,
@@ -578,7 +551,7 @@ var Agent = class extends Actor {
578
551
  comment: feedbackInput.comment ?? void 0,
579
552
  attributes: { ...feedbackInput.attributes },
580
553
  timestamp: feedbackInput.timestamp ?? Date.now(),
581
- episodeId: this.episodeId
554
+ episodeId: feedbackInput.episodeId ?? this.episodeId
582
555
  };
583
556
  this.send({
584
557
  type: "agent.feedback",
@@ -599,9 +572,12 @@ var Agent = class extends Actor {
599
572
  event,
600
573
  state,
601
574
  id: observationInput.id ?? randomId(),
602
- episodeId: this.episodeId,
575
+ episodeId: observationInput.episodeId ?? this.episodeId,
603
576
  timestamp: observationInput.timestamp ?? Date.now(),
604
- machineHash: observationInput.machine ? getMachineHash(observationInput.machine) : void 0
577
+ decisionId: observationInput.decisionId
578
+ // machineHash: observationInput.machine
579
+ // ? getMachineHash(observationInput.machine)
580
+ // : undefined,
605
581
  };
606
582
  this.send({
607
583
  type: "agent.observe",
@@ -660,19 +636,18 @@ var Agent = class extends Actor {
660
636
  event: inspEvent.event,
661
637
  prevState,
662
638
  state: inspEvent.snapshot,
663
- machine: actorRef.src,
664
- goal: decision?.goal
639
+ goal: decision?.goal,
640
+ decisionId
665
641
  };
666
642
  await handleObservation(observationInput);
667
643
  }
668
644
  }) : void 0;
669
645
  if (actorRef._processingStatus === 1) {
670
646
  handleObservation({
647
+ decisionId: void 0,
671
648
  prevState: void 0,
672
649
  event: void 0,
673
- state: actorRef.getSnapshot(),
674
- machine: actorRef.src,
675
- goal: void 0
650
+ state: actorRef.getSnapshot()
676
651
  });
677
652
  }
678
653
  return {
@@ -693,10 +668,10 @@ var Agent = class extends Actor {
693
668
  const decisionId = inspEvent.event["_decision"];
694
669
  const decision = decisionId ? this.getDecisions().find((d) => d.id === decisionId) : void 0;
695
670
  const observationInput = {
671
+ decisionId,
696
672
  event: inspEvent.event,
697
673
  prevState,
698
674
  state: inspEvent.snapshot,
699
- machine: actorRef.src,
700
675
  goal: decision?.goal
701
676
  };
702
677
  prevState = observationInput.state;
@@ -720,8 +695,8 @@ var Agent = class extends Actor {
720
695
  * - The `machine` (e.g. a state machine) that specifies what can happen next
721
696
  * - Additional `context`
722
697
  */
723
- async decide(opts) {
724
- return agentDecide(this, opts);
698
+ async decide(input) {
699
+ return agentDecide(this, input);
725
700
  }
726
701
  };
727
702
  export {
@@ -80,7 +80,7 @@ Achieve the goal. Consider both exploring unknown actions (high exploration_valu
80
80
 
81
81
  if (decision?.nextEvent?.type === 'submit') {
82
82
  const observation = await agent.addObservation({
83
- goal: decision.goal,
83
+ decisionId: decision.id,
84
84
  prevState: { value: 'editing' },
85
85
  event: { type: 'submit' },
86
86
  state: { value: 'editing' },
@@ -96,7 +96,7 @@ Achieve the goal. Consider both exploring unknown actions (high exploration_valu
96
96
  status = 'submitted';
97
97
 
98
98
  await agent.addObservation({
99
- goal: decision.goal,
99
+ decisionId: decision.id,
100
100
  prevState: { value: 'editing' },
101
101
  event: { type: 'pressEnter' },
102
102
  state: { value: 'submitted' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statelyai/agent",
3
- "version": "2.0.0-next.3",
3
+ "version": "2.0.0-next.4",
4
4
  "description": "Stateful agents that make decisions based on finite-state machine models",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/agent.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { test, expect, vi } from 'vitest';
2
- import { createAgent, TypesFromAgent } from './';
2
+ import { AgentDecision, createAgent, TypesFromAgent } from './';
3
3
  import { createActor, createMachine } from 'xstate';
4
4
  import { LanguageModelV1CallOptions } from 'ai';
5
5
  import { z } from 'zod';
@@ -66,15 +66,30 @@ test('agent.addMessage() adds to message history', () => {
66
66
  test('agent.addFeedback() adds to feedback', () => {
67
67
  const agent = createAgent({
68
68
  id: 'test',
69
- events: {},
69
+ events: {
70
+ play: z.object({
71
+ position: z.number(),
72
+ }),
73
+ },
70
74
  model: {} as any,
71
75
  });
72
76
 
77
+ const decision: AgentDecision<typeof agent> = {
78
+ goal: 'Win the game',
79
+ episodeId: agent.episodeId,
80
+ goalState: { value: 'won' },
81
+ id: 'decision-1',
82
+ nextEvent: { type: 'play', position: 3 },
83
+ paths: [],
84
+ strategy: 'simple',
85
+ timestamp: Date.now(),
86
+ };
87
+
73
88
  const obs = agent.addObservation({
89
+ decisionId: decision.id,
74
90
  prevState: { value: 'playing' },
75
- state: { value: 'lost' },
76
91
  event: { type: 'play', position: 3 },
77
- goal: 'Win the game',
92
+ state: { value: 'lost' },
78
93
  });
79
94
 
80
95
  const feedback = agent.addFeedback({
@@ -167,7 +182,6 @@ test('agent.addObservation() adds to observations with machine hash', () => {
167
182
  prevState: { value: 'playing', context: {} },
168
183
  event: { type: 'play', position: 3 },
169
184
  state: { value: 'lost', context: {} },
170
- machine,
171
185
  goal: 'Win the game',
172
186
  });
173
187
 
@@ -178,7 +192,6 @@ test('agent.addObservation() adds to observations with machine hash', () => {
178
192
  prevState: { value: 'playing', context: {} },
179
193
  event: { type: 'play', position: 3 },
180
194
  state: { value: 'lost', context: {} },
181
- machineHash: expect.any(String),
182
195
  episodeId: expect.any(String),
183
196
  timestamp: expect.any(Number),
184
197
  })
@@ -503,7 +516,6 @@ test('agent.observe() adds observations from actor snapshots', () => {
503
516
  expect(agent.getObservations()).toContainEqual(
504
517
  expect.objectContaining({
505
518
  state: expect.objectContaining({ value: 'idle' }),
506
- machineHash: expect.any(String),
507
519
  })
508
520
  );
509
521
 
@@ -512,9 +524,95 @@ test('agent.observe() adds observations from actor snapshots', () => {
512
524
  prevState: expect.objectContaining({ value: 'idle' }),
513
525
  event: { type: 'START' },
514
526
  state: expect.objectContaining({ value: 'running' }),
515
- machineHash: expect.any(String),
516
527
  })
517
528
  );
518
529
 
519
530
  subscription.unsubscribe();
520
531
  });
532
+
533
+ test('agent.addObservation() accepts custom episodeId', () => {
534
+ const agent = createAgent({
535
+ id: 'test',
536
+ events: {},
537
+ model: {} as any,
538
+ });
539
+
540
+ const customEpisodeId = 'custom-episode-123';
541
+ const observation = agent.addObservation({
542
+ state: { value: 'playing' },
543
+ goal: 'Win the game',
544
+ episodeId: customEpisodeId,
545
+ });
546
+
547
+ expect(observation.episodeId).toEqual(customEpisodeId);
548
+ expect(agent.getObservations()).toContainEqual(
549
+ expect.objectContaining({
550
+ episodeId: customEpisodeId,
551
+ })
552
+ );
553
+ });
554
+
555
+ test('agent.addFeedback() accepts custom episodeId', () => {
556
+ const agent = createAgent({
557
+ id: 'test',
558
+ events: {},
559
+ model: {} as any,
560
+ });
561
+
562
+ const customEpisodeId = 'custom-episode-123';
563
+ const feedback = agent.addFeedback({
564
+ score: 1,
565
+ observationId: 'obs-1',
566
+ episodeId: customEpisodeId,
567
+ });
568
+
569
+ expect(feedback.episodeId).toEqual(customEpisodeId);
570
+ expect(agent.getFeedback()).toContainEqual(
571
+ expect.objectContaining({
572
+ episodeId: customEpisodeId,
573
+ })
574
+ );
575
+ });
576
+
577
+ test('agent.addObservation() accepts decisionId', () => {
578
+ const agent = createAgent({
579
+ id: 'test',
580
+ events: {},
581
+ model: {} as any,
582
+ });
583
+
584
+ const decisionId = 'decision-123';
585
+ const observation = agent.addObservation({
586
+ state: { value: 'playing' },
587
+ goal: 'Win the game',
588
+ decisionId,
589
+ });
590
+
591
+ expect(observation.decisionId).toEqual(decisionId);
592
+ expect(agent.getObservations()).toContainEqual(
593
+ expect.objectContaining({
594
+ decisionId,
595
+ })
596
+ );
597
+ });
598
+
599
+ test('agent.addFeedback() accepts decisionId', () => {
600
+ const agent = createAgent({
601
+ id: 'test',
602
+ events: {},
603
+ model: {} as any,
604
+ });
605
+
606
+ const decisionId = 'decision-123';
607
+ const feedback = agent.addFeedback({
608
+ score: 1,
609
+ decisionId,
610
+ });
611
+
612
+ expect(feedback.decisionId).toEqual(decisionId);
613
+ expect(agent.getFeedback()).toContainEqual(
614
+ expect.objectContaining({
615
+ decisionId,
616
+ })
617
+ );
618
+ });