groove-dev 0.27.102 → 0.27.104
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/CLAUDE.md +0 -7
- package/moe-training/client/domain-tagger.js +205 -0
- package/moe-training/client/edit-normalizer.js +188 -0
- package/moe-training/client/envelope-builder.js +1 -1
- package/moe-training/client/index.js +1 -0
- package/moe-training/client/parsers/claude-code.js +56 -9
- package/moe-training/client/parsers/codex.js +25 -5
- package/moe-training/client/parsers/gemini.js +21 -2
- package/moe-training/client/parsers/grok.js +18 -0
- package/moe-training/client/step-classifier.js +1 -1
- package/moe-training/client/trajectory-capture.js +109 -8
- package/moe-training/server/routes/ingest.js +26 -0
- package/moe-training/server/verifier.js +34 -0
- package/moe-training/shared/constants.js +9 -0
- package/moe-training/shared/envelope-schema.js +128 -2
- package/moe-training/test/client/domain-tagger.test.js +203 -0
- package/moe-training/test/client/edit-normalizer.test.js +376 -0
- package/moe-training/test/client/envelope-builder.test.js +28 -0
- package/moe-training/test/client/parsers/claude-code.test.js +248 -38
- package/moe-training/test/client/parsers/codex.test.js +2 -0
- package/moe-training/test/client/parsers/gemini.test.js +2 -0
- package/moe-training/test/client/step-classifier.test.js +42 -0
- package/moe-training/test/client/trajectory-capture.test.js +440 -0
- package/moe-training/test/server/verifier.test.js +94 -0
- package/moe-training/test/shared/envelope-schema.test.js +291 -0
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +19 -3
- package/node_modules/@groove-dev/gui/dist/assets/{index-8gdXdRnq.js → index-oUBAPJv6.js} +15 -15
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/settings/ProviderSetupWizard.jsx +28 -2
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +23 -2
- package/node_modules/moe-training/client/domain-tagger.js +205 -0
- package/node_modules/moe-training/client/edit-normalizer.js +188 -0
- package/node_modules/moe-training/client/envelope-builder.js +1 -1
- package/node_modules/moe-training/client/index.js +1 -0
- package/node_modules/moe-training/client/parsers/claude-code.js +56 -9
- package/node_modules/moe-training/client/parsers/codex.js +25 -5
- package/node_modules/moe-training/client/parsers/gemini.js +21 -2
- package/node_modules/moe-training/client/parsers/grok.js +18 -0
- package/node_modules/moe-training/client/step-classifier.js +1 -1
- package/node_modules/moe-training/client/trajectory-capture.js +109 -8
- package/node_modules/moe-training/server/routes/ingest.js +26 -0
- package/node_modules/moe-training/server/verifier.js +34 -0
- package/node_modules/moe-training/shared/constants.js +9 -0
- package/node_modules/moe-training/shared/envelope-schema.js +128 -2
- package/node_modules/moe-training/test/client/domain-tagger.test.js +203 -0
- package/node_modules/moe-training/test/client/edit-normalizer.test.js +376 -0
- package/node_modules/moe-training/test/client/envelope-builder.test.js +28 -0
- package/node_modules/moe-training/test/client/parsers/claude-code.test.js +248 -38
- package/node_modules/moe-training/test/client/parsers/codex.test.js +2 -0
- package/node_modules/moe-training/test/client/parsers/gemini.test.js +2 -0
- package/node_modules/moe-training/test/client/step-classifier.test.js +42 -0
- package/node_modules/moe-training/test/client/trajectory-capture.test.js +440 -0
- package/node_modules/moe-training/test/server/verifier.test.js +94 -0
- package/node_modules/moe-training/test/shared/envelope-schema.test.js +291 -0
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +19 -3
- package/packages/gui/dist/assets/{index-8gdXdRnq.js → index-oUBAPJv6.js} +15 -15
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/settings/ProviderSetupWizard.jsx +28 -2
- package/packages/gui/src/views/settings.jsx +23 -2
|
@@ -3,6 +3,27 @@
|
|
|
3
3
|
import { describe, it } from 'node:test';
|
|
4
4
|
import assert from 'node:assert/strict';
|
|
5
5
|
import { ClaudeCodeParser } from '../../../client/parsers/claude-code.js';
|
|
6
|
+
import { PIIScrubber } from '../../../client/scrubber.js';
|
|
7
|
+
import { OBSERVATION_TOKEN_LIMIT } from '../../../shared/constants.js';
|
|
8
|
+
|
|
9
|
+
function makeToolUseEvent(id, name, input) {
|
|
10
|
+
return {
|
|
11
|
+
type: 'assistant',
|
|
12
|
+
message: {
|
|
13
|
+
content: [{ type: 'tool_use', id, name, input }],
|
|
14
|
+
usage: {},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function makeUserToolResult(toolUseId, content, isError = false) {
|
|
20
|
+
return {
|
|
21
|
+
type: 'user',
|
|
22
|
+
message: {
|
|
23
|
+
content: [{ type: 'tool_result', tool_use_id: toolUseId, content, is_error: isError }],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
6
27
|
|
|
7
28
|
describe('ClaudeCodeParser', () => {
|
|
8
29
|
it('parses assistant message as thought', () => {
|
|
@@ -35,44 +56,6 @@ describe('ClaudeCodeParser', () => {
|
|
|
35
56
|
assert.deepEqual(result.arguments, { file_path: '/test.js' });
|
|
36
57
|
});
|
|
37
58
|
|
|
38
|
-
it('parses tool_result as observation', () => {
|
|
39
|
-
const parser = new ClaudeCodeParser();
|
|
40
|
-
// First register a tool use
|
|
41
|
-
parser.parseEvent({
|
|
42
|
-
type: 'assistant',
|
|
43
|
-
message: {
|
|
44
|
-
content: [{ type: 'tool_use', id: 'tu_2', name: 'Read', input: {} }],
|
|
45
|
-
usage: {},
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
// Then parse result
|
|
49
|
-
const event = {
|
|
50
|
-
type: 'assistant',
|
|
51
|
-
message: {
|
|
52
|
-
content: [{ type: 'tool_result', tool_use_id: 'tu_2', content: 'file contents here', is_error: false }],
|
|
53
|
-
usage: {},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
const result = parser.parseEvent(event);
|
|
57
|
-
assert.equal(result.type, 'observation');
|
|
58
|
-
assert.equal(result.content, 'file contents here');
|
|
59
|
-
assert.equal(result.is_error, false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('parses error tool_result as error', () => {
|
|
63
|
-
const parser = new ClaudeCodeParser();
|
|
64
|
-
const event = {
|
|
65
|
-
type: 'assistant',
|
|
66
|
-
message: {
|
|
67
|
-
content: [{ type: 'tool_result', tool_use_id: 'tu_3', content: 'Permission denied', is_error: true }],
|
|
68
|
-
usage: {},
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
const result = parser.parseEvent(event);
|
|
72
|
-
assert.equal(result.type, 'error');
|
|
73
|
-
assert.equal(result.is_error, true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
59
|
it('parses result event as resolution', () => {
|
|
77
60
|
const parser = new ClaudeCodeParser();
|
|
78
61
|
const event = { type: 'result', result: 'Task completed successfully', total_tokens_used: 500 };
|
|
@@ -116,4 +99,231 @@ describe('ClaudeCodeParser', () => {
|
|
|
116
99
|
assert.equal(parser.parseEvent({ type: 'system' }), null);
|
|
117
100
|
assert.equal(parser.parseEvent(null), null);
|
|
118
101
|
});
|
|
102
|
+
|
|
103
|
+
describe('observation steps from user events', () => {
|
|
104
|
+
it('emits observation for Bash tool result', () => {
|
|
105
|
+
const parser = new ClaudeCodeParser();
|
|
106
|
+
parser.parseEvent(makeToolUseEvent('tu_bash', 'Bash', { command: 'ls -la' }));
|
|
107
|
+
|
|
108
|
+
const result = parser.parseEvent(makeUserToolResult('tu_bash', 'file1.js\nfile2.js'));
|
|
109
|
+
assert.equal(result.type, 'observation');
|
|
110
|
+
assert.equal(result.content, 'file1.js\nfile2.js');
|
|
111
|
+
assert.equal(result.is_error, false);
|
|
112
|
+
assert.equal(result.tool, 'Bash');
|
|
113
|
+
assert.equal(result.truncated, false);
|
|
114
|
+
assert.equal(result.original_token_count, Math.ceil('file1.js\nfile2.js'.length / 4));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('emits observation for Read tool result', () => {
|
|
118
|
+
const parser = new ClaudeCodeParser();
|
|
119
|
+
parser.parseEvent(makeToolUseEvent('tu_read', 'Read', { file_path: '/src/index.js' }));
|
|
120
|
+
|
|
121
|
+
const result = parser.parseEvent(makeUserToolResult('tu_read', 'const x = 1;'));
|
|
122
|
+
assert.equal(result.type, 'observation');
|
|
123
|
+
assert.equal(result.content, 'const x = 1;');
|
|
124
|
+
assert.equal(result.tool, 'Read');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('emits observation for Edit tool result', () => {
|
|
128
|
+
const parser = new ClaudeCodeParser();
|
|
129
|
+
parser.parseEvent(makeToolUseEvent('tu_edit', 'Edit', { file_path: '/src/app.js' }));
|
|
130
|
+
|
|
131
|
+
const result = parser.parseEvent(makeUserToolResult('tu_edit', 'Edit applied successfully'));
|
|
132
|
+
assert.equal(result.type, 'observation');
|
|
133
|
+
assert.equal(result.content, 'Edit applied successfully');
|
|
134
|
+
assert.equal(result.tool, 'Edit');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('emits error for failed tool result', () => {
|
|
138
|
+
const parser = new ClaudeCodeParser();
|
|
139
|
+
parser.parseEvent(makeToolUseEvent('tu_fail', 'Bash', { command: 'cat /nonexistent' }));
|
|
140
|
+
|
|
141
|
+
const result = parser.parseEvent(makeUserToolResult('tu_fail', 'cat: /nonexistent: No such file or directory', true));
|
|
142
|
+
assert.equal(result.type, 'error');
|
|
143
|
+
assert.equal(result.is_error, true);
|
|
144
|
+
assert.equal(result.tool, 'Bash');
|
|
145
|
+
assert.match(result.content, /No such file or directory/);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('handles array content in tool_result blocks', () => {
|
|
149
|
+
const parser = new ClaudeCodeParser();
|
|
150
|
+
parser.parseEvent(makeToolUseEvent('tu_arr', 'Grep', { pattern: 'TODO' }));
|
|
151
|
+
|
|
152
|
+
const event = {
|
|
153
|
+
type: 'user',
|
|
154
|
+
message: {
|
|
155
|
+
content: [{
|
|
156
|
+
type: 'tool_result',
|
|
157
|
+
tool_use_id: 'tu_arr',
|
|
158
|
+
content: [{ type: 'text', text: 'src/a.js:1:TODO fix' }, { type: 'text', text: 'src/b.js:5:TODO refactor' }],
|
|
159
|
+
is_error: false,
|
|
160
|
+
}],
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
const result = parser.parseEvent(event);
|
|
164
|
+
assert.equal(result.type, 'observation');
|
|
165
|
+
assert.equal(result.content, 'src/a.js:1:TODO fix\nsrc/b.js:5:TODO refactor');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('handles multiple tool_result blocks in one user event', () => {
|
|
169
|
+
const parser = new ClaudeCodeParser();
|
|
170
|
+
parser.parseEvent(makeToolUseEvent('tu_m1', 'Read', { file_path: '/a.js' }));
|
|
171
|
+
parser.parseEvent(makeToolUseEvent('tu_m2', 'Read', { file_path: '/b.js' }));
|
|
172
|
+
|
|
173
|
+
const event = {
|
|
174
|
+
type: 'user',
|
|
175
|
+
message: {
|
|
176
|
+
content: [
|
|
177
|
+
{ type: 'tool_result', tool_use_id: 'tu_m1', content: 'content of a', is_error: false },
|
|
178
|
+
{ type: 'tool_result', tool_use_id: 'tu_m2', content: 'content of b', is_error: false },
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
const results = parser.parseEvent(event);
|
|
183
|
+
assert.ok(Array.isArray(results));
|
|
184
|
+
assert.equal(results.length, 2);
|
|
185
|
+
assert.equal(results[0].type, 'observation');
|
|
186
|
+
assert.equal(results[0].tool, 'Read');
|
|
187
|
+
assert.equal(results[1].type, 'observation');
|
|
188
|
+
assert.equal(results[1].tool, 'Read');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('returns null for user event without tool_result blocks', () => {
|
|
192
|
+
const parser = new ClaudeCodeParser();
|
|
193
|
+
const result = parser.parseEvent({
|
|
194
|
+
type: 'user',
|
|
195
|
+
message: { content: [{ type: 'text', text: 'user said something' }] },
|
|
196
|
+
});
|
|
197
|
+
assert.equal(result, null);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('returns null for user event with non-array content', () => {
|
|
201
|
+
const parser = new ClaudeCodeParser();
|
|
202
|
+
assert.equal(parser.parseEvent({ type: 'user', message: { content: 'just text' } }), null);
|
|
203
|
+
assert.equal(parser.parseEvent({ type: 'user', message: {} }), null);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('cleans up _pendingToolUse after matching result', () => {
|
|
207
|
+
const parser = new ClaudeCodeParser();
|
|
208
|
+
parser.parseEvent(makeToolUseEvent('tu_clean', 'Bash', { command: 'echo hi' }));
|
|
209
|
+
assert.equal(parser._pendingToolUse.size, 1);
|
|
210
|
+
|
|
211
|
+
parser.parseEvent(makeUserToolResult('tu_clean', 'hi'));
|
|
212
|
+
assert.equal(parser._pendingToolUse.size, 0);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('handles unmatched tool_result (no prior tool_use)', () => {
|
|
216
|
+
const parser = new ClaudeCodeParser();
|
|
217
|
+
const result = parser.parseEvent(makeUserToolResult('tu_orphan', 'some output'));
|
|
218
|
+
assert.equal(result.type, 'observation');
|
|
219
|
+
assert.equal(result.tool, undefined);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('observation truncation', () => {
|
|
224
|
+
it('truncates observation exceeding token limit', () => {
|
|
225
|
+
const parser = new ClaudeCodeParser();
|
|
226
|
+
parser.parseEvent(makeToolUseEvent('tu_long', 'Bash', { command: 'cat bigfile' }));
|
|
227
|
+
|
|
228
|
+
const charLimit = OBSERVATION_TOKEN_LIMIT * 4;
|
|
229
|
+
const longContent = 'x'.repeat(charLimit + 1000);
|
|
230
|
+
|
|
231
|
+
const result = parser.parseEvent(makeUserToolResult('tu_long', longContent));
|
|
232
|
+
assert.equal(result.type, 'observation');
|
|
233
|
+
assert.equal(result.truncated, true);
|
|
234
|
+
assert.ok(result.original_token_count > OBSERVATION_TOKEN_LIMIT);
|
|
235
|
+
assert.match(result.content, /\[TRUNCATED/);
|
|
236
|
+
assert.ok(result.content.length <= charLimit + 100);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('does not truncate short observation content', () => {
|
|
240
|
+
const parser = new ClaudeCodeParser();
|
|
241
|
+
parser.parseEvent(makeToolUseEvent('tu_short', 'Bash', { command: 'echo hi' }));
|
|
242
|
+
|
|
243
|
+
const shortContent = 'line 1\nline 2\nline 3';
|
|
244
|
+
const result = parser.parseEvent(makeUserToolResult('tu_short', shortContent));
|
|
245
|
+
assert.equal(result.content, shortContent);
|
|
246
|
+
assert.equal(result.truncated, false);
|
|
247
|
+
assert.equal(result.original_token_count, Math.ceil(shortContent.length / 4));
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('does not truncate errors', () => {
|
|
251
|
+
const parser = new ClaudeCodeParser();
|
|
252
|
+
parser.parseEvent(makeToolUseEvent('tu_err_long', 'Bash', { command: 'bad' }));
|
|
253
|
+
|
|
254
|
+
const longError = 'x'.repeat(20000);
|
|
255
|
+
const result = parser.parseEvent(makeUserToolResult('tu_err_long', longError, true));
|
|
256
|
+
assert.equal(result.type, 'error');
|
|
257
|
+
assert.equal(result.content, longError);
|
|
258
|
+
assert.equal(result.truncated, undefined);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('scrubber integration', () => {
|
|
263
|
+
it('observation content is scrubber-compatible (string format)', () => {
|
|
264
|
+
const parser = new ClaudeCodeParser();
|
|
265
|
+
parser.parseEvent(makeToolUseEvent('tu_scrub', 'Bash', { command: 'env' }));
|
|
266
|
+
|
|
267
|
+
const result = parser.parseEvent(makeUserToolResult('tu_scrub', 'API_KEY=sk_test_abc123def456ghi789'));
|
|
268
|
+
assert.equal(typeof result.content, 'string');
|
|
269
|
+
|
|
270
|
+
const scrubber = new PIIScrubber();
|
|
271
|
+
const scrubbed = scrubber.scrub(result.content);
|
|
272
|
+
assert.match(scrubbed, /\[API_KEY\]/);
|
|
273
|
+
assert.ok(!scrubbed.includes('sk_test_abc123def456ghi789'));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('scrubber handles observation with email addresses', () => {
|
|
277
|
+
const parser = new ClaudeCodeParser();
|
|
278
|
+
parser.parseEvent(makeToolUseEvent('tu_email', 'Bash', { command: 'git log' }));
|
|
279
|
+
|
|
280
|
+
const result = parser.parseEvent(makeUserToolResult('tu_email', 'Author: user@example.com'));
|
|
281
|
+
const scrubber = new PIIScrubber();
|
|
282
|
+
const scrubbed = scrubber.scrub(result.content);
|
|
283
|
+
assert.match(scrubbed, /\[EMAIL\]/);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('full ReAct loop', () => {
|
|
288
|
+
it('produces thought → action → observation → thought sequence', () => {
|
|
289
|
+
const parser = new ClaudeCodeParser();
|
|
290
|
+
const steps = [];
|
|
291
|
+
|
|
292
|
+
// Thought + action (assistant event with text and tool_use)
|
|
293
|
+
const assistantEvent = {
|
|
294
|
+
type: 'assistant',
|
|
295
|
+
message: {
|
|
296
|
+
content: [
|
|
297
|
+
{ type: 'text', text: 'Let me check the file' },
|
|
298
|
+
{ type: 'tool_use', id: 'tu_react', name: 'Read', input: { file_path: '/src/app.js' } },
|
|
299
|
+
],
|
|
300
|
+
usage: { input_tokens: 100, output_tokens: 50 },
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
const parsed1 = parser.parseEvent(assistantEvent);
|
|
304
|
+
const events1 = Array.isArray(parsed1) ? parsed1 : [parsed1];
|
|
305
|
+
steps.push(...events1);
|
|
306
|
+
|
|
307
|
+
// Observation (user event with tool_result)
|
|
308
|
+
const parsed2 = parser.parseEvent(makeUserToolResult('tu_react', 'const app = express();'));
|
|
309
|
+
const events2 = Array.isArray(parsed2) ? parsed2 : [parsed2];
|
|
310
|
+
steps.push(...events2);
|
|
311
|
+
|
|
312
|
+
// Next thought (assistant event)
|
|
313
|
+
const parsed3 = parser.parseEvent({
|
|
314
|
+
type: 'assistant',
|
|
315
|
+
message: {
|
|
316
|
+
content: [{ type: 'text', text: 'I see the Express setup, now let me fix it' }],
|
|
317
|
+
usage: { input_tokens: 200, output_tokens: 60 },
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
steps.push(parsed3);
|
|
321
|
+
|
|
322
|
+
assert.equal(steps.length, 4);
|
|
323
|
+
assert.equal(steps[0].type, 'thought');
|
|
324
|
+
assert.equal(steps[1].type, 'action');
|
|
325
|
+
assert.equal(steps[2].type, 'observation');
|
|
326
|
+
assert.equal(steps[3].type, 'thought');
|
|
327
|
+
});
|
|
328
|
+
});
|
|
119
329
|
});
|
|
@@ -44,6 +44,8 @@ describe('CodexParser', () => {
|
|
|
44
44
|
});
|
|
45
45
|
assert.equal(result.type, 'observation');
|
|
46
46
|
assert.equal(result.content, 'file1\nfile2');
|
|
47
|
+
assert.equal(result.truncated, false);
|
|
48
|
+
assert.equal(typeof result.original_token_count, 'number');
|
|
47
49
|
});
|
|
48
50
|
|
|
49
51
|
it('parses item.completed with error exit code as error', () => {
|
|
@@ -57,6 +57,8 @@ describe('GeminiParser', () => {
|
|
|
57
57
|
});
|
|
58
58
|
assert.equal(result.type, 'observation');
|
|
59
59
|
assert.equal(result.content, 'Found 3 matches');
|
|
60
|
+
assert.equal(result.truncated, false);
|
|
61
|
+
assert.equal(typeof result.original_token_count, 'number');
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
it('parses error event', () => {
|
|
@@ -132,4 +132,46 @@ describe('StepClassifier', () => {
|
|
|
132
132
|
assert.ok(result);
|
|
133
133
|
assert.equal(result.type, 'action');
|
|
134
134
|
});
|
|
135
|
+
|
|
136
|
+
it('preserves observation type when is_error is false despite error keywords', () => {
|
|
137
|
+
const classifier = new StepClassifier();
|
|
138
|
+
const step = { type: 'observation', content: 'Cannot find module foo', is_error: false };
|
|
139
|
+
const result = classifier.onStep(step);
|
|
140
|
+
assert.equal(result.type, 'observation');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('preserves observation type when is_error:false and content has TypeError', () => {
|
|
144
|
+
const classifier = new StepClassifier();
|
|
145
|
+
const step = { type: 'observation', content: 'TypeError: something failed', is_error: false };
|
|
146
|
+
const result = classifier.onStep(step);
|
|
147
|
+
assert.equal(result.type, 'observation');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('still reclassifies observation to error when is_error is true', () => {
|
|
151
|
+
const classifier = new StepClassifier();
|
|
152
|
+
const step = { type: 'observation', content: 'Command failed with exit code 1', is_error: true };
|
|
153
|
+
const result = classifier.onStep(step);
|
|
154
|
+
assert.equal(result.type, 'error');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('still reclassifies observation to error when is_error is undefined', () => {
|
|
158
|
+
const classifier = new StepClassifier();
|
|
159
|
+
const step = { type: 'observation', content: 'ENOENT: no such file or directory' };
|
|
160
|
+
const result = classifier.onStep(step);
|
|
161
|
+
assert.equal(result.type, 'error');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('preserves action type when is_error is false', () => {
|
|
165
|
+
const classifier = new StepClassifier();
|
|
166
|
+
const step = { type: 'action', content: 'Command failed with exit code 1', is_error: false };
|
|
167
|
+
const result = classifier.onStep(step);
|
|
168
|
+
assert.equal(result.type, 'action');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('still reclassifies action to error when is_error is not set', () => {
|
|
172
|
+
const classifier = new StepClassifier();
|
|
173
|
+
const step = { type: 'action', content: 'Command failed with exit code 1' };
|
|
174
|
+
const result = classifier.onStep(step);
|
|
175
|
+
assert.equal(result.type, 'error');
|
|
176
|
+
});
|
|
135
177
|
});
|