@qduc/term2 0.1.2 → 0.1.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.
Files changed (216) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +5 -0
  3. package/dist/agent.js.map +1 -1
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js +3 -1
  6. package/dist/app.js.map +1 -1
  7. package/dist/cli.js +2 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/components/ApprovalPrompt.d.ts.map +1 -1
  10. package/dist/components/ApprovalPrompt.js +20 -0
  11. package/dist/components/ApprovalPrompt.js.map +1 -1
  12. package/dist/components/ChatMessage.js +1 -1
  13. package/dist/components/ChatMessage.js.map +1 -1
  14. package/dist/components/InputBox.d.ts.map +1 -1
  15. package/dist/components/InputBox.js +3 -8
  16. package/dist/components/InputBox.js.map +1 -1
  17. package/dist/components/MarkdownRenderer.js +1 -1
  18. package/dist/components/MarkdownRenderer.js.map +1 -1
  19. package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts +2 -0
  20. package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts.map +1 -0
  21. package/dist/components/ModelSelectionMenu.error-tabs.test.js +18 -0
  22. package/dist/components/ModelSelectionMenu.error-tabs.test.js.map +1 -0
  23. package/dist/components/SettingsSelectionMenu.js +1 -1
  24. package/dist/components/SettingsSelectionMenu.js.map +1 -1
  25. package/dist/components/SlashCommandMenu.js +2 -2
  26. package/dist/components/SlashCommandMenu.js.map +1 -1
  27. package/dist/hooks/use-conversation.d.ts.map +1 -1
  28. package/dist/hooks/use-conversation.js +53 -508
  29. package/dist/hooks/use-conversation.js.map +1 -1
  30. package/dist/hooks/use-model-selection.d.ts.map +1 -1
  31. package/dist/hooks/use-model-selection.js +7 -3
  32. package/dist/hooks/use-model-selection.js.map +1 -1
  33. package/dist/hooks/use-settings-completion.d.ts.map +1 -1
  34. package/dist/hooks/use-settings-completion.js +5 -0
  35. package/dist/hooks/use-settings-completion.js.map +1 -1
  36. package/dist/hooks/use-settings-completion.test.js +6 -0
  37. package/dist/hooks/use-settings-completion.test.js.map +1 -1
  38. package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
  39. package/dist/hooks/use-settings-value-completion.js +1 -0
  40. package/dist/hooks/use-settings-value-completion.js.map +1 -1
  41. package/dist/lib/editor-impl.test.d.ts +2 -0
  42. package/dist/lib/editor-impl.test.d.ts.map +1 -0
  43. package/dist/lib/editor-impl.test.js +188 -0
  44. package/dist/lib/editor-impl.test.js.map +1 -0
  45. package/dist/lib/openai-agent-client.d.ts.map +1 -1
  46. package/dist/lib/openai-agent-client.js +35 -15
  47. package/dist/lib/openai-agent-client.js.map +1 -1
  48. package/dist/lib/openai-agent-client.public-methods.test.d.ts +2 -0
  49. package/dist/lib/openai-agent-client.public-methods.test.d.ts.map +1 -0
  50. package/dist/lib/openai-agent-client.public-methods.test.js +188 -0
  51. package/dist/lib/openai-agent-client.public-methods.test.js.map +1 -0
  52. package/dist/lib/tool-invoke.test.js +1 -1
  53. package/dist/lib/tool-invoke.test.js.map +1 -1
  54. package/dist/providers/github-copilot/converters.d.ts +45 -0
  55. package/dist/providers/github-copilot/converters.d.ts.map +1 -0
  56. package/dist/providers/github-copilot/converters.js +118 -0
  57. package/dist/providers/github-copilot/converters.js.map +1 -0
  58. package/dist/providers/github-copilot/converters.test.d.ts +2 -0
  59. package/dist/providers/github-copilot/converters.test.d.ts.map +1 -0
  60. package/dist/providers/github-copilot/converters.test.js +162 -0
  61. package/dist/providers/github-copilot/converters.test.js.map +1 -0
  62. package/dist/providers/github-copilot/github-copilot.provider.d.ts +2 -0
  63. package/dist/providers/github-copilot/github-copilot.provider.d.ts.map +1 -0
  64. package/dist/providers/github-copilot/github-copilot.provider.js +75 -0
  65. package/dist/providers/github-copilot/github-copilot.provider.js.map +1 -0
  66. package/dist/providers/github-copilot/github-copilot.provider.test.d.ts +2 -0
  67. package/dist/providers/github-copilot/github-copilot.provider.test.d.ts.map +1 -0
  68. package/dist/providers/github-copilot/github-copilot.provider.test.js +26 -0
  69. package/dist/providers/github-copilot/github-copilot.provider.test.js.map +1 -0
  70. package/dist/providers/github-copilot/index.d.ts +4 -0
  71. package/dist/providers/github-copilot/index.d.ts.map +1 -0
  72. package/dist/providers/github-copilot/index.js +4 -0
  73. package/dist/providers/github-copilot/index.js.map +1 -0
  74. package/dist/providers/github-copilot/model-direct.d.ts +34 -0
  75. package/dist/providers/github-copilot/model-direct.d.ts.map +1 -0
  76. package/dist/providers/github-copilot/model-direct.js +443 -0
  77. package/dist/providers/github-copilot/model-direct.js.map +1 -0
  78. package/dist/providers/github-copilot/model.d.ts +24 -0
  79. package/dist/providers/github-copilot/model.d.ts.map +1 -0
  80. package/dist/providers/github-copilot/model.delta.test.d.ts +2 -0
  81. package/dist/providers/github-copilot/model.delta.test.d.ts.map +1 -0
  82. package/dist/providers/github-copilot/model.delta.test.js +15 -0
  83. package/dist/providers/github-copilot/model.delta.test.js.map +1 -0
  84. package/dist/providers/github-copilot/model.js +581 -0
  85. package/dist/providers/github-copilot/model.js.map +1 -0
  86. package/dist/providers/github-copilot/provider.d.ts +20 -0
  87. package/dist/providers/github-copilot/provider.d.ts.map +1 -0
  88. package/dist/providers/github-copilot/provider.js +30 -0
  89. package/dist/providers/github-copilot/provider.js.map +1 -0
  90. package/dist/providers/github-copilot/provider.test.d.ts +2 -0
  91. package/dist/providers/github-copilot/provider.test.d.ts.map +1 -0
  92. package/dist/providers/github-copilot/provider.test.js +52 -0
  93. package/dist/providers/github-copilot/provider.test.js.map +1 -0
  94. package/dist/providers/github-copilot/utils.d.ts +20 -0
  95. package/dist/providers/github-copilot/utils.d.ts.map +1 -0
  96. package/dist/providers/github-copilot/utils.js +142 -0
  97. package/dist/providers/github-copilot/utils.js.map +1 -0
  98. package/dist/providers/github-copilot/utils.test.d.ts +2 -0
  99. package/dist/providers/github-copilot/utils.test.d.ts.map +1 -0
  100. package/dist/providers/github-copilot/utils.test.js +21 -0
  101. package/dist/providers/github-copilot/utils.test.js.map +1 -0
  102. package/dist/providers/openai-compatible/model.d.ts.map +1 -1
  103. package/dist/providers/openai-compatible/model.js +13 -2
  104. package/dist/providers/openai-compatible/model.js.map +1 -1
  105. package/dist/providers/openai-compatible/reasoning-content.test.js +2 -2
  106. package/dist/providers/openai-compatible/reasoning-content.test.js.map +1 -1
  107. package/dist/providers/openrouter/converters.d.ts.map +1 -1
  108. package/dist/providers/openrouter/converters.js +64 -46
  109. package/dist/providers/openrouter/converters.js.map +1 -1
  110. package/dist/providers/openrouter/converters.test.js +13 -12
  111. package/dist/providers/openrouter/converters.test.js.map +1 -1
  112. package/dist/providers/openrouter/merge-messages.test.d.ts +2 -0
  113. package/dist/providers/openrouter/merge-messages.test.d.ts.map +1 -0
  114. package/dist/providers/openrouter/merge-messages.test.js +83 -0
  115. package/dist/providers/openrouter/merge-messages.test.js.map +1 -0
  116. package/dist/providers/openrouter/reasoning-content.test.js +2 -2
  117. package/dist/providers/openrouter/reasoning-content.test.js.map +1 -1
  118. package/dist/providers/openrouter.test.js +30 -21
  119. package/dist/providers/openrouter.test.js.map +1 -1
  120. package/dist/reproduce_issue.test.d.ts +2 -0
  121. package/dist/reproduce_issue.test.d.ts.map +1 -0
  122. package/dist/reproduce_issue.test.js +31 -0
  123. package/dist/reproduce_issue.test.js.map +1 -0
  124. package/dist/services/conversation-store.test.js +12 -11
  125. package/dist/services/conversation-store.test.js.map +1 -1
  126. package/dist/services/settings-service.d.ts +12 -3
  127. package/dist/services/settings-service.d.ts.map +1 -1
  128. package/dist/services/settings-service.js +26 -0
  129. package/dist/services/settings-service.js.map +1 -1
  130. package/dist/tools/ask-mentor.d.ts +1 -1
  131. package/dist/tools/ask-mentor.d.ts.map +1 -1
  132. package/dist/tools/ask-mentor.js +1 -0
  133. package/dist/tools/ask-mentor.js.map +1 -1
  134. package/dist/tools/edit-healing.d.ts +24 -0
  135. package/dist/tools/edit-healing.d.ts.map +1 -0
  136. package/dist/tools/edit-healing.js +230 -0
  137. package/dist/tools/edit-healing.js.map +1 -0
  138. package/dist/tools/edit-healing.test.d.ts +2 -0
  139. package/dist/tools/edit-healing.test.d.ts.map +1 -0
  140. package/dist/tools/edit-healing.test.js +34 -0
  141. package/dist/tools/edit-healing.test.js.map +1 -0
  142. package/dist/tools/find-files.d.ts +2 -2
  143. package/dist/tools/find-files.d.ts.map +1 -1
  144. package/dist/tools/find-files.js +2 -0
  145. package/dist/tools/find-files.js.map +1 -1
  146. package/dist/tools/grep.d.ts +1 -1
  147. package/dist/tools/grep.d.ts.map +1 -1
  148. package/dist/tools/grep.js +1 -0
  149. package/dist/tools/grep.js.map +1 -1
  150. package/dist/tools/read-file.d.ts.map +1 -1
  151. package/dist/tools/read-file.js +10 -9
  152. package/dist/tools/read-file.js.map +1 -1
  153. package/dist/tools/read-file.test.js +24 -19
  154. package/dist/tools/read-file.test.js.map +1 -1
  155. package/dist/tools/search-replace.d.ts +3 -1
  156. package/dist/tools/search-replace.d.ts.map +1 -1
  157. package/dist/tools/search-replace.js +331 -19
  158. package/dist/tools/search-replace.js.map +1 -1
  159. package/dist/tools/search-replace.test.js +241 -2
  160. package/dist/tools/search-replace.test.js.map +1 -1
  161. package/dist/tools/search.d.ts +4 -4
  162. package/dist/tools/search.d.ts.map +1 -1
  163. package/dist/tools/search.js +4 -0
  164. package/dist/tools/search.js.map +1 -1
  165. package/dist/tools/shell.d.ts +2 -2
  166. package/dist/tools/shell.d.ts.map +1 -1
  167. package/dist/tools/shell.js +2 -0
  168. package/dist/tools/shell.js.map +1 -1
  169. package/dist/tools/types.d.ts +2 -2
  170. package/dist/tools/types.d.ts.map +1 -1
  171. package/dist/tools/web-fetch.d.ts +20 -0
  172. package/dist/tools/web-fetch.d.ts.map +1 -0
  173. package/dist/tools/web-fetch.js +300 -0
  174. package/dist/tools/web-fetch.js.map +1 -0
  175. package/dist/tools/web-fetch.test.d.ts +2 -0
  176. package/dist/tools/web-fetch.test.d.ts.map +1 -0
  177. package/dist/tools/web-fetch.test.js +94 -0
  178. package/dist/tools/web-fetch.test.js.map +1 -0
  179. package/dist/utils/command-safety/index.d.ts +2 -2
  180. package/dist/utils/command-safety/index.d.ts.map +1 -1
  181. package/dist/utils/command-safety/index.js +7 -6
  182. package/dist/utils/command-safety/index.js.map +1 -1
  183. package/dist/utils/command-safety.find.test.js +19 -21
  184. package/dist/utils/command-safety.find.test.js.map +1 -1
  185. package/dist/utils/conversation-event-handler.d.ts +63 -0
  186. package/dist/utils/conversation-event-handler.d.ts.map +1 -0
  187. package/dist/utils/conversation-event-handler.js +132 -0
  188. package/dist/utils/conversation-event-handler.js.map +1 -0
  189. package/dist/utils/conversation-event-handler.test.d.ts +2 -0
  190. package/dist/utils/conversation-event-handler.test.d.ts.map +1 -0
  191. package/dist/utils/conversation-event-handler.test.js +281 -0
  192. package/dist/utils/conversation-event-handler.test.js.map +1 -0
  193. package/dist/utils/conversation-utils.d.ts +41 -0
  194. package/dist/utils/conversation-utils.d.ts.map +1 -0
  195. package/dist/utils/conversation-utils.js +109 -0
  196. package/dist/utils/conversation-utils.js.map +1 -0
  197. package/dist/utils/conversation-utils.test.d.ts +2 -0
  198. package/dist/utils/conversation-utils.test.d.ts.map +1 -0
  199. package/dist/utils/conversation-utils.test.js +190 -0
  200. package/dist/utils/conversation-utils.test.js.map +1 -0
  201. package/dist/utils/ink-render-options.d.ts +9 -0
  202. package/dist/utils/ink-render-options.d.ts.map +1 -0
  203. package/dist/utils/ink-render-options.js +8 -0
  204. package/dist/utils/ink-render-options.js.map +1 -0
  205. package/dist/utils/message-utils.d.ts +17 -0
  206. package/dist/utils/message-utils.d.ts.map +1 -0
  207. package/dist/utils/message-utils.js +52 -0
  208. package/dist/utils/message-utils.js.map +1 -0
  209. package/dist/utils/message-utils.test.d.ts +2 -0
  210. package/dist/utils/message-utils.test.d.ts.map +1 -0
  211. package/dist/utils/message-utils.test.js +48 -0
  212. package/dist/utils/message-utils.test.js.map +1 -0
  213. package/dist/utils/settings-command.d.ts.map +1 -1
  214. package/dist/utils/settings-command.js +13 -4
  215. package/dist/utils/settings-command.js.map +1 -1
  216. package/package.json +10 -4
