@dxos/assistant 0.8.4-main.f9ba587 → 0.8.4-main.fcfe5033a5

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 (233) hide show
  1. package/README.md +1 -0
  2. package/dist/lib/neutral/chunk-2TBLG3XY.mjs +1793 -0
  3. package/dist/lib/neutral/chunk-2TBLG3XY.mjs.map +7 -0
  4. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  5. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  6. package/dist/lib/neutral/extraction/index.mjs +219 -0
  7. package/dist/lib/neutral/extraction/index.mjs.map +7 -0
  8. package/dist/lib/neutral/index.mjs +537 -0
  9. package/dist/lib/neutral/index.mjs.map +7 -0
  10. package/dist/lib/neutral/meta.json +1 -0
  11. package/dist/lib/neutral/testing/index.mjs +111 -0
  12. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  13. package/dist/types/src/conversation/context.d.ts +155 -0
  14. package/dist/types/src/conversation/context.d.ts.map +1 -0
  15. package/dist/types/src/conversation/context.test.d.ts +2 -0
  16. package/dist/types/src/conversation/context.test.d.ts.map +1 -0
  17. package/dist/types/src/conversation/index.d.ts +2 -1
  18. package/dist/types/src/conversation/index.d.ts.map +1 -1
  19. package/dist/types/src/conversation/session.d.ts +78 -0
  20. package/dist/types/src/conversation/session.d.ts.map +1 -0
  21. package/dist/types/src/conversation/session.test.d.ts.map +1 -0
  22. package/dist/types/src/diff.d.ts +17 -0
  23. package/dist/types/src/diff.d.ts.map +1 -0
  24. package/dist/types/src/diff.test.d.ts +2 -0
  25. package/dist/types/src/diff.test.d.ts.map +1 -0
  26. package/dist/types/src/errors.d.ts +67 -0
  27. package/dist/types/src/errors.d.ts.map +1 -0
  28. package/dist/types/src/execution-graph.d.ts +85 -0
  29. package/dist/types/src/execution-graph.d.ts.map +1 -0
  30. package/dist/types/src/execution-graph.test.d.ts +2 -0
  31. package/dist/types/src/execution-graph.test.d.ts.map +1 -0
  32. package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
  33. package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
  34. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts +1 -1
  35. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -1
  36. package/dist/types/src/extraction/extraction.d.ts +1066 -215
  37. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  38. package/dist/types/src/extraction/index.d.ts +0 -1
  39. package/dist/types/src/extraction/index.d.ts.map +1 -1
  40. package/dist/types/src/extraction/quotes.d.ts +3 -3
  41. package/dist/types/src/extraction/quotes.d.ts.map +1 -1
  42. package/dist/types/src/functions/index.d.ts +2 -0
  43. package/dist/types/src/functions/index.d.ts.map +1 -0
  44. package/dist/types/src/functions/services.d.ts +17 -0
  45. package/dist/types/src/functions/services.d.ts.map +1 -0
  46. package/dist/types/src/functions.test.d.ts +2 -0
  47. package/dist/types/src/functions.test.d.ts.map +1 -0
  48. package/dist/types/src/index.d.ts +7 -5
  49. package/dist/types/src/index.d.ts.map +1 -1
  50. package/dist/types/src/service/AgentService.d.ts +75 -0
  51. package/dist/types/src/service/AgentService.d.ts.map +1 -0
  52. package/dist/types/src/service/AgentService.test.d.ts +2 -0
  53. package/dist/types/src/service/AgentService.test.d.ts.map +1 -0
  54. package/dist/types/src/service/agent-process.d.ts +21 -0
  55. package/dist/types/src/service/agent-process.d.ts.map +1 -0
  56. package/dist/types/src/service/index.d.ts +3 -0
  57. package/dist/types/src/service/index.d.ts.map +1 -0
  58. package/dist/types/src/session/artifact-diff.d.ts +27 -0
  59. package/dist/types/src/session/artifact-diff.d.ts.map +1 -0
  60. package/dist/types/src/session/error-handling.d.ts +4 -0
  61. package/dist/types/src/session/error-handling.d.ts.map +1 -0
  62. package/dist/types/src/session/format.d.ts +16 -0
  63. package/dist/types/src/session/format.d.ts.map +1 -0
  64. package/dist/types/src/session/format.test.d.ts +2 -0
  65. package/dist/types/src/session/format.test.d.ts.map +1 -0
  66. package/dist/types/src/session/index.d.ts +5 -1
  67. package/dist/types/src/session/index.d.ts.map +1 -1
  68. package/dist/types/src/session/observer.d.ts +23 -0
  69. package/dist/types/src/session/observer.d.ts.map +1 -0
  70. package/dist/types/src/session/request.d.ts +106 -0
  71. package/dist/types/src/session/request.d.ts.map +1 -0
  72. package/dist/types/src/session/request.test.d.ts +2 -0
  73. package/dist/types/src/session/request.test.d.ts.map +1 -0
  74. package/dist/types/src/session/session-ollama.test.d.ts +2 -0
  75. package/dist/types/src/session/session-ollama.test.d.ts.map +1 -0
  76. package/dist/types/src/session/toolkit.d.ts +16 -0
  77. package/dist/types/src/session/toolkit.d.ts.map +1 -0
  78. package/dist/types/src/session/version-pin.d.ts +5 -5
  79. package/dist/types/src/session/version-pin.d.ts.map +1 -1
  80. package/dist/types/src/templates/index.d.ts +2 -0
  81. package/dist/types/src/templates/index.d.ts.map +1 -0
  82. package/dist/types/src/templates/system.d.ts +17 -0
  83. package/dist/types/src/templates/system.d.ts.map +1 -0
  84. package/dist/types/src/testing/index.d.ts +1 -2
  85. package/dist/types/src/testing/index.d.ts.map +1 -1
  86. package/dist/types/src/testing/layer.d.ts +40 -0
  87. package/dist/types/src/testing/layer.d.ts.map +1 -0
  88. package/dist/types/src/tracing.d.ts +223 -0
  89. package/dist/types/src/tracing.d.ts.map +1 -0
  90. package/dist/types/src/types/artifact.d.ts +28 -0
  91. package/dist/types/src/types/artifact.d.ts.map +1 -0
  92. package/dist/types/src/types/index.d.ts +2 -0
  93. package/dist/types/src/types/index.d.ts.map +1 -0
  94. package/dist/types/tsconfig.tsbuildinfo +1 -1
  95. package/package.json +60 -29
  96. package/src/conversation/context.test.ts +52 -0
  97. package/src/conversation/context.ts +403 -0
  98. package/src/conversation/index.ts +2 -1
  99. package/src/conversation/session.test.ts +58 -0
  100. package/src/conversation/session.ts +300 -0
  101. package/src/diff.test.ts +74 -0
  102. package/src/diff.ts +93 -0
  103. package/src/errors.ts +15 -0
  104. package/src/execution-graph.test.ts +740 -0
  105. package/src/execution-graph.ts +509 -0
  106. package/src/extraction/extraction-ner-function/index.ts +0 -1
  107. package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +12 -6
  108. package/src/extraction/extraction-ner-function/named-entity-recognition.ts +3 -1
  109. package/src/extraction/extraction.ts +44 -15
  110. package/src/extraction/index.ts +0 -1
  111. package/src/extraction/quotes.test.ts +3 -3
  112. package/src/extraction/quotes.ts +6 -7
  113. package/src/functions/index.ts +5 -0
  114. package/src/functions/services.ts +214 -0
  115. package/src/functions.conversations.json +1 -0
  116. package/src/functions.test.ts +88 -0
  117. package/src/index.ts +7 -5
  118. package/src/service/AgentService.conversations.json +1 -0
  119. package/src/service/AgentService.test.ts +169 -0
  120. package/src/service/AgentService.ts +184 -0
  121. package/src/service/agent-process.ts +364 -0
  122. package/src/service/index.ts +6 -0
  123. package/src/session/artifact-diff.ts +41 -0
  124. package/src/session/error-handling.ts +41 -0
  125. package/src/session/format.test.ts +132 -0
  126. package/src/session/format.ts +149 -0
  127. package/src/session/index.ts +5 -1
  128. package/src/session/observer.ts +62 -0
  129. package/src/session/request.conversations.json +1 -0
  130. package/src/session/request.test.ts +195 -0
  131. package/src/session/request.ts +344 -0
  132. package/src/session/session-ollama.test.ts +81 -0
  133. package/src/session/toolkit.ts +47 -0
  134. package/src/session/version-pin.ts +12 -12
  135. package/src/templates/index.ts +5 -0
  136. package/src/templates/instructions/format.tpl +57 -0
  137. package/src/templates/instructions/system.tpl +85 -0
  138. package/src/templates/system.ts +42 -0
  139. package/src/testing/index.ts +2 -8
  140. package/src/testing/layer.ts +236 -0
  141. package/src/tracing.ts +43 -0
  142. package/src/types/artifact.ts +89 -0
  143. package/src/types/index.ts +5 -0
  144. package/src/typings.d.ts +5 -0
  145. package/dist/lib/browser/index.mjs +0 -2019
  146. package/dist/lib/browser/index.mjs.map +0 -7
  147. package/dist/lib/browser/meta.json +0 -1
  148. package/dist/lib/node-esm/index.mjs +0 -2021
  149. package/dist/lib/node-esm/index.mjs.map +0 -7
  150. package/dist/lib/node-esm/meta.json +0 -1
  151. package/dist/types/bin/chat.d.ts +0 -2
  152. package/dist/types/bin/chat.d.ts.map +0 -1
  153. package/dist/types/src/blueprint/blueprint.d.ts +0 -41
  154. package/dist/types/src/blueprint/blueprint.d.ts.map +0 -1
  155. package/dist/types/src/blueprint/index.d.ts +0 -3
  156. package/dist/types/src/blueprint/index.d.ts.map +0 -1
  157. package/dist/types/src/blueprint/registry.d.ts +0 -7
  158. package/dist/types/src/blueprint/registry.d.ts.map +0 -1
  159. package/dist/types/src/context/binder.d.ts +0 -34
  160. package/dist/types/src/context/binder.d.ts.map +0 -1
  161. package/dist/types/src/context/binding.d.ts +0 -38
  162. package/dist/types/src/context/binding.d.ts.map +0 -1
  163. package/dist/types/src/context/index.d.ts +0 -3
  164. package/dist/types/src/context/index.d.ts.map +0 -1
  165. package/dist/types/src/conversation/conversation.d.ts +0 -44
  166. package/dist/types/src/conversation/conversation.d.ts.map +0 -1
  167. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
  168. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
  169. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
  170. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
  171. package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
  172. package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
  173. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -208
  174. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
  175. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
  176. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
  177. package/dist/types/src/research/exa.d.ts +0 -7
  178. package/dist/types/src/research/exa.d.ts.map +0 -1
  179. package/dist/types/src/research/graph.d.ts +0 -41
  180. package/dist/types/src/research/graph.d.ts.map +0 -1
  181. package/dist/types/src/research/graph.test.d.ts +0 -2
  182. package/dist/types/src/research/graph.test.d.ts.map +0 -1
  183. package/dist/types/src/research/index.d.ts +0 -4
  184. package/dist/types/src/research/index.d.ts.map +0 -1
  185. package/dist/types/src/research/research.d.ts +0 -10
  186. package/dist/types/src/research/research.d.ts.map +0 -1
  187. package/dist/types/src/research/research.test.d.ts +0 -2
  188. package/dist/types/src/research/research.test.d.ts.map +0 -1
  189. package/dist/types/src/session/session.d.ts +0 -283
  190. package/dist/types/src/session/session.d.ts.map +0 -1
  191. package/dist/types/src/session/session.test.d.ts.map +0 -1
  192. package/dist/types/src/session/version-pin.test.d.ts +0 -2
  193. package/dist/types/src/session/version-pin.test.d.ts.map +0 -1
  194. package/dist/types/src/summarization/summarization.test.d.ts +0 -2
  195. package/dist/types/src/summarization/summarization.test.d.ts.map +0 -1
  196. package/dist/types/src/testing/exa-search-1748337321991.d.ts +0 -38
  197. package/dist/types/src/testing/exa-search-1748337321991.d.ts.map +0 -1
  198. package/dist/types/src/testing/exa-search-1748337331526.d.ts +0 -37
  199. package/dist/types/src/testing/exa-search-1748337331526.d.ts.map +0 -1
  200. package/dist/types/src/testing/exa-search-1748337344119.d.ts +0 -58
  201. package/dist/types/src/testing/exa-search-1748337344119.d.ts.map +0 -1
  202. package/dist/types/src/types.d.ts +0 -3
  203. package/dist/types/src/types.d.ts.map +0 -1
  204. package/src/blueprint/blueprint.ts +0 -75
  205. package/src/blueprint/index.ts +0 -6
  206. package/src/blueprint/registry.ts +0 -15
  207. package/src/context/binder.ts +0 -101
  208. package/src/context/binding.ts +0 -31
  209. package/src/context/index.ts +0 -6
  210. package/src/conversation/conversation.ts +0 -103
  211. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -95
  212. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -66
  213. package/src/extraction/extraction-llm-function/index.ts +0 -5
  214. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -115
  215. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -54
  216. package/src/research/exa.ts +0 -100
  217. package/src/research/graph.test.ts +0 -15
  218. package/src/research/graph.ts +0 -337
  219. package/src/research/index.ts +0 -7
  220. package/src/research/instructions-research.tpl +0 -97
  221. package/src/research/research.test.ts +0 -190
  222. package/src/research/research.ts +0 -90
  223. package/src/session/session.test.ts +0 -275
  224. package/src/session/session.ts +0 -582
  225. package/src/session/version-pin.test.ts +0 -26
  226. package/src/summarization/summarization.test.ts +0 -221
  227. package/src/testing/exa-search-1748337321991.ts +0 -131
  228. package/src/testing/exa-search-1748337331526.ts +0 -144
  229. package/src/testing/exa-search-1748337344119.ts +0 -133
  230. package/src/typedefs.d.ts +0 -8
  231. package/src/types.ts +0 -8
  232. /package/dist/types/src/{session → conversation}/session.test.d.ts +0 -0
  233. /package/src/extraction/extraction-llm-function/{instructions.tpl → transcription-instructions.tpl} +0 -0
