@mrpatronz/nexusflow 2.0.0 → 2.2.0
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/README.md +5 -1
- package/dist/agent/AcpCliAdapter.d.ts +75 -0
- package/dist/agent/AcpCliAdapter.d.ts.map +1 -0
- package/dist/agent/AcpCliAdapter.js +292 -0
- package/dist/agent/AcpCliAdapter.js.map +1 -0
- package/dist/agent/AcpCliAdapter.test.d.ts +2 -0
- package/dist/agent/AcpCliAdapter.test.d.ts.map +1 -0
- package/dist/agent/AcpCliAdapter.test.js +235 -0
- package/dist/agent/AcpCliAdapter.test.js.map +1 -0
- package/dist/agent/ClaudeCliAdapter.d.ts +40 -5
- package/dist/agent/ClaudeCliAdapter.d.ts.map +1 -1
- package/dist/agent/ClaudeCliAdapter.js +212 -15
- package/dist/agent/ClaudeCliAdapter.js.map +1 -1
- package/dist/agent/ClaudeCliAdapter.test.d.ts +2 -0
- package/dist/agent/ClaudeCliAdapter.test.d.ts.map +1 -0
- package/dist/agent/ClaudeCliAdapter.test.js +320 -0
- package/dist/agent/ClaudeCliAdapter.test.js.map +1 -0
- package/dist/agent/CliAdapterBase.d.ts +29 -12
- package/dist/agent/CliAdapterBase.d.ts.map +1 -1
- package/dist/agent/CliAdapterBase.js +76 -36
- package/dist/agent/CliAdapterBase.js.map +1 -1
- package/dist/agent/CodexCliAdapter.d.ts +51 -0
- package/dist/agent/CodexCliAdapter.d.ts.map +1 -0
- package/dist/agent/CodexCliAdapter.js +226 -0
- package/dist/agent/CodexCliAdapter.js.map +1 -0
- package/dist/agent/CodexCliAdapter.test.d.ts +2 -0
- package/dist/agent/CodexCliAdapter.test.d.ts.map +1 -0
- package/dist/agent/CodexCliAdapter.test.js +316 -0
- package/dist/agent/CodexCliAdapter.test.js.map +1 -0
- package/dist/agent/CopilotAcpAdapter.d.ts +7 -0
- package/dist/agent/CopilotAcpAdapter.d.ts.map +1 -0
- package/dist/agent/CopilotAcpAdapter.js +29 -0
- package/dist/agent/CopilotAcpAdapter.js.map +1 -0
- package/dist/agent/ProviderRegistry.d.ts +39 -3
- package/dist/agent/ProviderRegistry.d.ts.map +1 -1
- package/dist/agent/ProviderRegistry.js +43 -8
- package/dist/agent/ProviderRegistry.js.map +1 -1
- package/dist/agent/ProviderRegistry.test.js +52 -0
- package/dist/agent/ProviderRegistry.test.js.map +1 -1
- package/dist/agent/adapters.d.ts +7 -5
- package/dist/agent/adapters.d.ts.map +1 -1
- package/dist/agent/adapters.js +92 -17
- package/dist/agent/adapters.js.map +1 -1
- package/dist/agent/adapters.test.js +22 -0
- package/dist/agent/adapters.test.js.map +1 -1
- package/dist/agent/cliAvailability.d.ts +51 -13
- package/dist/agent/cliAvailability.d.ts.map +1 -1
- package/dist/agent/cliAvailability.js +163 -44
- package/dist/agent/cliAvailability.js.map +1 -1
- package/dist/agent/cliAvailability.test.js +140 -53
- package/dist/agent/cliAvailability.test.js.map +1 -1
- package/dist/agent/session.d.ts +2 -1
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +15 -4
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/session.test.js +29 -8
- package/dist/agent/session.test.js.map +1 -1
- package/dist/core/workspace.d.ts +6 -0
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +17 -7
- package/dist/core/workspace.js.map +1 -1
- package/dist/core/workspace.test.js +12 -1
- package/dist/core/workspace.test.js.map +1 -1
- package/dist/gui/assets/DashboardPage-CUspfCap.js +1 -0
- package/dist/gui/assets/GettingStartedPage-BqtgARZW.js +1 -0
- package/dist/gui/assets/ProjectsPage-UBoQxI2G.js +1 -0
- package/dist/gui/assets/{ScaffoldRepoInline-D-2ZeuDK.js → ScaffoldRepoInline-DqoourJe.js} +1 -1
- package/dist/gui/assets/SettingsPage-BZl24tqD.js +1 -0
- package/dist/gui/assets/StartWorkPage-BGNFdBfj.js +1 -0
- package/dist/gui/assets/{StrategiesPage-Cizm0GWk.js → StrategiesPage-D0Y6A4Tw.js} +2 -2
- package/dist/gui/assets/WorkspaceLauncher-Cxv0O_wt.js +1 -0
- package/dist/gui/assets/WorkspacesPage-Cvb1rOeh.js +6 -0
- package/dist/gui/assets/index-CllPiiFL.js +52 -0
- package/dist/gui/assets/index-DGCBhMAn.css +2 -0
- package/dist/gui/index.html +2 -2
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +267 -39
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +324 -18
- package/dist/server.test.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/detect-editors.d.ts.map +1 -1
- package/dist/utils/detect-editors.js +6 -0
- package/dist/utils/detect-editors.js.map +1 -1
- package/dist/utils/detect-editors.test.js +12 -0
- package/dist/utils/detect-editors.test.js.map +1 -1
- package/dist/utils/session-finder.d.ts +14 -0
- package/dist/utils/session-finder.d.ts.map +1 -1
- package/dist/utils/session-finder.js +127 -10
- package/dist/utils/session-finder.js.map +1 -1
- package/dist/utils/session-finder.test.js +104 -2
- package/dist/utils/session-finder.test.js.map +1 -1
- package/dist/utils/workspace-launch.d.ts +37 -0
- package/dist/utils/workspace-launch.d.ts.map +1 -0
- package/dist/utils/workspace-launch.js +200 -0
- package/dist/utils/workspace-launch.js.map +1 -0
- package/dist/utils/workspace-launch.test.d.ts +2 -0
- package/dist/utils/workspace-launch.test.d.ts.map +1 -0
- package/dist/utils/workspace-launch.test.js +94 -0
- package/dist/utils/workspace-launch.test.js.map +1 -0
- package/package.json +5 -3
- package/dist/gui/assets/DashboardPage-HNC3-OP0.js +0 -1
- package/dist/gui/assets/GettingStartedPage-Dguoya_Z.js +0 -1
- package/dist/gui/assets/ProjectsPage-DVR4j4sw.js +0 -1
- package/dist/gui/assets/SettingsPage-Bauo77q3.js +0 -1
- package/dist/gui/assets/StartWorkPage-DvuFEa5Y.js +0 -1
- package/dist/gui/assets/WorkspacesPage-DYOVfZqy.js +0 -6
- package/dist/gui/assets/external-link-BtYhMxno.js +0 -1
- package/dist/gui/assets/index-BqeoAIB1.js +0 -52
- package/dist/gui/assets/index-CsA2SueF.css +0 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { ClaudeCliAdapter, ClaudeJsonlDecoder, decodeClaudeLine, } from './ClaudeCliAdapter.js';
|
|
4
|
+
const SESSION_ID = '123e4567-e89b-42d3-a456-426614174000';
|
|
5
|
+
const OTHER_SESSION_ID = '123e4567-e89b-42d3-a456-426614174001';
|
|
6
|
+
const STREAM_ARGS = [
|
|
7
|
+
'--output-format', 'stream-json',
|
|
8
|
+
'--verbose',
|
|
9
|
+
'--include-partial-messages',
|
|
10
|
+
];
|
|
11
|
+
class FakeChild extends EventEmitter {
|
|
12
|
+
stdin = { write: vi.fn(), end: vi.fn() };
|
|
13
|
+
stdout = new EventEmitter();
|
|
14
|
+
stderr = new EventEmitter();
|
|
15
|
+
killed = false;
|
|
16
|
+
exitCode = null;
|
|
17
|
+
pid;
|
|
18
|
+
}
|
|
19
|
+
class TestClaudeCliAdapter extends ClaudeCliAdapter {
|
|
20
|
+
processes = [];
|
|
21
|
+
spawnProcess(args) {
|
|
22
|
+
const child = new FakeChild();
|
|
23
|
+
this.processes.push({ args, child });
|
|
24
|
+
return child;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function initRecord(sessionId = SESSION_ID) {
|
|
28
|
+
return `${JSON.stringify({ type: 'system', subtype: 'init', session_id: sessionId })}\n`;
|
|
29
|
+
}
|
|
30
|
+
function successRecord(sessionId = SESSION_ID, result = 'Done') {
|
|
31
|
+
return `${JSON.stringify({
|
|
32
|
+
type: 'result',
|
|
33
|
+
subtype: 'success',
|
|
34
|
+
is_error: false,
|
|
35
|
+
session_id: sessionId,
|
|
36
|
+
result,
|
|
37
|
+
})}\n`;
|
|
38
|
+
}
|
|
39
|
+
describe('Claude stream-json decoding', () => {
|
|
40
|
+
it('decodes chunked session, text delta, retry, and completion records', () => {
|
|
41
|
+
const decoder = new ClaudeJsonlDecoder();
|
|
42
|
+
const stream = [
|
|
43
|
+
initRecord(),
|
|
44
|
+
`${JSON.stringify({
|
|
45
|
+
type: 'stream_event',
|
|
46
|
+
event: { type: 'content_block_delta', delta: { type: 'text_delta', text: 'Hello' } },
|
|
47
|
+
})}\n`,
|
|
48
|
+
`${JSON.stringify({ type: 'system', subtype: 'api_retry', attempt: 2, max_retries: 5 })}\n`,
|
|
49
|
+
successRecord(),
|
|
50
|
+
].join('');
|
|
51
|
+
expect(decoder.push(stream.slice(0, 29))).toEqual([]);
|
|
52
|
+
expect(decoder.push(stream.slice(29))).toEqual([
|
|
53
|
+
{ type: 'session', id: SESSION_ID },
|
|
54
|
+
{ type: 'message', text: 'Hello' },
|
|
55
|
+
{ type: 'system', message: 'Claude is retrying the request (attempt 2 of 5).' },
|
|
56
|
+
{ type: 'session', id: SESSION_ID },
|
|
57
|
+
{ type: 'complete', text: 'Done' },
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
60
|
+
it('flushes an unterminated record and rejects malformed structured output', () => {
|
|
61
|
+
const decoder = new ClaudeJsonlDecoder();
|
|
62
|
+
decoder.push(JSON.stringify({
|
|
63
|
+
type: 'stream_event',
|
|
64
|
+
event: { type: 'content_block_delta', delta: { type: 'text_delta', text: 'Final' } },
|
|
65
|
+
}));
|
|
66
|
+
expect(decoder.finish()).toEqual([{ type: 'message', text: 'Final' }]);
|
|
67
|
+
expect(decodeClaudeLine('{not json')).toEqual([{
|
|
68
|
+
type: 'error',
|
|
69
|
+
message: 'Claude emitted malformed structured output.',
|
|
70
|
+
source: 'protocol',
|
|
71
|
+
}]);
|
|
72
|
+
expect(decodeClaudeLine('[]')).toEqual([{
|
|
73
|
+
type: 'error',
|
|
74
|
+
message: 'Claude emitted an invalid structured record.',
|
|
75
|
+
source: 'protocol',
|
|
76
|
+
}]);
|
|
77
|
+
});
|
|
78
|
+
it('fails closed when one structured record exceeds the buffer limit', () => {
|
|
79
|
+
const decoder = new ClaudeJsonlDecoder(32);
|
|
80
|
+
expect(decoder.push(`{"type":"unknown","padding":"${'x'.repeat(32)}`)).toEqual([{
|
|
81
|
+
type: 'error',
|
|
82
|
+
message: 'Claude structured output exceeded the supported record size.',
|
|
83
|
+
source: 'protocol',
|
|
84
|
+
}]);
|
|
85
|
+
expect(decoder.push(`"}\n${successRecord()}`)).toEqual([]);
|
|
86
|
+
expect(decoder.finish()).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
it('maps structured failures without exposing arbitrary object fields', () => {
|
|
89
|
+
expect(decodeClaudeLine(JSON.stringify({
|
|
90
|
+
type: 'result',
|
|
91
|
+
subtype: 'error_during_execution',
|
|
92
|
+
session_id: SESSION_ID,
|
|
93
|
+
errors: ['Permission was denied'],
|
|
94
|
+
secret: 'do-not-render',
|
|
95
|
+
}))).toEqual([
|
|
96
|
+
{ type: 'session', id: SESSION_ID },
|
|
97
|
+
{ type: 'error', message: 'Permission was denied', source: 'provider' },
|
|
98
|
+
]);
|
|
99
|
+
expect(decodeClaudeLine(JSON.stringify({
|
|
100
|
+
type: 'result',
|
|
101
|
+
subtype: 'attacker-controlled metadata',
|
|
102
|
+
session_id: SESSION_ID,
|
|
103
|
+
}))).toEqual([
|
|
104
|
+
{ type: 'session', id: SESSION_ID },
|
|
105
|
+
{ type: 'error', message: 'Claude could not complete the turn.', source: 'provider' },
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('ClaudeCliAdapter acknowledged session lifecycle', () => {
|
|
110
|
+
it('retries a failed new turn with --session-id until Claude acknowledges it', async () => {
|
|
111
|
+
const adapter = new TestClaudeCliAdapter();
|
|
112
|
+
const errors = [];
|
|
113
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
114
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
115
|
+
await adapter.send('Try to start');
|
|
116
|
+
adapter.processes[0].child.emit('close', 1);
|
|
117
|
+
await adapter.send('Retry the start');
|
|
118
|
+
expect(adapter.processes.map(process => process.args)).toEqual([
|
|
119
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--session-id', SESSION_ID],
|
|
120
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--session-id', SESSION_ID],
|
|
121
|
+
]);
|
|
122
|
+
expect(errors).toEqual(['claude CLI exited with code 1']);
|
|
123
|
+
});
|
|
124
|
+
it('does not retry a failed explicit resume as a new session', async () => {
|
|
125
|
+
const adapter = new TestClaudeCliAdapter();
|
|
126
|
+
const errors = [];
|
|
127
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
128
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: true });
|
|
129
|
+
await adapter.send('Continue from the saved context');
|
|
130
|
+
adapter.processes[0].child.emit('close', 1);
|
|
131
|
+
await adapter.send('Retry the exact resume');
|
|
132
|
+
expect(adapter.processes.map(process => process.args)).toEqual([
|
|
133
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--resume', SESSION_ID],
|
|
134
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--resume', SESSION_ID],
|
|
135
|
+
]);
|
|
136
|
+
expect(errors).toEqual(['claude CLI exited with code 1']);
|
|
137
|
+
});
|
|
138
|
+
it('acknowledges the requested session, streams once, then resumes with the next profile', async () => {
|
|
139
|
+
const adapter = new TestClaudeCliAdapter();
|
|
140
|
+
const sessions = [];
|
|
141
|
+
const data = [];
|
|
142
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
143
|
+
adapter.on('data', (text) => data.push(text));
|
|
144
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
145
|
+
await adapter.send('Inspect first', 'review');
|
|
146
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(initRecord()));
|
|
147
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(`${JSON.stringify({
|
|
148
|
+
type: 'stream_event',
|
|
149
|
+
event: { type: 'content_block_delta', delta: { type: 'text_delta', text: 'Done' } },
|
|
150
|
+
})}\n${successRecord()}`));
|
|
151
|
+
adapter.processes[0].child.emit('close', 0);
|
|
152
|
+
await adapter.send('Now edit', 'workspace-write');
|
|
153
|
+
expect(adapter.processes.map(process => process.args)).toEqual([
|
|
154
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--session-id', SESSION_ID],
|
|
155
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'acceptEdits', '--resume', SESSION_ID],
|
|
156
|
+
]);
|
|
157
|
+
expect(sessions).toEqual([SESSION_ID]);
|
|
158
|
+
expect(data).toEqual(['Done']);
|
|
159
|
+
});
|
|
160
|
+
it('resumes the acknowledged session after a structured provider failure', async () => {
|
|
161
|
+
const adapter = new TestClaudeCliAdapter();
|
|
162
|
+
const errors = [];
|
|
163
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
164
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
165
|
+
await adapter.send('Start the session');
|
|
166
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(initRecord() + `${JSON.stringify({
|
|
167
|
+
type: 'result',
|
|
168
|
+
subtype: 'error_during_execution',
|
|
169
|
+
session_id: SESSION_ID,
|
|
170
|
+
errors: ['Temporary provider failure'],
|
|
171
|
+
})}\n`));
|
|
172
|
+
adapter.processes[0].child.emit('close', 1);
|
|
173
|
+
await adapter.send('Resume after failure');
|
|
174
|
+
expect(errors).toEqual(['Temporary provider failure']);
|
|
175
|
+
expect(adapter.processes[1].args).toEqual([
|
|
176
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--resume', SESSION_ID,
|
|
177
|
+
]);
|
|
178
|
+
});
|
|
179
|
+
it('retains the acknowledged id when a later result returns a conflicting identity', async () => {
|
|
180
|
+
const adapter = new TestClaudeCliAdapter();
|
|
181
|
+
const sessions = [];
|
|
182
|
+
const errors = [];
|
|
183
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
184
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
185
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
186
|
+
await adapter.send('Start the session');
|
|
187
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(initRecord() + successRecord(OTHER_SESSION_ID)));
|
|
188
|
+
adapter.processes[0].child.emit('close', 0);
|
|
189
|
+
await adapter.send('Resume the acknowledged session');
|
|
190
|
+
expect(sessions).toEqual([SESSION_ID]);
|
|
191
|
+
expect(errors).toEqual([
|
|
192
|
+
'Claude returned a conflicting session identity. The unexpected identity was rejected; the acknowledged session remains resumable.',
|
|
193
|
+
]);
|
|
194
|
+
expect(adapter.processes[1].args).toEqual([
|
|
195
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--resume', SESSION_ID,
|
|
196
|
+
]);
|
|
197
|
+
});
|
|
198
|
+
it('accepts canonicalized casing while retaining the requested session id', async () => {
|
|
199
|
+
const adapter = new TestClaudeCliAdapter();
|
|
200
|
+
const upperSessionId = SESSION_ID.toUpperCase();
|
|
201
|
+
const sessions = [];
|
|
202
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
203
|
+
await adapter.start('C:\\workspace', { id: upperSessionId, resume: false });
|
|
204
|
+
await adapter.send('Start the session');
|
|
205
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(initRecord() + successRecord()));
|
|
206
|
+
adapter.processes[0].child.emit('close', 0);
|
|
207
|
+
await adapter.send('Resume the session');
|
|
208
|
+
expect(sessions).toEqual([upperSessionId]);
|
|
209
|
+
expect(adapter.processes[1].args).toEqual([
|
|
210
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--resume', upperSessionId,
|
|
211
|
+
]);
|
|
212
|
+
});
|
|
213
|
+
it('rejects a mismatched provider session and keeps the new session retryable', async () => {
|
|
214
|
+
const adapter = new TestClaudeCliAdapter();
|
|
215
|
+
const sessions = [];
|
|
216
|
+
const errors = [];
|
|
217
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
218
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
219
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
220
|
+
await adapter.send('Start safely');
|
|
221
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(initRecord(OTHER_SESSION_ID) + successRecord(OTHER_SESSION_ID)));
|
|
222
|
+
adapter.processes[0].child.emit('close', 0);
|
|
223
|
+
await adapter.send('Retry safely');
|
|
224
|
+
expect(sessions).toEqual([]);
|
|
225
|
+
expect(errors).toEqual([
|
|
226
|
+
'Claude returned an unexpected session identity. The turn was not marked resumable.',
|
|
227
|
+
]);
|
|
228
|
+
expect(adapter.processes[1].args).toEqual([
|
|
229
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--session-id', SESSION_ID,
|
|
230
|
+
]);
|
|
231
|
+
});
|
|
232
|
+
it('does not accept a later id after a malformed init record', async () => {
|
|
233
|
+
const adapter = new TestClaudeCliAdapter();
|
|
234
|
+
const sessions = [];
|
|
235
|
+
const errors = [];
|
|
236
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
237
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
238
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
239
|
+
await adapter.send('Start safely');
|
|
240
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(`${JSON.stringify({ type: 'system', subtype: 'init' })}\n${successRecord()}`));
|
|
241
|
+
adapter.processes[0].child.emit('close', 0);
|
|
242
|
+
await adapter.send('Retry safely');
|
|
243
|
+
expect(sessions).toEqual([]);
|
|
244
|
+
expect(errors).toEqual([
|
|
245
|
+
'Claude started without a valid session identity. The turn was not marked resumable.',
|
|
246
|
+
]);
|
|
247
|
+
expect(adapter.processes[1].args).toEqual([
|
|
248
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--session-id', SESSION_ID,
|
|
249
|
+
]);
|
|
250
|
+
});
|
|
251
|
+
it('keeps an acknowledged session resumable after later malformed output', async () => {
|
|
252
|
+
const adapter = new TestClaudeCliAdapter();
|
|
253
|
+
const sessions = [];
|
|
254
|
+
const errors = [];
|
|
255
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
256
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
257
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
258
|
+
await adapter.send('Start safely');
|
|
259
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(`${initRecord()}{not json}\n`));
|
|
260
|
+
adapter.processes[0].child.emit('close', 1);
|
|
261
|
+
await adapter.send('Resume the acknowledged session');
|
|
262
|
+
expect(sessions).toEqual([SESSION_ID]);
|
|
263
|
+
expect(errors).toEqual([
|
|
264
|
+
'Claude emitted malformed structured output. The acknowledged session remains resumable.',
|
|
265
|
+
]);
|
|
266
|
+
expect(adapter.processes[1].args).toEqual([
|
|
267
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--resume', SESSION_ID,
|
|
268
|
+
]);
|
|
269
|
+
});
|
|
270
|
+
it('does not accept a valid result after malformed JSONL output', async () => {
|
|
271
|
+
const adapter = new TestClaudeCliAdapter();
|
|
272
|
+
const sessions = [];
|
|
273
|
+
const errors = [];
|
|
274
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
275
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
276
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
277
|
+
await adapter.send('Start safely');
|
|
278
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(`{not json}\n${successRecord()}`));
|
|
279
|
+
await adapter.send('Do not overlap the failed process');
|
|
280
|
+
expect(errors).toEqual([]);
|
|
281
|
+
expect(adapter.processes).toHaveLength(1);
|
|
282
|
+
adapter.processes[0].child.emit('close', 0);
|
|
283
|
+
await adapter.send('Retry safely');
|
|
284
|
+
expect(sessions).toEqual([]);
|
|
285
|
+
expect(errors).toEqual([
|
|
286
|
+
'Claude emitted malformed structured output. The turn was not marked resumable.',
|
|
287
|
+
]);
|
|
288
|
+
expect(adapter.processes[1].args).toEqual([
|
|
289
|
+
'-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--session-id', SESSION_ID,
|
|
290
|
+
]);
|
|
291
|
+
});
|
|
292
|
+
it('keeps the legacy auto-assigned session continuation path working', async () => {
|
|
293
|
+
const adapter = new TestClaudeCliAdapter();
|
|
294
|
+
const sessions = [];
|
|
295
|
+
adapter.on('session', (id) => sessions.push(id));
|
|
296
|
+
await adapter.start('C:\\workspace');
|
|
297
|
+
await adapter.send('Start legacy session');
|
|
298
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from(initRecord() + successRecord()));
|
|
299
|
+
adapter.processes[0].child.emit('close', 0);
|
|
300
|
+
await adapter.send('Continue legacy session');
|
|
301
|
+
expect(sessions).toEqual([SESSION_ID]);
|
|
302
|
+
expect(adapter.processes.map(process => process.args)).toEqual([
|
|
303
|
+
['-p', ...STREAM_ARGS, '--permission-mode', 'plan'],
|
|
304
|
+
['-c', '-p', ...STREAM_ARGS, '--permission-mode', 'plan'],
|
|
305
|
+
]);
|
|
306
|
+
});
|
|
307
|
+
it('surfaces a successful process with no recognized structured result', async () => {
|
|
308
|
+
const adapter = new TestClaudeCliAdapter();
|
|
309
|
+
const errors = [];
|
|
310
|
+
adapter.on('error', (error) => errors.push(error.message));
|
|
311
|
+
await adapter.start('C:\\workspace', { id: SESSION_ID, resume: false });
|
|
312
|
+
await adapter.send('Inspect');
|
|
313
|
+
adapter.processes[0].child.stdout.emit('data', Buffer.from('{"type":"assistant"}\n'));
|
|
314
|
+
adapter.processes[0].child.emit('close', 0);
|
|
315
|
+
expect(errors).toEqual([
|
|
316
|
+
expect.stringMatching(/without a recognized result/i),
|
|
317
|
+
]);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
//# sourceMappingURL=ClaudeCliAdapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeCliAdapter.test.js","sourceRoot":"","sources":["../../src/agent/ClaudeCliAdapter.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { type ChildProcess } from 'node:child_process';
|
|
3
|
+
import type { AgentExecutionProfile } from './ProviderRegistry.js';
|
|
3
4
|
/**
|
|
4
|
-
* Shared lifecycle for CLI-backed agents (claude, agy). Each turn spawns the
|
|
5
|
+
* Shared lifecycle for CLI-backed agents (claude, codex, agy). Each turn spawns the
|
|
5
6
|
* binary once in print mode, streams stdout as `data`, and emits `idle` when
|
|
6
7
|
* the turn ends. Subclasses supply the binary, the per-turn args, how the
|
|
7
|
-
* prompt is delivered
|
|
8
|
-
* first attempt of a turn fails with no output.
|
|
8
|
+
* prompt is delivered.
|
|
9
9
|
*
|
|
10
10
|
* Prompt delivery differs per CLI: `claude` is an npm `.cmd` shim that needs
|
|
11
11
|
* `shell: true` on Windows, and under a shell an argv prompt would be split on
|
|
@@ -19,6 +19,7 @@ export declare abstract class CliAdapterBase extends EventEmitter {
|
|
|
19
19
|
private isProcessing;
|
|
20
20
|
private stopped;
|
|
21
21
|
private child;
|
|
22
|
+
private pendingTurnError;
|
|
22
23
|
/** Executable name, e.g. 'claude' or 'agy'. */
|
|
23
24
|
protected abstract readonly binary: string;
|
|
24
25
|
/** Short label for stderr/error diagnostics. */
|
|
@@ -27,20 +28,36 @@ export declare abstract class CliAdapterBase extends EventEmitter {
|
|
|
27
28
|
protected readonly useShell: boolean;
|
|
28
29
|
/** Pipe the prompt to stdin (true) vs. include it in argv via buildArgs (false). */
|
|
29
30
|
protected readonly promptViaStdin: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Most legacy CLI adapters treat process dispatch as enough to advance their
|
|
33
|
+
* first-turn argv. Structured adapters can opt out and call
|
|
34
|
+
* acknowledgeFirstTurn() only after the provider confirms session creation.
|
|
35
|
+
*/
|
|
36
|
+
protected readonly advanceFirstTurnOnDispatch: boolean;
|
|
30
37
|
/** Args for one print-mode turn. Receives the prompt so argv-mode CLIs can
|
|
31
38
|
* include it; stdin-mode CLIs ignore it. */
|
|
32
|
-
protected abstract buildArgs(isFirstTurn: boolean, prompt: string): string[];
|
|
39
|
+
protected abstract buildArgs(isFirstTurn: boolean, prompt: string, executionProfile?: AgentExecutionProfile): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Consume one stdout chunk. Structured-output adapters override this to
|
|
42
|
+
* decode provider events and emit only user-facing text. The return value
|
|
43
|
+
* means the chunk was handled as a meaningful result (including a structured
|
|
44
|
+
* provider error), so a later non-zero exit should not add a duplicate error.
|
|
45
|
+
*/
|
|
46
|
+
protected handleStdout(text: string): boolean;
|
|
47
|
+
/** Flush a final unterminated structured-output record, if any. */
|
|
48
|
+
protected finishStdout(_exitCode: number | null): boolean;
|
|
49
|
+
/** Mark the current provider session as created/resumable. */
|
|
50
|
+
protected acknowledgeFirstTurn(): void;
|
|
33
51
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
52
|
+
* Fail the active turn without publishing an idle/error state before its
|
|
53
|
+
* subprocess has actually settled. Protocol failures may also terminate the
|
|
54
|
+
* process so a write-capable CLI cannot outlive the UI's busy state.
|
|
36
55
|
*/
|
|
37
|
-
protected
|
|
38
|
-
/** Message emitted as a `system` event when the fallback kicks in. */
|
|
39
|
-
protected fallbackMessage: string;
|
|
40
|
-
/** Called once per turn after it settles; `succeeded` if it produced output or exited 0. */
|
|
41
|
-
protected onTurnComplete(_succeeded: boolean): void;
|
|
56
|
+
protected failCurrentTurn(error: Error, terminateProcess?: boolean): void;
|
|
42
57
|
start(cwd: string): Promise<void>;
|
|
43
|
-
send(data: string): Promise<void>;
|
|
58
|
+
send(data: string, executionProfile?: AgentExecutionProfile): Promise<void>;
|
|
59
|
+
/** Kept as a seam so lifecycle tests can exercise process failures without a real CLI. */
|
|
60
|
+
protected spawnProcess(args: string[]): ChildProcess;
|
|
44
61
|
private runTurn;
|
|
45
62
|
stop(): void;
|
|
46
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CliAdapterBase.d.ts","sourceRoot":"","sources":["../../src/agent/CliAdapterBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"CliAdapterBase.d.ts","sourceRoot":"","sources":["../../src/agent/CliAdapterBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,8BAAsB,cAAe,SAAQ,YAAY;IACvD,SAAS,CAAC,GAAG,EAAE,MAAM,CAAM;IAC3B,SAAS,CAAC,WAAW,EAAE,OAAO,CAAQ;IACtC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,gBAAgB,CAAsB;IAE9C,+CAA+C;IAC/C,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAC3C,gDAAgD;IAChD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAC1C,0EAA0E;IAC1E,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAS;IAC7C,oFAAoF;IACpF,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAS;IACnD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,0BAA0B,EAAE,OAAO,CAAQ;IAE9D;iDAC6C;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,CAC1B,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,gBAAgB,CAAC,EAAE,qBAAqB,GACvC,MAAM,EAAE;IAEX;;;;;OAKG;IACH,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAM7C,mEAAmE;IACnE,SAAS,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;IAIzD,8DAA8D;IAC9D,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAItC;;;;OAIG;IACH,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,gBAAgB,UAAQ,GAAG,IAAI;IAK1D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxF,0FAA0F;IAC1F,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY;IAQpD,OAAO,CAAC,OAAO;IAyDR,IAAI,IAAI,IAAI;CAQpB;AAID,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,QAOlD"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
/**
|
|
4
|
-
* Shared lifecycle for CLI-backed agents (claude, agy). Each turn spawns the
|
|
4
|
+
* Shared lifecycle for CLI-backed agents (claude, codex, agy). Each turn spawns the
|
|
5
5
|
* binary once in print mode, streams stdout as `data`, and emits `idle` when
|
|
6
6
|
* the turn ends. Subclasses supply the binary, the per-turn args, how the
|
|
7
|
-
* prompt is delivered
|
|
8
|
-
* first attempt of a turn fails with no output.
|
|
7
|
+
* prompt is delivered.
|
|
9
8
|
*
|
|
10
9
|
* Prompt delivery differs per CLI: `claude` is an npm `.cmd` shim that needs
|
|
11
10
|
* `shell: true` on Windows, and under a shell an argv prompt would be split on
|
|
@@ -19,40 +18,73 @@ export class CliAdapterBase extends EventEmitter {
|
|
|
19
18
|
isProcessing = false;
|
|
20
19
|
stopped = false;
|
|
21
20
|
child = null;
|
|
21
|
+
pendingTurnError = null;
|
|
22
22
|
/** Spawn through a shell (needed to resolve a `.cmd` shim on Windows). */
|
|
23
23
|
useShell = false;
|
|
24
24
|
/** Pipe the prompt to stdin (true) vs. include it in argv via buildArgs (false). */
|
|
25
25
|
promptViaStdin = false;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
27
|
+
* Most legacy CLI adapters treat process dispatch as enough to advance their
|
|
28
|
+
* first-turn argv. Structured adapters can opt out and call
|
|
29
|
+
* acknowledgeFirstTurn() only after the provider confirms session creation.
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
advanceFirstTurnOnDispatch = true;
|
|
32
|
+
/**
|
|
33
|
+
* Consume one stdout chunk. Structured-output adapters override this to
|
|
34
|
+
* decode provider events and emit only user-facing text. The return value
|
|
35
|
+
* means the chunk was handled as a meaningful result (including a structured
|
|
36
|
+
* provider error), so a later non-zero exit should not add a duplicate error.
|
|
37
|
+
*/
|
|
38
|
+
handleStdout(text) {
|
|
39
|
+
if (!text)
|
|
40
|
+
return false;
|
|
41
|
+
this.emit('data', text);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/** Flush a final unterminated structured-output record, if any. */
|
|
45
|
+
finishStdout(_exitCode) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
/** Mark the current provider session as created/resumable. */
|
|
49
|
+
acknowledgeFirstTurn() {
|
|
50
|
+
this.isFirstTurn = false;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fail the active turn without publishing an idle/error state before its
|
|
54
|
+
* subprocess has actually settled. Protocol failures may also terminate the
|
|
55
|
+
* process so a write-capable CLI cannot outlive the UI's busy state.
|
|
56
|
+
*/
|
|
57
|
+
failCurrentTurn(error, terminateProcess = false) {
|
|
58
|
+
this.pendingTurnError ??= error;
|
|
59
|
+
if (terminateProcess)
|
|
60
|
+
killTree(this.child);
|
|
32
61
|
}
|
|
33
|
-
/** Message emitted as a `system` event when the fallback kicks in. */
|
|
34
|
-
fallbackMessage = 'Retrying…';
|
|
35
|
-
/** Called once per turn after it settles; `succeeded` if it produced output or exited 0. */
|
|
36
|
-
onTurnComplete(_succeeded) { }
|
|
37
62
|
async start(cwd) {
|
|
38
63
|
this.cwd = cwd;
|
|
39
64
|
this.isFirstTurn = true;
|
|
65
|
+
this.pendingTurnError = null;
|
|
40
66
|
}
|
|
41
|
-
async send(data) {
|
|
67
|
+
async send(data, executionProfile) {
|
|
42
68
|
if (this.isProcessing)
|
|
43
69
|
return;
|
|
44
70
|
this.isProcessing = true;
|
|
45
71
|
this.stopped = false;
|
|
46
|
-
|
|
47
|
-
this.isFirstTurn
|
|
48
|
-
this.
|
|
72
|
+
this.pendingTurnError = null;
|
|
73
|
+
const args = this.buildArgs(this.isFirstTurn, data, executionProfile);
|
|
74
|
+
if (this.advanceFirstTurnOnDispatch)
|
|
75
|
+
this.acknowledgeFirstTurn();
|
|
76
|
+
this.runTurn(args, data);
|
|
49
77
|
}
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
/** Kept as a seam so lifecycle tests can exercise process failures without a real CLI. */
|
|
79
|
+
spawnProcess(args) {
|
|
80
|
+
return spawn(this.binary, args, {
|
|
52
81
|
cwd: this.cwd,
|
|
53
82
|
shell: this.useShell,
|
|
54
83
|
env: { ...process.env, FORCE_COLOR: '0' } // Strip colors for easier parsing
|
|
55
84
|
});
|
|
85
|
+
}
|
|
86
|
+
runTurn(args, data) {
|
|
87
|
+
const cliProcess = this.spawnProcess(args);
|
|
56
88
|
this.child = cliProcess;
|
|
57
89
|
if (this.promptViaStdin) {
|
|
58
90
|
cliProcess.stdin?.write(data);
|
|
@@ -60,9 +92,12 @@ export class CliAdapterBase extends EventEmitter {
|
|
|
60
92
|
}
|
|
61
93
|
let producedOutput = false;
|
|
62
94
|
let stderrTail = '';
|
|
95
|
+
let settled = false;
|
|
96
|
+
// Let Node's StringDecoder preserve UTF-8 code points split across OS
|
|
97
|
+
// buffer boundaries before structured adapters see the text.
|
|
98
|
+
cliProcess.stdout?.setEncoding?.('utf8');
|
|
63
99
|
cliProcess.stdout?.on('data', (chunk) => {
|
|
64
|
-
producedOutput =
|
|
65
|
-
this.emit('data', chunk.toString());
|
|
100
|
+
producedOutput = this.handleStdout(typeof chunk === 'string' ? chunk : chunk.toString()) || producedOutput;
|
|
66
101
|
});
|
|
67
102
|
// CLIs print warnings and progress on stderr; only keep it for diagnostics.
|
|
68
103
|
cliProcess.stderr?.on('data', (chunk) => {
|
|
@@ -70,35 +105,40 @@ export class CliAdapterBase extends EventEmitter {
|
|
|
70
105
|
stderrTail = (stderrTail + text).slice(-2000);
|
|
71
106
|
console.warn(`[${this.label} stderr]`, text.trimEnd());
|
|
72
107
|
});
|
|
73
|
-
|
|
108
|
+
const settle = (code, startError) => {
|
|
109
|
+
if (settled)
|
|
110
|
+
return;
|
|
111
|
+
settled = true;
|
|
74
112
|
this.isProcessing = false;
|
|
75
113
|
this.child = null;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const fb = this.fallbackArgs();
|
|
83
|
-
if (fb) {
|
|
84
|
-
this.emit('system', this.fallbackMessage);
|
|
85
|
-
this.runTurn(fb, data, false);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
114
|
+
if (!startError)
|
|
115
|
+
producedOutput = this.finishStdout(code) || producedOutput;
|
|
116
|
+
const turnError = this.pendingTurnError;
|
|
117
|
+
this.pendingTurnError = null;
|
|
118
|
+
if (turnError) {
|
|
119
|
+
this.emit('error', turnError);
|
|
88
120
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!this.stopped && code !== 0 && !producedOutput) {
|
|
121
|
+
else if (startError) {
|
|
122
|
+
this.emit('error', new Error(`Failed to start ${this.label}: ${startError.message}`));
|
|
123
|
+
}
|
|
124
|
+
else if (!this.stopped && code !== 0 && !producedOutput) {
|
|
125
|
+
// A non-zero exit after an intentional stop is expected, not an error.
|
|
93
126
|
this.emit('error', new Error(`${this.label} exited with code ${code}${stderrTail ? `: ${stderrTail.trim()}` : ''}`));
|
|
94
127
|
}
|
|
95
128
|
// Turn separation is the GUI's concern — do not inject stray output here,
|
|
96
129
|
// or a failed turn would spawn an empty bubble on the client.
|
|
97
130
|
this.emit('idle');
|
|
131
|
+
};
|
|
132
|
+
cliProcess.on('error', (err) => {
|
|
133
|
+
settle(null, err);
|
|
134
|
+
});
|
|
135
|
+
cliProcess.on('close', (code) => {
|
|
136
|
+
settle(code);
|
|
98
137
|
});
|
|
99
138
|
}
|
|
100
139
|
stop() {
|
|
101
140
|
this.stopped = true;
|
|
141
|
+
this.pendingTurnError = null;
|
|
102
142
|
killTree(this.child);
|
|
103
143
|
this.child = null;
|
|
104
144
|
this.isProcessing = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CliAdapterBase.js","sourceRoot":"","sources":["../../src/agent/CliAdapterBase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"CliAdapterBase.js","sourceRoot":"","sources":["../../src/agent/CliAdapterBase.ts"],"names":[],"mappings":"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"}
|