@@ -0,0 +1,281 @@
1
+ import test from 'ava';
2
+ import { createConversationEventHandler, } from './conversation-event-handler.js';
3
+ import { createStreamingState } from './conversation-utils.js';
4
+ function createMockDeps() {
5
+ const calls = {
6
+ liveResponsePushes: [],
7
+ liveResponseCancelled: false,
8
+ liveResponseFlushed: false,
9
+ reasoningPushes: [],
10
+ reasoningFlushed: false,
11
+ appendedMessages: [],
12
+ setMessagesCalls: [],
13
+ liveResponses: [],
14
+ };
15
+ return {
16
+ calls,
17
+ liveResponseUpdater: {
18
+ push: (text) => calls.liveResponsePushes.push(text),
19
+ cancel: () => {
20
+ calls.liveResponseCancelled = true;
21
+ },
22
+ flush: () => {
23
+ calls.liveResponseFlushed = true;
24
+ },
25
+ },
26
+ reasoningUpdater: {
27
+ push: (text) => calls.reasoningPushes.push(text),
28
+ cancel: () => { },
29
+ flush: () => {
30
+ calls.reasoningFlushed = true;
31
+ },
32
+ },
33
+ appendMessages: (messages) => calls.appendedMessages.push(messages),
34
+ setMessages: (updater) => calls.setMessagesCalls.push(updater),
35
+ setLiveResponse: (response) => calls.liveResponses.push(response),
36
+ trimMessages: (messages) => messages,
37
+ annotateCommandMessage: (msg) => ({ ...msg, annotated: true }),
38
+ };
39
+ }
40
+ // =============================================================================
41
+ // text_delta tests
42
+ // =============================================================================
43
+ test('text_delta: accumulates text and pushes to live response', t => {
44
+ const deps = createMockDeps();
45
+ const state = createStreamingState();
46
+ const handler = createConversationEventHandler(deps, state);
47
+ handler({ type: 'text_delta', delta: 'Hello ' });
48
+ handler({ type: 'text_delta', delta: 'world!' });
49
+ t.is(state.accumulatedText, 'Hello world!');
50
+ t.deepEqual(deps.calls.liveResponsePushes, ['Hello ', 'Hello world!']);
51
+ });
52
+ // =============================================================================
53
+ // reasoning_delta tests
54
+ // =============================================================================
55
+ test('reasoning_delta: accumulates reasoning after flushed position', t => {
56
+ const deps = createMockDeps();
57
+ const state = createStreamingState();
58
+ const handler = createConversationEventHandler(deps, state);
59
+ handler({
60
+ type: 'reasoning_delta',
61
+ delta: 'ing',
62
+ fullText: 'Thinking',
63
+ });
64
+ t.is(state.accumulatedReasoningText, 'Thinking');
65
+ t.deepEqual(deps.calls.reasoningPushes, ['Thinking']);
66
+ });
67
+ test('reasoning_delta: skips already flushed reasoning', t => {
68
+ const deps = createMockDeps();
69
+ const state = createStreamingState();
70
+ state.flushedReasoningLength = 5; // Already flushed "Think"
71
+ const handler = createConversationEventHandler(deps, state);
72
+ handler({
73
+ type: 'reasoning_delta',
74
+ delta: 'ing',
75
+ fullText: 'Thinking',
76
+ });
77
+ t.is(state.accumulatedReasoningText, 'ing');
78
+ t.deepEqual(deps.calls.reasoningPushes, ['ing']);
79
+ });
80
+ test('reasoning_delta: ignores empty reasoning', t => {
81
+ const deps = createMockDeps();
82
+ const state = createStreamingState();
83
+ const handler = createConversationEventHandler(deps, state);
84
+ handler({
85
+ type: 'reasoning_delta',
86
+ delta: '',
87
+ fullText: ' ',
88
+ });
89
+ t.is(state.accumulatedReasoningText, ' ');
90
+ t.deepEqual(deps.calls.reasoningPushes, []);
91
+ });
92
+ // =============================================================================
93
+ // tool_started tests
94
+ // =============================================================================
95
+ test('tool_started: flushes accumulated text before showing tool', t => {
96
+ const deps = createMockDeps();
97
+ const state = createStreamingState();
98
+ state.accumulatedText = 'Some text before tool';
99
+ const handler = createConversationEventHandler(deps, state);
100
+ handler({
101
+ type: 'tool_started',
102
+ toolCallId: 'call-1',
103
+ toolName: 'shell',
104
+ arguments: { command: 'ls' },
105
+ });
106
+ t.is(state.accumulatedText, '');
107
+ t.is(state.textWasFlushed, true);
108
+ t.true(deps.calls.liveResponseCancelled);
109
+ // Should append text message and command message
110
+ t.is(deps.calls.appendedMessages.length, 2);
111
+ t.is(deps.calls.appendedMessages[0][0].sender, 'bot');
112
+ t.is(deps.calls.appendedMessages[0][0].text, 'Some text before tool');
113
+ });
114
+ test('tool_started: flushes accumulated reasoning before showing tool', t => {
115
+ const deps = createMockDeps();
116
+ const state = createStreamingState();
117
+ state.accumulatedReasoningText = 'Some reasoning';
118
+ const handler = createConversationEventHandler(deps, state);
119
+ handler({
120
+ type: 'tool_started',
121
+ toolCallId: 'call-1',
122
+ toolName: 'shell',
123
+ arguments: { command: 'ls' },
124
+ });
125
+ t.true(deps.calls.reasoningFlushed);
126
+ t.is(state.accumulatedReasoningText, '');
127
+ t.is(state.flushedReasoningLength, 14); // 'Some reasoning'.length
128
+ t.is(state.currentReasoningMessageId, null);
129
+ });
130
+ test('tool_started: creates pending command message with shell command', t => {
131
+ const deps = createMockDeps();
132
+ const state = createStreamingState();
133
+ const handler = createConversationEventHandler(deps, state);
134
+ handler({
135
+ type: 'tool_started',
136
+ toolCallId: 'call-1',
137
+ toolName: 'shell',
138
+ arguments: { command: 'echo hello' },
139
+ });
140
+ t.is(deps.calls.appendedMessages.length, 1);
141
+ const cmdMsg = deps.calls.appendedMessages[0][0];
142
+ t.is(cmdMsg.sender, 'command');
143
+ t.is(cmdMsg.status, 'running');
144
+ t.is(cmdMsg.command, 'echo hello');
145
+ t.is(cmdMsg.toolName, 'shell');
146
+ t.is(cmdMsg.callId, 'call-1');
147
+ });
148
+ test('tool_started: parses JSON string arguments', t => {
149
+ const deps = createMockDeps();
150
+ const state = createStreamingState();
151
+ const handler = createConversationEventHandler(deps, state);
152
+ handler({
153
+ type: 'tool_started',
154
+ toolCallId: 'call-1',
155
+ toolName: 'shell',
156
+ arguments: '{"command": "pwd"}',
157
+ });
158
+ const cmdMsg = deps.calls.appendedMessages[0][0];
159
+ t.is(cmdMsg.command, 'pwd');
160
+ t.deepEqual(cmdMsg.toolArgs, { command: 'pwd' });
161
+ });
162
+ test('tool_started: formats grep command correctly', t => {
163
+ const deps = createMockDeps();
164
+ const state = createStreamingState();
165
+ const handler = createConversationEventHandler(deps, state);
166
+ handler({
167
+ type: 'tool_started',
168
+ toolCallId: 'call-1',
169
+ toolName: 'grep',
170
+ arguments: { pattern: 'TODO', path: 'src/' },
171
+ });
172
+ const cmdMsg = deps.calls.appendedMessages[0][0];
173
+ t.is(cmdMsg.command, 'grep "TODO" src/');
174
+ });
175
+ // =============================================================================
176
+ // command_message tests
177
+ // =============================================================================
178
+ test('command_message: annotates and updates existing pending message', t => {
179
+ const deps = createMockDeps();
180
+ const state = createStreamingState();
181
+ const handler = createConversationEventHandler(deps, state);
182
+ handler({
183
+ type: 'command_message',
184
+ message: {
185
+ id: 'call-1',
186
+ sender: 'command',
187
+ status: 'completed',
188
+ command: 'ls',
189
+ output: 'file.txt',
190
+ callId: 'call-1',
191
+ toolName: 'shell',
192
+ },
193
+ });
194
+ t.is(deps.calls.setMessagesCalls.length, 1);
195
+ // The annotateCommandMessage should have been called
196
+ const updater = deps.calls.setMessagesCalls[0];
197
+ const existingMessages = [
198
+ { id: 'call-1', sender: 'command', status: 'running', callId: 'call-1' },
199
+ ];
200
+ const result = updater(existingMessages);
201
+ t.true(result[0].annotated);
202
+ t.is(result[0].status, 'completed');
203
+ });
204
+ test('command_message: adds new message if no pending exists', t => {
205
+ const deps = createMockDeps();
206
+ const state = createStreamingState();
207
+ const handler = createConversationEventHandler(deps, state);
208
+ handler({
209
+ type: 'command_message',
210
+ message: {
211
+ id: 'call-new',
212
+ sender: 'command',
213
+ status: 'completed',
214
+ command: 'ls',
215
+ output: 'file.txt',
216
+ callId: 'call-new',
217
+ toolName: 'shell',
218
+ },
219
+ });
220
+ const updater = deps.calls.setMessagesCalls[0];
221
+ const existingMessages = [{ id: 1, sender: 'user', text: 'hi' }];
222
+ const result = updater(existingMessages);
223
+ t.is(result.length, 2);
224
+ t.true(result[1].annotated);
225
+ });
226
+ test('command_message: flushes accumulated text before adding', t => {
227
+ const deps = createMockDeps();
228
+ const state = createStreamingState();
229
+ state.accumulatedText = 'Partial response';
230
+ const handler = createConversationEventHandler(deps, state);
231
+ handler({
232
+ type: 'command_message',
233
+ message: {
234
+ id: 'call-1',
235
+ sender: 'command',
236
+ status: 'completed',
237
+ command: 'ls',
238
+ output: '',
239
+ callId: 'call-1',
240
+ },
241
+ });
242
+ t.is(state.accumulatedText, '');
243
+ t.is(state.textWasFlushed, true);
244
+ t.is(deps.calls.appendedMessages.length, 1);
245
+ t.is(deps.calls.appendedMessages[0][0].text, 'Partial response');
246
+ });
247
+ // =============================================================================
248
+ // retry tests
249
+ // =============================================================================
250
+ test('retry: adds system message about hallucination retry', t => {
251
+ const deps = createMockDeps();
252
+ const state = createStreamingState();
253
+ const handler = createConversationEventHandler(deps, state);
254
+ handler({
255
+ type: 'retry',
256
+ toolName: 'shell',
257
+ attempt: 1,
258
+ maxRetries: 2,
259
+ errorMessage: 'Tool hallucination detected',
260
+ });
261
+ t.is(deps.calls.setMessagesCalls.length, 1);
262
+ const updater = deps.calls.setMessagesCalls[0];
263
+ const result = updater([]);
264
+ t.is(result.length, 1);
265
+ t.is(result[0].sender, 'system');
266
+ t.true(result[0].text.includes('shell'));
267
+ t.true(result[0].text.includes('1/2'));
268
+ });
269
+ // =============================================================================
270
+ // unknown event tests
271
+ // =============================================================================
272
+ test('unknown event: is ignored without error', t => {
273
+ const deps = createMockDeps();
274
+ const state = createStreamingState();
275
+ const handler = createConversationEventHandler(deps, state);
276
+ // Should not throw
277
+ handler({ type: 'unknown_event' });
278
+ t.is(deps.calls.appendedMessages.length, 0);
279
+ t.is(deps.calls.setMessagesCalls.length, 0);
280
+ });
281
+ //# sourceMappingURL=conversation-event-handler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-event-handler.test.js","sourceRoot":"","sources":["../../source/utils/conversation-event-handler.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,KAAK,CAAC;AACvB,OAAO,EACN,8BAA8B,GAE9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAiB7D,SAAS,cAAc;IAYtB,MAAM,KAAK,GAAG;QACb,kBAAkB,EAAE,EAAc;QAClC,qBAAqB,EAAE,KAAK;QAC5B,mBAAmB,EAAE,KAAK;QAC1B,eAAe,EAAE,EAAc;QAC/B,gBAAgB,EAAE,KAAK;QACvB,gBAAgB,EAAE,EAAa;QAC/B,gBAAgB,EAAE,EAAmC;QACrD,aAAa,EAAE,EAAW;KAC1B,CAAC;IAEF,OAAO;QACN,KAAK;QACL,mBAAmB,EAAE;YACpB,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3D,MAAM,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC;YACpC,CAAC;YACD,KAAK,EAAE,GAAG,EAAE;gBACX,KAAK,CAAC,mBAAmB,GAAG,IAAI,CAAC;YAClC,CAAC;SACD;QACD,gBAAgB,EAAE;YACjB,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YACxD,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;YAChB,KAAK,EAAE,GAAG,EAAE;gBACX,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC/B,CAAC;SACD;QACD,cAAc,EAAE,CAAC,QAAe,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC1E,WAAW,EAAE,CAAC,OAA+B,EAAE,EAAE,CAChD,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC;QACrC,eAAe,EAAE,CAAC,QAAa,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;QACtE,YAAY,EAAE,CAAC,QAAe,EAAE,EAAE,CAAC,QAAQ;QAC3C,sBAAsB,EAAE,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,EAAC,GAAG,GAAG,EAAE,SAAS,EAAE,IAAI,EAAC,CAAC;KACjE,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF,IAAI,CAAC,0DAA0D,EAAE,CAAC,CAAC,EAAE;IACpE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC,EAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAsB,CAAC,CAAC;IACpE,OAAO,CAAC,EAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAsB,CAAC,CAAC;IAEpE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IAC5C,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,wBAAwB;AACxB,gFAAgF;AAEhF,IAAI,CAAC,+DAA+D,EAAE,CAAC,CAAC,EAAE;IACzE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,UAAU;KACC,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,UAAU,CAAC,CAAC;IACjD,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;AACvD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,CAAC,CAAC,EAAE;IAC5D,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,KAAK,CAAC,sBAAsB,GAAG,CAAC,CAAC,CAAC,0BAA0B;IAC5D,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,UAAU;KACC,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0CAA0C,EAAE,CAAC,CAAC,EAAE;IACpD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,iBAAiB;QACvB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,KAAK;KACM,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF,IAAI,CAAC,4DAA4D,EAAE,CAAC,CAAC,EAAE;IACtE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,KAAK,CAAC,eAAe,GAAG,uBAAuB,CAAC;IAChD,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC;KACL,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,iDAAiD;IACjD,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,CAAC,CAAC,EAAE;IAC3E,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,KAAK,CAAC,wBAAwB,GAAG,gBAAgB,CAAC;IAClD,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,EAAC,OAAO,EAAE,IAAI,EAAC;KACL,CAAC,CAAC;IAExB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,0BAA0B;IAClE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,yBAAyB,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kEAAkE,EAAE,CAAC,CAAC,EAAE;IAC5E,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,EAAC,OAAO,EAAE,YAAY,EAAC;KACb,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACnC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,CAAC,CAAC,EAAE;IACtD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,OAAO;QACjB,SAAS,EAAE,oBAAoB;KACV,CAAC,CAAC;IAExB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAC,OAAO,EAAE,KAAK,EAAC,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,CAAC,CAAC,EAAE;IACxD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,cAAc;QACpB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,MAAM;QAChB,SAAS,EAAE,EAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAC;KACrB,CAAC,CAAC;IAExB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,wBAAwB;AACxB,gFAAgF;AAEhF,IAAI,CAAC,iEAAiE,EAAE,CAAC,CAAC,EAAE;IAC3E,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE;YACR,EAAE,EAAE,QAAQ;YACZ,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,OAAO;SACjB;KACoB,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,qDAAqD;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAE,CAAC;IAChD,MAAM,gBAAgB,GAAG;QACxB,EAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAC;KACtE,CAAC;IACF,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wDAAwD,EAAE,CAAC,CAAC,EAAE;IAClE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE;YACR,EAAE,EAAE,UAAU;YACd,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,UAAU;YAClB,QAAQ,EAAE,OAAO;SACjB;KACoB,CAAC,CAAC;IAExB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAE,CAAC;IAChD,MAAM,gBAAgB,GAAG,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAC,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yDAAyD,EAAE,CAAC,CAAC,EAAE;IACnE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,KAAK,CAAC,eAAe,GAAG,kBAAkB,CAAC;IAC3C,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE;YACR,EAAE,EAAE,QAAQ;YACZ,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,QAAQ;SAChB;KACoB,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,cAAc;AACd,gFAAgF;AAEhF,IAAI,CAAC,sDAAsD,EAAE,CAAC,CAAC,EAAE;IAChE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,OAAO,CAAC;QACP,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,6BAA6B;KACtB,CAAC,CAAC;IAExB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAE,CAAC;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF,IAAI,CAAC,yCAAyC,EAAE,CAAC,CAAC,EAAE;IACnD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5D,mBAAmB;IACnB,OAAO,CAAC,EAAC,IAAI,EAAE,eAAe,EAAQ,CAAC,CAAC;IAExC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Pure utility functions extracted from use-conversation.ts for testability.
3
+ * These contain no React state or side effects.
4
+ */
5
+ /**
6
+ * State tracked during streaming event processing.
7
+ */
8
+ export interface StreamingState {
9
+ accumulatedText: string;
10
+ accumulatedReasoningText: string;
11
+ flushedReasoningLength: number;
12
+ textWasFlushed: boolean;
13
+ currentReasoningMessageId: number | null;
14
+ }
15
+ /**
16
+ * Normalize tool arguments from JSON string or object.
17
+ * Provider implementations may send arguments as either format.
18
+ */
19
+ export declare function parseToolArguments(rawArgs: unknown): unknown;
20
+ /**
21
+ * Build a human-readable command string for display in the UI.
22
+ * Each tool has its own formatting logic.
23
+ */
24
+ export declare function formatToolCommand(toolName: string, args: Record<string, unknown> | null | undefined): string;
25
+ /**
26
+ * Create initial streaming state for a new message send/approval flow.
27
+ */
28
+ export declare function createStreamingState(): StreamingState;
29
+ /**
30
+ * Enhance error messages for common API key issues.
31
+ */
32
+ export declare function enhanceApiKeyError(message: string): string;
33
+ /**
34
+ * Check if an error message indicates max turns exceeded.
35
+ */
36
+ export declare function isMaxTurnsError(message: string): boolean;
37
+ /**
38
+ * Format shell command output for display.
39
+ */
40
+ export declare function createShellMessageOutput(returnCode: number | null, stdout: string, stderr: string): string;
41
+ //# sourceMappingURL=conversation-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-utils.d.ts","sourceRoot":"","sources":["../../source/utils/conversation-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,cAAc,EAAE,OAAO,CAAC;IACxB,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAa5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAC9C,MAAM,CAoCR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAQrD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAW1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACvC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACZ,MAAM,CAsBR"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Pure utility functions extracted from use-conversation.ts for testability.
3
+ * These contain no React state or side effects.
4
+ */
5
+ /**
6
+ * Normalize tool arguments from JSON string or object.
7
+ * Provider implementations may send arguments as either format.
8
+ */
9
+ export function parseToolArguments(rawArgs) {
10
+ if (typeof rawArgs !== 'string') {
11
+ return rawArgs;
12
+ }
13
+ const trimmed = rawArgs.trim();
14
+ if (!trimmed) {
15
+ return rawArgs;
16
+ }
17
+ try {
18
+ return JSON.parse(trimmed);
19
+ }
20
+ catch {
21
+ return rawArgs;
22
+ }
23
+ }
24
+ /**
25
+ * Build a human-readable command string for display in the UI.
26
+ * Each tool has its own formatting logic.
27
+ */
28
+ export function formatToolCommand(toolName, args) {
29
+ if (!args) {
30
+ return toolName;
31
+ }
32
+ if (toolName === 'shell') {
33
+ const cmd = args.command ?? args.commands;
34
+ if (typeof cmd === 'string' && cmd.trim()) {
35
+ return cmd;
36
+ }
37
+ if (Array.isArray(cmd) && cmd.length > 0) {
38
+ return cmd.join('\n');
39
+ }
40
+ return toolName;
41
+ }
42
+ if (toolName === 'grep') {
43
+ if (args.pattern) {
44
+ return `grep "${args.pattern}" ${args.path ?? '.'}`;
45
+ }
46
+ return toolName;
47
+ }
48
+ if (toolName === 'search_replace') {
49
+ return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
50
+ }
51
+ if (toolName === 'apply_patch') {
52
+ return `apply_patch ${args.type ?? 'unknown'} ${args.path ?? ''}`;
53
+ }
54
+ if (toolName === 'ask_mentor') {
55
+ return `ask_mentor: ${args.question ?? ''}`;
56
+ }
57
+ return toolName;
58
+ }
59
+ /**
60
+ * Create initial streaming state for a new message send/approval flow.
61
+ */
62
+ export function createStreamingState() {
63
+ return {
64
+ accumulatedText: '',
65
+ accumulatedReasoningText: '',
66
+ flushedReasoningLength: 0,
67
+ textWasFlushed: false,
68
+ currentReasoningMessageId: null,
69
+ };
70
+ }
71
+ /**
72
+ * Enhance error messages for common API key issues.
73
+ */
74
+ export function enhanceApiKeyError(message) {
75
+ if (message.includes('OPENAI_API_KEY') ||
76
+ (message.includes('401') && message.toLowerCase().includes('unauthorized'))) {
77
+ return ('OpenAI API key is not configured or invalid. Please set the OPENAI_API_KEY environment variable. ' +
78
+ 'Get your API key from: https://platform.openai.com/api-keys');
79
+ }
80
+ return message;
81
+ }
82
+ /**
83
+ * Check if an error message indicates max turns exceeded.
84
+ */
85
+ export function isMaxTurnsError(message) {
86
+ return message.includes('Max turns') && message.includes('exceeded');
87
+ }
88
+ /**
89
+ * Format shell command output for display.
90
+ */
91
+ export function createShellMessageOutput(returnCode, stdout, stderr) {
92
+ const parts = [];
93
+ // Combine stdout and stderr
94
+ const trimmedStdout = stdout.replace(/\n+$/, '');
95
+ const trimmedStderr = stderr.replace(/\n+$/, '');
96
+ if (trimmedStdout) {
97
+ parts.push(trimmedStdout);
98
+ }
99
+ if (trimmedStderr) {
100
+ parts.push(trimmedStderr);
101
+ }
102
+ let output = parts.join('\n');
103
+ // Add return code if available
104
+ if (returnCode !== null) {
105
+ output += `\n\nReturn code: ${returnCode}`;
106
+ }
107
+ return output;
108
+ }
109
+ //# sourceMappingURL=conversation-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-utils.js","sourceRoot":"","sources":["../../source/utils/conversation-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=conversation-utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-utils.test.d.ts","sourceRoot":"","sources":["../../source/utils/conversation-utils.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,190 @@
1
+ import test from 'ava';
2
+ import { parseToolArguments, formatToolCommand, createStreamingState, enhanceApiKeyError, isMaxTurnsError, createShellMessageOutput, } from './conversation-utils.js';
3
+ // =============================================================================
4
+ // parseToolArguments tests
5
+ // =============================================================================
6
+ test('parseToolArguments: returns object as-is', t => {
7
+ const args = { command: 'echo hi' };
8
+ t.deepEqual(parseToolArguments(args), args);
9
+ });
10
+ test('parseToolArguments: parses valid JSON string', t => {
11
+ const args = '{"command": "echo hi"}';
12
+ t.deepEqual(parseToolArguments(args), { command: 'echo hi' });
13
+ });
14
+ test('parseToolArguments: returns invalid JSON string as-is', t => {
15
+ const args = 'not valid json';
16
+ t.is(parseToolArguments(args), args);
17
+ });
18
+ test('parseToolArguments: returns empty string as-is', t => {
19
+ t.is(parseToolArguments(''), '');
20
+ });
21
+ test('parseToolArguments: returns whitespace-only string as-is', t => {
22
+ t.is(parseToolArguments(' '), ' ');
23
+ });
24
+ test('parseToolArguments: handles null', t => {
25
+ t.is(parseToolArguments(null), null);
26
+ });
27
+ test('parseToolArguments: handles undefined', t => {
28
+ t.is(parseToolArguments(undefined), undefined);
29
+ });
30
+ test('parseToolArguments: parses array JSON', t => {
31
+ const args = '["a", "b", "c"]';
32
+ t.deepEqual(parseToolArguments(args), ['a', 'b', 'c']);
33
+ });
34
+ // =============================================================================
35
+ // formatToolCommand tests
36
+ // =============================================================================
37
+ test('formatToolCommand: shell with string command', t => {
38
+ const result = formatToolCommand('shell', { command: 'echo hello' });
39
+ t.is(result, 'echo hello');
40
+ });
41
+ test('formatToolCommand: shell with commands (plural) string', t => {
42
+ const result = formatToolCommand('shell', { commands: 'ls -la' });
43
+ t.is(result, 'ls -la');
44
+ });
45
+ test('formatToolCommand: shell with array commands', t => {
46
+ const result = formatToolCommand('shell', { commands: ['cd /tmp', 'ls'] });
47
+ t.is(result, 'cd /tmp\nls');
48
+ });
49
+ test('formatToolCommand: shell with empty command returns tool name', t => {
50
+ const result = formatToolCommand('shell', { command: '' });
51
+ t.is(result, 'shell');
52
+ });
53
+ test('formatToolCommand: grep with pattern and path', t => {
54
+ const result = formatToolCommand('grep', {
55
+ pattern: 'TODO',
56
+ path: 'src/',
57
+ });
58
+ t.is(result, 'grep "TODO" src/');
59
+ });
60
+ test('formatToolCommand: grep with pattern only uses default path', t => {
61
+ const result = formatToolCommand('grep', { pattern: 'FIXME' });
62
+ t.is(result, 'grep "FIXME" .');
63
+ });
64
+ test('formatToolCommand: grep without pattern returns tool name', t => {
65
+ const result = formatToolCommand('grep', {});
66
+ t.is(result, 'grep');
67
+ });
68
+ test('formatToolCommand: search_replace with all args', t => {
69
+ const result = formatToolCommand('search_replace', {
70
+ search_content: 'old',
71
+ replace_content: 'new',
72
+ path: 'file.ts',
73
+ });
74
+ t.is(result, 'search_replace "old" → "new" file.ts');
75
+ });
76
+ test('formatToolCommand: search_replace with missing args', t => {
77
+ const result = formatToolCommand('search_replace', {});
78
+ t.is(result, 'search_replace "" → "" ');
79
+ });
80
+ test('formatToolCommand: apply_patch with type and path', t => {
81
+ const result = formatToolCommand('apply_patch', {
82
+ type: 'create',
83
+ path: 'newfile.ts',
84
+ });
85
+ t.is(result, 'apply_patch create newfile.ts');
86
+ });
87
+ test('formatToolCommand: apply_patch with missing args', t => {
88
+ const result = formatToolCommand('apply_patch', {});
89
+ t.is(result, 'apply_patch unknown ');
90
+ });
91
+ test('formatToolCommand: ask_mentor with question', t => {
92
+ const result = formatToolCommand('ask_mentor', {
93
+ question: 'How do I refactor this?',
94
+ });
95
+ t.is(result, 'ask_mentor: How do I refactor this?');
96
+ });
97
+ test('formatToolCommand: ask_mentor with empty question', t => {
98
+ const result = formatToolCommand('ask_mentor', {});
99
+ t.is(result, 'ask_mentor: ');
100
+ });
101
+ test('formatToolCommand: unknown tool returns tool name', t => {
102
+ const result = formatToolCommand('custom_tool', { foo: 'bar' });
103
+ t.is(result, 'custom_tool');
104
+ });
105
+ test('formatToolCommand: handles null/undefined args', t => {
106
+ const result = formatToolCommand('shell', null);
107
+ t.is(result, 'shell');
108
+ });
109
+ // =============================================================================
110
+ // createStreamingState tests
111
+ // =============================================================================
112
+ test('createStreamingState: returns correct initial values', t => {
113
+ const state = createStreamingState();
114
+ t.is(state.accumulatedText, '');
115
+ t.is(state.accumulatedReasoningText, '');
116
+ t.is(state.flushedReasoningLength, 0);
117
+ t.is(state.textWasFlushed, false);
118
+ t.is(state.currentReasoningMessageId, null);
119
+ });
120
+ test('createStreamingState: returns new object each call', t => {
121
+ const state1 = createStreamingState();
122
+ const state2 = createStreamingState();
123
+ t.not(state1, state2);
124
+ });
125
+ // =============================================================================
126
+ // enhanceApiKeyError tests
127
+ // =============================================================================
128
+ test('enhanceApiKeyError: enhances OPENAI_API_KEY message', t => {
129
+ const result = enhanceApiKeyError('Missing OPENAI_API_KEY');
130
+ t.true(result.includes('OpenAI API key is not configured'));
131
+ t.true(result.includes('platform.openai.com/api-keys'));
132
+ });
133
+ test('enhanceApiKeyError: enhances 401 unauthorized message', t => {
134
+ const result = enhanceApiKeyError('Error 401: Unauthorized access');
135
+ t.true(result.includes('OpenAI API key is not configured'));
136
+ });
137
+ test('enhanceApiKeyError: passes through other errors', t => {
138
+ const result = enhanceApiKeyError('Network timeout');
139
+ t.is(result, 'Network timeout');
140
+ });
141
+ test('enhanceApiKeyError: handles empty string', t => {
142
+ const result = enhanceApiKeyError('');
143
+ t.is(result, '');
144
+ });
145
+ // =============================================================================
146
+ // isMaxTurnsError tests
147
+ // =============================================================================
148
+ test('isMaxTurnsError: returns true for max turns exceeded', t => {
149
+ t.true(isMaxTurnsError('Max turns (10) exceeded'));
150
+ });
151
+ test('isMaxTurnsError: returns false for partial match - only Max turns', t => {
152
+ t.false(isMaxTurnsError('Max turns reached'));
153
+ });
154
+ test('isMaxTurnsError: returns false for partial match - only exceeded', t => {
155
+ t.false(isMaxTurnsError('Rate limit exceeded'));
156
+ });
157
+ test('isMaxTurnsError: returns false for unrelated message', t => {
158
+ t.false(isMaxTurnsError('Something went wrong'));
159
+ });
160
+ test('isMaxTurnsError: returns false for empty string', t => {
161
+ t.false(isMaxTurnsError(''));
162
+ });
163
+ // =============================================================================
164
+ // createShellMessageOutput tests
165
+ // =============================================================================
166
+ test('createShellMessageOutput: stdout only', t => {
167
+ const result = createShellMessageOutput(0, 'hello world', '');
168
+ t.is(result, 'hello world\n\nReturn code: 0');
169
+ });
170
+ test('createShellMessageOutput: stderr only', t => {
171
+ const result = createShellMessageOutput(1, '', 'error occurred');
172
+ t.is(result, 'error occurred\n\nReturn code: 1');
173
+ });
174
+ test('createShellMessageOutput: both stdout and stderr', t => {
175
+ const result = createShellMessageOutput(0, 'output', 'warning');
176
+ t.is(result, 'output\nwarning\n\nReturn code: 0');
177
+ });
178
+ test('createShellMessageOutput: null return code', t => {
179
+ const result = createShellMessageOutput(null, 'output', '');
180
+ t.is(result, 'output');
181
+ });
182
+ test('createShellMessageOutput: empty output with return code', t => {
183
+ const result = createShellMessageOutput(0, '', '');
184
+ t.is(result, '\n\nReturn code: 0');
185
+ });
186
+ test('createShellMessageOutput: trims trailing newlines from stdout', t => {
187
+ const result = createShellMessageOutput(0, 'hello\n\n', '');
188
+ t.is(result, 'hello\n\nReturn code: 0');
189
+ });
190
+ //# sourceMappingURL=conversation-utils.test.js.map