@@ -0,0 +1,740 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, it } from '@effect/vitest';
6
+
7
+ import { AgentStatus } from '@dxos/ai';
8
+ import { Obj } from '@dxos/echo';
9
+ import { ObjectId } from '@dxos/keys';
10
+ import { log } from '@dxos/log';
11
+ import { type ContentBlock, Message } from '@dxos/types';
12
+
13
+ import { ExecutionGraph } from './execution-graph';
14
+
15
+ // Helper function to create valid object IDs for testing.
16
+ const createTestId = () => ObjectId.random();
17
+
18
+ const SKIP_BLOCKS: ContentBlock.Any['_tag'][] = [];
19
+
20
+ describe('ExecutionGraph', () => {
21
+ describe('basic functionality', () => {
22
+ it('should create an empty graph initially', () => {
23
+ const graph = new ExecutionGraph();
24
+ const result = graph.getGraph();
25
+
26
+ expect(result.branches).toEqual([]);
27
+ expect(result.commits).toEqual([]);
28
+ });
29
+
30
+ it('should add a simple user message', () => {
31
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
32
+ const messageId = createTestId();
33
+ const message = Obj.make(Message.Message, {
34
+ id: messageId,
35
+ created: '2025-01-01T00:00:00Z',
36
+ sender: { role: 'user' },
37
+ blocks: [
38
+ {
39
+ _tag: 'text',
40
+ text: 'Hello, world!',
41
+ },
42
+ ],
43
+ properties: {},
44
+ });
45
+
46
+ graph.addEvents([message]);
47
+ const result = graph.getGraph();
48
+
49
+ expect(result.branches).toEqual(['main']);
50
+ expect(result.commits).toHaveLength(1);
51
+ expect(result.commits[0]).toMatchObject({
52
+ id: `${messageId}_block_0`,
53
+ branch: 'main',
54
+ message: 'Processing request...',
55
+ parents: [],
56
+ tags: ['user'],
57
+ });
58
+ });
59
+
60
+ it('should handle basic status & message processing', () => {
61
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
62
+ const status = Obj.make(AgentStatus, {
63
+ created: '2025-01-01T00:00:00Z',
64
+ message: 'Running Research',
65
+ });
66
+ const message = Obj.make(Message.Message, {
67
+ created: '2025-09-29T14:29:37.860Z',
68
+ sender: {
69
+ role: 'user',
70
+ },
71
+ blocks: [
72
+ {
73
+ _tag: 'text',
74
+ text: '{"id":"01K6AXZKK79H4E54RKP1YZDARK","@type":"dxn:type:org.dxos.type.organization:0.1.0","@meta":{"keys":[]},"name":"Blue Yard","website":"https://blueyard.com"}',
75
+ },
76
+ ],
77
+ properties: {},
78
+ });
79
+
80
+ graph.addEvents([status, message]);
81
+ const result = graph.getGraph();
82
+ log.info('result', result);
83
+
84
+ expect(result.branches).toEqual(['main']);
85
+ expect(result.commits).toHaveLength(2);
86
+ expect(result.commits[0].branch).toEqual('main');
87
+ expect(result.commits[0].message).toEqual('Running Research');
88
+ expect(result.commits[0].parents).toEqual([]);
89
+ expect(result.commits[1].branch).toEqual('main');
90
+ expect(result.commits[1].message).toEqual('Processing request...');
91
+ expect(result.commits[1].parents).toEqual([status.id]);
92
+ });
93
+
94
+ it('should add an assistant message with text', () => {
95
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
96
+ const messageId = createTestId();
97
+ const message = Obj.make(Message.Message, {
98
+ id: messageId,
99
+ created: '2025-01-01T00:00:00Z',
100
+ sender: { role: 'assistant' },
101
+ blocks: [
102
+ {
103
+ _tag: 'text',
104
+ text: 'Hello! How can I help you today?',
105
+ },
106
+ ],
107
+ properties: {},
108
+ });
109
+
110
+ graph.addEvents([message]);
111
+ const result = graph.getGraph();
112
+
113
+ expect(result.commits).toHaveLength(1);
114
+ expect(result.commits[0]).toMatchObject({
115
+ id: `${messageId}_block_0`,
116
+ branch: 'main',
117
+ message: 'Response (7 words)',
118
+ parents: [],
119
+ });
120
+ });
121
+ });
122
+
123
+ describe('sequential chaining within messages', () => {
124
+ it('should chain blocks within a message', () => {
125
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
126
+ const messageId = createTestId();
127
+ const message = Obj.make(Message.Message, {
128
+ id: messageId,
129
+ created: '2025-01-01T00:00:00Z',
130
+ sender: { role: 'assistant' },
131
+ blocks: [
132
+ {
133
+ _tag: 'text',
134
+ text: 'First block',
135
+ },
136
+ {
137
+ _tag: 'text',
138
+ text: 'Second block',
139
+ },
140
+ {
141
+ _tag: 'text',
142
+ text: 'Third block',
143
+ },
144
+ ],
145
+ properties: {},
146
+ });
147
+
148
+ graph.addEvents([message]);
149
+ const result = graph.getGraph();
150
+
151
+ expect(result.commits).toHaveLength(3);
152
+
153
+ // First block has no parents
154
+ expect(result.commits[0]).toMatchObject({
155
+ id: `${messageId}_block_0`,
156
+ parents: [],
157
+ });
158
+
159
+ // Second block has first block as parent
160
+ expect(result.commits[1]).toMatchObject({
161
+ id: `${messageId}_block_1`,
162
+ parents: [`${messageId}_block_0`],
163
+ });
164
+
165
+ // Third block has second block as parent
166
+ expect(result.commits[2]).toMatchObject({
167
+ id: `${messageId}_block_2`,
168
+ parents: [`${messageId}_block_1`],
169
+ });
170
+ });
171
+
172
+ it('should chain across multiple messages', () => {
173
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
174
+
175
+ // First message
176
+ const message1Id = createTestId();
177
+ const message1 = Obj.make(Message.Message, {
178
+ id: message1Id,
179
+ created: '2025-01-01T00:00:00Z',
180
+ sender: { role: 'user' },
181
+ blocks: [{ _tag: 'text', text: 'Hello' }],
182
+ properties: {},
183
+ });
184
+
185
+ // Second message
186
+ const message2Id = createTestId();
187
+ const message2 = Obj.make(Message.Message, {
188
+ id: message2Id,
189
+ created: '2025-01-01T00:01:00Z',
190
+ sender: { role: 'assistant' },
191
+ blocks: [{ _tag: 'text', text: 'Hi there!' }],
192
+ properties: {},
193
+ });
194
+
195
+ graph.addEvents([message1, message2]);
196
+ const result = graph.getGraph();
197
+
198
+ expect(result.commits).toHaveLength(2);
199
+
200
+ // First message block has no parents
201
+ expect(result.commits[0]).toMatchObject({
202
+ id: `${message1Id}_block_0`,
203
+ parents: [],
204
+ });
205
+
206
+ // Second message block has first message block as parent
207
+ expect(result.commits[1]).toMatchObject({
208
+ id: `${message2Id}_block_0`,
209
+ parents: [`${message1Id}_block_0`],
210
+ });
211
+ });
212
+ });
213
+
214
+ describe('tool calls and results', () => {
215
+ it('should handle tool calls and results with proper parent relationships', () => {
216
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
217
+
218
+ const message1Id = createTestId();
219
+ const message2Id = createTestId();
220
+ const statusId = createTestId();
221
+ const toolCallId = 'tool1';
222
+
223
+ // Assistant message with tool call
224
+ const toolCallMessage = Obj.make(Message.Message, {
225
+ id: message1Id,
226
+ created: '2025-01-01T00:00:00Z',
227
+ sender: { role: 'assistant' },
228
+ blocks: [
229
+ {
230
+ _tag: 'text',
231
+ text: 'I will help you with that.',
232
+ },
233
+ {
234
+ _tag: 'toolCall',
235
+ toolCallId,
236
+ name: 'test_tool',
237
+ input: '{"param": "value"}',
238
+ providerExecuted: false,
239
+ },
240
+ ],
241
+ properties: {},
242
+ });
243
+
244
+ // User message with tool result
245
+ const toolResultMessage = Obj.make(Message.Message, {
246
+ id: message2Id,
247
+ created: '2025-01-01T00:01:00Z',
248
+ sender: { role: 'user' },
249
+ blocks: [
250
+ {
251
+ _tag: 'toolResult',
252
+ toolCallId,
253
+ name: 'test_tool',
254
+ result: '{"result": "success"}',
255
+ providerExecuted: false,
256
+ },
257
+ ],
258
+ properties: {},
259
+ });
260
+
261
+ // AgentStatus event
262
+ const agentStatus = Obj.make(AgentStatus, {
263
+ id: statusId,
264
+ created: '2025-01-01T00:02:00Z',
265
+ parentMessage: message1Id,
266
+ toolCallId,
267
+ message: 'Processing tool call...',
268
+ });
269
+
270
+ graph.addEvents([toolCallMessage, toolResultMessage, agentStatus]);
271
+ const result = graph.getGraph();
272
+
273
+ expect(result.commits).toHaveLength(4);
274
+
275
+ // Text block
276
+ expect(result.commits[0]).toMatchObject({
277
+ id: `${message1Id}_block_0`,
278
+ parents: [],
279
+ });
280
+
281
+ // Tool call block
282
+ expect(result.commits[1]).toMatchObject({
283
+ id: `${message1Id}_toolCall_${toolCallId}`,
284
+ parents: [`${message1Id}_block_0`],
285
+ });
286
+
287
+ // Tool result should have two parents: AgentStatus and tool call
288
+ expect(result.commits[2]).toMatchObject({
289
+ id: `${message2Id}_toolResult_${toolCallId}`,
290
+ parents: [statusId, `${message1Id}_toolCall_${toolCallId}`],
291
+ });
292
+
293
+ // AgentStatus
294
+ expect(result.commits[3]).toMatchObject({
295
+ id: statusId,
296
+ parents: [`${message1Id}_toolCall_${toolCallId}`],
297
+ });
298
+ });
299
+
300
+ it('should handle tool errors with proper parent relationships', () => {
301
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
302
+
303
+ const message1Id = createTestId();
304
+ const message2Id = createTestId();
305
+ const statusId = createTestId();
306
+ const toolCallId = 'tool1';
307
+
308
+ // Assistant message with tool call
309
+ const toolCallMessage = Obj.make(Message.Message, {
310
+ id: message1Id,
311
+ created: '2025-01-01T00:00:00Z',
312
+ sender: { role: 'assistant' },
313
+ blocks: [
314
+ {
315
+ _tag: 'toolCall',
316
+ toolCallId,
317
+ name: 'test_tool',
318
+ input: '{"param": "value"}',
319
+ providerExecuted: false,
320
+ },
321
+ ],
322
+ properties: {},
323
+ });
324
+
325
+ // User message with tool error
326
+ const toolErrorMessage = Obj.make(Message.Message, {
327
+ id: message2Id,
328
+ created: '2025-01-01T00:01:00Z',
329
+ sender: { role: 'user' },
330
+ blocks: [
331
+ {
332
+ _tag: 'toolResult',
333
+ toolCallId,
334
+ name: 'test_tool',
335
+ error: 'Tool execution failed',
336
+ providerExecuted: false,
337
+ },
338
+ ],
339
+ properties: {},
340
+ });
341
+
342
+ // AgentStatus event
343
+ const agentStatus = Obj.make(AgentStatus, {
344
+ id: statusId,
345
+ created: '2025-01-01T00:02:00Z',
346
+ parentMessage: message1Id,
347
+ toolCallId,
348
+ message: 'Tool execution failed',
349
+ });
350
+
351
+ graph.addEvents([toolCallMessage, toolErrorMessage, agentStatus]);
352
+ const result = graph.getGraph();
353
+
354
+ expect(result.commits).toHaveLength(3);
355
+
356
+ // Tool call block
357
+ expect(result.commits[0]).toMatchObject({
358
+ id: `${message1Id}_toolCall_${toolCallId}`,
359
+ parents: [],
360
+ });
361
+
362
+ // Tool error should have two parents: AgentStatus and tool call
363
+ expect(result.commits[1]).toMatchObject({
364
+ id: `${message2Id}_toolResult_${toolCallId}`,
365
+ parents: [statusId, `${message1Id}_toolCall_${toolCallId}`],
366
+ });
367
+
368
+ // AgentStatus
369
+ expect(result.commits[2]).toMatchObject({
370
+ id: statusId,
371
+ parents: [`${message1Id}_toolCall_${toolCallId}`],
372
+ });
373
+ });
374
+ });
375
+
376
+ describe('different processing orders', () => {
377
+ it('should handle AgentStatus processed after tool result', () => {
378
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
379
+
380
+ const message1Id = createTestId();
381
+ const message2Id = createTestId();
382
+ const statusId = createTestId();
383
+ const toolCallId = 'tool1';
384
+
385
+ // Tool call message
386
+ const toolCallMessage = Obj.make(Message.Message, {
387
+ id: message1Id,
388
+ created: '2025-01-01T00:00:00Z',
389
+ sender: { role: 'assistant' },
390
+ blocks: [
391
+ {
392
+ _tag: 'toolCall',
393
+ toolCallId,
394
+ name: 'test_tool',
395
+ input: '{}',
396
+ providerExecuted: false,
397
+ },
398
+ ],
399
+ properties: {},
400
+ });
401
+
402
+ // Tool result message
403
+ const toolResultMessage = Obj.make(Message.Message, {
404
+ id: message2Id,
405
+ created: '2025-01-01T00:01:00Z',
406
+ sender: { role: 'user' },
407
+ blocks: [
408
+ {
409
+ _tag: 'toolResult',
410
+ toolCallId,
411
+ name: 'test_tool',
412
+ result: '{"result": "success"}',
413
+ providerExecuted: false,
414
+ },
415
+ ],
416
+ properties: {},
417
+ });
418
+
419
+ // AgentStatus event (processed after tool result)
420
+ const agentStatus = Obj.make(AgentStatus, {
421
+ id: statusId,
422
+ created: '2025-01-01T00:00:00Z',
423
+ parentMessage: message1Id,
424
+ toolCallId,
425
+ message: 'Processing...',
426
+ });
427
+
428
+ graph.addEvents([toolCallMessage, toolResultMessage, agentStatus]);
429
+ const result = graph.getGraph();
430
+
431
+ // Tool result should be updated to point to AgentStatus
432
+ const toolResult = result.commits.find((c) => c.id === `${message2Id}_toolResult_${toolCallId}`);
433
+ expect(toolResult?.parents).toContain(statusId);
434
+ });
435
+
436
+ it('should handle AgentStatus processed before tool result', () => {
437
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
438
+
439
+ const message1Id = createTestId();
440
+ const message2Id = createTestId();
441
+ const statusId = createTestId();
442
+ const toolCallId = 'tool1';
443
+
444
+ // Tool call message
445
+ const toolCallMessage = Obj.make(Message.Message, {
446
+ id: message1Id,
447
+ created: '2025-01-01T00:00:00Z',
448
+ sender: { role: 'assistant' },
449
+ blocks: [
450
+ {
451
+ _tag: 'toolCall',
452
+ toolCallId,
453
+ name: 'test_tool',
454
+ input: '{}',
455
+ providerExecuted: false,
456
+ },
457
+ ],
458
+ properties: {},
459
+ });
460
+
461
+ // AgentStatus event (processed before tool result)
462
+ const agentStatus = Obj.make(AgentStatus, {
463
+ id: statusId,
464
+ created: '2025-01-01T00:00:00Z',
465
+ parentMessage: message1Id,
466
+ toolCallId,
467
+ message: 'Processing...',
468
+ });
469
+
470
+ // Tool result message
471
+ const toolResultMessage = Obj.make(Message.Message, {
472
+ id: message2Id,
473
+ created: '2025-01-01T00:01:00Z',
474
+ sender: { role: 'user' },
475
+ blocks: [
476
+ {
477
+ _tag: 'toolResult',
478
+ toolCallId,
479
+ name: 'test_tool',
480
+ result: '{"result": "success"}',
481
+ providerExecuted: false,
482
+ },
483
+ ],
484
+ properties: {},
485
+ });
486
+
487
+ graph.addEvents([toolCallMessage, agentStatus, toolResultMessage]);
488
+ const result = graph.getGraph();
489
+
490
+ // Tool result should point to AgentStatus
491
+ const toolResult = result.commits.find((c) => c.id === `${message2Id}_toolResult_${toolCallId}`);
492
+ expect(toolResult?.parents).toContain(statusId);
493
+ });
494
+ });
495
+
496
+ describe('complex scenarios', () => {
497
+ it('should handle multiple tool calls in sequence', () => {
498
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
499
+
500
+ const msg1Id = createTestId();
501
+ const msg2Id = createTestId();
502
+ const msg3Id = createTestId();
503
+ const msg4Id = createTestId();
504
+ const msg5Id = createTestId();
505
+ const status1Id = createTestId();
506
+ const status2Id = createTestId();
507
+
508
+ const events = [
509
+ // User message
510
+ Obj.make(Message.Message, {
511
+ id: msg1Id,
512
+ created: '2025-01-01T00:00:00Z',
513
+ sender: { role: 'user' },
514
+ blocks: [{ _tag: 'text', text: 'Help me with two tasks' }],
515
+ properties: {},
516
+ }),
517
+
518
+ // Assistant with first tool call
519
+ Obj.make(Message.Message, {
520
+ id: msg2Id,
521
+ created: '2025-01-01T00:01:00Z',
522
+ sender: { role: 'assistant' },
523
+ blocks: [
524
+ { _tag: 'text', text: 'I will help with both tasks' },
525
+ { _tag: 'toolCall', toolCallId: 'tool1', name: 'task1', input: '{}', providerExecuted: false },
526
+ ],
527
+ properties: {},
528
+ }),
529
+
530
+ // First tool result
531
+ Obj.make(Message.Message, {
532
+ id: msg3Id,
533
+ created: '2025-01-01T00:02:00Z',
534
+ sender: { role: 'user' },
535
+ blocks: [
536
+ {
537
+ _tag: 'toolResult',
538
+ toolCallId: 'tool1',
539
+ name: 'task1',
540
+ result: '{"done": true}',
541
+ providerExecuted: false,
542
+ },
543
+ ],
544
+ properties: {},
545
+ }),
546
+
547
+ // Assistant with second tool call
548
+ Obj.make(Message.Message, {
549
+ id: msg4Id,
550
+ created: '2025-01-01T00:03:00Z',
551
+ sender: { role: 'assistant' },
552
+ blocks: [
553
+ { _tag: 'text', text: 'Now for the second task' },
554
+ { _tag: 'toolCall', toolCallId: 'tool2', name: 'task2', input: '{}', providerExecuted: false },
555
+ ],
556
+ properties: {},
557
+ }),
558
+
559
+ // Second tool result
560
+ Obj.make(Message.Message, {
561
+ id: msg5Id,
562
+ created: '2025-01-01T00:04:00Z',
563
+ sender: { role: 'user' },
564
+ blocks: [
565
+ {
566
+ _tag: 'toolResult',
567
+ toolCallId: 'tool2',
568
+ name: 'task2',
569
+ result: '{"done": true}',
570
+ providerExecuted: false,
571
+ },
572
+ ],
573
+ properties: {},
574
+ }),
575
+
576
+ // AgentStatus events
577
+ Obj.make(AgentStatus, {
578
+ id: status1Id,
579
+ created: '2025-01-01T00:05:00Z',
580
+ parentMessage: msg2Id,
581
+ toolCallId: 'tool1',
582
+ message: 'Processing task 1...',
583
+ }),
584
+
585
+ Obj.make(AgentStatus, {
586
+ id: status2Id,
587
+ created: '2025-01-01T00:06:00Z',
588
+ parentMessage: msg4Id,
589
+ toolCallId: 'tool2',
590
+ message: 'Processing task 2...',
591
+ }),
592
+ ];
593
+
594
+ graph.addEvents(events);
595
+ const result = graph.getGraph();
596
+
597
+ expect(result.commits).toHaveLength(9);
598
+
599
+ // Check sequential chaining
600
+ expect(result.commits[0].parents).toEqual([]); // msg1_block_0
601
+ expect(result.commits[1].parents).toEqual([`${msg1Id}_block_0`]); // msg2_block_0
602
+ expect(result.commits[2].parents).toEqual([`${msg2Id}_block_0`]); // msg2_toolCall_tool1
603
+ expect(result.commits[3].parents).toEqual([status1Id, `${msg2Id}_toolCall_tool1`]); // msg3_toolResult_tool1
604
+ expect(result.commits[4].parents).toEqual([`${msg3Id}_toolResult_tool1`]); // msg4_block_0
605
+ expect(result.commits[5].parents).toEqual([`${msg4Id}_block_0`]); // msg4_toolCall_tool2
606
+ expect(result.commits[6].parents).toEqual([status2Id, `${msg4Id}_toolCall_tool2`]); // msg5_toolResult_tool2
607
+ });
608
+
609
+ it('should handle mixed block types in a message', () => {
610
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
611
+
612
+ const messageId = createTestId();
613
+ const message = Obj.make(Message.Message, {
614
+ id: messageId,
615
+ created: '2025-01-01T00:00:00Z',
616
+ sender: { role: 'assistant' },
617
+ blocks: [
618
+ { _tag: 'text', text: 'Let me help you' },
619
+ { _tag: 'reasoning', reasoningText: 'I need to think about this' },
620
+ { _tag: 'toolCall', toolCallId: 'tool1', name: 'analyze', input: '{}', providerExecuted: false },
621
+ {
622
+ _tag: 'stats',
623
+ message: 'OK',
624
+ duration: 1000,
625
+ toolCalls: 1,
626
+ usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
627
+ },
628
+ ],
629
+ properties: {},
630
+ });
631
+
632
+ graph.addEvents([message]);
633
+ const result = graph.getGraph();
634
+
635
+ expect(result.commits).toHaveLength(4);
636
+
637
+ // All blocks should be chained sequentially
638
+ expect(result.commits[0].parents).toEqual([]); // text
639
+ expect(result.commits[1].parents).toEqual([`${messageId}_block_0`]); // reasoning
640
+ expect(result.commits[2].parents).toEqual([`${messageId}_block_1`]); // toolCall
641
+ expect(result.commits[3].parents).toEqual([`${messageId}_toolCall_tool1`]); // summary
642
+ });
643
+ });
644
+
645
+ describe('edge cases', () => {
646
+ it('should handle empty text blocks', () => {
647
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
648
+
649
+ const messageId = createTestId();
650
+ const message = Obj.make(Message.Message, {
651
+ id: messageId,
652
+ created: '2025-01-01T00:00:00Z',
653
+ sender: { role: 'assistant' },
654
+ blocks: [
655
+ { _tag: 'text', text: 'Valid text' },
656
+ { _tag: 'text', text: '' }, // Empty text should be filtered out
657
+ { _tag: 'text', text: 'More valid text' },
658
+ ],
659
+ properties: {},
660
+ });
661
+
662
+ graph.addEvents([message]);
663
+ const result = graph.getGraph();
664
+
665
+ expect(result.commits).toHaveLength(2); // Empty block should be filtered out
666
+ expect(result.commits[0].parents).toEqual([]); // First text block
667
+ expect(result.commits[1].parents).toEqual([`${messageId}_block_0`]); // Third text block
668
+ });
669
+
670
+ it('should handle messages with no blocks', () => {
671
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
672
+
673
+ const messageId = createTestId();
674
+ const message = Obj.make(Message.Message, {
675
+ id: messageId,
676
+ created: '2025-01-01T00:00:00Z',
677
+ sender: { role: 'assistant' },
678
+ blocks: [],
679
+ properties: {},
680
+ });
681
+
682
+ graph.addEvents([message]);
683
+ const result = graph.getGraph();
684
+
685
+ expect(result.commits).toHaveLength(0);
686
+ });
687
+
688
+ it('should handle getGraph with lastRequest filter', () => {
689
+ const graph = new ExecutionGraph(SKIP_BLOCKS);
690
+
691
+ const msg1Id = createTestId();
692
+ const msg2Id = createTestId();
693
+ const msg3Id = createTestId();
694
+ const msg4Id = createTestId();
695
+
696
+ const events = [
697
+ Obj.make(Message.Message, {
698
+ id: msg1Id,
699
+ created: '2025-01-01T00:00:00Z',
700
+ sender: { role: 'user' },
701
+ blocks: [{ _tag: 'text', text: 'First request' }],
702
+ properties: {},
703
+ }),
704
+ Obj.make(Message.Message, {
705
+ id: msg2Id,
706
+ created: '2025-01-01T00:01:00Z',
707
+ sender: { role: 'assistant' },
708
+ blocks: [{ _tag: 'text', text: 'Response 1' }],
709
+ properties: {},
710
+ }),
711
+ Obj.make(Message.Message, {
712
+ id: msg3Id,
713
+ created: '2025-01-01T00:02:00Z',
714
+ sender: { role: 'user' },
715
+ blocks: [{ _tag: 'text', text: 'Second request' }],
716
+ properties: {},
717
+ }),
718
+ Obj.make(Message.Message, {
719
+ id: msg4Id,
720
+ created: '2025-01-01T00:03:00Z',
721
+ sender: { role: 'assistant' },
722
+ blocks: [{ _tag: 'text', text: 'Response 2' }],
723
+ properties: {},
724
+ }),
725
+ ];
726
+
727
+ graph.addEvents(events);
728
+
729
+ // Get all commits
730
+ const allCommits = graph.getGraph();
731
+ expect(allCommits.commits).toHaveLength(4);
732
+
733
+ // Get commits from last user request
734
+ const lastRequestCommits = graph.getGraph(true);
735
+ expect(lastRequestCommits.commits).toHaveLength(2); // Only msg3 and msg4
736
+ expect(lastRequestCommits.commits[0].id).toBe(`${msg3Id}_block_0`);
737
+ expect(lastRequestCommits.commits[1].id).toBe(`${msg4Id}_block_0`);
738
+ });
739
+ });
740
+ });